Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/infer/InferredType.java')
-rw-r--r--bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/infer/InferredType.java1411
1 files changed, 942 insertions, 469 deletions
diff --git a/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/infer/InferredType.java b/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/infer/InferredType.java
index 90f3eae6..ec865d57 100644
--- a/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/infer/InferredType.java
+++ b/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/infer/InferredType.java
@@ -1,469 +1,942 @@
-/*******************************************************************************
- * Copyright (c) 2005, 2011 IBM Corporation 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:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.wst.jsdt.core.infer;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import org.eclipse.wst.jsdt.core.ast.IASTNode;
-import org.eclipse.wst.jsdt.core.ast.IAbstractFunctionDeclaration;
-import org.eclipse.wst.jsdt.core.ast.IFunctionDeclaration;
-import org.eclipse.wst.jsdt.core.compiler.CharOperation;
-import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode;
-import org.eclipse.wst.jsdt.internal.compiler.ast.MethodDeclaration;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.ArrayBinding;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.ClassScope;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.MethodBinding;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.MultipleTypeBinding;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.Scope;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.TagBits;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding;
-import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeConstants;
-import org.eclipse.wst.jsdt.internal.compiler.util.HashtableOfObject;
-
-
-/**
- * The represenation of an inferred type.
- *
- * Provisional API: This class/interface is part of an interim API that is still under development and expected to
- * change significantly before reaching stability. It is being made available at this early stage to solicit feedback
- * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
- * (repeatedly) as the API evolves.
- */
-public class InferredType extends ASTNode {
-
- char [] name;
- public ArrayList methods;
- public InferredAttribute[] attributes=new InferredAttribute[5];
- public int numberAttributes=0;
- HashtableOfObject attributesHash = new HashtableOfObject();
- public InferredType superClass;
-
- public InferredType referenceClass;
-
- public SourceTypeBinding binding;
- public boolean isDefinition;
- private TypeBinding resolvedType;
- public ClassScope scope;
- ReferenceBinding resolvedSuperType;
-
- public boolean isArray=false;
- public boolean isAnonymous=false;
- public boolean isObjectLiteral=false;
-
- private int nameStart = -1;
-
- public String inferenceProviderID;
- public String inferenceStyle;
-
- public ArrayList mixins;
-
- public final static char[] OBJECT_NAME=new char[]{'O','b','j','e','c','t'};
- public final static char[] OBJECT_LITERAL_NAME = new char[]{'{','}'};
-
- public final static char[] ARRAY_NAME=new char[]{'A','r','r','a','y'};
- public final static char[] FUNCTION_NAME=new char[]{'F','u','n','c','t','i','o','n'};
- public final static char[] GLOBAL_NAME=new char[]{'G','l','o','b','a','l'};
-
- public Object userData;
-
- boolean allStatic=false;
-
- /**
- * Create a new inferred type
- *
- * @param className inferred type name
- */
- public InferredType(char [] className)
- {
- this.name=className;
- this.sourceStart=-1;
- }
-
- /**
- * Gets the name of the inferred type
- *
- * @return the inferred type name
- */
- public char [] getName() {
- return name;
- }
-
- /**
- * Get the superclass name of the inferred type
- *
- * @return superclass name
- */
- public char [] getSuperClassName()
- {
- return superClass!=null ? superClass.getName() : OBJECT_NAME;
- }
-
- /**
- * Add a new inferred attribute to the inferred type
- *
- * @param name the attribute name
- * @param definer the ASTNode which this attribute is inferred from
- * @param nameStart character position (in the source) of the attribute name
- * @return a new InferredAttribute
- */
- public InferredAttribute addAttribute(char [] name, IASTNode definer, int nameStart)
- {
- InferredAttribute attribute = findAttribute(name);
- if (attribute==null)
- {
- attribute=new InferredAttribute(name, this, definer);
- attribute.node=(ASTNode)definer;
-
- if (this.numberAttributes == this.attributes.length)
-
- System.arraycopy(
- this.attributes,
- 0,
- this.attributes = new InferredAttribute[this.numberAttributes * 2],
- 0,
- this.numberAttributes );
- this.attributes [this.numberAttributes ++] = attribute;
-
-
- attributesHash.put(name, attribute);
-
- if (!isAnonymous) {
- this.updatePositions(definer.sourceStart(), definer.sourceEnd());
- }
- }
- attribute.nameStart = nameStart;
- return attribute;
- }
-
- /**
- * Add an InferredAttribute to this inferred type.
- *
- * @param newAttribute the attribute to add.
- * @return
- */
- public InferredAttribute addAttribute(InferredAttribute newAttribute)
- {
- IASTNode definer=newAttribute.node;
- InferredAttribute attribute = findAttribute(newAttribute.name);
- if (attribute==null)
- {
-
- if (this.numberAttributes == this.attributes.length)
-
- System.arraycopy(
- this.attributes,
- 0,
- this.attributes = new InferredAttribute[this.numberAttributes * 2],
- 0,
- this.numberAttributes );
- this.attributes [this.numberAttributes ++] = newAttribute;
-
-
- attributesHash.put(newAttribute.name, newAttribute);
-
- if (!isAnonymous) {
- if (definer != null) {
- this.updatePositions(definer.sourceStart(), definer.sourceEnd());
- }
- else {
- this.updatePositions(newAttribute.sourceStart(), newAttribute.sourceEnd());
- }
- }
- }
- return newAttribute;
- }
- /**
- * Find the inferred attribute with the given name
- *
- * @param name name of the attribute to find
- * @return the found InferredAttribute, or null if not found
- */
- public InferredAttribute findAttribute(char [] name)
- {
- return (InferredAttribute)attributesHash.get(name);
-// if (attributes!=null)
-// for (Iterator attrIterator = attributes.iterator(); attrIterator.hasNext();) {
-// InferredAttribute attribute = (InferredAttribute) attrIterator.next();
-// if (CharOperation.equals(name,attribute.name))
-// return attribute;
-// }
-// return null;
- }
-
-
- /**
- * Add a new constructor method to the inferred type
- *
- * @param methodName name of the method to add
- * @param functionDeclaration the AST Node containing the method bode
- * @param nameStart character position (in the source) of the method name
- * @return a new inferred method
- */
- public InferredMethod addConstructorMethod(char [] methodName, IFunctionDeclaration functionDeclaration, int nameStart) {
- InferredMethod method = this.addMethod(methodName, functionDeclaration, nameStart, true);
- method.isConstructor = true;
- this.setNameStart(nameStart);
- method.getFunctionDeclaration().setInferredType(this);
- return method;
- }
-
- /**
- * Add a new method to the inferred type
- *
- * @param methodName name of the method to add
- * @param functionDeclaration the AST Node containing the method bode
- * @param nameStart character position (in the source) of the method name
- * @return a new inferred method
- */
- public InferredMethod addMethod(char [] methodName, IFunctionDeclaration functionDeclaration, int nameStart) {
- return this.addMethod(methodName, functionDeclaration, nameStart, false);
- }
-
- /**
- * Add a new method to the inferred type
- *
- * @param methodName name of the method to add
- * @param functionDeclaration the AST Node containing the method bode
- * @param isConstructor true if it is a constructor
- * @return a new inferred method
- */
- private InferredMethod addMethod(char [] methodName, IFunctionDeclaration functionDeclaration, int nameStart, boolean isConstructor) {
- MethodDeclaration methodDeclaration = (MethodDeclaration)functionDeclaration;
- InferredMethod method = findMethod(methodName, methodDeclaration);
- if (method==null) {
- method=new InferredMethod(methodName,methodDeclaration,this);
- if (methodDeclaration.inferredMethod==null)
- methodDeclaration.inferredMethod = method;
- else
- {
- if (isConstructor)
- {
- methodDeclaration.inferredMethod.inType=this;
- method.isStatic=methodDeclaration.inferredMethod.isStatic;
- method.bits=methodDeclaration.inferredMethod.bits;
- methodDeclaration.inferredMethod = method;
- } else if (methodDeclaration.inferredMethod.isConstructor)
- method.inType=methodDeclaration.inferredMethod.inType;
-
- }
- if (methods==null)
- methods=new ArrayList();
- methods.add(method);
-
- if( !isAnonymous )
- this.updatePositions(methodDeclaration.sourceStart, methodDeclaration.sourceEnd);
- method.isConstructor=isConstructor;
- method.nameStart = nameStart;
- } else {
- if (methodDeclaration.inferredMethod==null) {
- methodDeclaration.inferredMethod=method;
- }
- }
-
- return method;
- }
-
- /**
- * Find an inferred method
- *
- * @param methodName name of the method to find
- * @param methodDeclaration not used
- * @return the found method, or null
- */
- public InferredMethod findMethod(char [] methodName, IFunctionDeclaration methodDeclaration) {
- boolean isConstructor= methodName==TypeConstants.INIT;
- if (methods!=null)
- for (Iterator methodIterator = methods.iterator(); methodIterator.hasNext();) {
- InferredMethod method = (InferredMethod) methodIterator.next();
- if (CharOperation.equals(methodName,method.name))
- return method;
- if (isConstructor && method.isConstructor)
- return method;
- }
- return null;
-
- }
-
- public TypeBinding resolveType(Scope scope, ASTNode node) {
- // handle the error here
- if (this.resolvedType != null) // is a shared type reference which was already resolved
- return this.resolvedType.isValidBinding() ? this.resolvedType : null; // already reported error
-
-
- if (isArray())
- {
- TypeBinding memberType = (referenceClass!=null)?referenceClass.resolveType(scope,node):null;
- if (memberType==null)
- memberType=TypeBinding.UNKNOWN;
- this.resolvedType=new ArrayBinding(memberType, 1, scope.compilationUnitScope().environment) ;
-
- }
- else {
- if (CharOperation.indexOf('|', name)>0)
- {
- char[][] names = CharOperation.splitAndTrimOn('|', name);
- this.resolvedType=new MultipleTypeBinding(scope,names);
- }
- else
- this.resolvedType = scope.getType(name);
- /* the inferred type isn't valid, so don't assign it to the variable */
- if(!this.resolvedType.isValidBinding()) this.resolvedType = null;
- }
-
-
- if (this.resolvedType == null)
- return null; // detected cycle while resolving hierarchy
- if (node!=null && !this.resolvedType.isValidBinding()) {
- scope.problemReporter().invalidType(node, this.resolvedType);
- return null;
- }
- if (node!=null && node.isTypeUseDeprecated(this.resolvedType, scope))
- scope.problemReporter().deprecatedType(this.resolvedType, node);
-
- if( isAnonymous )
- this.resolvedType.tagBits |= TagBits.AnonymousTypeMask;
-
- return this.resolvedType ;
- }
-
-
-
- public void dumpReference(StringBuffer sb)
- {
- sb.append(name);
- if (referenceClass!=null)
- {
- sb.append('(');
- referenceClass.dumpReference(sb);
- sb.append(')');
- }
- }
-
- public boolean containsMethod(IAbstractFunctionDeclaration inMethod) {
- if (methods!=null)
- for (Iterator iter = methods.iterator(); iter.hasNext();) {
- InferredMethod method = (InferredMethod) iter.next();
- if (method.getFunctionDeclaration()==inMethod)
- return true;
- }
- return false;
- }
-
-
-
- public ReferenceBinding resolveSuperType(ClassScope classScope) {
- if (this.resolvedSuperType != null)
- return this.resolvedSuperType;
-
- if(superClass != null)
- this.resolvedSuperType = (ReferenceBinding)classScope.getType(superClass.getName());
-
- return this.resolvedSuperType;
- }
-
- public boolean isArray()
- {
- return CharOperation.equals(ARRAY_NAME, name);
- }
-
- public boolean isFunction()
- {
- return CharOperation.equals(FUNCTION_NAME, name);
- }
-
- public StringBuffer print(int indent, StringBuffer output) {
- printIndent(indent, output);
- char[] superName= getSuperClassName();
- output.append("class ").append(name).append(" extends ").append(superName).append("{\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- for (int i=0;i<this.numberAttributes;i++) {
- this.attributes[i].print(indent+1,output);
- output.append(";\n"); //$NON-NLS-1$
- }
- if (methods!=null)
- for (Iterator methodIterator = methods.iterator(); methodIterator.hasNext();) {
- InferredMethod method = (InferredMethod) methodIterator.next();
- method.print(indent+1,output);
- output.append("\n"); //$NON-NLS-1$
- }
- output.append("}"); //$NON-NLS-1$
- return output;
- }
-
- public boolean isInferred()
- {
- return true;
- }
-
- public void updatePositions(int start, int end)
- {
- if (this.sourceStart==-1 ||(start>=0 && start<this.sourceStart))
- this.sourceStart=start;
- if (end>0&&end>this.sourceEnd)
- this.sourceEnd=end;
- }
-
- public IAbstractFunctionDeclaration declarationOf(MethodBinding methodBinding) {
- if (methodBinding != null && this.methods != null) {
- for (int i = 0, max = this.methods.size(); i < max; i++) {
- InferredMethod method=(InferredMethod) this.methods.get(i);
-
- if (method.methodBinding==methodBinding)
- return method.getFunctionDeclaration();
- }
- }
- return null;
- }
-
- public boolean isNamed()
- {
- return !isAnonymous || !CharOperation.prefixEquals(IInferEngine.ANONYMOUS_PREFIX, this.name);
- }
-
- /**
- * Set the charactor position (in the source) of the type name
- *
- * @param start type name position
- */
- public void setNameStart(int start)
- {
- this.nameStart=start;
- }
-
- public int getNameStart()
- {
- return this.nameStart!= -1 ? this.nameStart : this.sourceStart;
- }
-
- public boolean isEmptyGlobal()
- {
- return (CharOperation.equals(GLOBAL_NAME, this.name) &&
- this.numberAttributes==0 &&
- (this.methods==null || this.methods.isEmpty()));
- }
-
-
- /**
- * <p>Adds the name of a type to mix into this type once all of the types have
- * been inferred</p>
- *
- * @param mixinTypeName the name of the type to mix into this type
- */
- public void addMixin(char[] mixinTypeName)
- {
- if (mixins==null)
- mixins=new ArrayList();
- mixins.add(mixinTypeName);
- }
-
-}
+/*******************************************************************************
+ * Copyright (c) 2005, 2013 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.jsdt.core.infer;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.wst.jsdt.core.ast.IASTNode;
+import org.eclipse.wst.jsdt.core.ast.IAbstractFunctionDeclaration;
+import org.eclipse.wst.jsdt.core.ast.IFunctionDeclaration;
+import org.eclipse.wst.jsdt.core.compiler.CharOperation;
+import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode;
+import org.eclipse.wst.jsdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.wst.jsdt.internal.compiler.ast.ObjectLiteral;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.MultipleTypeBinding;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.Scope;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.TagBits;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.wst.jsdt.internal.compiler.util.HashtableOfObject;
+import org.eclipse.wst.jsdt.internal.core.Logger;
+
+
+/**
+ * The representation of an inferred type.
+ *
+ * Provisional API: This class/interface is part of an interim API that is still under development and expected to
+ * change significantly before reaching stability. It is being made available at this early stage to solicit feedback
+ * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
+ * (repeatedly) as the API evolves.
+ */
+public class InferredType extends ASTNode {
+
+ char [] name;
+ public ArrayList methods;
+ public InferredAttribute[] attributes=new InferredAttribute[5];
+ public int numberAttributes=0;
+ public HashtableOfObject attributesHash = new HashtableOfObject();
+
+ /**
+ * <p>The parent type of this type, or <code>null</code> if this type does not have a parent type</p>
+ *
+ * <p><b>NOTE: </b>This field should not be accessed directly, use the appropriate getter and setter.</p>
+ *
+ * @see {@link #setSuperType(InferredType)}
+ * @see {@link #getSuperType()}
+ */
+ public InferredType superClass;
+
+ public InferredType referenceClass;
+
+ public SourceTypeBinding binding;
+
+ /**
+ * @deprecated this will not remain public forever
+ *
+ * @see #isDefinition()
+ * @see #setIsDefinition(boolean)
+ */
+ public boolean isDefinition;
+ private TypeBinding resolvedType;
+ public ClassScope scope;
+ ReferenceBinding resolvedSuperType;
+
+ public boolean isArray=false;
+ public boolean isAnonymous=false;
+ public boolean isObjectLiteral=false;
+
+ private int nameStart = -1;
+
+ public String inferenceProviderID;
+ public String inferenceStyle;
+
+ public ArrayList mixins;
+ /**
+ * @since 1.1
+ */
+ public int modifiers;
+
+ // in the case where a type extends functions, we sometimes need
+ // to know the actual function in order to create inferred methods
+ private IFunctionDeclaration correspondingFunction;
+
+ /**
+ * <p>
+ * <code>true</code> if this type is a globally visible type,
+ * <code>false</code> otherwise.
+ * </p>
+ *
+ * <p>
+ * EX: The anonymous type for a global variable would be globally visible,
+ * the anonymous type for a local would not.
+ * </p>
+ *
+ * @see #isIndexed()
+ * @see #setIsGlobal(boolean)
+ *
+ * @since 1.2
+ */
+ private boolean fIsGlobal;
+ /**
+ * Contains the InferredTypes that with which this type is synonymous
+ *
+ * @see #getSynonyms()
+ * @see #addSynonym(InferredType)
+ *
+ * @since 1.2
+ */
+ private InferredType[] fSynonyms;
+
+ public final static char[] OBJECT_NAME=new char[]{'O','b','j','e','c','t'};
+
+ /**
+ * @deprecated this is not used internally, this will be removed
+ */
+ public final static char[] OBJECT_LITERAL_NAME = new char[]{'{','}'};
+
+ public final static char[] ARRAY_NAME=new char[]{'A','r','r','a','y'};
+ public final static char[] FUNCTION_NAME =new char[]{'F','u','n','c','t','i','o','n'};
+ public final static char[] VOID_NAME =new char[]{'v','o','i','d'};
+
+ /**
+ * @deprecated - no longer used
+ */
+ public final static char[] GLOBAL_NAME=new char[]{'G','l','o','b','a','l'};
+
+ /**
+ * @deprecated this is not used internally, this will be removed
+ */
+ public Object userData;
+
+ /**
+ * @deprecated this is not used internally, this will be removed
+ */
+ boolean allStatic=false;
+
+ /**
+ * Create a new inferred type
+ *
+ * @param className inferred type name
+ */
+ public InferredType(char [] className) {
+ this.name=className;
+ this.sourceStart=-1;
+ this.fIsGlobal = false;
+ this.isDefinition = false;
+ }
+
+ /**
+ * @since 1.1
+ * @return
+ */
+ public int getModifiers() {
+ return modifiers;
+ }
+
+ /**
+ * Gets the name of the inferred type
+ *
+ * @return the inferred type name
+ */
+ public char [] getName() {
+ return name;
+ }
+
+ /**
+ * Get the superclass name of the inferred type
+ *
+ * @return superclass name
+ */
+ public char [] getSuperClassName()
+ {
+ return superClass!=null ? superClass.getName() : OBJECT_NAME;
+ }
+
+ /**
+ * Add a new inferred attribute to the inferred type
+ *
+ * @param name the attribute name
+ * @param definer the ASTNode which this attribute is inferred from
+ * @param nameStart character position (in the source) of the attribute name
+ * @return a new InferredAttribute
+ */
+ public InferredAttribute addAttribute(char [] name, IASTNode definer, int nameStart)
+ {
+ InferredAttribute attribute = findAttribute(name);
+ if (attribute==null)
+ {
+ attribute=new InferredAttribute(name, this, definer);
+ attribute.node=(ASTNode)definer;
+
+ if (this.numberAttributes == this.attributes.length)
+
+ System.arraycopy(
+ this.attributes,
+ 0,
+ this.attributes = new InferredAttribute[this.numberAttributes * 2],
+ 0,
+ this.numberAttributes );
+ this.attributes [this.numberAttributes ++] = attribute;
+
+
+ attributesHash.put(name, attribute);
+
+ if (!isAnonymous) {
+ this.updatePositions(definer.sourceStart(), definer.sourceEnd());
+ }
+ }
+ attribute.nameStart = nameStart;
+ return attribute;
+ }
+
+ /**
+ * Adds a new inferred attribute to the inferred type if it doesn't already exist
+ * otherwise it replaces the existing one with the new one.
+ *
+ * @param name the attribute name
+ * @param definer the ASTNode which this attribute is inferred from
+ * @param nameStart character position (in the source) of the attribute name
+ * @return a inferredAttribute
+ */
+ public InferredAttribute replaceAttribute(char [] name, IASTNode definer, int nameStart) {
+ InferredAttribute attribute = findAttribute(name);
+ if (attribute == null)
+ return addAttribute(name, definer, nameStart);
+
+ attributesHash.removeKey(name);
+
+ InferredAttribute newAttribute = new InferredAttribute(name, this, definer);
+ newAttribute.node=(ASTNode)definer;
+
+ for (int i = 0; i < this.numberAttributes; i++) {
+ if (this.attributes[i].equals(attribute)) {
+ this.attributes [i] = newAttribute;
+ }
+ }
+ attributesHash.put(name, newAttribute);
+
+ if (!isAnonymous) {
+ this.updatePositions(definer.sourceStart(), definer.sourceEnd());
+ }
+ newAttribute.nameStart = nameStart;
+ return newAttribute;
+ }
+
+ /**
+ * Add an InferredAttribute to this inferred type.
+ *
+ * @param newAttribute the attribute to add.
+ * @return
+ */
+ public InferredAttribute addAttribute(InferredAttribute newAttribute)
+ {
+ IASTNode definer=newAttribute.node;
+ InferredAttribute attribute = findAttribute(newAttribute.name);
+ if (attribute==null)
+ {
+
+ if (this.numberAttributes == this.attributes.length)
+
+ System.arraycopy(
+ this.attributes,
+ 0,
+ this.attributes = new InferredAttribute[this.numberAttributes * 2],
+ 0,
+ this.numberAttributes );
+ this.attributes [this.numberAttributes ++] = newAttribute;
+
+
+ attributesHash.put(newAttribute.name, newAttribute);
+
+ if (!isAnonymous) {
+ if (definer != null) {
+ this.updatePositions(definer.sourceStart(), definer.sourceEnd());
+ }
+ else {
+ this.updatePositions(newAttribute.sourceStart(), newAttribute.sourceEnd());
+ }
+ }
+ }
+ return newAttribute;
+ }
+ /**
+ * Find the inferred attribute with the given name
+ *
+ * @param name name of the attribute to find
+ * @return the found InferredAttribute, or null if not found
+ */
+ public InferredAttribute findAttribute(char [] name)
+ {
+ return (InferredAttribute)attributesHash.get(name);
+// if (attributes!=null)
+// for (Iterator attrIterator = attributes.iterator(); attrIterator.hasNext();) {
+// InferredAttribute attribute = (InferredAttribute) attrIterator.next();
+// if (CharOperation.equals(name,attribute.name))
+// return attribute;
+// }
+// return null;
+ }
+
+
+ /**
+ * Add a new constructor method to the inferred type
+ *
+ * @param methodName name of the method to add
+ * @param functionDeclaration the AST Node containing the method bode
+ * @param nameStart character position (in the source) of the method name
+ * @return a new inferred method
+ */
+ public InferredMethod addConstructorMethod(char [] methodName, IFunctionDeclaration functionDeclaration, int nameStart) {
+ InferredMethod method = this.addMethod(methodName, functionDeclaration, nameStart, true);
+ method.isConstructor = true;
+ this.setNameStart(nameStart);
+ //method.getFunctionDeclaration().setInferredType(this);
+ return method;
+ }
+
+ /**
+ * Add a new method to the inferred type
+ *
+ * @param methodName name of the method to add
+ * @param functionDeclaration the AST Node containing the method bode
+ * @param nameStart character position (in the source) of the method name
+ * @return a new inferred method
+ */
+ public InferredMethod addMethod(char [] methodName, IFunctionDeclaration functionDeclaration, int nameStart) {
+ return this.addMethod(methodName, functionDeclaration, nameStart, false);
+ }
+
+ /**
+ * Add a new method to the inferred type
+ *
+ * @param methodName name of the method to add
+ * @param functionDeclaration the AST Node containing the method bode
+ * @param isConstructor true if it is a constructor
+ * @return a new inferred method
+ */
+ private InferredMethod addMethod(char [] methodName, IFunctionDeclaration functionDeclaration, int nameStart, boolean isConstructor) {
+ MethodDeclaration methodDeclaration = (MethodDeclaration)functionDeclaration;
+ InferredMethod method = findMethod(methodName, methodDeclaration);
+ if (method==null) {
+ /* if the inferred method for the declaration specifies that it is in a
+ * type use that one.
+ *
+ * This is for the case where a method has been mixed in from another type
+ * but we still want that method to be reported as defined on the other
+ * type and not this type
+ */
+ InferredType inType = this;
+ if(methodDeclaration.getInferredMethod() != null && methodDeclaration.getInferredMethod().inType != null && !isConstructor &&
+ !methodDeclaration.getInferredMethod().isConstructor &&
+ !methodDeclaration.getInferredMethod().inType.isAnonymous && this.isAnonymous) {
+ inType = methodDeclaration.getInferredMethod().inType;
+ }
+
+ method=new InferredMethod(methodName,methodDeclaration,inType);
+ if (methodDeclaration.inferredMethod==null)
+ methodDeclaration.inferredMethod = method;
+ else
+ {
+ if (isConstructor)
+ {
+ methodDeclaration.inferredMethod.inType=this;
+ method.isStatic=methodDeclaration.inferredMethod.isStatic;
+ method.bits=methodDeclaration.inferredMethod.bits;
+ //methodDeclaration.inferredMethod = method;
+ } //else if (methodDeclaration.inferredMethod.isConstructor)
+ //method.inType=methodDeclaration.inferredMethod.inType;
+
+ }
+ if (methods==null)
+ methods=new ArrayList();
+ methods.add(method);
+
+ if(!isAnonymous && !isConstructor)
+ this.updatePositions(methodDeclaration.sourceStart, methodDeclaration.sourceEnd);
+ method.isConstructor=isConstructor;
+ method.nameStart = nameStart;
+ } else {
+ if (methodDeclaration.inferredMethod==null) {
+ methodDeclaration.inferredMethod=method;
+ }
+ }
+
+ return method;
+ }
+
+ /**
+ * Adds a new inferred method to the inferred type if it doesn't already exist
+ * otherwise it replaces the existing one with the new one.
+ *
+ * @param methodName name of the method to add
+ * @param functionDeclaration the AST Node containing the method bode
+ * @param isConstructor true if it is a constructor
+ * @return an inferred method
+ */
+ private InferredMethod replaceMethod(char [] methodName, IFunctionDeclaration functionDeclaration, int nameStart) {
+ MethodDeclaration methodDeclaration = (MethodDeclaration) functionDeclaration;
+ InferredMethod method = findMethod(methodName, methodDeclaration);
+ if (method == null)
+ return addMethod(methodName, functionDeclaration, nameStart);
+
+ /* if the inferred method for the declaration specifies that it is in a
+ * type use that one.
+ *
+ * This is for the case where a method has been mixed in from another type
+ * but we still want that method to be reported as defined on the other
+ * type and not this type
+ */
+ InferredType inType = this;
+ if (methodDeclaration.getInferredMethod() != null && methodDeclaration.getInferredMethod().inType != null) {
+ inType = methodDeclaration.getInferredMethod().inType;
+ } else {
+ inType = this;
+ }
+ methods.remove(method);
+
+ InferredMethod newMethod = new InferredMethod(methodName, methodDeclaration, inType);
+ if (methodDeclaration.inferredMethod == null)
+ methodDeclaration.inferredMethod = newMethod;
+ else if (methodDeclaration.inferredMethod.isConstructor)
+ newMethod.inType = methodDeclaration.inferredMethod.inType;
+
+ methods.add(newMethod);
+
+ if (!isAnonymous)
+ this.updatePositions(methodDeclaration.sourceStart, methodDeclaration.sourceEnd);
+ newMethod.isConstructor = false;
+ newMethod.nameStart = nameStart;
+
+ return newMethod;
+ }
+
+ /**
+ * Find an inferred method
+ *
+ * @param methodName name of the method to find
+ * @param methodDeclaration not used
+ * @return the found method, or null
+ */
+ public InferredMethod findMethod(char [] methodName, IFunctionDeclaration methodDeclaration) {
+ boolean isConstructor= methodName==TypeConstants.INIT;
+ if (methods!=null)
+ for (Iterator methodIterator = methods.iterator(); methodIterator.hasNext();) {
+ InferredMethod method = (InferredMethod) methodIterator.next();
+ if (CharOperation.equals(methodName,method.name))
+ return method;
+ if (isConstructor && method.isConstructor)
+ return method;
+ }
+ return null;
+
+ }
+
+ public TypeBinding resolveType(Scope scope, ASTNode node) {
+ if (scope == null)
+ return null;
+
+ // handle the error here
+ if (this.resolvedType != null) // is a shared type reference which was already resolved
+ return this.resolvedType.isValidBinding() ? this.resolvedType : null; // already reported error
+
+
+ if (isArray())
+ {
+ TypeBinding memberType = (referenceClass!=null)?referenceClass.resolveType(scope,node):null;
+ if (memberType==null)
+ memberType=TypeBinding.UNKNOWN;
+ this.resolvedType=new ArrayBinding(memberType, 1, scope.compilationUnitScope().environment) ;
+
+ }
+ else {
+ if (CharOperation.indexOf('|', name)>0)
+ {
+ char[][] names = CharOperation.splitAndTrimOn('|', name);
+ this.resolvedType=new MultipleTypeBinding(scope,names);
+ }
+ else
+ this.resolvedType = scope.getType(name);
+ /* the inferred type isn't valid, so don't assign it to the variable */
+ if(!this.resolvedType.isValidBinding()) this.resolvedType = null;
+ }
+
+
+ if (this.resolvedType == null)
+ return null; // detected cycle while resolving hierarchy
+ if (node!=null && !this.resolvedType.isValidBinding()) {
+ scope.problemReporter().invalidType(node, this.resolvedType);
+ return null;
+ }
+ if (node!=null && node.isTypeUseDeprecated(this.resolvedType, scope))
+ scope.problemReporter().deprecatedType(this.resolvedType, node);
+
+ if( !isNamed() )
+ this.resolvedType.tagBits |= TagBits.AnonymousTypeMask;
+
+ return this.resolvedType ;
+ }
+
+
+
+ public void dumpReference(StringBuffer sb)
+ {
+ sb.append(name);
+ if (referenceClass!=null)
+ {
+ sb.append('(');
+ referenceClass.dumpReference(sb);
+ sb.append(')');
+ }
+ }
+
+ public boolean containsMethod(IAbstractFunctionDeclaration inMethod) {
+ if (methods!=null)
+ for (Iterator iter = methods.iterator(); iter.hasNext();) {
+ InferredMethod method = (InferredMethod) iter.next();
+ if (method.getFunctionDeclaration()==inMethod)
+ return true;
+ }
+ return false;
+ }
+
+
+
+ public ReferenceBinding resolveSuperType(ClassScope classScope) {
+ if (this.resolvedSuperType != null)
+ return this.resolvedSuperType;
+
+ if(superClass != null) {
+ TypeBinding typeBinding = classScope.getType(superClass.getName());
+ if ( typeBinding instanceof ReferenceBinding ) this.resolvedSuperType = (ReferenceBinding)typeBinding;
+ }
+
+ return this.resolvedSuperType;
+ }
+
+ public boolean isArray()
+ {
+ return CharOperation.equals(ARRAY_NAME, name);
+ }
+
+ public boolean isFunction()
+ {
+ return CharOperation.equals(FUNCTION_NAME, name);
+ }
+
+ public boolean isVoid()
+ {
+ return CharOperation.equals(VOID_NAME, name);
+ }
+
+ public StringBuffer print(int indent, StringBuffer output) {
+ printIndent(indent, output);
+ char[] superName= getSuperClassName();
+ output.append("class ").append(name).append(" extends ").append(superName).append("{\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ for (int i=0;i<this.numberAttributes;i++) {
+ this.attributes[i].print(indent+1,output);
+ output.append(";\n"); //$NON-NLS-1$
+ }
+ if (methods!=null)
+ for (Iterator methodIterator = methods.iterator(); methodIterator.hasNext();) {
+ InferredMethod method = (InferredMethod) methodIterator.next();
+ method.print(indent+1,output);
+ output.append("\n"); //$NON-NLS-1$
+ }
+ output.append("}"); //$NON-NLS-1$
+ return output;
+ }
+
+ public boolean isInferred()
+ {
+ return true;
+ }
+
+ public void updatePositions(int start, int end)
+ {
+ if (this.sourceStart==-1 ||(start>=0 && start<this.sourceStart))
+ this.sourceStart=start;
+ if (end>0&&end>this.sourceEnd)
+ this.sourceEnd=end;
+ }
+
+ public IAbstractFunctionDeclaration declarationOf(MethodBinding methodBinding) {
+ if (methodBinding != null && this.methods != null) {
+ for (int i = 0, max = this.methods.size(); i < max; i++) {
+ InferredMethod method=(InferredMethod) this.methods.get(i);
+
+ if (method.methodBinding==methodBinding)
+ return method.getFunctionDeclaration();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return <code>true</code> if this type is anonymous or the the type
+ * name starts with the anonymous prefix, <code>false</code>
+ * otherwise
+ */
+ public boolean isNamed() {
+ return !isAnonymous || !CharOperation.prefixEquals(IInferEngine.ANONYMOUS_PREFIX, this.name);
+ }
+
+ /**
+ * @param modifiers the modifiers to set
+ * @since 1.1
+ */
+ public void setModifiers(int modifiers) {
+ this.modifiers = modifiers;
+ }
+
+ /**
+ * Set the charactor position (in the source) of the type name
+ *
+ * @param start type name position
+ */
+ public void setNameStart(int start)
+ {
+ this.nameStart=start;
+ }
+
+ public int getNameStart()
+ {
+ return this.nameStart!= -1 ? this.nameStart : this.sourceStart;
+ }
+
+ /**
+ * @deprecated - no longer used
+ */
+ public boolean isEmptyGlobal() {
+ return (CharOperation.equals(GLOBAL_NAME, this.name) && this.numberAttributes == 0 && (this.methods == null || this.methods.isEmpty()));
+ }
+
+ /**
+ * <p>Adds the name of a type to mix into this type during the resolving step.</p>
+ *
+ * <p>Use {@link #mixin(InferredType)} if the type to mixin is an anonymous type
+ * since anonymous types are only available at the inference step and will not be
+ * available at the resolve step when mixins added with this method are mixed in.</p>
+ *
+ * <p><b>NOTE:</b> Do not confuse this with <code>dojo.mixin()</code>, this operation is actually
+ * more akin to <code>dojo.extend</code></p>
+ *
+ * @param mixinTypeName the name of the type to mix into this type
+ */
+ public void addMixin(char[] mixinTypeName) {
+ if (mixins==null) {
+ mixins=new ArrayList();
+ }
+
+ //prevent duplicates
+ if(!mixins.contains(mixinTypeName)) {
+ mixins.add(mixinTypeName);
+ }
+ }
+
+ /**
+ * <p>Mixes an {@link InferredType} into this {@link InferredType} right now. Thus if the type being
+ * mixed in changes at all, fields or methods added/removed, at a later point those changes will
+ * not get reflected in this inferred type. Thus this method should only be used when it is certain the
+ * mixin type will not change again, such as during the resolving step or an {@link ObjectLiteral} during
+ * the inference step.</p>
+ *
+ * <p><b>NOTE:</b> Do not confuse this with <code>dojo.mixin()</code>, this operation is actually
+ * more akin to <code>dojo.extend</code></p>
+ *
+ * @param mixin
+ * @since 1.1
+ */
+ public void mixin(InferredType mixin) {
+ if(mixin !=null) {
+ InferredAttribute[] attributes = mixin.attributes;
+ ArrayList methods = mixin.methods;
+ if(methods == null)
+ methods = new ArrayList(1);
+
+ // get the full list of methods and attributes from the mix class and its super class
+ InferredType mixSuperType = mixin.getSuperType();
+ while(mixSuperType != null && !CharOperation.equals(mixSuperType.getName(), TypeConstants.OBJECT)) {
+ // attributes
+ InferredAttribute[] tempAttributes = new InferredAttribute[attributes.length + mixSuperType.numberAttributes];
+ System.arraycopy(attributes, 0, tempAttributes, 0, attributes.length);
+ System.arraycopy(mixSuperType.attributes, 0, tempAttributes, attributes.length - 1, mixSuperType.numberAttributes);
+ attributes = tempAttributes;
+
+ // methods
+ if (mixSuperType.methods != null)
+ methods.addAll(mixSuperType.methods);
+ mixSuperType = mixSuperType.getSuperType();
+ }
+
+ // add attributes to the type
+ for(int a = 0; a < attributes.length; a++) {
+ //do not mix in statics
+ if(attributes[a] != null && !attributes[a].isStatic) {
+ InferredAttribute attr = this.replaceAttribute( attributes[a].name, attributes[a].node, attributes[a].nameStart);
+ attr.type=attributes[a].type;
+ attr.isStatic = false;
+ attr.nameStart = attributes[a].nameStart;
+ attr.modifiers = attributes[a].modifiers;
+ attr.initializationStart = attributes[a].initializationStart;
+ }
+ }
+
+ // add functions to the type
+ for(int m = 0; m < methods.size(); m++) {
+ InferredMethod functToMixin = (InferredMethod)methods.get(m);
+
+ //do not mix in constructors or statics
+ if(!functToMixin.isConstructor && !functToMixin.isStatic) {
+ this.replaceMethod(functToMixin.name, functToMixin.getFunctionDeclaration(), functToMixin.nameStart);
+ }
+ }
+ }
+ }
+
+ /**
+ * <p>Mixes an {@link InferredType} into this {@link InferredType}. By passing true for the second argument,
+ * objects will be recursively mixed. This means that if a property of the first object is itself an object,
+ * a mix will be performed if a property with the same key exists in the second object. Otherwise it would be completely
+ * overriden by the property of the second object.
+ *
+ * @param mixin
+ */
+ public void mixin(InferredType mixin, boolean isDeepCopy) {
+ if (!isDeepCopy)
+ mixin(mixin);
+ else if (mixin != null) {
+ InferredAttribute[] attributes = mixin.attributes;
+ ArrayList methods = mixin.methods;
+ if (methods == null)
+ methods = new ArrayList(1);
+
+ // get the full list of methods and attributes from the mix class and its super class
+ InferredType mixSuperType = mixin.getSuperType();
+ while (mixSuperType != null && !CharOperation.equals(mixSuperType.getName(), TypeConstants.OBJECT)) {
+ // attributes
+ InferredAttribute[] tempAttributes = new InferredAttribute[attributes.length + mixSuperType.numberAttributes];
+ System.arraycopy(attributes, 0, tempAttributes, 0, attributes.length);
+ System.arraycopy(mixSuperType.attributes, 0, tempAttributes, attributes.length - 1, mixSuperType.numberAttributes);
+ attributes = tempAttributes;
+
+ // methods
+ if (mixSuperType.methods != null)
+ methods.addAll(mixSuperType.methods);
+ mixSuperType = mixSuperType.getSuperType();
+ }
+
+ // add attributes to the type
+ for (int a = 0; a < attributes.length; a++) {
+ //do not mix in statics
+ if (attributes[a] != null && !attributes[a].isStatic) {
+ InferredAttribute existingAttr = findAttribute(attributes[a].name);
+ if (existingAttr != null && existingAttr.type != null && existingAttr.type.isAnonymous) {
+ existingAttr.type.mixin(attributes[a].type, true);
+ }
+ else {
+ InferredAttribute attr = this.replaceAttribute( attributes[a].name, attributes[a].node, attributes[a].nameStart);
+ attr.type=attributes[a].type;
+ attr.isStatic = false;
+ attr.nameStart = attributes[a].nameStart;
+ attr.modifiers = attributes[a].modifiers;
+ attr.initializationStart = attributes[a].initializationStart;
+ }
+ }
+ }
+
+ // add functions to the type
+ for (int m = 0; m < methods.size(); m++) {
+ InferredMethod functToMixin = (InferredMethod)methods.get(m);
+
+ //do not mix in constructors or statics
+ if (!functToMixin.isConstructor && !functToMixin.isStatic) {
+ this.replaceMethod(functToMixin.name, functToMixin.getFunctionDeclaration(), functToMixin.nameStart);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return super {@link InferredType} of this {@link InferredType}, or
+ * <code>null</code> if none is set
+ * @since 1.1
+ */
+ public InferredType getSuperType() {
+ return this.superClass;
+ }
+
+ /**
+ * <p>Sets the super type of this type unless the given super type is
+ * itself then this is a no op</p>
+ *
+ * @param superType {@link InferredType} to set as the super type of this type,
+ * can not be the same as this type
+ * @since 1.1
+ */
+ public void setSuperType(InferredType superType) {
+ // prevent cycles, and log if someone attempts to create one
+ InferredType testType = superType;
+ while (testType != null) {
+ if (testType == this) {
+ if (InferEngine.DEBUG)
+ Logger.log(Logger.WARNING, "InferredType#setSuperType: a hierarchy loop would be caused between: " + new String(getName()) + " and " + new String(superType.getName())); //$NON-NLS-1$ //$NON-NLS-2$
+ return;
+ }
+ testType = testType.getSuperType();
+ }
+
+ this.superClass = superType;
+ }
+
+ /**
+ * <p>
+ * Determines if this type should be indexed or not. A type should be indexed if it is named or
+ * has specifically been set to be a global type.
+ * </p>
+ *
+ * @return <code>true</code> if this type should be indexed, <code>false</code> otherwise
+ *
+ * @see #isNamed()
+ * @see #isGlobal()
+ *
+ * @since 1.1
+ */
+ public boolean isIndexed() {
+ return this.isGlobal() || this.isNamed();
+ }
+
+ /**
+ * <p>
+ * EX: The anonymous type for a global variable would be globally visible, the anonymous type
+ * for a local would not.
+ * </p>
+ *
+ * @param isGlobal
+ * <code>true</code> if this type is a globally visible type, <code>false</code>
+ * otherwise.
+ *
+ * @since 1.1
+ */
+ public void setIsGlobal(boolean isGlobal) {
+ this.fIsGlobal = isGlobal;
+ }
+
+ /**
+ * @return <code>true</code> if this type is a globally visible type, <code>false</code>
+ * otherwise.
+ *
+ * @since 1.1
+ */
+ public boolean isGlobal() {
+ return this.fIsGlobal;
+ }
+
+ /**
+ * @return <code>true</code> if this type is a definition,
+ * <code>false</code> otherwise
+ */
+ public boolean isDefinition() {
+ return isDefinition;
+ }
+
+ /**
+ * @param isDefinition
+ * <code>true</code> if this type is a definition,
+ * <code>false</code> otherwise
+ */
+ public void setIsDefinition(boolean isDefinition) {
+ this.isDefinition = isDefinition;
+ }
+
+ /**
+ * @since 1.2
+ * @return the types for which this type is synonymous, or null if none have been set
+ */
+ public InferredType[] getSynonyms() {
+ return fSynonyms;
+ }
+
+ /**
+ * @since 1.2
+ * @param type - adds a type for which this type is synonymous
+ */
+ public void addSynonym(InferredType type) {
+ /* be sure given synonym does not have the same name as this type
+ * also be sure the new synonym is not the super type of this type
+ * or that this type is the super type of the given synonym
+ *
+ * This situation can arise when a pattern like this is used:
+ *
+ * define("foo.BarImpl", "foo.Bar", {}):
+ * foo.Bar = foo.BarImpl; */
+ if((type != this && !CharOperation.equals(type.getName(), this.getName())) &&
+ !((this.getSuperType() != null && CharOperation.equals(type.getName(), this.getSuperType().getName())) ||
+ (type.getSuperType() != null && CharOperation.equals(this.getName(), type.getSuperType().getName())))) {
+
+ if (fSynonyms == null) {
+ fSynonyms = new InferredType[]{type};
+ } else {
+ boolean alreadyContains = false;
+ for(int i = 0; i < this.fSynonyms.length && !alreadyContains; ++i) {
+ alreadyContains = type == this.fSynonyms[i];
+ }
+
+ if(!alreadyContains) {
+ InferredType[] synonyms = new InferredType[fSynonyms.length + 1];
+ System.arraycopy(fSynonyms, 0, synonyms, 0, fSynonyms.length);
+ synonyms[fSynonyms.length] = type;
+ fSynonyms = synonyms;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * @return the correspondingFunction
+ */
+ public IFunctionDeclaration getCorrespondingFunction() {
+ return correspondingFunction;
+ }
+
+ /**
+ * @param coorespondingFunction the coorespondingFunction to set
+ */
+ public void setCorrespondingFunction(IFunctionDeclaration coorespondingFunction) {
+ this.correspondingFunction = coorespondingFunction;
+ }
+} \ No newline at end of file

Back to the top