http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/common/ISourceLocation.java ---------------------------------------------------------------------- diff --cc compiler/src/main/java/org/apache/flex/compiler/common/ISourceLocation.java index 738bdf3,0000000..25f6b6d mode 100644,000000..100644 --- a/compiler/src/main/java/org/apache/flex/compiler/common/ISourceLocation.java +++ b/compiler/src/main/java/org/apache/flex/compiler/common/ISourceLocation.java @@@ -1,75 -1,0 +1,85 @@@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.flex.compiler.common; + +/** + * This interface provides information about where something + * (a token, a tag, an attribute, a node, a definition, etc.) + * appears in source code. + * <p> + * Its primary purpose is for problem reporting. + * The start and end offsets are used to highlight + * a soruce range in the Flash Builder editor. + * The line and column numbers are used by command-line + * tools to report problems. + */ +public interface ISourceLocation +{ + /** + * Indicates an unknown or implicit starting offset, ending offset, + * line number, or column number. + */ + static final int UNKNOWN = -1; + + /** + * Gets the normalized source path. + */ + String getSourcePath(); + + /** + * Gets the local starting offset. It is zero-based. + */ + int getStart(); + + /** + * Gets the local ending offset. It is zero-based. + */ + int getEnd(); + + /** + * Gets the local line number. It is zero-based. + */ + int getLine(); + + /** + * Gets the local column number. It is zero-based. + */ + int getColumn(); + + /** ++ * Gets the local line number at the end. It is zero-based. ++ */ ++ int getEndLine(); ++ ++ /** ++ * Gets the local column number at the end. It is zero-based. ++ */ ++ int getEndColumn(); ++ ++ /** + * Gets the absolute starting offset. It is zero-based. + */ + int getAbsoluteStart(); + + /** + * Gets the absolute starting offset. It is zero-based. + */ + int getAbsoluteEnd(); +}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/common/SourceLocation.java ---------------------------------------------------------------------- diff --cc compiler/src/main/java/org/apache/flex/compiler/common/SourceLocation.java index 88998b7,0000000..0d0e1b0 mode 100644,000000..100644 --- a/compiler/src/main/java/org/apache/flex/compiler/common/SourceLocation.java +++ b/compiler/src/main/java/org/apache/flex/compiler/common/SourceLocation.java @@@ -1,361 -1,0 +1,415 @@@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.flex.compiler.common; + +import org.apache.flex.utils.FilenameNormalization; + +/** + * Common class to store file/location information across all source types + * such as AS, CSS etc + */ +public class SourceLocation implements ISourceLocation +{ + /** + * Constructor for a known source location. + */ + public SourceLocation(String sourcePath, int start, int end, int line, int column) + { + this.sourcePath = sourcePath; + this.start = start; + this.end = end; + this.line = line; + this.column = column; ++ this.endLine = UNKNOWN; ++ this.endColumn = UNKNOWN; + } + + /** + * Copy Constructor for a known source location. + */ + public SourceLocation(ISourceLocation location) + { + this(location.getSourcePath(), + location.getStart(), + location.getEnd(), + location.getLine(), + location.getColumn()); + } + + /** + * Constructor for an unknown source location. + */ + public SourceLocation() + { + this(null, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN); + } + + /** + * Source path. + */ + private String sourcePath; + + /** + * Zero-based starting offset. + * <p> + * This class does not distinguish between local and absolute offsets, + * but subclasses may. If so, they should store the absolute starting + * offset in this field and use it to compute the local starting offset. + */ + private int start; + + /** + * Zero-based ending offset. + * <p> + * This class does not distinguish between local and absolute offsets, + * but subclasses may. If so, they should store the absolute ending + * offset in this field and use it to compute the local ending offset. + */ + private int end; + + /** + * Zero-based line number. + * Corresponds to start, not end. + */ + private int line; + + /** + * Zero-based column number. + * Corresponds to start, not end. + */ + private int column; ++ ++ /** ++ * Zero-based line number that corresponds to end. ++ */ ++ private int endLine; ++ ++ /** ++ * Zero-based column number that corresponds to end. ++ */ ++ private int endColumn; + + /** + * Copies source location information from another instance + * into this instance. + */ + public final void setSourceLocation(ISourceLocation src) + { + assert src != null : "source location can't be null"; + + this.start = src.getStart(); + this.end = src.getEnd(); + this.line = src.getLine(); + this.column = src.getColumn(); ++ this.endLine = src.getEndLine(); ++ this.endColumn = src.getEndColumn(); + this.sourcePath = src.getSourcePath(); + } + + /** + * @return The local start offset + */ + @Override + public int getStart() + { + assert start >= 0 || start == UNKNOWN : "Invalid value for start: " + start; + return start; + } + + /** + * @return The absolute start offset. + */ + @Override + public int getAbsoluteStart() + { + assert start >= 0 || start == UNKNOWN : "Invalid value for start: " + start; + return start; + } + + /** + * @return The absolute end offset. + */ + @Override + public int getAbsoluteEnd() + { + assert end >= 0 || end == UNKNOWN : "Invalid value for end: " + end; + return end; + } + + /** + * Set the absolute offset where this node starts. + */ + public void setStart(int start) + { + if (start != UNKNOWN) + this.start = start; + } + + /** + * @return The local end offset. + */ + @Override + public int getEnd() + { + assert end >= 0 || end == UNKNOWN : "Invalid value for end: " + end; + return end; + } + + /** + * Set the absolute offset where this node ends. + */ + public void setEnd(int end) + { + if (end != UNKNOWN) + this.end = end; + } + + /** + * Get the line number where this node starts. + * Line numbers start at 0, not 1. + * @return The line number + */ + @Override + public int getLine() + { + assert line >= 0 || line == UNKNOWN : "Invalid value for line: " + line; + return line; + } + + /** + * Set the line number where this node starts. + * Column numbers start at 0, not 1. + * @param line The line number + */ + public void setLine(int line) + { + if (line != UNKNOWN) + this.line = line; + } + + /** + * Get the column number where this node starts. + * @return The column number + */ + @Override + public int getColumn() + { + assert column >= 0 || column == UNKNOWN : "Invalid value for column: " + column; + return column; + } + + /** + * Set the column number where this node starts. + * @param column The column number + */ + public void setColumn(int column) + { + if (column != UNKNOWN) + this.column = column; + } + + /** ++ * Get the line number where this node ends. ++ * Line numbers start at 0, not 1. ++ * @return The line number ++ */ ++ public int getEndLine() ++ { ++ return endLine; ++ } ++ ++ /** ++ * Set the line number where this node ends. ++ * Column numbers start at 0, not 1. ++ * @param line The line number ++ */ ++ public void setEndLine(int line) ++ { ++ this.endLine = line; ++ } ++ ++ /** ++ * Get the column number where this node ends. ++ * @return The column number ++ */ ++ public int getEndColumn() ++ { ++ return endColumn; ++ } ++ ++ /** ++ * Set the column number where this node ends. ++ * @param column The column number ++ */ ++ public void setEndColumn(int column) ++ { ++ this.endColumn = column; ++ } ++ ++ /** + * Get the source path for this node. + * @return The source path for this node + */ + @Override + public final String getSourcePath() + { + // null means the source is unknown. + // "" means the source is a buffer that hasn't yet been saved to a file. + // Something like "framework.swc:defaults.css" means the source is a file inside a SWC. + // TODO Shouldn't the part before the colon be normalized? + // Anything else should be a normalized path to a source file. + assert sourcePath == null || + sourcePath.isEmpty() || + sourcePath.contains(".swc:") || + FilenameNormalization.isNormalized(sourcePath) : + "Invalid value for sourcePath: " + sourcePath; + return sourcePath; + } + + /** + * Set the source path of the node. + * @param sourcePath The source path + */ + public final void setSourcePath(String sourcePath) + { + this.sourcePath = sourcePath; + } + + /** + * Displays line, column, start, end, and sourcepath in a format such as + * <pre> + * "17:5 160-188 C:\test.as" + * </pre> + */ + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getLineColumnString()); + sb.append(getOffsetsString()); + sb.append(getSourcePathString()); + return sb.toString(); + } + + /** + * Displays Line and Column numbers + */ + protected String getLineColumnString() + { + StringBuilder sb = new StringBuilder(); + int line = getLine(); + if (line != UNKNOWN) + sb.append(line); + else + sb.append('?'); + sb.append(':'); + int column = getColumn(); + if (column != UNKNOWN) + sb.append(column); + else + sb.append('?'); + + sb.append(' '); + return sb.toString(); + } + + /** + * Displays sourcepath + * + */ + protected String getSourcePathString() + { + StringBuilder sb = new StringBuilder(); + sb.append(' '); + + String sourcePath = getSourcePath(); + if (sourcePath != null) + sb.append(sourcePath); + else + sb.append('?'); + return sb.toString(); + } + + /** + * Displays line, column, start, end + * + */ + protected String getOffsetsString() + { + StringBuilder sb = new StringBuilder(); + sb.append("loc: "); + int start = getStart(); + if (start != UNKNOWN) + sb.append(start); + else + sb.append('?'); + sb.append('-'); + int end = getEnd(); + if (end != UNKNOWN) + sb.append(end); + else + sb.append('?'); + + sb.append(' '); + + sb.append("abs: "); + int absoluteStart = getAbsoluteStart(); + if (absoluteStart != UNKNOWN) + sb.append(absoluteStart); + else + sb.append('?'); + sb.append('-'); + int absoluteEnd = getAbsoluteEnd(); + if (absoluteEnd != UNKNOWN) + sb.append(absoluteEnd); + else + sb.append('?'); + return sb.toString(); + } + + + /** + * Span the location range from {@code start} to {@code end}. + * + * @param start Start location. + * @param end End location. + */ + public final void span(ISourceLocation start, ISourceLocation end) + { + setSourcePath(start.getSourcePath()); + setStart(start.getStart()); + setEnd(end.getEnd()); + setLine(start.getLine()); + setColumn(start.getColumn()); ++ setEndLine(end.getEndLine()); ++ setEndColumn(end.getEndColumn()); + } + + /** + * Span the location range from {@code start} to {@code end}. + * + * @param location The location + */ + public final void span(ISourceLocation location) + { + if (location != null) + setSourceLocation(location); + } +} http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/internal/definitions/metadata/MetaTag.java ---------------------------------------------------------------------- diff --cc compiler/src/main/java/org/apache/flex/compiler/internal/definitions/metadata/MetaTag.java index 92b9350,0000000..5b0b18e mode 100644,000000..100644 --- a/compiler/src/main/java/org/apache/flex/compiler/internal/definitions/metadata/MetaTag.java +++ b/compiler/src/main/java/org/apache/flex/compiler/internal/definitions/metadata/MetaTag.java @@@ -1,365 -1,0 +1,377 @@@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.flex.compiler.internal.definitions.metadata; + +import java.util.Arrays; + +import org.apache.flex.compiler.common.IMetaInfo; +import org.apache.flex.compiler.common.NodeReference; +import org.apache.flex.compiler.constants.IMetaAttributeConstants; +import org.apache.flex.compiler.definitions.IDefinition; +import org.apache.flex.compiler.definitions.metadata.IMetaTag; +import org.apache.flex.compiler.definitions.metadata.IMetaTagAttribute; +import org.apache.flex.compiler.filespecs.IFileSpecification; +import org.apache.flex.compiler.internal.definitions.DefinitionBase; +import org.apache.flex.compiler.internal.parsing.as.OffsetLookup; +import org.apache.flex.compiler.internal.scopes.ASFileScope; +import org.apache.flex.compiler.internal.scopes.ASScope; +import org.apache.flex.compiler.internal.tree.as.ClassNode; +import org.apache.flex.compiler.tree.as.IASNode; +import org.apache.flex.compiler.tree.metadata.IMetaTagNode; +import org.apache.flex.compiler.tree.metadata.IMetaTagsNode; +import org.apache.flex.compiler.workspaces.IWorkspace; + +public class MetaTag implements IMetaTag +{ + /** + * Append a {@link IMetaTag} to an array of {@link IMetaTag}. + * + * @param metaTags An existing array of meta tags. May be empty but + * may not be null. + * @param metaTag The new meta tag to append to the metaTags array. + * If null, the metaTags parameter is returned unmodified. + * @return The new array of meta tags. + */ + public static IMetaInfo[] addMetaTag(IMetaInfo[] metaTags, IMetaInfo metaTag) + { + assert metaTags != null; + + if (metaTag != null) + { + IMetaInfo[] newMetaTags = Arrays.copyOf(metaTags, metaTags.length + 1, IMetaInfo[].class); + newMetaTags[metaTags.length] = metaTag; + metaTags = newMetaTags; + } + + return metaTags; + } + + /** + * Create a new meta tag for either "__go_to_ctor_definition_help" or + * "__go_to_definition_help". + * + * @param definition The definition to add the meta data for. + * @param file The absolute path of the source file the definition is found + * in. May be null. + * @param pos The position of the definition in the source file. If "-1" + * no MetaTag is created. + * @param ctor True if the definition is for a constructor, false otherwise. + * @return A new MetaTag. If the pos paramater is "-1", null is returned. + */ + public static MetaTag createGotoDefinitionHelp(IDefinition definition, + String file, String pos, boolean ctor) + { + assert pos != null; + + if (pos.equals("-1")) + return null; + + IMetaTagAttribute[] attributes = new MetaTagAttribute[file != null ? 2 : 1]; + if (file != null) + { + attributes[0] = new MetaTagAttribute(IMetaAttributeConstants.NAME_GOTODEFINITIONHELP_FILE, + file); + } + + attributes[file != null ? 1 : 0] = new MetaTagAttribute(IMetaAttributeConstants.NAME_GOTODEFINITIONHELP_POS, + pos); + + return new MetaTag(definition, + ctor ? IMetaAttributeConstants.ATTRIBUTE_GOTODEFINITION_CTOR_HELP : + IMetaAttributeConstants.ATTRIBUTE_GOTODEFINITIONHELP, + attributes); + } + + public MetaTag(IDefinition decoratedDefinition, String tagName, IMetaTagAttribute[] attributes) + { + this.decoratedDefinition = decoratedDefinition; + this.tagName = tagName; + if (attributes == null) + { + // this is a low cost way to make sure that clients never get null attributes + attributes = emptyAttributes; + } + this.attributes = attributes; + } + + private IDefinition decoratedDefinition; + + private String tagName; + + private IMetaTagAttribute[] attributes; + + // Singleton empty array to be shared by all instances that don't have attributes + private static final IMetaTagAttribute[] emptyAttributes = new IMetaTagAttribute[0]; + + private String sourcePath; + + private int absoluteStart = UNKNOWN; + + private int absoluteEnd = UNKNOWN; + + private int line = UNKNOWN; + + private int column = UNKNOWN; + + // Hold a reference to the node this definition came from + // (NodeReference only holds onto the node weakly, so we don't have to worry about leaks). + private NodeReference nodeRef = NodeReference.noReference; + + @Override + public String getTagName() + { + return tagName; + } + + @Override + public IMetaTagAttribute[] getAllAttributes() + { + return attributes; + } + + @Override + public IMetaTagAttribute getAttribute(String key) + { + for (IMetaTagAttribute attribute : attributes) + { + String attrKey = attribute.getKey(); + if (attrKey != null && attrKey.equals(key)) + return attribute; + } + return null; + } + + @Override + public String getAttributeValue(String key) + { + for (IMetaTagAttribute attribute : attributes) + { + // For metadata such as [Foo("abc", "def")], + // the attribute keys are null, so a null + // check on the key is necessary. + // BTW, keyless values like "abc" and "def" + // cannot be retrieved by this API; + // you have to use getAttributes()[i].getValue(). + String attrKey = attribute.getKey(); + if (attrKey != null && attrKey.equals(key)) + return attribute.getValue(); + } + return null; + } + + @Override + public String getSourcePath() + { + return sourcePath; + } + + private OffsetLookup getOffsetLookup() + { + DefinitionBase definition = (DefinitionBase)getDecoratedDefinition(); + if (definition == null) + return null; + + final ASFileScope fileScope = definition.getFileScope(); + if (fileScope == null) + return null; + + return fileScope.getOffsetLookup(); + } + + @Override + public int getStart() + { + OffsetLookup offsetLookup = getOffsetLookup(); + + if (offsetLookup == null) + return absoluteStart; + + return offsetLookup.getLocalOffset(absoluteStart); + } + + @Override + public int getEnd() + { + OffsetLookup offsetLookup = getOffsetLookup(); + + if (offsetLookup == null) + return absoluteEnd; + + return offsetLookup.getLocalOffset(absoluteEnd); + } + + @Override + public int getLine() + { + return line; + } + + @Override + public int getColumn() + { + return column; + } + + @Override ++ public int getEndLine() ++ { ++ return line; ++ } ++ ++ @Override ++ public int getEndColumn() ++ { ++ return column; ++ } ++ ++ @Override + public int getAbsoluteStart() + { + return absoluteStart; + } + + @Override + public int getAbsoluteEnd() + { + return absoluteEnd; + } + + @Override + public IDefinition getDecoratedDefinition() + { + return decoratedDefinition; + } + + @Override + public String getValue() + { + return attributes.length == 1 && !attributes[0].hasKey() ? + attributes[0].getValue() : + null; + } + + @Override + public IMetaTagNode getTagNode() + { + // If this definition didn't come from source, return null. + if (nodeRef == NodeReference.noReference) + return null; + + // Get the scope for the definition this metadata is attached to. + ASScope containingScope = (ASScope)getDecoratedDefinition().getContainingScope(); + if (containingScope == null) + return null; + + // Get the file scope for that scope. + ASFileScope fileScope = containingScope.getFileScope(); + if (fileScope == null) + return null; + + // Get the workspace. + IWorkspace workspace = fileScope.getWorkspace(); + assert workspace != null; + + // Use the stored NodeReference to get the original node. + IASNode node = nodeRef.getNode(workspace, containingScope); + if (!(node instanceof IMetaTagNode)) + { + // CMP-2168: The NodeReference resolver assumes that all definitions + // have a unique start offset. This true in every case except when + // there are metadata definitions decorating a class. In this case, the + // start of the metadata and the start of the class are the same, and the + // resolver will return the ClassNode, not the MetaTagNode. Catch this + // case by when we get a ClassNode, walking any metatags on the class + // looking for an offset that matches this MetaTag offset. + if (node instanceof ClassNode) + { + IMetaTagsNode metaTags = ((ClassNode)node).getMetaTags(); + if (metaTags != null) + { + for (IMetaTagNode metaTagNode : metaTags.getAllTags()) + { + if (metaTagNode.getAbsoluteStart() == getAbsoluteStart()) + return metaTagNode; + } + } + } + + return null; + } + + return (IMetaTagNode)node; + } + + /** + * Updates the location information of this tag. + */ + public void setLocation(IFileSpecification containingFileSpec, int absoluteStart, int absoluteEnd, int line, int column) + { + this.nodeRef = new NodeReference(containingFileSpec, absoluteStart); + this.sourcePath = containingFileSpec.getPath(); + this.absoluteStart = absoluteStart; + this.absoluteEnd = absoluteEnd; + this.line = line; + this.column = column; + } + + /** + * For debugging only. + */ + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append('['); + sb.append(tagName); + + IMetaTagAttribute[] attrs = getAllAttributes(); + if (attrs != null && attrs.length > 0) + { + sb.append('('); + + int i = 0; + for (IMetaTagAttribute attr : getAllAttributes()) + { + if (i != 0) + { + sb.append(','); + sb.append(' '); + } + + String key = attr.getKey(); + String value = attr.getValue(); + + sb.append(key); + sb.append('='); + sb.append('"'); + sb.append(value); + sb.append('"'); + + i++; + } + sb.append(')'); + } + + sb.append(']'); + + return sb.toString(); + } +} http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/internal/parsing/TokenBase.java ---------------------------------------------------------------------- diff --cc compiler/src/main/java/org/apache/flex/compiler/internal/parsing/TokenBase.java index 68a76eb,0000000..ff726da mode 100644,000000..100644 --- a/compiler/src/main/java/org/apache/flex/compiler/internal/parsing/TokenBase.java +++ b/compiler/src/main/java/org/apache/flex/compiler/internal/parsing/TokenBase.java @@@ -1,515 -1,0 +1,559 @@@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.flex.compiler.internal.parsing; + +import antlr.Token; + +import org.apache.flex.compiler.common.ISourceLocation; +import org.apache.flex.compiler.internal.common.Counter; +import org.apache.flex.compiler.internal.parsing.as.ASTokenTypes; +import org.apache.flex.compiler.internal.parsing.as.IncludeHandler; +import org.apache.flex.compiler.parsing.ICMToken; + +/** + * Base class of ASToken, MXMLToken, CSSToken + */ +public abstract class TokenBase extends Token implements ICMToken, ISourceLocation +{ + /** + * Constructor + * + * @param tokenType type of token + * @param start location location information + * @param end location location information + * @param line location location information + * @param column location location information + * @param text actual text represented by token + */ + public TokenBase(int tokenType, int start, int end, int line, int column, CharSequence text) + { + type = tokenType; + localStart = this.start = start; + localEnd = this.end = end; + this.line = line; + this.column = column; + this.text = text; ++ this.endLine = line; ++ this.endColumn = column + end - start; + + if (Counter.COUNT_TOKENS) + countTokens(); + } + + /** + * Copy constructor + * + * @param o token to copy + */ + public TokenBase(TokenBase o) + { + type = o.type; + start = o.start; + end = o.end; + line = o.line; + column = o.column; ++ endLine = o.endLine; ++ endColumn = o.endColumn; + text = o.text; + + localStart = o.localStart; + localEnd = o.localEnd; + sourcePath = o.sourcePath; + + if (Counter.COUNT_TOKENS) + countTokens(); + } + + /** + * Text represented by this token + */ + private CharSequence text; + + /** + * Start of this token + */ + private int start; + + /** + * End offset of this token + */ + private int end; + + /** + * Line of this token + */ + private int line; + + /** + * Column of this token + */ + private int column; + + /** ++ * End line of this token ++ */ ++ private int endLine; ++ ++ /** ++ * End column of this token ++ */ ++ private int endColumn; ++ ++ /** + * Flag to determine if this token is locked + */ + private boolean locked; + + /** + * Local start offset. + */ + private int localStart; + + /** + * Local end offset. + */ + private int localEnd; + + protected abstract String getTypeString(); + + /** + * @return Local start offset. + */ + public final int getLocalStart() + { + return localStart; + } + + /** + * @return Local end offset. + */ + public final int getLocalEnd() + { + return localEnd; + } + + public final void reuse(final int tokenType, final int start, final int end, + final int line, final int column, final CharSequence text) + { + type = tokenType; + this.start = start; + this.end = end; + this.line = line; + this.column = column; ++ this.endLine = line; ++ this.endColumn = column + end - start; + this.text = text; + } + + /** + * Locks this token. When locked, if this token is in a token pool, it will + * not be overwritten + */ + public void lock() + { + locked = true; + } + + /** + * Returns whether this token can be overwritten when it is a member of a + * token pool + * + * @return true if we are locked + */ + public boolean isLocked() + { + return locked; + } + + /** + * Get the text represented by this token + * + * @return text represented by this token + * @see antlr.Token#getText() + */ + @Override + public String getText() + { + //we're either going to be a String or a StringBuilder + //String.toString returns itself + //StringBuilder toString returns a new String + if (text != null) + return text.toString(); + return ""; + } + + /** + * Returns the underlying CharSequence that represents the contents of this + * token + * + * @return a {@link CharSequence} or null + */ + public CharSequence getCharSequence() + { + return text; + } + + /** + * Set the text represented by this token + * + * @param text text represented by this token + * @see antlr.Token#setText(java.lang.String) + */ + @Override + public void setText(String text) + { + this.text = text; + } + + /** + * Set the CharSequence represented by this token + * + * @param text text represented by this token + */ + public void setText(CharSequence text) + { + this.text = text; + } + + public void setLocation(int start, int end, int line, int column) + { + this.start = start; + this.end = end; + this.line = line; + this.column = column; ++ this.endLine = line; ++ this.endColumn = column + end - start; + } + + @Override + public int getStart() + { + return start; + } + + public void setStart(int start) + { + this.start = start; + } + + @Override + public int getEnd() + { + return end; + } + + public void setEnd(int end) + { + this.end = end; + } + + @Override + public int getLine() + { + return line; + } + + @Override + public void setLine(int line) + { + this.line = line; + } + + public final boolean matchesLine(final TokenBase other) + { + return other != null && other.line == line; + } + + @Override + public int getColumn() + { + return column; + } + + @Override + public void setColumn(int column) + { + this.column = column; + } + ++ @Override ++ public int getEndLine() ++ { ++ return endLine; ++ } ++ ++ public void setEndLine(int line) ++ { ++ endLine = line; ++ } ++ ++ @Override ++ public int getEndColumn() ++ { ++ return endColumn; ++ } ++ ++ public void setEndColumn(int column) ++ { ++ endColumn = column; ++ } ++ + /** + * Determine whether or not this token is bogus (i.e. the start and end + * offsets are the same, which implies that it was inserted from an included + * file or during token fixup) + * + * @return true iff the token is bogus + */ + @Override + public boolean isImplicit() + { + return start == end; + } + + /** + * For debugging only. + */ + private String getEscapedText() + { + String text = getText(); + + text = text.replaceAll("\n", "\\\\n"); + text = text.replaceAll("\r", "\\\\r"); + text = text.replaceAll("\t", "\\\\t"); + + return text; + } + + /** + * For debugging only. This format is nice in the Eclipse debugger. + */ + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append('|'); + sb.append(getEscapedText()); + sb.append('|'); + + sb.append(' '); + + sb.append(getTypeString()); + + sb.append(' '); + + if (locked) + sb.append("locked "); + + int line = getLine(); + if (line != UNKNOWN) + sb.append(line + 1); + else + sb.append('?'); + sb.append(':'); + int column = getColumn(); + if (column != UNKNOWN) + sb.append(column + 1); + else + sb.append('?'); + + sb.append(' '); + + int start = getStart(); + if (start != UNKNOWN) + sb.append(start); + else + sb.append('?'); + sb.append('-'); + int end = getEnd(); + if (end != UNKNOWN) + sb.append(end); + else + sb.append('?'); + + sb.append(' '); + String sourcePath = getSourcePath(); + if (sourcePath != null) + { + sb.append('"'); + sb.append(sourcePath); + sb.append('"'); + } + else + { + sb.append('?'); + } + + return sb.toString(); + } + + /** + * For debugging only. This format is nice in a text file. + */ + public String toDumpString() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getLine() + 1); + sb.append('\t'); + sb.append(getColumn() + 1); + sb.append('\t'); + sb.append(getStart()); + sb.append('\t'); + sb.append(getEnd()); + sb.append('\t'); + + String typeString = getTypeString(); + sb.append(typeString); + int n = 28 - typeString.length(); + for (int i = 0; i < n; i++) + sb.append(' '); + sb.append('\t'); + + sb.append('|'); + sb.append(getEscapedText()); + sb.append('|'); + + return sb.toString(); + } + + /** + * Reduce the span of the token by removing characters from the beginning + * and end. This is used to remove quote characters and such from tokens. + * + * @param trimLeft number of characters to remove from the left of the token + * @param trimRight number of characters to remove from the right of the + * token + */ + public void truncate(int trimLeft, int trimRight) + { + String text = getText(); + if (trimLeft + trimRight <= text.length()) + { + text = text.substring(trimLeft, text.length() - trimRight); + setText(text); + start += trimLeft; + end -= trimRight; + } + } + + /** + * Adjust all associated offsets by the adjustment amount + * + * @param offsetAdjustment amount to add to offsets + */ + public void adjustOffsets(int offsetAdjustment) + { + start += offsetAdjustment; + end += offsetAdjustment; + } + + /** + * Adjust all associated offsets by the adjustment amount + * + * @param offsetAdjustment amount to add to offsets + * @param lineAdjustment amount to add to the line number + * @param columnAdjustment amount to add to the column number + */ + public void adjustLocation(int offsetAdjustment, int lineAdjustment, int columnAdjustment) + { + start += offsetAdjustment; + end += offsetAdjustment; + line += lineAdjustment; + column += columnAdjustment; ++ endLine += lineAdjustment; ++ endColumn += columnAdjustment; + } + + /** + * Capture the current start/end offsets as this token's local offsets. This + * method is called in {@code StreamingASTokenizer#nextTokenFromReader()} + * after the token is initialized, and before being updated by + * {@link IncludeHandler#onNextToken}. + */ + public final void storeLocalOffset() + { + this.localStart = start; + this.localEnd = end; + } + + private String sourcePath; + + @Override + public final String getSourcePath() + { + return sourcePath; + } + + public final void setSourcePath(String path) + { + this.sourcePath = path; + } + + /** + * Verifies that this token has its type and location information set. + * <p> + * This is used only in asserts. + */ + public boolean verify() + { + // Verify the token type. + int type = getType(); + assert type != 0 : "Token has no type: " + toString(); + + // Verify the source location (except for EOF tokens, + // which are special and don't have a source location). + if (type != ASTokenTypes.EOF) + { + assert getStart() != UNKNOWN : "Token has unknown start: " + toString(); + assert getEnd() != UNKNOWN : "Token has unknown end: " + toString(); + assert getLine() != UNKNOWN : "Token has an unknown line: " + toString(); + assert getColumn() != UNKNOWN : "Token has an unknown column: " + toString(); ++ assert getEndLine() != UNKNOWN : "Token has an unknown end line: " + toString(); ++ assert getEndColumn() != UNKNOWN : "Token has an unknown end column: " + toString(); + } + + return true; + } + + /** + * Counts various types of tokens that are created, as well as the total + * number of tokens. + */ + private void countTokens() + { + Counter counter = Counter.getInstance(); + counter.incrementCount(getClass().getSimpleName()); + counter.incrementCount("tokens"); + } + + @Override + public int getAbsoluteEnd() + { + return getEnd(); + } + + @Override + public int getAbsoluteStart() + { + return getStart(); + } +} http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/NodeBase.java ---------------------------------------------------------------------- diff --cc compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/NodeBase.java index be0510d,0000000..8dd4fb6 mode 100644,000000..100644 --- a/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/NodeBase.java +++ b/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/NodeBase.java @@@ -1,1038 -1,0 +1,1049 @@@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.flex.compiler.internal.tree.as; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; + +import antlr.Token; + +import org.apache.flex.compiler.common.ASModifier; +import org.apache.flex.compiler.common.ISourceLocation; +import org.apache.flex.compiler.common.SourceLocation; +import org.apache.flex.compiler.definitions.IDefinition; +import org.apache.flex.compiler.filespecs.IFileSpecification; +import org.apache.flex.compiler.internal.common.Counter; +import org.apache.flex.compiler.internal.definitions.DefinitionBase; +import org.apache.flex.compiler.internal.parsing.as.OffsetLookup; +import org.apache.flex.compiler.internal.scopes.ASFileScope; +import org.apache.flex.compiler.internal.scopes.ASScope; +import org.apache.flex.compiler.internal.scopes.TypeScope; +import org.apache.flex.compiler.internal.semantics.PostProcessStep; +import org.apache.flex.compiler.problems.ICompilerProblem; +import org.apache.flex.compiler.scopes.IASScope; +import org.apache.flex.compiler.tree.ASTNodeID; +import org.apache.flex.compiler.tree.as.IASNode; +import org.apache.flex.compiler.tree.as.IFileNode; +import org.apache.flex.compiler.tree.as.IImportNode; +import org.apache.flex.compiler.tree.as.IPackageNode; +import org.apache.flex.compiler.tree.as.IScopedNode; +import org.apache.flex.compiler.tree.mxml.IMXMLClassDefinitionNode; +import org.apache.flex.compiler.workspaces.IWorkspace; + +/** + * Base class for ActionScript parse tree nodes + */ +public abstract class NodeBase extends SourceLocation implements IASNode +{ + /** + * Used in calls to CheapArray functions. It represents the type of objects + * that appear in fSymbols. + */ + protected static final IASNode[] emptyNodeArray = new IASNode[0]; + + + /** + * Combine the attributes from the base class with the attributes specific + * to this class + * + * @param superAttributes The attributes of the base class. + * @param myAttributes The attributes of this class. + * @return attribute value + */ + public static String[] combineAttributes(String[] superAttributes, String[] myAttributes) + { + String[] combinedAttributes = new String[superAttributes.length + myAttributes.length]; + for (int i = 0; i < superAttributes.length; i++) + { + combinedAttributes[i] = superAttributes[i]; + } + for (int i = 0; i < myAttributes.length; i++) + { + combinedAttributes[superAttributes.length + i] = myAttributes[i]; + } + return combinedAttributes; + } + + /** + * Constructor. + */ + public NodeBase() + { + super(); + parent = null; + + if (Counter.COUNT_NODES) + countNodes(); + } + + /** + * Parent node. + * <p> + * Methods (even in NodeBase itself) should use getParent() instead of + * accessing this member directly. getParent() is overridden in FileNode to + * do something special when the FileNode represents a shared file. + */ + protected IASNode parent; + + @Override + public IASNode getParent() + { + return parent; + } + + @Override + public int getChildCount() + { + return 0; + } + + @Override + public IASNode getChild(int i) + { + return null; + } + + @Override + public IFileSpecification getFileSpecification() + { + // TODO Make sure this works with include processing!!! + ASFileScope fileScope = getFileScope(); + IWorkspace w = fileScope.getWorkspace(); + return w.getFileSpecification(fileScope.getContainingPath()); + } + + @Override + public int getSpanningStart() + { + return getStart(); + } + + /** + * Get the node type as a string. For example, this will return + * "IdentifierNode" for an IdentifierNode. + * + * @return the node type + */ + public String getNodeKind() + { + return getClass().getSimpleName(); + } + + @Override + public boolean contains(int offset) + { + return getAbsoluteStart() < offset && getAbsoluteEnd() >= offset; + } + + /** + * Determine whether the offset "loosely" fits within this node. With normal + * nodes, this will return true if the offset matches fLocation.fStart or + * fLocation.fEnd or anything in between. With bogus nodes (which have + * fStart == fEnd), this will return true if the offset comes after fStart + * and before any other nodes in the parse tree + * + * @param offset the offset to test + * @return true if the offset is "loosely" contained within this node + */ + public boolean looselyContains(int offset) + { + if (getAbsoluteStart() == getAbsoluteEnd()) + { + // This is a bogus placeholder node (generally an identifier that's been inserted to complete an expression). + // We'll pretend that it extends all the way to the start offset of the next node. Now to find that next node... + if (getAbsoluteStart() <= offset) + { + NodeBase containingNode = (NodeBase)getParent(); + // Step 1: Find a node that extends beyond the target offset + while (containingNode != null && containingNode.getAbsoluteEnd() <= getAbsoluteEnd()) + containingNode = (NodeBase)containingNode.getParent(); + // If no such node exists, we know that there aren't any tokens in the parse + // tree that end after this bogus token. So we can go ahead and pretend this node + // extends all the way out. + if (containingNode == null) + return true; + // Step 2: Find a node that starts after the target offset + IASNode succeedingNode = containingNode.getSucceedingNode(getAbsoluteEnd()); + // If no such node exists, we know that there aren't any tokens in the parse + // tree that start after this bogus token. So we can go ahead and pretend this + // node extends all the way out. + if (succeedingNode == null) + return true; + // Otherwise, pretend this node extends all the way to the next token. + if (succeedingNode.getAbsoluteStart() >= offset) + return true; + } + } + else + { + // This is a real token. See if the test offset fits within (including the boundaries, since we're looking + // at "loose" containment. + return getAbsoluteStart() <= offset && getAbsoluteEnd() >= offset; + } + return false; + } + + @Override + public IASNode getSucceedingNode(int offset) + { + // This node ends before the offset is even reached. This is hopeless. + if (getAbsoluteEnd() <= offset) + return null; + + // See if one of our children starts after the offset + for (int i = 0; i < getChildCount(); i++) + { + IASNode child = getChild(i); + + if (child.getAbsoluteStart() > offset) + return child; + else if (child.getAbsoluteEnd() > offset) + return child.getSucceedingNode(offset); + } + + return null; + } + + /** + * Determine whether this node is transparent. If a node is transparent, it + * can never be returned from getContainingNode... the offset will be + * considered part of the parent node instead. + * + * @return true if the node is transparent + */ + public boolean isTransparent() + { + return false; + } + + @Override + public IASNode getContainingNode(int offset) + { + // This node doesn't even contain the offset. This is hopeless. + if (!contains(offset)) + { + return null; + } + IASNode containingNode = this; + int childCount = getChildCount(); + // See if the offset is contained within one of our children. + for (int i = 0; i < childCount; i++) + { + IASNode child = getChild(i); + if (child.getAbsoluteStart() <= offset) + { + if (child.contains(offset)) + { + containingNode = child.getContainingNode(offset); + if (child instanceof NodeBase && ((NodeBase)child).canContinueContainmentSearch(containingNode, this, i, true)) + continue; + break; + } + } + else + { + if (child instanceof NodeBase && ((NodeBase)child).canContinueContainmentSearch(containingNode, this, i, false)) + continue; + // We've passed this offset without finding a child that contains it. This is + // the nearest enclosing node. + break; + } + } + // Make sure we don't return a transparent node + while (containingNode != null && containingNode instanceof NodeBase && ((NodeBase)containingNode).isTransparent()) + { + containingNode = containingNode.getParent(); + } + return containingNode; + } + + @Override + public boolean isTerminal() + { + return false; + } + + @Override + public IScopedNode getContainingScope() + { + return (IScopedNode)getAncestorOfType(IScopedNode.class); + } + + // TODO Can probably eliminate this. + protected boolean canContinueContainmentSearch(IASNode containingNode, IASNode currentNode, int childOffset, boolean offsetsStillValid) + { + return false; + } + + @Override + public IASNode getAncestorOfType(Class<? extends IASNode> nodeType) + { + IASNode current = getParent(); + while (current != null && !(nodeType.isInstance(current))) + current = current.getParent(); + if (current != null) + return current; + return null; + } + + @Override + public String getPackageName() + { + // Starting with this node's parent, walk up the parent chain + // looking for a package node or an MXML class definition node. + // These two types of nodes understand what their package is. + IASNode current = getParent(); + while (current != null && + !(current instanceof IPackageNode || + current instanceof IMXMLClassDefinitionNode)) + { + current = current.getParent(); + } + + if (current instanceof IPackageNode) + return ((IPackageNode)current).getPackageName(); + else if (current instanceof IMXMLClassDefinitionNode) + return ((IMXMLClassDefinitionNode)current).getPackageName(); + + return null; + } + + /** + * Set the start offset of the node to fall just after the token. Used + * during parsing. + * + * @param token start this node after this token + */ + public void startAfter(Token token) + { + if (token instanceof ISourceLocation) + { + startAfter((ISourceLocation) token); - setColumn(getColumn() + token.getText().length()); + } + } + + /** + * Set the start offset of the node to fall just after the token. Used + * during parsing. + * + * @param location start this node after this token + */ + public final void startAfter(ISourceLocation location) + { + final int end = location.getEnd(); + if (end != UNKNOWN) + { + setSourcePath(location.getSourcePath()); + setStart(end); - setLine(location.getLine()); - setColumn(location.getColumn()); ++ setLine(location.getEndLine()); ++ setColumn(location.getEndColumn()); + } + } + + /** + * Set the start offset of the node to fall just before the token. Used + * during parsing. + * + * @param token start this node before this token + */ + public final void startBefore(Token token) + { + if (token instanceof ISourceLocation) + startBefore((ISourceLocation)token); + } + + /** + * Set the start offset of the node to fall just before the token. Used + * during parsing. + * + * @param location start this node before this token + */ + public final void startBefore(ISourceLocation location) + { + final int start = location.getStart(); + if (start != UNKNOWN) + { + setSourcePath(location.getSourcePath()); + setStart(start); + setLine(location.getLine()); + setColumn(location.getColumn()); + } + } + + /** + * Set the end offset of the node to fall just after the token. Used during + * parsing. + * + * @param token end this node after this token + */ + public final void endAfter(Token token) + { + if (token instanceof ISourceLocation) + endAfter((ISourceLocation)token); + } + + /** + * Set the end offset of the node to fall just after the token. Used during + * parsing. + * + * @param location end this node after this token + */ + public final void endAfter(ISourceLocation location) + { + final int end = location.getEnd(); + if (end != UNKNOWN) ++ { + setEnd(end); ++ setEndLine(location.getEndLine()); ++ setEndColumn(location.getEndColumn()); ++ } + } + + /** + * Set the end offset of the node to fall just before the token. Used during + * parsing. + * + * @param token start this node before this token + */ + public final void endBefore(Token token) + { + if (token instanceof ISourceLocation) + endBefore((ISourceLocation)token); + } + + /** + * Set the end offset of the node to fall just before the token. Used during + * parsing. + * + * @param location start this node before this token + */ + public final void endBefore(ISourceLocation location) + { + final int start = location.getStart(); + if (start != UNKNOWN) ++ { + setEnd(start); ++ setEndLine(location.getLine()); ++ setEndColumn(location.getColumn()); ++ } + } + + /** + * Set the start and end offsets of the node to coincide with the token's + * offsets. Used during parsing. + * + * @param token the token to take the offsets from + */ + public final void span(Token token) + { + if (token instanceof ISourceLocation) + span((ISourceLocation)token); + } + + /** + * Set the start and end offsets of the node to coincide with the tokens' + * offsets. Used during parsing. + * + * @param firstToken the token to take the start offset and line number from + * @param lastToken the token to take the end offset from + */ + public final void span(Token firstToken, Token lastToken) + { + if (firstToken instanceof ISourceLocation && lastToken instanceof ISourceLocation) + span((ISourceLocation)firstToken, (ISourceLocation)lastToken); + } + + /** + * Set the start and end offsets of the node. Used during parsing. + * + * @param start start offset for the node + * @param end end offset for the node + * @param line line number for the node + */ + public final void span(int start, int end, int line, int column) + { + setStart(start); + setEnd(end); + setLine(line); + setColumn(column); + } + + public Collection<ICompilerProblem> runPostProcess(EnumSet<PostProcessStep> set, ASScope containingScope) + { + ArrayList<ICompilerProblem> problems = new ArrayList<ICompilerProblem>(10); + normalize(set.contains(PostProcessStep.CALCULATE_OFFSETS)); + if (set.contains(PostProcessStep.POPULATE_SCOPE) || set.contains(PostProcessStep.RECONNECT_DEFINITIONS)) + analyze(set, containingScope, problems); + return problems; + } + + protected void analyze(EnumSet<PostProcessStep> set, ASScope scope, Collection<ICompilerProblem> problems) + { + int childrenSize = getChildCount(); + // Populate this scope with definitions found among the children + for (int i = 0; i < childrenSize; i++) + { + IASNode child = getChild(i); + if (child instanceof NodeBase) + { + if (child.getParent() == null) + ((NodeBase)child).setParent(this); + ((NodeBase)child).analyze(set, scope, problems); + } + } + } + + /** + * Changes the position of two children in our tree. Neither child can be + * null and both must have the same parent + * + * @param child the child to replace target + * @param target the child to replace child + */ + protected void swapChildren(NodeBase child, NodeBase target) + { + //no op in this impl + } + + /** + * Replaces the child with the given target. The child will be removed from + * the tree, and its parentage will be updated + * + * @param child the {@link NodeBase} to replace + * @param target the {@link NodeBase} to replace the replaced + */ + protected void replaceChild(NodeBase child, NodeBase target) + { + //no op + } + + /** + * Normalize the tree. Move custom children into the real child list and + * fill in missing offsets so that offset lookup will work. Used during + * parsing. + */ + public void normalize(boolean fillInOffsets) + { + // The list of children doesn't change, so the child count should be constant throughout the loop + int childrenSize = getChildCount(); + // Normalize the regular children + for (int i = 0; i < childrenSize; i++) + { + IASNode child = getChild(i); + if (child instanceof NodeBase) + { + ((NodeBase)child).setParent(this); + ((NodeBase)child).normalize(fillInOffsets); + } + } + // Add the special children (which get normalized as they're added) + if (childrenSize == 0) + setChildren(fillInOffsets); + // Fill in offsets based on the child nodes + if (fillInOffsets) + fillInOffsets(); + // fill in any dangling ends + //fillEndOffsets(); + // get rid of unused child space + } + + /** + * Allow various subclasses to do special kludgy things like identify + * mx.core.Application.application + */ + protected void connectedToProjectScope() + { + // The list of children doesn't change, so the child count should be constant throughout the loop + int childrenSize = getChildCount(); + for (int i = 0; i < childrenSize; i++) + { + IASNode child = getChild(i); + if (child instanceof NodeBase) + ((NodeBase)child).connectedToProjectScope(); + } + } + + /** + * If this node has custom children (names, arguments, etc), shove them into + * the list of children. Used during parsing. + */ + protected void setChildren(boolean fillInOffsets) + { + //nothing in this class + } + + /** + * If the start and end offsets haven't been set explicitly, fill them in + * based on the offsets of the children. Used during parsing. If the end + * offset is less than any of the kids' offsets, change fLocation.fEnd to + * encompass kids. + */ + protected void fillInOffsets() + { + int numChildren = getChildCount(); + if (numChildren > 0) + { + int start = getAbsoluteStart(); + int end = getAbsoluteEnd(); + if (start == -1) + { + for (int i = 0; i < numChildren; i++) + { + IASNode child = getChild(i); + int childStart = child.getAbsoluteStart(); + if (childStart != -1) + { + if (getSourcePath() == null) + setSourcePath(child.getSourcePath()); + setStart(childStart); + setLine(child.getLine()); + setColumn(child.getColumn()); + break; + } + } + } + for (int i = numChildren - 1; i >= 0; i--) + { + IASNode child = getChild(i); + int childEnd = child.getAbsoluteEnd(); + if (childEnd != -1) + { + if (end < childEnd) ++ { + setEnd(childEnd); ++ setEndLine(child.getEndLine()); ++ setEndColumn(child.getEndColumn()); ++ } + break; + } + } + } + } + + /** + * Set the parent node. Used during parsing. + * + * @param parent parent node + */ + public void setParent(NodeBase parent) + { + this.parent = parent; + } + + /** + * Get the nearest containing scope for this node. Used during type + * decoration. + * + * @return nearest containing scope for this node + */ + // TODO Make this more efficient using overrides on BlockNode and FileNode. + public IScopedNode getScopeNode() + { + if (this instanceof IScopedNode && ((IScopedNode)this).getScope() != null) + return (IScopedNode)this; + IASNode parent = getParent(); + if (parent != null && parent instanceof NodeBase) + return ((NodeBase)parent).getScopeNode(); + return null; + } + + /** + * Get the path of the file in which this parse tree node resides. + * <p> + * The tree builder can set a node's origin source file using + * {@link #setSourcePath(String)}. If the source path was set, return the + * source path; Otherwise, use root {@link FileNode}'s path. + * + * @return file path that contains this node + */ + public String getContainingFilePath() + { + String path = getSourcePath(); + if (path != null) + return path; + + final FileNode fileNode = (FileNode)getAncestorOfType(FileNode.class); + if (fileNode != null) + return fileNode.getSourcePath(); + return null; + } + + /** + * For debugging only. Displays this node and its children in a form like + * this: + * + * <pre> + * FileNode "D:\tests\UIComponent.as" 0:0 0-467464 D:\tests\UIComponent.as + * PackageNode "mx.core" 12:1 436-465667 D:\tests\UIComponent.as + * FullNameNode "mx.core" 0:0 444-451 null + * IdentifierNode "mx" 12:9 444-446 D:\tests\UIComponent.as + * IdentifierNode "core" 12:12 447-451 D:\tests\UIComponent.as + * ScopedBlockNode 13:1 454-465667 D:\tests\UIComponent.as + * ImportNode "flash.accessibility.Accessibility" 14:1 457-497 D:\tests\UIComponent.as + * FullNameNode "flash.accessibility.Accessibility" 0:0 464-497 null + * FullNameNode "flash.accessibility" 0:0 464-483 null + * IdentifierNode "flash" 14:8 464-469 D:\tests\UIComponent.as + * IdentifierNode "accessibility" 14:14 470-483 D:\tests\UIComponent.as + * IdentifierNode "Accessibility" 14:28 484-497 D:\tests\UIComponent.as + * ... + * </pre> + * <p> + * Subclasses may not override this, because we want to maintain regularity + * for how all nodes display. + */ + @Override + public final String toString() + { + StringBuilder sb = new StringBuilder(); + buildStringRecursive(sb, 0, false); + return sb.toString(); + } + + /** + * For debugging only. Called by {@code toString()}, and recursively by + * itself, to display this node on one line and each descendant node on + * subsequent indented lines. + * + * * */ + public void buildStringRecursive(StringBuilder sb, int level, boolean skipSrcPath) + { + // Indent two spaces for each nesting level. + for (int i = 0; i < level; i++) + { + sb.append(" "); + } + + // Build the string that represents this node. + buildOuterString(sb, skipSrcPath); + + sb.append('\n'); + + //To test scopes in ParserSuite + if(skipSrcPath && (this instanceof IScopedNode)) { + for (int i = 0; i < level+1; i++) + sb.append(" "); + sb.append("[Scope]"); + sb.append("\n"); + IScopedNode scopedNode = (IScopedNode)this; + IASScope scope = scopedNode.getScope(); + Collection<IDefinition> definitions = scope.getAllLocalDefinitions(); + for(IDefinition def : definitions) { + for (int i = 0; i < level+2; i++) + sb.append(" "); + ((DefinitionBase)def).buildString(sb, false); + sb.append('\n'); + } + } + + // Recurse over the child nodes. + int n = getChildCount(); + for (int i = 0; i < n; i++) + { + NodeBase child = (NodeBase)getChild(i); + // The child can be null if we're toString()'ing + // in the debugger during tree building. + if (child != null) + child.buildStringRecursive(sb, level + 1, skipSrcPath); + } + } + + /** + * For debugging only. Called by {@code buildStringRecursive()} to display + * information for this node only. + * <p> + * An example is + * + * <pre> + * PackageNode "mx.core" 12:1 436-465667 D:\tests\UIComponent.as + * </pre>. + * <p> + * The type of node (PackageNode) is displayed first, followed by + * node-specific information ("mx.core") followed by location information in + * the form + * + * <pre> + * line:column start-end sourcepath + * </pre> + * + */ + private void buildOuterString(StringBuilder sb, boolean skipSrcPath) + { + // First display the type of node, as represented by + // the short name of the node class. + sb.append(getNodeKind()); + + sb.append("(").append(getNodeID().name()).append(")"); + + sb.append(' '); + + // Display optional node-specific information in the middle. + if (buildInnerString(sb)) + sb.append(' '); + + // The SourceLocation superclass handles producing a string such as + // "17:5 160-188" C:/test.as". + if(skipSrcPath) + sb.append(getOffsetsString()); + else + sb.append(super.toString()); + } + + /** + * For debugging only. This method is called by {@code buildOuterString()}. + * It is overridden by subclasses to display optional node-specific + * information in the middle of the string, between the node type and the + * location information. + */ + protected boolean buildInnerString(StringBuilder sb) + { + return false; + } + + /** + * For debugging only. + */ + public String getInnerString() + { + StringBuilder sb = new StringBuilder(); + buildInnerString(sb); + return sb.toString(); + } + + public ASFileScope getFileScope() + { + ASScope scope = getASScope(); + assert scope != null; + while (!(scope instanceof ASFileScope)) + { + scope = scope.getContainingScope(); + assert scope != null; + } + return (ASFileScope)scope; + } + + /** + * @return {@link OffsetLookup} object for the current tree or null. + */ + protected final OffsetLookup tryGetOffsetLookup() + { + // Try FileNode.getOffsetLookup() + final IASNode fileNode = getAncestorOfType(IFileNode.class); + if (fileNode != null) + return ((IFileNode)fileNode).getOffsetLookup(); + else + return null; + } + + /** + * Get's the {@link IWorkspace} in which this {@link NodeBase} lives. + * + * @return The {@link IWorkspace} in which this {@link NodeBase} lives. + */ + public IWorkspace getWorkspace() + { + return getFileScope().getWorkspace(); + } + + /** + * Get the scope this Node uses for name resolution as an ASScope. + * + * @return the ASScope for this node, or null if there isn't one. + */ + public ASScope getASScope() + { + IScopedNode scopeNode = getContainingScope(); + IASScope scope = scopeNode != null ? scopeNode.getScope() : null; + + // If the ScopedNode had a null scope, keep looking up the tree until we + // find one with a non-null scope. + // TODO: Is it a bug that an IScopedNode returns null for it's scope? + // TODO: this seems like a leftover from block scoping - for example, a + // TODO: ForLoopNode is an IScopedNode, but it doesn't really have a scope + while (scope == null && scopeNode != null) + { + scopeNode = scopeNode.getContainingScope(); + scope = scopeNode != null ? scopeNode.getScope() : null; + } + + if (scope instanceof TypeScope) + { + TypeScope typeScope = (TypeScope)scope; + if (this instanceof BaseDefinitionNode) + { + if (((BaseDefinitionNode)this).hasModifier(ASModifier.STATIC)) + scope = typeScope.getStaticScope(); + else + scope = typeScope.getInstanceScope(); + } + else + { + // Do we need the class or instance scope? + BaseDefinitionNode bdn = (BaseDefinitionNode)this.getAncestorOfType(BaseDefinitionNode.class); + if (bdn instanceof ClassNode) + { + // We must be loose code in a class + scope = typeScope.getStaticScope(); + } + else + { + if (bdn != null && bdn.hasModifier(ASModifier.STATIC) + // Namespaces are always static + || bdn instanceof NamespaceNode) + scope = typeScope.getStaticScope(); + else + scope = typeScope.getInstanceScope(); + } + } + } + ASScope asScope = scope instanceof ASScope ? (ASScope)scope : null; + return asScope; + } + + /** + * Get the node's local start offset. + */ + @Override + public int getStart() + { + final OffsetLookup offsetLookup = tryGetOffsetLookup(); + if (offsetLookup != null) + { + // to handle start offset in an included file + int absoluteOffset = getAbsoluteStart(); + final String pathBeforeCaret = offsetLookup.getFilename(absoluteOffset - 1); + // if the offset is the first offset in the included file return without adjustment + if (pathBeforeCaret != null && pathBeforeCaret.equals(getSourcePath())) + return offsetLookup.getLocalOffset(absoluteOffset-1)+1; + + return offsetLookup.getLocalOffset(absoluteOffset); + } + + return super.getStart(); + } + + /** + * Get the node's local end offset. + */ + @Override + public int getEnd() + { + final OffsetLookup offsetLookup = tryGetOffsetLookup(); + return offsetLookup != null ? + // to handle end offset in an included file + offsetLookup.getLocalOffset(super.getEnd() - 1) + 1 : + super.getEnd(); + } + + /** + * @return The node's absolute start offset. + */ + @Override + public int getAbsoluteStart() + { + return super.getStart(); + } + + /** + * @return The node's absolute end offset. + */ + @Override + public int getAbsoluteEnd() + { + return super.getEnd(); + } + + /** + * Collects the import nodes that are descendants of this node + * but which are not contained within a scoped node. + * <p> + * This is a helper method for {@link IScopedNode#getAllImportNodes}(). + * Since that method walks up the chain of scoped nodes, we don't want + * to look inside scoped nodes that were already processed. + * + * @param importNodes The collection of import nodes being built. + */ + public void collectImportNodes(Collection<IImportNode> importNodes) + { + int childCount = getChildCount(); + for (int i = 0; i < childCount; ++i) + { + IASNode child = getChild(i); + + if (child instanceof IImportNode) + importNodes.add((IImportNode)child); + + else if (!(child instanceof IScopedNode)) + ((NodeBase)child).collectImportNodes(importNodes); + } + } + + /** + * Used only in asserts. + */ + public boolean verify() + { + // Verify the id. + ASTNodeID id = getNodeID(); + assert id != null && + id != ASTNodeID.InvalidNodeID && + id != ASTNodeID.UnknownID : "Definition has bad id"; + + // TODO: Verify the source location eventually. + // assert getSourcePath() != null : "Node has null source path:\n" + toString(); + // assert getStart() != UNKNOWN : "Node has unknown start:\n" + toString(); + // assert getEnd() != UNKNOWN : "Node has unknown end:\n" + toString(); + // assert getLine() != UNKNOWN : "Node has unknown line:\n" + toString(); + // assert getColumn() != UNKNOWN : "Node has unknown column:\n" + toString(); + + // Verify the children. + int n = getChildCount(); + for (int i = 0; i < n; i++) + { + // Any null children? + NodeBase child = (NodeBase)getChild(i); + assert child != null : "Node has null child"; + + // Does each child have this node as its parent? + // (Note: Two node classes override getParent() to not return the parent, + // so exclude these for now.) + if (!(child instanceof NamespaceIdentifierNode || child instanceof QualifiedNamespaceExpressionNode)) + { + assert child.getParent() == this : "Child node has bad parent"; + } + + // Recurse on each child. + child.verify(); + } + + return true; + } + + /** + * Counts various types of nodes that are created, + * as well as the total number of nodes. + */ + private void countNodes() + { + Counter counter = Counter.getInstance(); + counter.incrementCount(getClass().getSimpleName()); + counter.incrementCount("nodes"); + } +} http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/OperatorNodeBase.java ---------------------------------------------------------------------- diff --cc compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/OperatorNodeBase.java index 5de6521,0000000..51eb0fc mode 100644,000000..100644 --- a/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/OperatorNodeBase.java +++ b/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/OperatorNodeBase.java @@@ -1,130 -1,0 +1,132 @@@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.flex.compiler.internal.tree.as; + +import org.apache.flex.compiler.internal.parsing.as.OffsetLookup; +import org.apache.flex.compiler.parsing.IASToken; +import org.apache.flex.compiler.tree.as.IOperatorNode; + +/** + * ActionScript parse tree node representing a binary operator expression (e.g. + * x + 2 or var1 == var2) + */ +public abstract class OperatorNodeBase extends ExpressionNodeBase implements IOperatorNode +{ + /** + * Constructor. + * + * @param operator ASToken holding the operator itself + */ + public OperatorNodeBase(IASToken operator) + { + if (operator != null) + { + operatorStart = operator.getStart(); + setLine(operator.getLine()); + setColumn(operator.getColumn()); ++ setEndLine(operator.getEndLine()); ++ setEndColumn(operator.getEndColumn()); + setSourcePath(operator.getSourcePath()); + } + } + + /** + * Copy constructor. + * + * @param other The node to copy. + */ + protected OperatorNodeBase(OperatorNodeBase other) + { + super(other); + + this.operatorStart = other.operatorStart; + } + + /** + * Offset where the operator starts + */ + protected int operatorStart = UNKNOWN; + + // + // NodeBase overrides + // + + @Override + public boolean isTerminal() + { + return false; + } + + /* + * For debugging only. Builds a string such as <code>"+"</code> from the + * operator. + */ + @Override + protected boolean buildInnerString(StringBuilder sb) + { + sb.append('"'); + sb.append(getOperatorText()); + sb.append('"'); + + return true; + } + + // + // IOperatorNode implementations + // + + @Override + public int getOperatorStart() + { + final OffsetLookup offsetLookup = tryGetOffsetLookup(); + return offsetLookup != null ? + offsetLookup.getLocalOffset(operatorStart) : + operatorStart; + } + + @Override + public int getOperatorEnd() + { + int operatorStart = getOperatorStart(); + return operatorStart != -1 ? operatorStart + getOperatorText().length() : operatorStart; + } + + @Override + public int getOperatorAbsoluteStart() + { + return operatorStart; + } + + @Override + public int getOperatorAbsoluteEnd() + { + return operatorStart != -1 ? operatorStart + getOperatorText().length() : operatorStart; + } + + // + // Other methods + // + + public String getOperatorText() + { + OperatorType operator = getOperator(); + return operator != null ? operator.getOperatorText() : ""; + } +}
