| /******************************************************************************* |
| * Copyright (c) 2007 University of Illinois at Urbana-Champaign and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * UIUC - Initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.photran.internal.core.analysis.binding; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.photran.internal.core.analysis.types.ArraySpec; |
| import org.eclipse.photran.internal.core.analysis.types.DerivedType; |
| import org.eclipse.photran.internal.core.analysis.types.FunctionType; |
| import org.eclipse.photran.internal.core.analysis.types.Type; |
| import org.eclipse.photran.internal.core.analysis.types.TypeProcessor; |
| import org.eclipse.photran.internal.core.lexer.Token; |
| import org.eclipse.photran.internal.core.lexer.Token.FakeToken; |
| import org.eclipse.photran.internal.core.parser.ASTAccessSpecNode; |
| import org.eclipse.photran.internal.core.parser.ASTArraySpecNode; |
| import org.eclipse.photran.internal.core.parser.ASTAttrSpecNode; |
| import org.eclipse.photran.internal.core.parser.ASTAttrSpecSeqNode; |
| import org.eclipse.photran.internal.core.parser.ASTExternalStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTFunctionSubprogramNode; |
| import org.eclipse.photran.internal.core.parser.ASTIntentSpecNode; |
| import org.eclipse.photran.internal.core.parser.ASTInterfaceBlockNode; |
| import org.eclipse.photran.internal.core.parser.ASTMainProgramNode; |
| import org.eclipse.photran.internal.core.parser.ASTModuleNode; |
| import org.eclipse.photran.internal.core.parser.ASTSubroutineSubprogramNode; |
| import org.eclipse.photran.internal.core.parser.ASTTypeSpecNode; |
| import org.eclipse.photran.internal.core.parser.GenericASTVisitor; |
| import org.eclipse.photran.internal.core.parser.IASTListNode; |
| import org.eclipse.photran.internal.core.parser.IASTNode; |
| import org.eclipse.photran.internal.core.parser.ISpecificationStmt; |
| import org.eclipse.photran.internal.core.vpg.EdgeType; |
| import org.eclipse.photran.internal.core.vpg.IPhotranSerializable; |
| import org.eclipse.photran.internal.core.vpg.PhotranTokenRef; |
| import org.eclipse.photran.internal.core.vpg.PhotranVPG; |
| import org.eclipse.photran.internal.core.vpg.PhotranVPGSerializer; |
| import org.eclipse.rephraserengine.core.vpg.IVPGNode; |
| |
| /** |
| * A declaration of a variable, subprogram, main program, interface, |
| * or similar entity. Note that IF-statements, DO-loops, etc. can |
| * also be named, so they may have Definitions as well. |
| * <p> |
| * A Definition's <b>classification</b> indicates the entity it is defining: variable, function, module, etc. |
| * <p> |
| * When applicable, the Definition's <b>type</b> gives the type of that entity (integer, real, etc.); its |
| * <b>array spec</b> may give an array specification (e.g., one-dimensional, indexed from 3 to 5). |
| * |
| * @author Jeff Overbey |
| */ |
| public class Definition implements IPhotranSerializable, Comparable<Definition> |
| { |
| private static final long serialVersionUID = 1L; |
| |
| // ***WARNING*** If the enum values change order or new values are inserted in the middle, serialization will break! |
| |
| /** Enumerates the various entities that can be named with an identifier: variables, functions, namelists, etc. */ |
| public static enum Classification |
| { |
| BLOCK_DATA, |
| COMMON_BLOCK, |
| DERIVED_TYPE, |
| DERIVED_TYPE_COMPONENT, |
| DO, |
| ENTRY, |
| EXTERNAL, |
| FORALL, |
| FUNCTION, |
| IMPLICIT_LOCAL_VARIABLE, |
| IF, |
| INTERFACE, |
| INTRINSIC, |
| MAIN_PROGRAM, |
| MODULE, |
| MODULE_ENTITY_BEFORE_RENAME, // subprogram name before rename |
| NAMELIST, |
| RENAMED_MODULE_ENTITY, // subprogram name after rename |
| SUBROUTINE, |
| SELECT, |
| VARIABLE_DECLARATION { @Override public String toString() { return "Local variable"; } }, //$NON-NLS-1$ |
| WHERE, |
| ENUMERATOR, // F03 |
| IMPLICIT_EXTERNAL_SUBPROGRAM, |
| ; |
| |
| @Override public String toString() |
| { |
| String name = super.toString().replaceAll("_", " "); //$NON-NLS-1$ //$NON-NLS-2$ |
| return name.charAt(0) + name.substring(1).toLowerCase(); |
| } |
| } |
| |
| // ***WARNING*** If the enum values change order or new values are inserted in the middle, serialization will break! |
| |
| /** Enumerates visibilities of module entities */ |
| public static enum Visibility |
| { |
| PUBLIC, |
| PRIVATE; |
| //INHERIT_FROM_SCOPE; |
| |
| @Override public String toString() |
| { |
| String name = super.toString().replaceAll("_", " "); //$NON-NLS-1$ //$NON-NLS-2$ |
| return name.charAt(0) + name.substring(1).toLowerCase(); |
| } |
| } |
| |
| // ***WARNING*** If any fields change, the serialization methods (below) must also change! |
| |
| protected Classification classification; |
| protected PhotranTokenRef tokenRef; |
| protected String declaredName, canonicalizedName; |
| //protected Visibility visibility; |
| protected Type type; |
| protected ArraySpec arraySpec; |
| |
| private boolean subprogramArgument = false; |
| private boolean parameter = false; |
| private boolean typeBoundProcedure = false; |
| private boolean renamedTypeBoundProcedure = false; |
| private boolean target = false; |
| private boolean pointer = false; |
| private boolean allocatable = false; |
| private boolean intent_in = false; |
| private boolean intent_out = false; |
| private boolean optional = false; |
| private boolean save = false; |
| |
| // ***WARNING*** If any fields change, the serialization methods (below) must also change! |
| |
| protected Definition() {} |
| |
| /** Creates a definition and binds it to the given token */ |
| public Definition(String declaredName, PhotranTokenRef tokenRef, Classification classification, /*Visibility visibility,*/ Type type) |
| { |
| this.classification = classification; |
| this.tokenRef = tokenRef; |
| this.declaredName = declaredName; |
| this.canonicalizedName = canonicalize(declaredName); |
| //this.visibility = visibility; //Visibility.INHERIT_FROM_SCOPE; |
| this.type = type; |
| this.arraySpec = null; |
| } |
| |
| protected String canonicalize(String identifier) |
| { |
| return identifier.toLowerCase(); |
| } |
| |
| public boolean matches(String canonicalizedName) |
| { |
| return canonicalizedName != null && |
| (canonicalizedName.equals(this.canonicalizedName) || this.canonicalizedName.matches(canonicalizedName)); |
| } |
| |
| void markAsSubprogramArgument() |
| { |
| this.subprogramArgument = true; |
| } |
| |
| public boolean isSubprogramArgument() |
| { |
| return subprogramArgument; |
| } |
| |
| private boolean isInternal() |
| { |
| IASTNode startFrom = tokenRef.findToken(); |
| if (this.isSubprogram()) startFrom = startFrom.findNearestAncestor(ScopingNode.class); // Look upward from this function/subroutine |
| |
| for (IASTNode parent = startFrom.getParent(); parent != null; parent = parent.getParent()) |
| if (parent instanceof ASTModuleNode |
| || parent instanceof ASTSubroutineSubprogramNode |
| || parent instanceof ASTFunctionSubprogramNode |
| || parent instanceof ASTMainProgramNode) |
| return true; |
| |
| return false; |
| } |
| |
| public boolean isInternalSubprogramDefinition() |
| { |
| return isInternal() && isSubprogram(); |
| } |
| |
| public boolean isExternallyVisibleSubprogramDefinition() |
| { |
| return !isInternal() && isSubprogram(); |
| } |
| |
| public boolean isModuleReference() |
| { |
| return false; |
| } |
| |
| public boolean isLocalVariable() |
| { |
| return classification == Classification.IMPLICIT_LOCAL_VARIABLE |
| || classification == Classification.VARIABLE_DECLARATION |
| ;//|| classification == Classification.IMPLIED_FUNCTION_RESULT_VARIABLE; |
| // TODO: More? |
| } |
| |
| public boolean isDerivedType() |
| { |
| return classification == Classification.DERIVED_TYPE; |
| } |
| |
| public boolean isSubprogram() |
| { |
| return classification == Classification.SUBROUTINE |
| || classification == Classification.FUNCTION; |
| } |
| |
| public boolean isModule() |
| { |
| return classification == Classification.MODULE; |
| } |
| |
| public boolean isExternal() |
| { |
| return classification == Classification.EXTERNAL; |
| } |
| |
| public boolean isImplicitExternalSubprogram() |
| { |
| return classification == Classification.IMPLICIT_EXTERNAL_SUBPROGRAM; |
| } |
| |
| public boolean isInterface() |
| { |
| return classification == Classification.INTERFACE; |
| } |
| |
| public boolean isRenamedModuleEntity() |
| { |
| return classification == Classification.RENAMED_MODULE_ENTITY; |
| } |
| |
| public boolean isModuleEntityBeforeRename() |
| { |
| return classification == Classification.MODULE_ENTITY_BEFORE_RENAME; |
| } |
| |
| public boolean isMainProgram() |
| { |
| return classification == Classification.MAIN_PROGRAM; |
| } |
| |
| public boolean isIntrinsic() |
| { |
| return classification == Classification.INTRINSIC; |
| } |
| |
| public boolean isImplicit() |
| { |
| return this.classification == Classification.IMPLICIT_LOCAL_VARIABLE |
| ;//|| this.classification == Classification.IMPLIED_FUNCTION_RESULT_VARIABLE; |
| } |
| |
| public boolean isNamelist() |
| { |
| return classification == Classification.NAMELIST; |
| } |
| |
| public boolean isCommon() |
| { |
| return classification == Classification.COMMON_BLOCK; |
| } |
| |
| public boolean isBlockData() |
| { |
| return classification == Classification.BLOCK_DATA; |
| } |
| |
| /** @return the classification */ |
| public Classification getClassification() |
| { |
| return classification; |
| } |
| |
| /** Sets the type of this definition (integer, real, etc.) according to the given TypeSpec node */ |
| void setType(ASTTypeSpecNode typeSpecNode) |
| { |
| this.type = Type.parse(typeSpecNode); |
| } |
| |
| /** Sets the type of this definition (integer, real, etc.) */ |
| void setType(Type type) |
| { |
| this.type = type; |
| } |
| |
| /** @return the type of this node (integer, real, etc.) */ |
| public Type getType() |
| { |
| return type; |
| } |
| |
| /** @return the name of the entity this defines, cased according to its declaration */ |
| public String getDeclaredName() |
| { |
| return declaredName; |
| } |
| |
| /** @return the name of the entity this defines, canonicalized by <code>PhotranVPG.canonicalizeIdentifier</code> */ |
| public String getCanonicalizedName() |
| { |
| return canonicalizedName; |
| } |
| |
| /** @return a description of the type of entity being defined */ |
| public String describeClassification() |
| { |
| StringBuilder result = new StringBuilder(); |
| |
| result.append(classification.toString()); |
| |
| if (!type.equals(Type.VOID) && !(type instanceof FunctionType)) |
| { |
| result.append(" - "); //$NON-NLS-1$ |
| result.append(type); |
| if (arraySpec != null) result.append(arraySpec); |
| } |
| |
| // if (visibility != Visibility.INHERIT_FROM_SCOPE) |
| // result.append(" (" + visibility + ")"); |
| |
| return result.toString(); |
| } |
| |
| /** @return the location of the token containing this definition */ |
| public PhotranTokenRef getTokenRef() |
| { |
| return tokenRef; |
| } |
| |
| /** Sets the array spec for this definition */ |
| void setArraySpec(ASTArraySpecNode arraySpecNode) |
| { |
| if (arraySpecNode != null) |
| this.arraySpec = new ArraySpec(arraySpecNode); |
| } |
| |
| /** @return the array spec for this definition */ |
| public ArraySpec getArraySpec() |
| { |
| return arraySpec; |
| } |
| |
| /** @return true iff this is the definition of an array */ |
| public boolean isArray() |
| { |
| return arraySpec != null; |
| } |
| |
| // <AttrSpecSeq> ::= |
| // T_COMMA <AttrSpec> |
| // | @:<AttrSpecSeq> T_COMMA <AttrSpec> |
| |
| /** Sets the attributes according to an AttrSpecSeq node */ |
| void setAttributes(IASTListNode<ASTAttrSpecSeqNode> listNode, ScopingNode setInScope) |
| { |
| if (listNode == null) return; |
| |
| for (int i = 0; i < listNode.size(); i++) |
| setAttribute(listNode.get(i).getAttrSpec(), setInScope); |
| } |
| |
| // # R503 |
| // <AttrSpec> ::= |
| // T_PARAMETER |
| // | <AccessSpec> |
| // | T_ALLOCATABLE |
| // | T_DIMENSION T_LPAREN <ArraySpec> T_RPAREN |
| // | T_EXTERNAL |
| // | T_INTENT T_LPAREN <IntentSpec> T_RPAREN |
| // | T_INTRINSIC |
| // | T_OPTIONAL |
| // | T_POINTER |
| // | T_SAVE |
| // | T_TARGET |
| |
| private void setAttribute(ASTAttrSpecNode attrSpec, ScopingNode setInScope) |
| { |
| ASTArraySpecNode arraySpec = attrSpec.getArraySpec(); |
| ASTAccessSpecNode accessSpec = attrSpec.getAccessSpec(); |
| |
| if (arraySpec != null) |
| setArraySpec(arraySpec); |
| else if (accessSpec != null) |
| setVisibility(accessSpec, setInScope); |
| else if (attrSpec.isParameter()) |
| setParameter(); |
| else if (attrSpec.isPointer()) |
| setPointer(); |
| else if (attrSpec.isTarget()) |
| setTarget(); |
| else if (attrSpec.isAllocatable()) |
| setAllocatable(); |
| else if (attrSpec.isIntent()) |
| setIntent(attrSpec.getIntentSpec()); |
| else if (attrSpec.isOptional()) |
| setOptional(); |
| else if (attrSpec.isSave()) |
| setSave(); |
| } |
| |
| // # R511 |
| // <AccessSpec> ::= |
| // T_PUBLIC |
| // | T_PRIVATE |
| |
| void setVisibility(ASTAccessSpecNode accessSpec, ScopingNode setInScope) |
| { |
| // if (accessSpec.isPublic()) |
| // this.visibility = Visibility.PUBLIC; |
| // else if (accessSpec.isPrivate()) |
| // this.visibility = Visibility.PRIVATE; |
| |
| PhotranVPG.getProvider().markDefinitionVisibilityInScope( |
| tokenRef, |
| setInScope, |
| accessSpec.isPrivate() ? Visibility.PRIVATE : Visibility.PUBLIC); |
| } |
| |
| /** @return true iff this entity was declared as a PARAMETER (i.e., it a constant variable) */ |
| public boolean isParameter() { return parameter; } |
| void setParameter() { this.parameter = true; } |
| |
| /** @return true iff this entity was declared as a POINTER */ |
| public boolean isPointer() { return pointer; } |
| void setPointer() { this.pointer = true; } |
| |
| /** @return true iff this entity was declared as a POINTER */ |
| public boolean isTarget() { return target; } |
| void setTarget() { this.target = true; } |
| |
| /** @return true iff this entity was declared as ALLOCATABLE */ |
| public boolean isAllocatable() { return allocatable; } |
| void setAllocatable() { this.allocatable = true; } |
| |
| /** @return true iff this entity was declared as OPTIONAL */ |
| public boolean isOptional() { return optional; } |
| void setOptional() { this.optional = true; } |
| |
| /** @return true iff this entity was declared as SAVE */ |
| public boolean isSave() { return save; } |
| void setSave() { this.save = true; } |
| |
| /** @return true iff this entity was declared with INTENT(IN) */ |
| public boolean isIntentIn() { return intent_in; } |
| /** @return true iff this entity was declared with INTENT(OUT) */ |
| public boolean isIntentOut() { return intent_out; } |
| void setIntent(ASTIntentSpecNode intent) |
| { |
| if (intent.isIntentIn() || intent.isIntentInOut()) |
| this.intent_in = true; |
| if (intent.isIntentOut() || intent.isIntentInOut()) |
| this.intent_out = true; |
| } |
| |
| // boolean isPublic() |
| // { |
| // // TODO: Can interface blocks contain PRIVATE statements or can their members have visibilities specified? |
| // return this.visibility.equals(Visibility.PUBLIC); |
| // //|| this.visibility.equals(Visibility.INHERIT_FROM_SCOPE) && getTokenRef().findToken().getEnclosingScope().isDefaultVisibilityPrivate() == false; |
| // } |
| |
| void markAsTypeBoundProcedure(boolean renamed) |
| { |
| this.typeBoundProcedure = true; |
| this.renamedTypeBoundProcedure = renamed; |
| } |
| |
| public boolean isTypeBoundProcedure() |
| { |
| return this.typeBoundProcedure; |
| } |
| |
| public boolean isRenamedTypeBoundProcedure() |
| { |
| return this.renamedTypeBoundProcedure; |
| } |
| |
| public IMarker createMarker() |
| { |
| try |
| { |
| Token token = tokenRef.findTokenOrReturnNull(); |
| if (token == null || token.getPhysicalFile().getIFile() == null) return null; |
| IMarker marker = token.getPhysicalFile().getIFile().createMarker(IMarker.TEXT); |
| marker.setAttribute(IMarker.CHAR_START, token.getFileOffset()); |
| marker.setAttribute(IMarker.CHAR_END, token.getFileOffset()+token.getLength()); |
| return marker; |
| } |
| catch (CoreException e) |
| { |
| return null; |
| } |
| } |
| |
| /** @return all workspace references to this definition, not including renamed references */ |
| public Set<PhotranTokenRef> findAllReferences(boolean shouldBindInterfacesAndExternals) |
| { |
| if (this.isImpliedFunctionResultVar() && findEnclosingFunctionDefinition() != null) |
| return findAllReferencesToEnclosingSubprogramInstead(shouldBindInterfacesAndExternals); |
| else |
| return internalFindAllReferences(shouldBindInterfacesAndExternals); |
| } |
| |
| private Set<PhotranTokenRef> findAllReferencesToEnclosingSubprogramInstead(boolean aggressive) |
| { |
| Definition fnDef = findEnclosingFunctionDefinition(); |
| Set<PhotranTokenRef> result = fnDef.internalFindAllReferences(aggressive); |
| result.add(fnDef.getTokenRef()); |
| result.remove(this.getTokenRef()); |
| return result; |
| } |
| |
| private boolean isImpliedFunctionResultVar() |
| { |
| if (!this.isLocalVariable()) return false; |
| |
| ASTFunctionSubprogramNode fn = findEnclosingFunction(); |
| if (fn != null && !fn.getFunctionStmt().hasResultClause()) |
| { |
| Definition fnDef = findEnclosingFunctionDefinition(); |
| return this.getCanonicalizedName().equals(fnDef.getCanonicalizedName()); |
| } |
| return false; |
| } |
| |
| private ASTFunctionSubprogramNode findEnclosingFunction() |
| { |
| return this.getTokenRef().findToken().findNearestAncestor(ASTFunctionSubprogramNode.class); |
| } |
| |
| private Definition findEnclosingFunctionDefinition() |
| { |
| ASTFunctionSubprogramNode fn = findEnclosingFunction(); |
| return fn == null ? null : PhotranVPG.getInstance().getDefinitionFor(fn.getRepresentativeToken()); |
| } |
| |
| private Set<PhotranTokenRef> internalFindAllReferences(boolean shouldBindInterfacesAndExternals) |
| { |
| if ((this.isSubprogram() || this.isExternal()) && shouldBindInterfacesAndExternals) |
| return internalFindAllReferencesToSubprogramAggressively(); |
| else |
| return findAllImmediateReferences(); |
| } |
| |
| private Set<PhotranTokenRef> findAllImmediateReferences() |
| { |
| Set<PhotranTokenRef> result = new TreeSet<PhotranTokenRef>(); |
| addImmediateBindings(result); |
| if (this.isFunction()) |
| matchFunctionAndImpliedResultVariable(result); |
| result.remove(this.getTokenRef()); // By contract, the set of references does not include this |
| return result; |
| } |
| |
| private void matchFunctionAndImpliedResultVariable(Collection<PhotranTokenRef> result) |
| { |
| try |
| { |
| ASTFunctionSubprogramNode fn = findEnclosingFunction(); |
| if (fn == null || fn.getFunctionStmt().hasResultClause()) return; |
| |
| for (Definition def : fn.getAllDefinitions()) |
| { |
| if (def != null && def.getCanonicalizedName().equals(this.getCanonicalizedName())) |
| { |
| result.add(def.getTokenRef()); |
| result.addAll(def.internalFindAllReferences(false)); |
| } |
| } |
| } |
| catch (Throwable t) |
| { |
| // Ignore |
| } |
| } |
| |
| private boolean isFunction() |
| { |
| return this.getClassification().equals(Classification.FUNCTION); |
| } |
| |
| private void addImmediateBindings(Collection<PhotranTokenRef> result) |
| { |
| result.add(this.getTokenRef()); |
| |
| for (IVPGNode<Token> r : tokenRef.followIncoming(EdgeType.BINDING_EDGE_TYPE)) |
| result.add((PhotranTokenRef)r); |
| } |
| |
| private Set<PhotranTokenRef> internalFindAllReferencesToSubprogramAggressively() |
| { |
| assert this.isSubprogram() || this.isExternal(); |
| |
| Collection<Definition> subprogramDefinitions; |
| if (this.isInInterfaceBlock()) |
| subprogramDefinitions = this.resolveInterfaceBinding(); |
| else if (this.isInExternalStmt()) |
| subprogramDefinitions = this.resolveExternalBinding(); |
| else if (this.isExternallyVisibleSubprogramDefinition()) |
| subprogramDefinitions = this.findAllSimilarlyNamedExternalSubprograms(); |
| else // probably an internal subprogram |
| return findAllImmediateReferences(); |
| |
| Set<PhotranTokenRef> result = new TreeSet<PhotranTokenRef>(); |
| result.addAll(findAllImmediateReferences()); // e.g., PRIVATE referring to subprogram in an INTERFACE block |
| for (Definition subprogram : subprogramDefinitions) |
| result.addAll(subprogram.internalFindAllReferencesToSubprogIncludingInterfacesAndExternalStmts()); |
| result.remove(this.getTokenRef()); // By contract, the set of references does not include this |
| return result; |
| } |
| |
| private Set<PhotranTokenRef> internalFindAllReferencesToSubprogIncludingInterfacesAndExternalStmts() |
| { |
| assert this.isExternallyVisibleSubprogramDefinition(); |
| |
| Set<PhotranTokenRef> result = new TreeSet<PhotranTokenRef>(); |
| addExternalSubprogramDefinitions(result); |
| addInterfaceDecls(result); |
| addExternalStmts(result); |
| return result; |
| } |
| |
| private void addExternalSubprogramDefinitions(Collection<PhotranTokenRef> result) |
| { |
| for (Definition externalSubprogDef : this.findAllSimilarlyNamedExternalSubprograms()) |
| { |
| result.add(externalSubprogDef.getTokenRef()); |
| result.addAll(externalSubprogDef.internalFindAllReferences(false)); |
| } |
| } |
| |
| private void addInterfaceDecls(Collection<PhotranTokenRef> result) |
| { |
| for (Definition interfaceDef : this.findMatchingDeclarationsInInterfaces()) |
| { |
| result.add(interfaceDef.getTokenRef()); |
| result.addAll(interfaceDef.internalFindAllReferences(false)); |
| } |
| } |
| |
| private void addExternalStmts(Collection<PhotranTokenRef> result) |
| { |
| for (Definition externalDef : this.findMatchingDeclarationsInExternalStmts()) |
| { |
| result.add(externalDef.getTokenRef()); |
| result.addAll(externalDef.internalFindAllReferences(false)); |
| } |
| } |
| |
| /** @return true iff this is an entity defined inside an INTERFACE block */ |
| public boolean isExternalSubprogramReferenceInInterfaceBlock() |
| { |
| if (!isInInterfaceBlock()) return false; |
| |
| Token token = getTokenRef().findToken(); |
| ScopingNode scopeOfThisDef = token.getEnclosingScope(); |
| |
| HashSet<Definition> result = collectResolutions(token, scopeOfThisDef); |
| return !resolvesToSubprogramArgument(result); |
| } |
| |
| // private boolean isInAnonymousInterfaceBlock() |
| // { |
| // if (!isInInterfaceBlock()) return false; |
| // ASTInterfaceStmtNode stmt = tokenRef.findToken().findNearestAncestor(ASTInterfaceBlockNode.class).getInterfaceStmt(); |
| // return stmt.getGenericName() == null && stmt.getGenericSpec() == null; |
| // } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * @return if this is a subprogram declared in an INTERFACE block, a list of all possible matching subprogram |
| * Definitions; otherwise, <code>null</code> |
| * |
| * @see #findMatchingDeclarationsInInterfaces() |
| */ |
| public Collection<Definition> resolveInterfaceBinding() |
| { |
| if (!isInInterfaceBlock()) return Collections.emptySet(); |
| |
| Token token = getTokenRef().findToken(); |
| ScopingNode scopeOfThisDef = token.getEnclosingScope(); |
| ScopingNode parentScope = scopeOfThisDef.getEnclosingScope(); |
| |
| HashSet<Definition> result = collectResolutions(token, scopeOfThisDef); |
| if (resolvesToSubprogramArgument(result)) return Collections.emptyList(); |
| if (needToResolveInParentScope(result)) result = collectResolutions(token, parentScope); |
| result.addAll(collectMatchingExternalSubprograms(token)); |
| return result; |
| } |
| |
| /** @return true iff this is an entity defined inside an INTERFACE block */ |
| public boolean isInInterfaceBlock() |
| { |
| Token tok = getTokenRef().findTokenOrReturnNull(); |
| return tok != null && tok.findNearestAncestor(ASTInterfaceBlockNode.class) != null; |
| } |
| |
| private HashSet<Definition> collectResolutions(Token token, ScopingNode scope) |
| { |
| HashSet<Definition> result = new HashSet<Definition>(); |
| for (PhotranTokenRef d : scope.manuallyResolve(new FakeToken(token, token.getText()))) |
| { |
| Definition def = PhotranVPG.getInstance().getDefinitionFor(d); |
| if (def != null) |
| if ((def.isSubprogram() && !def.isInInterfaceBlock()) || def.isSubprogramArgument()) |
| result.add(def); |
| } |
| return result; |
| } |
| |
| private boolean resolvesToSubprogramArgument(Set<Definition> listOfDefs) |
| { |
| for (Definition def : listOfDefs) |
| if (def != null && def.isSubprogramArgument()) |
| return true; |
| |
| return false; |
| } |
| |
| private boolean needToResolveInParentScope(HashSet<Definition> result) |
| { |
| // If the subprogram declaration in the INTERFACE block only resolves to |
| // itself, then we should check the parent scope: There might be a |
| // matching subprogram imported from a module (or defined as an external |
| // subprogram) there. |
| |
| return result.size() < 2; |
| } |
| |
| private ArrayList<Definition> collectMatchingExternalSubprograms(Token token) |
| { |
| return PhotranVPG.getInstance().findAllExternalSubprogramsNamed(token.getText()); |
| } |
| |
| /** |
| * @return if this is an external subprogram, a list of all other external subprograms with the same name |
| * (i.e., subprograms that might also be referenced by a similar INTERFACE block or EXTERNAL statement) |
| */ |
| public Collection<Definition> findAllSimilarlyNamedExternalSubprograms() |
| { |
| return PhotranVPG.getInstance().findAllExternalSubprogramsNamed(this.canonicalizedName); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * @return if this is a subprogram declared in an EXTERNAL statement, a list of all possible matching subprogram |
| * Definitions; otherwise, <code>null</code> |
| * |
| * @see #findMatchingDeclarationsInExternalStmts() |
| */ |
| public Collection<Definition> resolveExternalBinding() |
| { |
| if (!isInExternalStmt()) return Collections.emptySet(); |
| |
| Token token = getTokenRef().findToken(); |
| ScopingNode scopeOfThisDef = token.getEnclosingScope(); |
| ScopingNode parentScope = scopeOfThisDef.getEnclosingScope(); |
| |
| HashSet<Definition> result = collectExternalResolutions(token, scopeOfThisDef); |
| if (resolvesToSubprogramArgument(result)) return Collections.emptyList(); |
| if (needToResolveInParentScope(result)) result = collectResolutions(token, parentScope); |
| result.addAll(collectMatchingExternalSubprograms(token)); |
| return result; |
| } |
| |
| /** @return true iff this is an entity defined inside an EXTERNAL statement */ |
| public boolean isInExternalStmt() |
| { |
| Token tok = getTokenRef().findTokenOrReturnNull(); |
| return tok != null && tok.findNearestAncestor(ASTExternalStmtNode.class) != null; |
| } |
| |
| private HashSet<Definition> collectExternalResolutions(Token token, ScopingNode scope) |
| { |
| HashSet<Definition> result = new HashSet<Definition>(); |
| for (PhotranTokenRef d : scope.manuallyResolve(new FakeToken(token, token.getText()))) |
| { |
| Definition def = PhotranVPG.getInstance().getDefinitionFor(d); |
| if (def != null) |
| if ((def.isSubprogram() && !def.isInInterfaceBlock()) || def.isSubprogramArgument()) |
| result.add(def); |
| } |
| return result; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * @return if this is an external subprogram, a list of all possible matching declarations in INTERFACE blocks; |
| * otherwise, the empty set |
| * |
| * @see #resolveInterfaceBinding() |
| */ |
| public Collection<Definition> findMatchingDeclarationsInInterfaces() |
| { |
| if /*(isModuleSubprogram()) |
| return findMatchesForModuleSubprogram(); |
| else if*/ (isExternallyVisibleSubprogramDefinition()) |
| return PhotranVPG.getInstance().findAllDeclarationsInInterfacesForExternalSubprogram(canonicalizedName); |
| else |
| return Collections.emptySet(); |
| } |
| |
| // private boolean isModuleSubprogram() |
| // { |
| // return isSubprogram() && getTokenRef().findToken().findNearestAncestor(ASTModuleNode.class) != null; |
| // } |
| // |
| // private List<PhotranTokenRef> findMatchesForModuleSubprogram() |
| // { |
| // // TODO Auto-generated method stub |
| // return null; |
| // } |
| |
| /** |
| * @return if this is an external subprogram, a list of all possible matching declarations in EXTERNAL statements; |
| * otherwise, <code>null</code> |
| * |
| * @see #resolveExternalBinding() |
| */ |
| public Collection<Definition> findMatchingDeclarationsInExternalStmts() |
| { |
| return PhotranVPG.getInstance().findAllDeclarationsInExternalStmts(canonicalizedName); |
| } |
| |
| @Override public String toString() |
| { |
| return canonicalizedName |
| + " - " //$NON-NLS-1$ |
| + classification |
| + (subprogramArgument ? " (Subprogram Argument)" : "") //$NON-NLS-1$ //$NON-NLS-2$ |
| + " (" //$NON-NLS-1$ |
| + tokenRef.getFilename() |
| + ", offset " //$NON-NLS-1$ |
| + tokenRef.getOffset() |
| + ")"; //$NON-NLS-1$ |
| } |
| |
| |
| @Override public boolean equals(Object other) |
| { |
| if (!(other instanceof Definition)) return false; |
| |
| Definition o = (Definition)other; |
| return equals(this.arraySpec, o.arraySpec) |
| && equals(this.canonicalizedName, o.canonicalizedName) |
| && equals(this.classification, o.classification) |
| && this.parameter == o.parameter |
| && this.subprogramArgument == o.subprogramArgument |
| && equals(this.tokenRef, o.tokenRef) |
| && equals(this.type, o.type) |
| ; // && equals(this.visibility, o.visibility); |
| } |
| |
| private boolean equals(Object a, Object b) |
| { |
| if (a == null && b == null) |
| return true; |
| else if (a != null && b != null) |
| return a.equals(b); |
| else |
| return false; |
| } |
| |
| @Override public int hashCode() |
| { |
| return hashCode(this.arraySpec) |
| + hashCode(this.canonicalizedName) |
| + hashCode(this.classification) |
| + (this.parameter ? 1 : 0) |
| + (this.subprogramArgument ? 1 : 0) |
| + hashCode(this.tokenRef) |
| + 0 //hashCode(this.type) |
| ; // + hashCode(this.visibility); |
| } |
| |
| private int hashCode(Object o) |
| { |
| return o == null ? 0 : o.hashCode(); |
| } |
| |
| public int compareTo(Definition o) |
| { |
| return canonicalizedName.compareTo(o.canonicalizedName); |
| } |
| |
| public String describe() |
| { |
| String commentsBefore = "\n", name = getCanonicalizedName(), commentsAfter = ""; //$NON-NLS-1$ //$NON-NLS-2$ |
| boolean isScopingUnit = false; |
| |
| Token tok = tokenRef.findTokenOrReturnNull(); |
| if (tok != null) |
| { |
| if (!name.equals("(anonymous)")) //$NON-NLS-1$ |
| name = tok.getText(); |
| |
| ScopingNode localScope = tok.getLocalScope(); |
| if (localScope != null) |
| { |
| isScopingUnit = (localScope != tok.getEnclosingScope()); |
| |
| IASTNode headerStmt; |
| if (isScopingUnit) |
| headerStmt = localScope.getHeaderStmt(); |
| else |
| headerStmt = findEnclosingSpecificationStmt(tok); |
| |
| if (headerStmt != null) |
| { |
| Token first = headerStmt.findFirstToken(); |
| Token last = headerStmt.findLastToken(); |
| if (first != null) commentsBefore = first.getWhiteBefore(); |
| if (last != null) commentsAfter = last.getWhiteAfter(); |
| |
| Token after = findFirstTokenAfter(last, localScope); |
| if (after != null && !startsWithBlankLine(after.getWhiteBefore())) |
| commentsAfter += after.getWhiteBefore(); |
| } |
| } |
| } |
| |
| return commentsBefore + describe(name) + "\n" + commentsAfter; //$NON-NLS-1$ |
| } |
| |
| private boolean startsWithBlankLine(String string) |
| { |
| while (string.startsWith(" ") || string.startsWith("\t")) //$NON-NLS-1$ //$NON-NLS-2$ |
| string = string.substring(1); |
| |
| return string.startsWith("\r") || string.startsWith("\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| private Token findFirstTokenAfter(final Token target, ScopingNode localScope) |
| { |
| class TokenFinder extends GenericASTVisitor |
| { |
| private Token lastToken = null; |
| private Token result = null; |
| |
| @Override public void visitToken(Token thisToken) |
| { |
| if (lastToken == target) |
| result = thisToken; |
| |
| lastToken = thisToken; |
| } |
| } |
| |
| TokenFinder t = new TokenFinder(); |
| localScope.accept(t); |
| return t.result; |
| } |
| |
| private IASTNode findEnclosingSpecificationStmt(Token tok) |
| { |
| for (IASTNode candidate = tok.getParent(); candidate != null; candidate = candidate.getParent()) |
| if (candidate instanceof ISpecificationStmt) |
| return candidate; |
| |
| return null; |
| } |
| |
| private String describe(String name) |
| { |
| StringBuilder sb = new StringBuilder(); |
| |
| switch (classification) |
| { |
| case VARIABLE_DECLARATION: |
| sb.append("! "); sb.append(describeClassification()); sb.append('\n'); //$NON-NLS-1$ |
| sb.append(describeType()); |
| sb.append(":: "); //$NON-NLS-1$ |
| sb.append(name); |
| break; |
| |
| case IMPLICIT_LOCAL_VARIABLE: |
| sb.append("! "); sb.append(describeClassification()); sb.append('\n'); //$NON-NLS-1$ |
| sb.append(describeType()); |
| sb.append(":: "); //$NON-NLS-1$ |
| sb.append(name); |
| break; |
| |
| case SUBROUTINE: |
| sb.append("subroutine "); //$NON-NLS-1$ |
| sb.append(name); |
| //TODO: describeParameters(def.getType()); |
| break; |
| |
| case FUNCTION: |
| //TODO: describeReturnType(def.getType()); |
| sb.append("function "); //$NON-NLS-1$ |
| sb.append(name); |
| //TODO: describeParameters(def.getType()); |
| break; |
| |
| case INTERFACE: |
| sb.append("interface "); //$NON-NLS-1$ |
| sb.append(name); |
| // TODO: Describe contents |
| break; |
| |
| case MODULE: |
| sb.append("module "); //$NON-NLS-1$ |
| sb.append(name); |
| // TODO: Describe contents |
| break; |
| |
| case DERIVED_TYPE: |
| sb.append("type :: "); //$NON-NLS-1$ |
| sb.append(name); |
| // TODO: Describe contents |
| break; |
| |
| default: |
| sb.append("! "); sb.append(describeClassification()); sb.append('\n'); //$NON-NLS-1$ |
| sb.append(name); |
| } |
| |
| return sb.toString(); |
| } |
| |
| private String describeType() |
| { |
| return type.processUsing(new TypeProcessor<String>() |
| { |
| @Override |
| public String ifCharacter(Type type) |
| { |
| return "character "; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String ifComplex(Type type) |
| { |
| return "complex "; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String ifDerivedType(String derivedTypeName, |
| DerivedType type) |
| { |
| return "type(" + derivedTypeName + ") "; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| @Override |
| public String ifDoublePrecision(Type type) |
| { |
| return "double precision "; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String ifFunctionType(String name, |
| FunctionType functionType) |
| { |
| return "function "; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String ifInteger(Type type) |
| { |
| return "integer "; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String ifLogical(Type type) |
| { |
| return "logical "; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String ifReal(Type type) |
| { |
| return "real "; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String ifUnclassified(Type type) |
| { |
| return ""; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String ifUnknown(Type type) |
| { |
| return ""; //$NON-NLS-1$ |
| } |
| }); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // IPhotranSerializable Implementation |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| public static Definition readFrom(InputStream in) throws IOException |
| { |
| Definition result = new Definition(); |
| result.classification = Classification.values()[PhotranVPGSerializer.deserialize(in)]; |
| result.tokenRef = PhotranVPGSerializer.deserialize(in); |
| result.declaredName = PhotranVPGSerializer.deserialize(in); |
| result.canonicalizedName = PhotranVPG.canonicalizeIdentifier(result.declaredName); |
| result.type = PhotranVPGSerializer.deserialize(in); |
| result.arraySpec = PhotranVPGSerializer.deserialize(in); |
| result.subprogramArgument = PhotranVPGSerializer.deserialize(in); |
| result.parameter = PhotranVPGSerializer.deserialize(in); |
| result.typeBoundProcedure = PhotranVPGSerializer.deserialize(in); |
| result.renamedTypeBoundProcedure = PhotranVPGSerializer.deserialize(in); |
| result.pointer = PhotranVPGSerializer.deserialize(in); |
| result.target = PhotranVPGSerializer.deserialize(in); |
| result.allocatable = PhotranVPGSerializer.deserialize(in); |
| result.intent_in = PhotranVPGSerializer.deserialize(in); |
| result.intent_out = PhotranVPGSerializer.deserialize(in); |
| result.optional = PhotranVPGSerializer.deserialize(in); |
| result.save = PhotranVPGSerializer.deserialize(in); |
| return result; |
| } |
| |
| public void writeTo(OutputStream out) throws IOException |
| { |
| PhotranVPGSerializer.serialize(classification.ordinal(), out); |
| PhotranVPGSerializer.serialize(tokenRef, out); |
| PhotranVPGSerializer.serialize(declaredName, out); |
| PhotranVPGSerializer.serialize(type, out); |
| PhotranVPGSerializer.serialize(arraySpec, out); |
| PhotranVPGSerializer.serialize(subprogramArgument, out); |
| PhotranVPGSerializer.serialize(parameter, out); |
| PhotranVPGSerializer.serialize(typeBoundProcedure, out); |
| PhotranVPGSerializer.serialize(renamedTypeBoundProcedure, out); |
| PhotranVPGSerializer.serialize(pointer, out); |
| PhotranVPGSerializer.serialize(target, out); |
| PhotranVPGSerializer.serialize(allocatable, out); |
| PhotranVPGSerializer.serialize(intent_in, out); |
| PhotranVPGSerializer.serialize(intent_out, out); |
| PhotranVPGSerializer.serialize(optional, out); |
| PhotranVPGSerializer.serialize(save, out); |
| } |
| |
| public char getSerializationCode() |
| { |
| return PhotranVPGSerializer.CLASS_DEFINITION; |
| } |
| } |