blob: 8f87c686e45264af04f764b2bcca4fb30043161b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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.internal.compiler.lookup;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.zip.CRC32;
import org.eclipse.wst.jsdt.core.ast.IAssignment;
import org.eclipse.wst.jsdt.core.ast.IExpression;
import org.eclipse.wst.jsdt.core.ast.IFieldReference;
import org.eclipse.wst.jsdt.core.ast.IFunctionDeclaration;
import org.eclipse.wst.jsdt.core.compiler.CharOperation;
import org.eclipse.wst.jsdt.core.infer.InferredAttribute;
import org.eclipse.wst.jsdt.core.infer.InferredMethod;
import org.eclipse.wst.jsdt.core.infer.InferredType;
import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.ast.Argument;
import org.eclipse.wst.jsdt.internal.compiler.ast.Assignment;
import org.eclipse.wst.jsdt.internal.compiler.ast.Expression;
import org.eclipse.wst.jsdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.wst.jsdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.wst.jsdt.internal.compiler.ast.TypeReference;
import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.wst.jsdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.wst.jsdt.internal.compiler.util.Util;
public class SourceTypeBinding extends ReferenceBinding {
/**
* <p>
* Enable for enhanced debugging
* </p>
*/
private static final boolean DEBUG = false;
/**
* <p>
* Super type binding for this binding.
* </p>
*
* <p>
* <b>WARNING:</b> a linked binding may have a different supper binding.
* </p>
*
* @see #getSuperBinding0()
* @see #getSuperBinding()
* @see #setSuperBinding(ReferenceBinding)
*/
private ReferenceBinding fSuperBinding;
protected FieldBinding[] fields;
protected MethodBinding[] methods;
public ReferenceBinding[] memberTypes = Binding.NO_MEMBER_TYPES;
public Scope scope;
public ClassScope classScope;
/**
* <p>
* The next type in the circular linked list of linked types. If this type is not linked to any other
* types then the value of {@link #fNextType} will be this type forming a circle of 1.
* </p>
*
* <p>
* Due to the circular nature of this linked list always use {@link #performActionOnLinkedBindings(LinkedBindingAction)}
* when needing to perform any action on all of the linked types.
* </p>
*
* @see #performActionOnLinkedBindings(LinkedBindingAction)
* @see LinkedBindingAction
*/
private SourceTypeBinding fNextType;
private static final CRC32 checksumCalculator = new CRC32();
/**
* <code>true</code> if all fields and functions have already been built,
* <code>false</code> otherwise
*/
private boolean fHasBuiltFieldsAndMethods;
/**
* <code>true</code> if currently building fields and functions,
* <code>false</code> otherwise
*/
private boolean fBuildingAllFieldsAndFunctions;
/**
* <p>
* When building specific selectors using
* {@link #buildFieldsAndMethods(char[])} then this list contains all of
* the selectors currently being built.
* </p>
*
* <p>
* If all fields and functions are being built at once then this list is
* not used. use {@link #fBuildingAllFieldsAndFunctions} to detect this situation.
* </p>
*
* @see #buildFieldsAndMethods(char[])
* @see #fBuildingAllFieldsAndFunctions
*/
private char[][] fBuildingSelectors;
/**
* <p>
* If have built specific selectors using
* {@link #buildFieldsAndMethods(char[])} then this list contains all of
* the selectors that have already been built.
* </p>
*
* <p>
* If all fields and functions have been built then this list is not accurate.
* Use {@link #fHasBuiltFieldsAndMethods} to detect this situation.
* </p>
*
* @see #buildFieldsAndMethods(char[])
* @see #fHasBuiltFieldsAndMethods
*/
private char[][] fBuiltSelectors;
/**
* <p>
* Used to synchronize access to fields that keep track of field and function building.
* </p>
*
* @see #fBuildingAllFieldsAndFunctions
* @see #fBuildingSelectors
* @see #fBuiltSelectors
*/
private final Object fBuildFieldsAndFunctionsLock = new Object();
public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage,
Scope scope) {
this();
this.compoundName = compoundName;
this.fPackage = fPackage;
this.fileName = scope == null ? null : scope.referenceCompilationUnit().getFileName();
if (scope instanceof ClassScope) {
this.classScope = (ClassScope) scope;
if (this.classScope.referenceContext != null) {
this.modifiers = this.classScope.referenceContext.modifiers;
this.sourceName = this.classScope.referenceContext.name;
} else {
this.sourceName = this.classScope.inferredType.getName();
this.modifiers = ClassFileConstants.AccPublic;
}
}
this.scope = scope;
// expect the fields & methods to be initialized correctly later
this.fields = Binding.NO_FIELDS;
this.methods = Binding.NO_METHODS;
computeId();
}
protected SourceTypeBinding() {
//next type is a circular linked list
this.fNextType = this;
this.fHasBuiltFieldsAndMethods = false;
this.fBuildingAllFieldsAndFunctions = false;
this.fBuildingSelectors = null;
this.fBuiltSelectors = null;
}
/**
* <p>
* Builds all fields and functions for this type.
* </p>
*
* <p>
* No operation if all fields and functions are already built. Also if an
* individual field or function has already been built then it will not be
* built again.
* </p>
*/
void buildFieldsAndMethods() {
this.buildFieldsAndMethods(null);
}
/**
* <p>
* If no restriction is given then the same as
* {@link #buildFieldsAndMethods()}, else if a restriction is given only
* fields and functions with the given selector are built.
* </p>
*
* <p>
* No operation if all fields and functions are already built. Also if an
* individual field or function has already been built then it will not be
* built again.
* </p>
*
* @param restrictToSelector
* restrict building to only fields and functions with this
* selector, or if <code>null</code> build all fields and
* functions
*/
private void buildFieldsAndMethods(char[] restrictToSelector) {
synchronized (this.fBuildFieldsAndFunctionsLock) {
//if already building or have already built then do nothing
if(this.fBuildingAllFieldsAndFunctions || this.fHasBuiltFieldsAndMethods ) {
return;
}
//if already building restrict to selector then do nothing
if(restrictToSelector != null && this.fBuildingSelectors != null && this.fBuildingSelectors.length > 0) {
if(CharOperation.contains(restrictToSelector, this.fBuildingSelectors)) {
return;
}
}
//if already built selector then do nothing
if(restrictToSelector != null && this.fBuiltSelectors != null && this.fBuiltSelectors.length > 0) {
if(CharOperation.contains(restrictToSelector, this.fBuiltSelectors)) {
return;
}
}
/* if restrict building to specific selector add it to list of currently building specific selectors
* else set building all fields and functions */
if(restrictToSelector != null) {
if(this.fBuildingSelectors == null) {
this.fBuildingSelectors = new char[1][];
this.fBuildingSelectors[0] = restrictToSelector;
} else {
char[][] newBuildingSelectors = new char[this.fBuildingSelectors.length+1][];
System.arraycopy(this.fBuildingSelectors, 0, newBuildingSelectors, 0, this.fBuildingSelectors.length);
this.fBuildingSelectors = newBuildingSelectors;
this.fBuildingSelectors[this.fBuildingSelectors.length-1] = restrictToSelector;
}
} else {
this.fBuildingAllFieldsAndFunctions = true;
}
}
try {
/* build functions first because building fields depends on built functions
*
* This is because building fields can add functions if their assignment is a function
* but only if a function with the fields name has not already been created */
buildFunctions(restrictToSelector);
buildFields(restrictToSelector);
if(restrictToSelector == null) {
this.fHasBuiltFieldsAndMethods = true;
}
} finally {
synchronized (this.fBuildFieldsAndFunctionsLock) {
if(restrictToSelector != null) {
//remove selector from list of building selectors
if(this.fBuildingSelectors.length == 1) {
this.fBuildingSelectors = null;
} else {
char[][] newBuildingSelectors = new char[this.fBuildingSelectors.length-1][];
int j = 0;
for(int i = 0; i < this.fBuildingSelectors.length; ++i) {
if(!CharOperation.equals(restrictToSelector, this.fBuildingSelectors[i])) {
newBuildingSelectors[j++] = this.fBuildingSelectors[i];
}
}
this.fBuildingSelectors = newBuildingSelectors;
}
//add selector to list of built selectors
if(this.fBuiltSelectors == null) {
this.fBuiltSelectors = new char[1][];
this.fBuiltSelectors[0] = restrictToSelector;
} else {
char[][] newBuiltSelectors = new char[this.fBuiltSelectors.length+1][];
System.arraycopy(this.fBuiltSelectors, 0, newBuiltSelectors, 0, this.fBuiltSelectors.length);
this.fBuiltSelectors = newBuiltSelectors;
this.fBuiltSelectors[this.fBuiltSelectors.length-1] = restrictToSelector;
}
} else {
this.fBuildingAllFieldsAndFunctions = false;
}
}
}
}
/**
* <p><b>IMPORTANT:</b> Gets the {@link InferredType} for this binding only.
* This means that if this binding has a {@link #nextType} then the {@link InferredType}
* returned here is only a partially {@link InferredType}.</p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#getInferredType()
*/
public InferredType getInferredType() {
ClassScope classScope = scope.classScope();
return classScope.inferredType;
}
/**
* <p>
* Build all or a specific field.
* </p>
*
* @param restrictToSelector
* build only fields with this selector, or if
* <code>null</code> build all fields
*/
private void buildFields(char[] restrictToSelector) {
if(this.classScope == null)
return;
InferredType inferredType = this.classScope.inferredType;
int size = inferredType.numberAttributes;
// iterate the field declarations to create the bindings, lose all duplicates
FieldBinding[] fieldBindings = new FieldBinding[size + 1];
HashtableOfObject knownFieldNames = new HashtableOfObject(size);
boolean duplicate = false;
int count = 0;
for (int i = 0; i < size; i++) {
InferredAttribute field = inferredType.attributes[i];
/* only build field if either the restricted selector is not set or
* the field name matches the restricted selector.
*
* Also skip any field that is already building or has already been built */
if((restrictToSelector == null || CharOperation.equals(field.name, restrictToSelector)) &&
(restrictToSelector != null || !CharOperation.contains(field.name, this.fBuildingSelectors)) &&
!CharOperation.contains(field.name, this.fBuiltSelectors)) {
int modifiers = 0;
if (field.isStatic) {
modifiers |= ClassFileConstants.AccStatic;
}
InferredType fieldType = field.type;
TypeBinding fieldTypeBinding = null;
Scope searchScope = null;
/* if field type set then use that for binding
* else if field node is an assignment use that
*
* TODO: this is not the correct logic because it does not deal
* with cases where the RHS is a function and the field type
* has already been resolved to function because then the function
* itself has not actually been resolved yet, so we can not create
* a function binding on this type for it. Initial attempts to fix
* this flaw have caused performance issues, so it will have to be
* addressed latter. To fix this turn this into two ifs, but again
* that then tanks performance for SOME scenarios.*/
if (fieldType != null) {
fieldTypeBinding = fieldType.resolveType(scope, field.node);
} else if(field.node instanceof IAssignment) {
IExpression rhs = ((IAssignment)field.node).getExpression();
if(rhs instanceof Expression) {
fieldTypeBinding = ((Expression) rhs).resolvedType;
/* if field binding for RHS not set or is any look for a better one,
* if function then local scope needs to be built so function can be resolved */
if(fieldTypeBinding == null || fieldTypeBinding.isAnyType()) {
/* if node is an assignment and that assignment is contained
* in a function resolve the function first */
IFunctionDeclaration containingFunction = null;
if(field.node instanceof Assignment) {
containingFunction = ((Assignment)field.node).getContainingFunction();
if(containingFunction != null && containingFunction instanceof AbstractMethodDeclaration) {
((AbstractMethodDeclaration)containingFunction).buildLocals(this.scope.compilationUnitScope());
searchScope = ((AbstractMethodDeclaration)containingFunction).getScope();
}
}
//if no search scope found yet find first parent scope that is a BlockScope
if(searchScope == null) {
searchScope = this.scope;
while(searchScope != null && !(searchScope instanceof BlockScope)) {
searchScope = searchScope.parent;
}
}
//use search scope to find binding
TypeBinding resolvedBinding = ((Expression) rhs).resolveType((BlockScope)searchScope);
if(resolvedBinding != null) {
fieldTypeBinding = resolvedBinding;
}
}
}
//if RHS binding is a function so create a new function binding on this source binding
if(fieldTypeBinding != null && isFunctionType(fieldTypeBinding)) {
/* if RHS is a field reference search its receiver for the function binding for the assigned function
* else if single name reference just use its method bindingn */
MethodBinding assignedFuncBinding = null;
/* this is to deal with cases like:
* foo = (bar = function() {}); */
while(rhs instanceof IAssignment) {
rhs = ((IAssignment)rhs).getExpression();
}
if(rhs instanceof IFieldReference) {
char[] selector = ((IFieldReference) rhs).getToken();
IExpression receiver = ((IFieldReference) rhs).getReceiver();
if(receiver instanceof Expression) {
TypeBinding receiverType = ((Expression) receiver).resolvedType;
if(receiverType instanceof SourceTypeBinding) {
//if found bindings use first one
MethodBinding[] funcBindings = ((SourceTypeBinding) receiverType).getMethods(selector);
if(funcBindings != null && funcBindings.length > 0) {
assignedFuncBinding = funcBindings[0];
}
}
}
} else if(rhs instanceof SingleNameReference) {
Binding binding = ((SingleNameReference) rhs).binding;
/* if binding is method binding just use that
* else if binding is local variable, find func binding in scope with same name as variable
* var foo;
* foo = function() {} */
if(binding instanceof MethodBinding) {
assignedFuncBinding = (MethodBinding)binding;
} else if(binding instanceof LocalVariableBinding && searchScope != null) {
if(searchScope instanceof BlockScope) {
assignedFuncBinding = ((BlockScope)searchScope).findMethod(field.name, null, true);
} else {
assignedFuncBinding = searchScope.findMethod(null, field.name, null, null);
}
}
}
//if RHS was a function binding create a new function binding on this type binding
if(assignedFuncBinding != null) {
InferredMethod dupMeth = inferredType.findMethod(field.name, null);
if(dupMeth == null) {
MethodBinding[] funcBindings = this.getMethods(field.name);
if(funcBindings == null || funcBindings.length == 0) {
MethodBinding funcBinding = new MethodBinding(assignedFuncBinding, this);
funcBinding.setSelector(field.name);
this.addMethod(funcBinding);
}
}
}
}
}
if (fieldTypeBinding == null) {
fieldTypeBinding = TypeBinding.UNKNOWN;
}
FieldBinding fieldBinding = new FieldBinding(field,
fieldTypeBinding, modifiers
| ExtraCompilerModifiers.AccUnresolved, this);
fieldBinding.id = count;
if (knownFieldNames.containsKey(field.name)) {
duplicate = true;
FieldBinding previousBinding = (FieldBinding) knownFieldNames
.get(field.name);
if (previousBinding != null) {
for (int f = 0; f < i; f++) {
InferredAttribute previousField = inferredType.attributes[f];
if (previousField.binding == previousBinding) {
scope.problemReporter().duplicateFieldInType(this,
previousField);
previousField.binding = null;
break;
}
}
}
// ensure that the duplicate field is found & removed
knownFieldNames.put(field.name, null);
scope.problemReporter().duplicateFieldInType(this, field);
field.binding = null;
} else {
knownFieldNames.put(field.name, fieldBinding);
// remember that we have seen a field with this name
fieldBindings[count++] = fieldBinding;
}
}
}
//only add prototype if not building specific selector
if(restrictToSelector == null) {
FieldBinding prototype = new FieldBinding(TypeConstants.PROTOTYPE,
TypeBinding.UNKNOWN, modifiers
| ExtraCompilerModifiers.AccUnresolved, this);
fieldBindings[count++] = prototype;
}
// remove duplicate fields
if (duplicate) {
FieldBinding[] newFieldBindings = new FieldBinding[fieldBindings.length];
// we know we'll be removing at least 1 duplicate name
size = count;
count = 0;
for (int i = 0; i < size; i++) {
FieldBinding fieldBinding = fieldBindings[i];
if (knownFieldNames.get(fieldBinding.name) != null) {
fieldBinding.id = count;
newFieldBindings[count++] = fieldBinding;
}
}
fieldBindings = newFieldBindings;
}
//make sure array length is correct
if (count != fieldBindings.length) {
System.arraycopy(fieldBindings, 0,
fieldBindings = new FieldBinding[count], 0, count);
}
this.addFields(fieldBindings);
}
/**
* <p>
* Build all or a specific function.
* </p>
*
* @param restrictToSelector
* build only functions with this selector, or if
* <code>null</code> build all functions
*/
private void buildFunctions(char[] restrictToSelector) {
if(this.classScope == null) {
return;
}
InferredType inferredType = this.classScope.inferredType;
int size = (inferredType.methods != null) ? inferredType.methods.size()
: 0;
if (size == 0) {
return;
}
int count = 0;
MethodBinding[] methodBindings = new MethodBinding[size];
// create bindings for source methods
for (int i = 0; i < size; i++) {
InferredMethod inferredMethod = (InferredMethod) inferredType.methods.get(i);
/* only build function if either the restricted selector is not set or
* the function name matches the restricted selector.
*
* Also skip any function that is already building or has already been built */
if((restrictToSelector == null || CharOperation.equals(inferredMethod.name, restrictToSelector)) &&
(restrictToSelector != null || !CharOperation.contains(inferredMethod.name, this.fBuildingSelectors)) &&
!CharOperation.contains(inferredMethod.name, this.fBuiltSelectors)) {
//determine if the method already has a resolved scope or not
boolean doesNotHaveResolvedScope = inferredMethod.getFunctionDeclaration() instanceof AbstractMethodDeclaration &&
((AbstractMethodDeclaration)inferredMethod.getFunctionDeclaration()).getScope() == null;
//build method scope
MethodDeclaration methDec = (MethodDeclaration) inferredMethod.getFunctionDeclaration();
MethodBinding methodBinding;
/* if does not already have a binding or existing binding has a different name then
* current inferred function create a new method binding
* else use existing method binding */
if(!methDec.hasBinding() || !CharOperation.equals(methDec.getBinding().selector, inferredMethod.name)) {
MethodScope scope = new MethodScope(this.scope, methDec, false);
/* if the inferred method 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 */
SourceTypeBinding declaringTypeBinding = null;
if(inferredMethod.inType != null && inferredMethod.inType.binding != null && !inferredMethod.isConstructor) {
declaringTypeBinding = inferredMethod.inType.binding;
} else {
declaringTypeBinding = this;
}
/* if not existing binding or is a constructor then use scope to create new binding
* else create new binding based on existing binding */
if(!methDec.hasBinding() || inferredMethod.isConstructor) {
methodBinding = scope.createMethod(inferredMethod, declaringTypeBinding);
} else {
methodBinding = new MethodBinding(methDec.getBinding(), declaringTypeBinding);
methodBinding.setSelector(inferredMethod.name);
}
} else {
methodBinding = methDec.getBinding();
}
//set bindings
inferredMethod.methodBinding = methodBinding;
methDec.setBinding(methodBinding);
//is null if binding could not be created
if (methodBinding != null) {
methodBindings[count++] = methodBinding;
// if method did not already have a resolved scope, then add it to the environment
if(doesNotHaveResolvedScope) {
this.scope.environment().defaultPackage.addBinding(
methodBinding, methodBinding.selector,
Binding.METHOD);
}
}
}
}
if (count != methodBindings.length) {
System.arraycopy(methodBindings, 0,
methodBindings = new MethodBinding[count], 0, count);
}
// in case some static imports reached already into this type
tagBits &= ~TagBits.AreMethodsSorted;
this.addMethods(methodBindings);
}
public int kind() {
return Binding.TYPE;
}
public char[] computeUniqueKey(boolean isLeaf) {
char[] uniqueKey = super.computeUniqueKey(isLeaf);
if (uniqueKey.length == 2)
return uniqueKey; // problem type's unique key is "L;"
if (Util.isClassFileName(this.fileName)
|| org.eclipse.wst.jsdt.internal.core.util.Util
.isMetadataFileName(new String(this.fileName)))
return uniqueKey; // no need to insert compilation unit name for a
// .class file
// insert compilation unit name if the type name is not the main type
// name
int end = CharOperation.lastIndexOf('.', this.fileName);
if (end != -1) {
int start = CharOperation.lastIndexOf('/', this.fileName) + 1;
char[] mainTypeName = CharOperation.subarray(this.fileName, start,
end);
start = CharOperation.lastIndexOf('/', uniqueKey) + 1;
if (start == 0)
start = 1; // start after L
end = CharOperation.indexOf('<', uniqueKey, start);
if (end == -1)
end = CharOperation.indexOf(';', uniqueKey, start);
char[] topLevelType = CharOperation.subarray(uniqueKey, start, end);
if (!CharOperation.equals(topLevelType, mainTypeName)) {
StringBuffer buffer = new StringBuffer();
buffer.append(uniqueKey, 0, start);
buffer.append(mainTypeName);
buffer.append('~');
buffer.append(topLevelType);
buffer.append(uniqueKey, end, uniqueKey.length - end);
int length = buffer.length();
uniqueKey = new char[length];
buffer.getChars(0, length, uniqueKey, 0);
return uniqueKey;
}
}
return uniqueKey;
}
void faultInTypesForFieldsAndMethods() {
// check @Deprecated annotation
// getAnnotationTagBits(); // marks as deprecated by side effect
ReferenceBinding enclosingType = this.enclosingType();
if (enclosingType != null && enclosingType.isViewedAsDeprecated()
&& !this.isDeprecated())
this.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly;
fields();
methods();
}
/**
* <p>
* NOTE: the type of each field of a source type is resolved when needed
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#fields()
*/
public FieldBinding[] fields() {
final Map fieldCache = new HashMap();
//get fields across all linked types
this.performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding linkedBinding) {
//be sure fields and methods are built
linkedBinding.buildFieldsAndMethods();
// complete fields if not yet complete
if ((linkedBinding.tagBits & TagBits.AreFieldsComplete) == 0) {
int failed = 0;
FieldBinding[] resolvedFields = linkedBinding.fields;
try {
// lazily sort fields
if ((linkedBinding.tagBits & TagBits.AreFieldsSorted) == 0) {
int length = linkedBinding.fields.length;
if (length > 1) {
ReferenceBinding.sortFields(linkedBinding.fields, 0, length);
}
linkedBinding.tagBits |= TagBits.AreFieldsSorted;
}
for (int i = 0, length = linkedBinding.fields.length; i < length; i++) {
if (linkedBinding.resolveTypeFor(linkedBinding.fields[i]) == null) {
/* do not alter original field array until resolution is
* over, due to reentrance (143259) */
if (resolvedFields == linkedBinding.fields) {
System.arraycopy(linkedBinding.fields, 0,
resolvedFields = new FieldBinding[length],
0, length);
}
resolvedFields[i] = null;
failed++;
}
}
} finally {
if (failed > 0) {
// ensure fields are consistent regardless of the error
int newSize = resolvedFields.length - failed;
if (newSize == 0) {
linkedBinding.setFields(Binding.NO_FIELDS);
} else {
FieldBinding[] newFields = new FieldBinding[newSize];
for (int i = 0, j = 0, length = resolvedFields.length; i < length; i++) {
if (resolvedFields[i] != null) {
newFields[j++] = resolvedFields[i];
}
}
linkedBinding.setFields(newFields);
}
}
}
//mark fields as complete
linkedBinding.tagBits |= TagBits.AreFieldsComplete;
}
//add fields to combined cache
for(int i = 0; i < linkedBinding.fields.length; i++) {
if(linkedBinding.fields[i] != null) {
fieldCache.put(linkedBinding.fields[i].name, linkedBinding.fields[i]);
}
}
// always search every linked type
return true;
}
});
return (FieldBinding[]) fieldCache.values().toArray(new FieldBinding[0]);
}
/**
* <p>
* Finds an exact constructor match searching across all linked type bindings.
* </p>
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#getExactConstructor(org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding[])
*/
public MethodBinding getExactConstructor(final TypeBinding[] argumentTypes) {
MethodBinding exactConstructor = (MethodBinding)this.performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* <p>
* The exact constructor match found on any of the linked types.
* </p>
*/
private MethodBinding fExactConstructorMatch = null;
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding linkedBinding) {
//be sure fields and methods are built
linkedBinding.buildFieldsAndMethods();
this.fExactConstructorMatch = linkedBinding.getExactConstructor0(argumentTypes);
//keep processing if have not yet found exact match
return this.fExactConstructorMatch == null;
}
/**
* @return {@link MethodBinding}
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return this.fExactConstructorMatch;
}
});
return exactConstructor;
}
// NOTE: the return type, arg & exception types of each method of a source
// type are resolved when needed
private MethodBinding getExactConstructor0(TypeBinding[] argumentTypes) {
// have resolved all arg types & return type of the methods
if ((this.tagBits & TagBits.AreMethodsComplete) != 0) {
long range;
if ((range = ReferenceBinding.binarySearch(TypeConstants.INIT, this.methods)) >= 0) {
if((int) range <= (int) (range >> 32)) {
MethodBinding method = this.methods[(int) range];
return method;
}
}
} else {
// lazily sort methods
if ((this.tagBits & TagBits.AreMethodsSorted) == 0) {
int length = this.methods.length;
if (length > 1) {
ReferenceBinding.sortMethods(this.methods, 0, length);
}
this.tagBits |= TagBits.AreMethodsSorted;
}
long range;
if ((range = ReferenceBinding.binarySearch(TypeConstants.INIT, this.methods)) >= 0) {
if((int) range <= (int) (range >> 32)) {
MethodBinding method = this.methods[(int) range];
if (resolveTypesFor(method) == null
|| method.returnType == null) {
methods();
// try again since the problem methods have been removed
return getExactConstructor(argumentTypes);
}
return method;
}
}
}
return null;
}
/**
* <p>
* Finds an exact function match searching across all linked type bindings
* and their super types.
* </p>
*
* <p>
* <b>NOTE:</b> this uses a breadth first search to find an exact function
* binding, first it searches all linked bindings, then their parents, so
* forth and so on.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#getExactMethod(char[],
* org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding[],
* org.eclipse.wst.jsdt.internal.compiler.lookup.CompilationUnitScope)
*/
public MethodBinding getExactMethod(final char[] selector,
final TypeBinding[] argumentTypes, final CompilationUnitScope refScope) {
MethodBinding exactMethod = null;
//search all linked types for exact method match
if(selector != null) {
final LinkedList typesToCheck = new LinkedList();
/* this set will contain every type that has already been checked
* this includes all linked types, therefore a simple contains check
* can be done to see if a given type has already been checked
* rather then having to iterate and do an expensive #isEquivalentTo check */
final Set checkedTypes = new HashSet();
typesToCheck.add(this);
while(!typesToCheck.isEmpty() && exactMethod == null) {
ReferenceBinding typeToCheck = (ReferenceBinding)typesToCheck.removeFirst();
/* if type to check is SourceTypeBinding then have to check all linked bindings
* else just check the ReferenceBinding directly */
if(typeToCheck instanceof SourceTypeBinding) {
exactMethod = (MethodBinding)((SourceTypeBinding)typeToCheck).performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* <p>
* The located exact function match.
* </p>
*/
private MethodBinding fExactFunctionMatch;
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding typeToCheckLinkedBinding) {
checkedTypes.add(typeToCheckLinkedBinding);
/* only build fields and functions if inferred type has a function with correct name,
* and only bother checking inferred type if the fields and functions have not already been built
*
* this saves time avoiding building fields and functions when it is not needed */
if(!SourceTypeBinding.this.fHasBuiltFieldsAndMethods && typeToCheckLinkedBinding.inferredTypeHasFunction(selector)) {
//be sure fields and methods are built
typeToCheckLinkedBinding.buildFieldsAndMethods(selector);
}
//check self for exact function
this.fExactFunctionMatch = typeToCheckLinkedBinding.getExactMethod0(selector, argumentTypes, refScope);
/* add super type of current linked binding to types to check if
* not already there and not already checked */
if(this.fExactFunctionMatch == null) {
boolean alreadyGoingToCheck = false;
ReferenceBinding superBinding = typeToCheckLinkedBinding.getSuperBinding0();
if(superBinding != null) {
Iterator typesToCheckIter = typesToCheck.iterator();
while(typesToCheckIter.hasNext()) {
alreadyGoingToCheck = ((ReferenceBinding)typesToCheckIter.next()).isEquivalentTo(superBinding);
}
boolean alreadyChecked = false;
if(!alreadyGoingToCheck) {
alreadyChecked = checkedTypes.contains(superBinding);
}
if(!alreadyGoingToCheck && !alreadyChecked) {
typesToCheck.add(superBinding);
}
}
}
//keep processing if have not yet found exact match
return this.fExactFunctionMatch == null;
}
/**
* @return {@link MethodBinding}
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return this.fExactFunctionMatch;
}
});
} else {
checkedTypes.add(typeToCheck);
exactMethod = typeToCheck.getExactMethod(selector, argumentTypes, refScope);
/* add super type of current binding to types to check if
* not already there and not already checked */
if(exactMethod == null) {
boolean alreadyGoingToCheck = false;
ReferenceBinding superBinding = typeToCheck.getSuperBinding();
if(superBinding != null) {
Iterator typesToCheckIter = typesToCheck.iterator();
while(typesToCheckIter.hasNext()) {
alreadyGoingToCheck = ((ReferenceBinding)typesToCheckIter.next()).isEquivalentTo(superBinding);
}
boolean alreadyChecked = false;
if(!alreadyGoingToCheck) {
alreadyChecked = checkedTypes.contains(superBinding);
}
if(!alreadyGoingToCheck && !alreadyChecked) {
typesToCheck.add(superBinding);
}
}
}
}
}
}
return exactMethod;
}
/**
* <p>
* <b>NOTES:</b>
* <ul>
* <li>the return type, arg & exception types of each method of a source
* type are resolved when needed.</li>
* <li>this method only searches this specific binding, it does not search
* any linked bindings or any super bindings</li>
* </ul>
* </p>
*
* @param selector
* @param argumentTypes
* @param refScope
* @return
*/
private MethodBinding getExactMethod0(char[] selector,
TypeBinding[] argumentTypes, CompilationUnitScope refScope) {
if(selector == null || this.methods == null || this.methods.length == 0)
return null;
// have resolved all arg types & return type of the methods
if ((this.tagBits & TagBits.AreMethodsComplete) != 0) {
long range;
if ((range = ReferenceBinding.binarySearch(selector, this.methods)) >= 0) {
if((int) range <= (int) (range >> 32)) {
MethodBinding method = this.methods[(int) range];
// inner type lookups must know that a method with this name exists
return method;
}
}
} else {
// lazily sort methods
if ((this.tagBits & TagBits.AreMethodsSorted) == 0) {
int length = this.methods.length;
if (length > 1)
ReferenceBinding.sortMethods(this.methods, 0, length);
this.tagBits |= TagBits.AreMethodsSorted;
}
long range;
if ((range = ReferenceBinding.binarySearch(selector, this.methods)) >= 0) {
// check unresolved method
int start = (int) range, end = (int) (range >> 32);
for (int imethod = start; imethod <= end; imethod++) {
MethodBinding method = this.methods[imethod];
if (resolveTypesFor(method) == null
|| method.returnType == null) {
methods();
// try again since the problem methods have been removed
return getExactMethod0(selector, argumentTypes, refScope);
}
}
// check dup collisions
boolean isSource15 = this.scope != null
&& this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
for (int i = start; i <= end; i++) {
MethodBinding method1 = this.methods[i];
for (int j = end; j > i; j--) {
MethodBinding method2 = this.methods[j];
boolean paramsMatch = isSource15 ? method1
.areParametersEqual(method2) : method1
.areParametersEqual(method2);
if (paramsMatch) {
methods();
// try again since the problem methods have been removed
return getExactMethod0(selector, argumentTypes, refScope);
}
}
}
return this.methods[start];
}
}
return null;
}
/**
* <p>
* Searches all linked types for a field with the given name.
* </p>
*
* <p>
* <b>NOTE:</p> this does not check super types.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#getField(char[], boolean)
*/
public FieldBinding getField(final char[] fieldName, final boolean needResolve) {
FieldBinding field = null;
//search all linked types for exact method match
if(fieldName != null) {
field = (FieldBinding)this.performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* <p>
* The located exact function match.
* </p>
*/
private FieldBinding fFieldMatch;
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding linkedBinding) {
/* only build fields and functions if inferred type has field with correct name,
* and only bother checking inferred type if the fields and functions have not already been built
*
* this saves time avoiding building fields and functions when it is not needed */
if(!SourceTypeBinding.this.fHasBuiltFieldsAndMethods && linkedBinding.inferredTypeHasField(fieldName)) {
//be sure fields and methods are built
linkedBinding.buildFieldsAndMethods(fieldName);
}
//check self for exact field
this.fFieldMatch = linkedBinding.getField0(fieldName, needResolve);
//keep processing if have not yet found exact match
return this.fFieldMatch == null;
}
/**
* @return {@link FieldBinding}
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return this.fFieldMatch;
}
});
}
return field;
}
public FieldBinding getFieldInHierarchy(char[] fieldName, boolean needResolve) {
SourceTypeBinding currentType = this;
while (currentType != null) {
FieldBinding field = currentType.getField(fieldName, needResolve);
if (field != null) {
return field;
}
currentType = (SourceTypeBinding) currentType.getSuperBinding();
}
return null;
}
// NOTE: the type of a field of a source type is resolved when needed
private FieldBinding getField0(char[] fieldName, boolean needResolve) {
if ((this.tagBits & TagBits.AreFieldsComplete) != 0) {
return ReferenceBinding.binarySearch(fieldName, this.fields);
}
// lazily sort fields
if ((this.tagBits & TagBits.AreFieldsSorted) == 0) {
int length = this.fields.length;
if (length > 1)
ReferenceBinding.sortFields(this.fields, 0, length);
this.tagBits |= TagBits.AreFieldsSorted;
}
// always resolve anyway on source types
FieldBinding field = ReferenceBinding.binarySearch(fieldName, this.fields);
if (field != null) {
FieldBinding result = null;
try {
result = resolveTypeFor(field);
return result;
} finally {
if (result == null) {
// ensure fields are consistent reqardless of the error
int newSize = this.fields.length - 1;
if (newSize == 0) {
this.setFields(Binding.NO_FIELDS);
} else {
FieldBinding[] newFields = new FieldBinding[newSize];
int index = 0;
for (int i = 0, length = this.fields.length; i < length; i++) {
FieldBinding f = this.fields[i];
if (f == field) {
continue;
}
newFields[index++] = f;
}
this.setFields(newFields);
}
}
}
}
return null;
}
/**
* <p>
* Get all methods across all linked type bindings.
* </p>
*
* <p>
* <b>NOTE:</p> this does not check super types.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#getMethods(char[])
*/
public MethodBinding[] getMethods(final char[] selector) {
MethodBinding[] allFunctionMatches = Binding.NO_METHODS;
//search all linked types for functions matching the given selector
if(selector != null) {
allFunctionMatches = (MethodBinding[])this.performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* <p>
* All of the functions matching a given selector found across all of the linked types.
* </p>
*/
private MethodBinding[] fAllFunctionMatches = Binding.NO_METHODS;
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding linkedBinding) {
/* only build fields and functions if inferred type has field with correct name,
* and only bother checking inferred type if the fields and functions have not already been built
*
* this saves time avoiding building fields and functions when it is not needed */
if(!SourceTypeBinding.this.fHasBuiltFieldsAndMethods && linkedBinding.inferredTypeHasFunction(selector)) {
linkedBinding.buildFieldsAndMethods(selector);
}
//get current types functions
MethodBinding[] functionMatches = linkedBinding.getMethods0(selector);
//combine all function matches into one array
if(this.fAllFunctionMatches == null) {
this.fAllFunctionMatches = functionMatches;
} else {
MethodBinding[] combinedFunctionMatches = new MethodBinding[this.fAllFunctionMatches.length + functionMatches.length];
System.arraycopy(this.fAllFunctionMatches, 0, combinedFunctionMatches, 0, this.fAllFunctionMatches.length);
System.arraycopy(functionMatches, 0, combinedFunctionMatches, this.fAllFunctionMatches.length, functionMatches.length);
this.fAllFunctionMatches = combinedFunctionMatches;
}
// always search every linked type for methods
return true;
}
/**
* @return {@link MethodBinding}[]
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return this.fAllFunctionMatches;
}
});
}
return allFunctionMatches;
}
/**
* <p>
* <b>NOTES:</b>
* <ul>
* <li>the return type, arg & exception types of each method of a source
* type are resolved when needed</li>
* <li>this does not check super types</li>
* </ul>
* </p>
*
* @param selector
* @return
*/
private MethodBinding[] getMethods0(char[] selector) {
// lazily sort methods
if ((this.tagBits & TagBits.AreMethodsSorted) == 0) {
int length = this.methods.length;
if (length > 1) {
ReferenceBinding.sortMethods(this.methods, 0, length);
}
this.tagBits |= TagBits.AreMethodsSorted;
}
if ((this.tagBits & TagBits.AreMethodsComplete) != 0) {
long range;
if ((range = ReferenceBinding.binarySearch(selector, this.methods)) >= 0) {
int start = (int) range, end = (int) (range >> 32);
int length = end - start + 1;
MethodBinding[] result;
System.arraycopy(this.methods, start,
result = new MethodBinding[length], 0, length);
return result;
} else {
return Binding.NO_METHODS;
}
}
MethodBinding[] result;
long range;
if ((range = ReferenceBinding.binarySearch(selector, this.methods)) >= 0) {
int start = (int) range, end = (int) (range >> 32);
for (int i = start; i <= end; i++) {
MethodBinding method = this.methods[i];
if (resolveTypesFor(method) == null || method.returnType == null) {
// try again since the problem methods have been removed
methods();
return getMethods(selector);
}
}
int length = end - start + 1;
System.arraycopy(this.methods, start,
result = new MethodBinding[length], 0, length);
} else {
return Binding.NO_METHODS;
}
boolean isSource15 = this.scope != null
&& this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
for (int i = 0, length = result.length - 1; i < length; i++) {
MethodBinding method = result[i];
for (int j = length; j > i; j--) {
boolean paramsMatch = isSource15 ? method
.areParametersEqual(result[j]) : method
.areParametersEqual(result[j]);
if (paramsMatch) {
methods();
// try again since the duplicate methods have been removed
return getMethods(selector);
}
}
}
return result;
}
/**
* Returns true if a type is identical to another one.
*/
public boolean isEquivalentTo(final TypeBinding otherType) {
//short cut for simple case
boolean isEquivalent = this == otherType;
if(!isEquivalent && otherType != null) {
final boolean isOtherTypeSourceType = otherType instanceof SourceTypeBinding;
isEquivalent = ((Boolean)this.performActionOnLinkedBindings(new LinkedBindingAction() {
private boolean fIsEquivalent = false;
public boolean performAction(final SourceTypeBinding selfLinkedBinding) {
/* if other type is also a source type binding have to loop
* through all its types as well
*
* else just compare current linked binding with other type */
this.fIsEquivalent = selfLinkedBinding == otherType;
return !this.fIsEquivalent;
}
public Object getFinalResult() {
return new Boolean(this.fIsEquivalent);
}
})).booleanValue();
}
return isEquivalent;
}
/**
* <p>
* Get all member types across all of the linked type bindings.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#memberTypes()
*/
public ReferenceBinding[] memberTypes() {
//search all linked types for member types
ReferenceBinding[] allMemberTypes = (ReferenceBinding[])this.performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* <p>
* All of the member types found across all of the linked types.
* </p>
*/
private ReferenceBinding[] fAllMemberTypes;
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding linkedBinding) {
//combine all methods into one array
if(this.fAllMemberTypes == null) {
this.fAllMemberTypes = linkedBinding.memberTypes;
} else {
ReferenceBinding[] combinedMemberTypes = new ReferenceBinding[this.fAllMemberTypes.length + linkedBinding.memberTypes.length];
System.arraycopy(this.fAllMemberTypes, 0, combinedMemberTypes, 0, this.fAllMemberTypes.length);
System.arraycopy(linkedBinding.memberTypes, 0, combinedMemberTypes, this.fAllMemberTypes.length, linkedBinding.memberTypes.length);
this.fAllMemberTypes = combinedMemberTypes;
}
// always search every linked type for member types
return true;
}
/**
* @return {@link ReferenceBinding}[]
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return this.fAllMemberTypes;
}
});
return allMemberTypes;
}
public FieldBinding getUpdatedFieldBinding(FieldBinding targetField,
ReferenceBinding newDeclaringClass) {
Hashtable fieldMap = new Hashtable(5);
FieldBinding updatedField = new FieldBinding(targetField,
newDeclaringClass);
fieldMap.put(newDeclaringClass, updatedField);
return updatedField;
}
public MethodBinding getUpdatedMethodBinding(MethodBinding targetMethod,
ReferenceBinding newDeclaringClass) {
MethodBinding updatedMethod = new MethodBinding(targetMethod,
newDeclaringClass);
updatedMethod.createFunctionTypeBinding(scope);
return updatedMethod;
}
/**
* <p>
* <code>true</code> if any of the linked types has have member types, <code>false</code> otherwise.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#hasMemberTypes()
*/
public boolean hasMemberTypes() {
//search all linked types for member types
Boolean hasMemberTypes = (Boolean)this.performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* <p>
* <code>true</code> if any linked type has member types, <code>false</code> otherwise
* </p>
*/
private boolean fHasMemberTypes = false;
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding linkedBinding) {
//check if this type has member types
this.fHasMemberTypes = (linkedBinding.memberTypes != null
&& linkedBinding.memberTypes.length > 0);
//keep checking linked types if have not yet found member types
return !this.fHasMemberTypes;
}
/**
* @return {@link Boolean}
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return new Boolean(this.fHasMemberTypes);
}
});
return hasMemberTypes.booleanValue();
}
/**
* <p>
* NOTE: the return type, arg & exception types of each method of a source
* type are resolved when needed
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#methods()
*/
public MethodBinding[] methods() {
//gather all the functions across all the linked types
MethodBinding[] allFunctions = (MethodBinding[])this.performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* <p>
* All of the functions defined across all the linked types.
* </p>
*/
private MethodBinding[] fAllFunctions;
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding linkedBinding) {
//be sure fields and methods are built
linkedBinding.buildFieldsAndMethods();
if ((linkedBinding.tagBits & TagBits.AreMethodsComplete) == 0) {
// lazily sort methods
if ((linkedBinding.tagBits & TagBits.AreMethodsSorted) == 0) {
int length = linkedBinding.methods.length;
if (length > 1) {
ReferenceBinding.sortMethods(linkedBinding.methods, 0, length);
}
linkedBinding.tagBits |= TagBits.AreMethodsSorted;
}
int failed = 0;
MethodBinding[] resolvedMethods = linkedBinding.methods;
try {
for (int i = 0, length = linkedBinding.methods.length; i < length; i++) {
if (resolveTypesFor(linkedBinding.methods[i]) == null) {
/* do not alter original method array until resolution
* is over, due to reentrance (143259) */
if (resolvedMethods == linkedBinding.methods) {
System.arraycopy(linkedBinding.methods, 0,
resolvedMethods = new MethodBinding[length], 0, length);
}
// unable to resolve parameters
resolvedMethods[i] = null;
failed++;
}
}
// find & report collision cases
boolean complyTo15 = (linkedBinding.scope != null &&
linkedBinding.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5);
for (int i = 0, length = linkedBinding.methods.length; i < length; i++) {
MethodBinding method = resolvedMethods[i];
if (method == null) {
continue;
}
char[] selector = method.selector;
AbstractMethodDeclaration methodDecl = null;
nextSibling: for (int j = i + 1; j < length; j++) {
MethodBinding method2 = resolvedMethods[j];
if (method2 == null) {
continue nextSibling;
}
//if its the same method skip ahead
if(method == method2) {
break nextSibling;
}
// methods with same selector are contiguous
if (!CharOperation.equals(selector, method2.selector)) {
break nextSibling;
}
if (complyTo15 && method.returnType != null
&& method2.returnType != null) {
/* 8.4.2, for collision to be detected between m1
* and m2:
* signature(m1) == signature(m2) i.e. same arity,
* same type parameter count, can be substituted
* signature(m1) == erasure(signature(m2)) or
* erasure(signature(m1)) == signature(m2) */
TypeBinding[] params1 = method.parameters;
TypeBinding[] params2 = method2.parameters;
int pLength = params1.length;
if (pLength != params2.length) {
continue nextSibling;
}
MethodBinding subMethod = method2;
boolean equalParams = method .areParametersEqual(subMethod);
if (equalParams) {
// duplicates regardless of return types
} else if (method.returnType == subMethod.returnType
&& (equalParams || method.areParametersEqual(method2))) {
// name clash for sure if not duplicates, report as duplicates
} else if (pLength > 0) {
// check to see if the erasure of either method is equal to the other
int index = pLength;
for (; --index >= 0;) {
if (params1[index] != params2[index]) {
break;
}
}
if (index >= 0 && index < pLength) {
for (index = pLength; --index >= 0;) {
if (params1[index] != params2[index]) {
break;
}
}
}
if (index >= 0) {
continue nextSibling;
}
}
}
// prior to 1.5, parameter identity meant a collision case
else if (!method.areParametersEqual(method2)) {
continue nextSibling;
}
// report duplicate
if (methodDecl == null) {
// cannot be retrieved after binding is lost & may still be null if method is special
methodDecl = method.sourceMethod();
//ensure its a valid user defined method
if (methodDecl != null && methodDecl.hasBinding()) {
linkedBinding.scope.problemReporter().duplicateMethodInType(linkedBinding, methodDecl);
methodDecl.setBinding(null);
/* do not alter original method array until
* resolution is over, due to reentrance (143259) */
if (resolvedMethods == linkedBinding.methods) {
System.arraycopy(linkedBinding.methods, 0,
resolvedMethods = new MethodBinding[length], 0, length);
}
resolvedMethods[i] = null;
failed++;
}
}
AbstractMethodDeclaration method2Decl = method2.sourceMethod();
//ensure its a valid user defined method
if (method2Decl != null && method2Decl.hasBinding()) {
linkedBinding.scope.problemReporter().duplicateMethodInType(linkedBinding, method2Decl);
method2Decl.setBinding(null);
/* do not alter original method array until
* resolution is over, due to reentrance (143259) */
if (resolvedMethods == linkedBinding.methods) {
System.arraycopy(linkedBinding.methods, 0,
resolvedMethods = new MethodBinding[length], 0, length);
}
resolvedMethods[j] = null;
failed++;
}
}
//forget method with invalid return type... was kept to detect possible collisions
if (method != null && method.returnType == null && methodDecl == null) {
methodDecl = method.sourceMethod();
if (methodDecl != null) {
methodDecl.setBinding(null);
}
/* do not alter original method array until resolution
* is over, due to reentrance (143259) */
if (resolvedMethods == linkedBinding.methods) {
System.arraycopy(linkedBinding.methods, 0,
resolvedMethods = new MethodBinding[length], 0, length);
}
resolvedMethods[i] = null;
failed++;
}
}
} finally {
if (failed > 0) {
int newSize = resolvedMethods.length - failed;
if (newSize == 0) {
linkedBinding.setMethods(Binding.NO_METHODS);
} else {
MethodBinding[] newMethods = new MethodBinding[newSize];
for (int i = 0, j = 0, length = resolvedMethods.length; i < length; i++) {
if (resolvedMethods[i] != null) {
newMethods[j++] = resolvedMethods[i];
}
}
linkedBinding.setMethods(newMethods);
}
}
// mark functions as complete
linkedBinding.tagBits |= TagBits.AreMethodsComplete;
}
}
//combine newly found functions with already found ones
if(this.fAllFunctions == null) {
this.fAllFunctions = linkedBinding.methods;
} else {
MethodBinding[] combinedFunctions = new MethodBinding[this.fAllFunctions.length + linkedBinding.methods.length];
System.arraycopy(this.fAllFunctions, 0, combinedFunctions, 0, this.fAllFunctions.length);
System.arraycopy(linkedBinding.methods, 0, combinedFunctions, this.fAllFunctions.length, linkedBinding.methods.length);
this.fAllFunctions = combinedFunctions;
}
// always search every linked type
return true;
}
/**
* @return {@link MethodBinding}[]
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return this.fAllFunctions;
}
});
return allFunctions;
}
private FieldBinding resolveTypeFor(FieldBinding field) {
if ((field.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0) {
return field;
}
if (isViewedAsDeprecated() && !field.isDeprecated()) {
field.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly;
}
if (hasRestrictedAccess()) {
field.modifiers |= ExtraCompilerModifiers.AccRestrictedAccess;
}
return field;
}
public MethodBinding resolveTypesFor(MethodBinding method) {
return resolveTypesFor(method, null);
}
public MethodBinding resolveTypesFor(MethodBinding method,
AbstractMethodDeclaration methodDecl) {
if ((method.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0)
return method;
if (isViewedAsDeprecated() && !method.isDeprecated())
method.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly;
if (hasRestrictedAccess())
method.modifiers |= ExtraCompilerModifiers.AccRestrictedAccess;
if (methodDecl == null)
methodDecl = method.sourceMethod();
if (methodDecl == null)
return null; // method could not be resolved in previous iteration
boolean foundArgProblem = false;
Argument[] arguments = methodDecl.arguments;
if (arguments != null) {
int size = arguments.length;
method.setParameters(Binding.NO_PARAMETERS);
TypeBinding[] newParameters = new TypeBinding[size];
for (int i = 0; i < size; i++) {
Argument arg = arguments[i];
TypeBinding parameterType = TypeBinding.UNKNOWN;
if (arg.type != null) {
parameterType = arg.type
.resolveType(methodDecl.getScope(), true /* check bounds */);
} else if (arg.inferredType != null) {
/* if argument has an anonymous inferred type then it has not been built
* at this point, so build it before attempt to resolve it. */
if(arg.inferredType.isAnonymous && arg.inferredType.binding == null) {
ReferenceBinding argTypeBinding = methodDecl.getScope().findType(
arg.inferredType.getName(), this.getPackage(), this.getPackage());
if(argTypeBinding instanceof SourceTypeBinding) {
arg.inferredType.binding = (SourceTypeBinding) argTypeBinding;
}
}
parameterType = arg.inferredType.resolveType(
methodDecl.getScope(), arg);
}
if (parameterType == null) {
parameterType = TypeBinding.ANY;
}
newParameters[i] = parameterType;
if(arg.binding == null) {
arg.binding = new LocalVariableBinding(arg, parameterType,
arg.modifiers, true);
}
}
// only assign parameters if no problems are found
if (!foundArgProblem) {
method.setParameters(newParameters);
}
}
boolean foundReturnTypeProblem = false;
if (!method.isConstructor()) {
TypeReference returnType = methodDecl instanceof MethodDeclaration ? ((MethodDeclaration) methodDecl).returnType
: null;
if (returnType == null
&& !(methodDecl instanceof MethodDeclaration)) {
methodDecl.getScope().problemReporter()
.missingReturnType(methodDecl);
method.returnType = null;
foundReturnTypeProblem = true;
} else {
TypeBinding methodType = (returnType != null) ? returnType
.resolveType(methodDecl.getScope(), true /* check bounds */)
: null;
if (methodType == null)
methodType = (methodDecl.inferredType != null) ? methodDecl.inferredType
.resolveType(methodDecl.getScope(), methodDecl)
: TypeBinding.UNKNOWN;
if (methodType == null) {
foundReturnTypeProblem = true;
} else {
method.returnType = methodType;
}
}
}
if (foundArgProblem) {
methodDecl.setBinding(null);
method.setParameters(Binding.NO_PARAMETERS); // see 107004
// nullify type parameter bindings as well as they have a
// backpointer to the method binding
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=81134)
return null;
}
if (foundReturnTypeProblem)
return method; // but its still unresolved with a null return type &
// is still connected to its method declaration
method.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
return method;
}
public void setFields(FieldBinding[] fields) {
this.tagBits &= ~TagBits.AreFieldsSorted;
this.fields = fields;
}
public void setMethods(MethodBinding[] methods) {
this.tagBits &= ~TagBits.AreMethodsSorted;
this.methods = methods;
}
public int sourceEnd() {
if (this.classScope.referenceContext != null) {
return this.classScope.referenceContext.sourceEnd;
} else {
return this.classScope.inferredType.sourceEnd;
}
}
public int sourceStart() {
if (this.classScope.referenceContext != null) {
return this.classScope.referenceContext.sourceStart;
} else {
return this.classScope.inferredType.sourceStart;
}
}
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#superclass()
*/
/**
* <p>
* Will return the super binding set on this binding or if this bindings
* super biding is null or <code>Object</code> will return the super
* binding set on the first linked binding who's super binding is not null
* and not <code>Object</code>
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#getSuperBinding()
*
* @see #getSuperBinding0()
*/
public ReferenceBinding getSuperBinding() {
//search all linked type bindings for the first one with a super type that is not Object
ReferenceBinding superBinding = (ReferenceBinding)this.performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* <p>
* First super type found when searching all linked type bindings.
* </p>
*/
private ReferenceBinding fFoundSuperBinding = null;
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding linkedBinding) {
ReferenceBinding linkedSuperBinding = linkedBinding.getSuperBinding0();
/* Be sure that the super type of a linked binding is not the same as this type
* This can legitimately happen when using a pattern like:
*
* define("foo.BarImpl", "foo.Bar", {}):
* foo.Bar = foo.BarImpl;
*
* A best effort is made to avoid this at the infer level by setting it only as the
* super type and not as a synonym, but still best to have this check here */
if(linkedSuperBinding != null && linkedSuperBinding != SourceTypeBinding.this &&
(this.fFoundSuperBinding == null || (linkedSuperBinding.id != TypeIds.T_JavaLangObject))) {
this.fFoundSuperBinding = linkedSuperBinding;
}
//keep searching if super type is null or Object
return this.fFoundSuperBinding == null || this.fFoundSuperBinding.id == TypeIds.T_JavaLangObject;
}
/**
* @return {@link ReferenceBinding}
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return this.fFoundSuperBinding;
}
});
return superBinding;
}
/**
* @return {@link SourceTypeBinding} set as the super binding for this
* binding only. Unlike {@link #getSuperBinding()} this function
* will not search the linked bindings.
*
* @see #getSuperBinding()
*/
public ReferenceBinding getSuperBinding0() {
return this.fSuperBinding;
}
/**
* <p>
* Sets the super binding for this specific binding. This will overwrite
* any currently set super binding for this binding.
* </p>
*
* <p>
* <b>WARNING:</b> A linked binding may have a different super binding.
* </p>
*
* @param newSuperBinding
* {@link SourceTypeBinding} to set as the super binding for
* this binding, will overwrite any currently set super binding
*/
public void setSuperBinding(ReferenceBinding newSuperBinding) {
this.fSuperBinding = newSuperBinding;
}
public String toString() {
final StringBuffer buffer = new StringBuffer(30);
buffer.append("(id="); //$NON-NLS-1$
if (this.id == TypeIds.NoId)
buffer.append("NoId"); //$NON-NLS-1$
else
buffer.append(this.id);
buffer.append(")\n"); //$NON-NLS-1$
if (isDeprecated())
buffer.append("deprecated "); //$NON-NLS-1$
if (isPublic())
buffer.append("public "); //$NON-NLS-1$
if (isPrivate())
buffer.append("private "); //$NON-NLS-1$
if (isStatic() && isNestedType())
buffer.append("static "); //$NON-NLS-1$
if (isClass())
buffer.append("class "); //$NON-NLS-1$
else
buffer.append("interface "); //$NON-NLS-1$
buffer.append((this.compoundName != null) ? CharOperation
.toString(this.compoundName) : "UNNAMED TYPE"); //$NON-NLS-1$
buffer.append("\n\textends "); //$NON-NLS-1$
buffer.append((this.fSuperBinding != null) ? this.fSuperBinding.debugName()
: "NULL TYPE"); //$NON-NLS-1$
if (enclosingType() != null) {
buffer.append("\n\tenclosing type : "); //$NON-NLS-1$
buffer.append(enclosingType().debugName());
}
if (this.fields != null) {
if (this.fields != Binding.NO_FIELDS) {
buffer.append("\n/* fields */"); //$NON-NLS-1$
for (int i = 0, length = this.fields.length; i < length; i++)
buffer.append('\n').append(
(this.fields[i] != null) ? this.fields[i]
.toString() : "NULL FIELD"); //$NON-NLS-1$
}
} else {
buffer.append("NULL FIELDS"); //$NON-NLS-1$
}
if (this.methods != null) {
if (this.methods != Binding.NO_METHODS) {
buffer.append("\n/* methods */"); //$NON-NLS-1$
for (int i = 0, length = this.methods.length; i < length; i++)
buffer.append('\n').append(
(this.methods[i] != null) ? this.methods[i]
.toString() : "NULL METHOD"); //$NON-NLS-1$
}
} else {
buffer.append("NULL METHODS"); //$NON-NLS-1$
}
if (this.memberTypes != null) {
if (this.memberTypes != Binding.NO_MEMBER_TYPES) {
buffer.append("\n/* members */"); //$NON-NLS-1$
for (int i = 0, length = this.memberTypes.length; i < length; i++)
buffer.append('\n').append(
(this.memberTypes[i] != null) ? this.memberTypes[i]
.toString() : "NULL TYPE"); //$NON-NLS-1$
}
} else {
buffer.append("NULL MEMBER TYPES"); //$NON-NLS-1$
}
//if debugging enabled then print out all the linked type names and their hashes
if(DEBUG) {
buffer.append("\n\nLINKED TYPE NAMES:\n"); //$NON-NLS-1$
this.performActionOnLinkedBindings(new LinkedBindingAction() {
public boolean performAction(SourceTypeBinding linkedBinding) {
buffer.append(linkedBinding.qualifiedSourceName0());
buffer.append(" -> "); //$NON-NLS-1$
buffer.append(Integer.toHexString(System.identityHashCode(linkedBinding)));
buffer.append("\n"); //$NON-NLS-1$
return true;
}
});
}
buffer.append("\n\n"); //$NON-NLS-1$
return buffer.toString();
}
public AbstractMethodDeclaration sourceMethod(MethodBinding binding) {
if (this.classScope == null) {
return null;
}
InferredType inferredType = this.classScope.inferredType;
InferredMethod inferredMethod = inferredType.findMethod(
binding.selector, null);
if (inferredMethod != null) {
return (AbstractMethodDeclaration) inferredMethod.getFunctionDeclaration();
}
return null;
}
public void addMethod(MethodBinding binding) {
this.tagBits &= ~TagBits.AreMethodsSorted;
int length = this.methods.length;
System.arraycopy(this.methods, 0,
this.methods = new MethodBinding[length + 1], 0, length);
this.methods[length] = binding;
}
/**
* @param binding {@link FieldBinding} to add to this type binding
*/
public void addField(FieldBinding binding) {
this.tagBits &= ~TagBits.AreFieldsSorted;
int length = this.fields.length;
System.arraycopy(this.fields, 0,
this.fields = new FieldBinding[length + 1], 0, length);
this.fields[length] = binding;
}
/**
* <p>
* Adds new function bindings to this type binding.
* </p>
*
* @param newFunctionBindings
* {@link MethodBinding}s to add to this type binding
*/
private void addMethods(MethodBinding[] newFunctionBindings) {
this.tagBits &= ~TagBits.AreMethodsSorted;
int length = this.methods.length;
System.arraycopy(this.methods, 0,
this.methods = new MethodBinding[length + newFunctionBindings.length], 0, length);
System.arraycopy(newFunctionBindings, 0, this.methods, length, newFunctionBindings.length);
}
/**
* <p>
* Adds new field bindings to this type binding.
* </p>
*
* @param newFieldBindings
* {@link FieldBinding}s to add to this type binding
*/
private void addFields(FieldBinding[] newFieldBindings) {
this.tagBits &= ~TagBits.AreFieldsSorted;
int length = this.fields.length;
System.arraycopy(this.fields, 0,
this.fields = new FieldBinding[length + newFieldBindings.length], 0, length);
System.arraycopy(newFieldBindings, 0, this.fields, length, newFieldBindings.length);
}
public void cleanup() {
this.scope = null;
this.classScope = null;
}
/**
* <p>
* Determines if a given binding is linked to this binding.
* </p>
*
* @param searchBinding
* check if this {@link ReferenceBinding} is linked to this binding
*
* @return <code>true</code> if any linked types are the given binding,
* <code>false</code> otherwise
*/
boolean isLinkedType(final ReferenceBinding searchBinding) {
// searches all linked bindings to see if any of them are the given search binding
Boolean isBindingLinked = (Boolean)this.performActionOnLinkedBindings(new LinkedBindingAction() {
/**
* <p>
* <code>true</code> if any linked types are the given binding, <code>false</code> otherwise
* </p>
*/
private boolean fIsBindingLinked = false;
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding linkedBinding) {
this.fIsBindingLinked = searchBinding == linkedBinding;
// keep searching if not found linked the given binding to be linked
return !this.fIsBindingLinked;
}
/**
* @return {@link Boolean}
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return new Boolean(this.fIsBindingLinked);
}
});
return isBindingLinked.booleanValue();
}
/**
* <p>
* Adds the given new linked type and all of its linked types to this
* types circle of linked types. If the given new linked type is already
* linked to this type then no operation is taken.
* </p>
*
* <p>
* EX: <br>
* this type: A0 -> A1 -> A2 -> A3 -> A0<br>
* new linked type: B0 -> B1 -> B0<br>
* <br>
* combined after this operation:<br>
* A0 -> B0 -> B1 -> A1 -> A2 -> A3 -> A0
* </p>
*
* @param newLinkedBinding
* {@link SourceTypeBinding} to link to this one
*/
public void addLinkedBinding(final SourceTypeBinding newLinkedBinding) {
// determine if the new linked type is a duplicate of a current linked type
boolean isDuplicate = this.isLinkedType(newLinkedBinding);
/* if linked type is not a duplicate then combine this types linked types
* circle with the new types circle into one giant circle
*
* EX:
*
* this type: A0 -> A1 -> A2 -> A3 -> A0
* new linked type: B0 -> B1 -> B0
*
* combined after this operation:
* A0 -> B0 -> B1 -> A1 -> A2 -> A3 -> A0
*/
if(!isDuplicate) {
SourceTypeBinding currNextType = this.fNextType;
this.fNextType = newLinkedBinding;
//search for the end of the linked type circle, aka the type that links back to the new linked type
SourceTypeBinding newTypesLastNextType = newLinkedBinding;
while(newTypesLastNextType.fNextType != newLinkedBinding) {
newTypesLastNextType = newTypesLastNextType.fNextType;
}
/* assign what was this types next type as the next type for
* the last next type in the new linked type's next type circle
*
* clear as mud, see comment up a few lines for example of what is going on
*/
newTypesLastNextType.fNextType = currNextType;
}
}
public static boolean checkIfDuplicateType(SourceTypeBinding binding1, SourceTypeBinding binding2) {
InferredType type2 = binding2.classScope.inferredType;
if(binding1.classScope == null) {
if(binding1.fSuperBinding == null && type2.getSuperType() != null)
return false;
if(binding1.fSuperBinding != null && type2.getSuperType() == null)
return false;
if(binding1.fSuperBinding != null && type2.getSuperType() != null &&
!CharOperation.equals(binding1.fSuperBinding.sourceName, type2.getSuperType().getName()))
return false;
if(binding1.fields.length != type2.attributes.length)
return false;
if(binding1.methods == null && type2.methods != null)
return false;
if(binding1.methods != null && type2.methods == null)
return false;
if(binding1.methods != null && type2.methods != null && binding1.methods.length != type2.methods.size())
return false;
} else {
InferredType type1 = binding1.classScope.inferredType;
if(type1.getSuperType() == null && type2.getSuperType() != null)
return false;
if(type1.getSuperType() != null && type2.getSuperType() == null)
return false;
if(type1.getSuperType() != null && type2.getSuperType() != null &&
!CharOperation.equals(type1.getSuperType().getName(), type2.getSuperType().getName()))
return false;
if(type1.attributes.length != type2.attributes.length)
return false;
if(type1.methods == null && type2.methods != null)
return false;
if(type1.methods != null && type2.methods == null)
return false;
if(type1.methods != null && type2.methods != null && type1.methods.size() != type2.methods.size())
return false;
StringBuffer checkSumString1 = new StringBuffer();
StringBuffer checkSumString2 = new StringBuffer();
for(int i = 0; i < type1.attributes.length; i++) {
checkSumString1.append((type1.attributes[i] == null ? "" : new String(type1.attributes[i].name))); //$NON-NLS-1$
checkSumString2.append((type2.attributes[i] == null ? "" : new String(type2.attributes[i].name))); //$NON-NLS-1$
}
checksumCalculator.reset();
checksumCalculator.update(checkSumString1.toString().getBytes());
long checkSum1 = checksumCalculator.getValue();
checksumCalculator.reset();
checksumCalculator.update(checkSumString2.toString().getBytes());
long checkSum2 = checksumCalculator.getValue();
if(checkSum1 != checkSum2)
return false;
checkSumString1 = new StringBuffer();
checkSumString2 = new StringBuffer();
if(type1.methods != null && type2.methods != null) {
for(int i = 0; i < type1.methods.size(); i++) {
checkSumString1.append(new String(((InferredMethod)type1.methods.get(i)).name));
checkSumString2.append(new String(((InferredMethod)type2.methods.get(i)).name));
}
}
checksumCalculator.reset();
checksumCalculator.update(checkSumString1.toString().getBytes());
checkSum1 = checksumCalculator.getValue();
checksumCalculator.reset();
checksumCalculator.update(checkSumString2.toString().getBytes());
checkSum2 = checksumCalculator.getValue();
if(checkSum1 != checkSum2)
return false;
}
return true;
}
public TypeBinding reconcileAnonymous(TypeBinding other) {
if (!(other instanceof SourceTypeBinding)) {
return null;
}
SourceTypeBinding otherBinding = (SourceTypeBinding) other;
if (!otherBinding.isAnonymousType()) {
return null;
}
if (otherBinding.methods != null) {
for (int i = 0; i < otherBinding.methods.length; i++) {
MethodBinding methodBinding = otherBinding.methods[i];
MethodBinding exactMethod = this.getExactMethod(
methodBinding.selector, methodBinding.parameters, null);
if (exactMethod == null) {
return null;
}
}
}
if (otherBinding.fields != null) {
for (int i = 0; i < otherBinding.fields.length; i++) {
FieldBinding fieldBinding = otherBinding.fields[i];
FieldBinding myField = this.getFieldInHierarchy(
fieldBinding.name, true);
if (myField == null) {
return null;
}
}
}
return this;
}
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#readableName()
*/
public char[] readableName() {
return this.qualifiedSourceName();
}
/**
* <p>
* Will return the qualified source name from the first linked binding
* that is not anonymous, or if no linked bindings are not anonymous then
* returns the qualified source name for this binding.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#qualifiedSourceName()
*/
public char[] qualifiedSourceName() {
char[] qualifiedSourceName = (char[])performActionOnLinkedBindings(new LinkedBindingAction() {
private char[] fQualifiedSourceName = null;
public boolean performAction(SourceTypeBinding linkedBinding) {
if(!linkedBinding.isAnonymousType()) {
this.fQualifiedSourceName = linkedBinding.qualifiedSourceName0();
}
return this.fQualifiedSourceName == null;
}
/**
* @return char[]
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return this.fQualifiedSourceName;
}
});
if(qualifiedSourceName == null) {
qualifiedSourceName = this.qualifiedSourceName0();
}
return qualifiedSourceName;
}
/**
* @return qualified source name for this binding
*/
private char[] qualifiedSourceName0() {
return super.qualifiedSourceName();
}
/**
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#isSuperclassOf(org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding)
*/
public boolean isSuperclassOf(ReferenceBinding otherType) {
boolean isSuperTypeOf = false;
if(otherType instanceof SourceTypeBinding) {
//NOTE: this is a breadth first search of the super types
/* compare this type against the super types of the types in this list
*
* use the list for quickly iterating over and the set for preventing
* duplicates. */
final LinkedList compareAgainstSupersOfList = new LinkedList();
compareAgainstSupersOfList.add(otherType);
final Set compareAgainstSupersOfSet = new HashSet();
compareAgainstSupersOfSet.add(otherType);
//prevent searching the super of the same types more then once
final Set alreadyComparedAgainstSupersOf = new HashSet();
//while there are types to compare this type against their super types with keep going
while(!compareAgainstSupersOfList.isEmpty() && !isSuperTypeOf) {
SourceTypeBinding checkSupersOf = (SourceTypeBinding)compareAgainstSupersOfList.removeFirst();
compareAgainstSupersOfSet.remove(checkSupersOf);
alreadyComparedAgainstSupersOf.add(checkSupersOf);
isSuperTypeOf = ((Boolean)checkSupersOf.performActionOnLinkedBindings(new LinkedBindingAction() {
private boolean fIsSuperTypeOf = false;
/**
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#performAction(org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding)
*/
public boolean performAction(SourceTypeBinding otherLinkedBinding) {
ReferenceBinding otherLinkedSuperBinding = otherLinkedBinding.getSuperBinding0();
if(otherLinkedSuperBinding != null &&
!alreadyComparedAgainstSupersOf.contains(otherLinkedSuperBinding)) {
fIsSuperTypeOf = otherLinkedSuperBinding.isEquivalentTo(SourceTypeBinding.this);
//prevent searching the super of the same types more then once
if(!compareAgainstSupersOfSet.contains(otherLinkedSuperBinding)) {
compareAgainstSupersOfList.add(otherLinkedSuperBinding);
compareAgainstSupersOfSet.add(otherLinkedSuperBinding);
}
}
return !fIsSuperTypeOf;
}
/**
* @return {@link ReferenceBinding}
*
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding.LinkedBindingAction#getFinalResult()
*/
public Object getFinalResult() {
return new Boolean(this.fIsSuperTypeOf);
}
})).booleanValue();
}
} else {
isSuperTypeOf = super.isSuperclassOf(otherType);
}
return isSuperTypeOf;
}
/**
* <p>
* Determine if this binding's inferred type has a function with the given
* name
* </p>
*
* @param functionName
* determine if this binding's inferred type has a function
* with this name
*
* @return <code>true</code>if this binding's inferred type has a function
* with the given name, <code>false</code> otherwise
*/
private boolean inferredTypeHasFunction(char[] functionName) {
InferredType currentType = this.classScope != null ? this.classScope.inferredType : null;
if(currentType != null) {
if(currentType.methods != null && currentType.methods.size() > 0) {
for(int i = 0; i < currentType.methods.size(); i++) {
InferredMethod method = (InferredMethod) currentType.methods.get(i);
if(method != null && CharOperation.equals(method.name, functionName)) {
return true;
}
}
}
}
return false;
}
/**
* <p>
* Determine if this binding's inferred type has a field with the given
* name
* </p>
*
* @param fieldName
* determine if this binding's inferred type has a field with
* this name
*
* @return <code>true</code>if this binding's inferred type has a field
* with the given name, <code>false</code> otherwise
*/
private boolean inferredTypeHasField(char[] fieldName) {
InferredType currentType = this.classScope != null ? this.classScope.inferredType : null;
if(currentType != null) {
InferredAttribute[] attributes = currentType.attributes;
if(attributes != null && currentType.numberAttributes > 0) {
for(int i = 0; i < currentType.numberAttributes; i++) {
if(attributes[i] != null && CharOperation.equals(attributes[i].name, fieldName)) {
return true;
}
}
}
}
return false;
}
/**
* @param binding
* determine if this binding is a function binding
*
* @return <code>true</code> if the given {@link TypeBinding} is a
* function binding, <code>false</code> otherwise
*/
private static boolean isFunctionType(TypeBinding binding) {
return binding.isFunctionType() ||
binding instanceof FunctionTypeBinding ||
CharOperation.equals(binding.sourceName(), InferredType.FUNCTION_NAME);
}
/**
* <p>
* Performs the given action on all of the {@link SourceTypeBinding}s
* linked to this one, including this one, unless the loop is cut short by
* a <code>false</code> result from
* {@link LinkedBindingAction#performAction(SourceTypeBinding)}.
* </p>
*
* <p>
* Whenever an action needs to be performed on all of the linked bindings
* this is the method that should be used.
* </p>
*
* @param action
* {@link LinkedBindingAction} to perform on this binding and
* all of its linked bindings or until
* {@link LinkedBindingAction#performAction(SourceTypeBinding)}
* returns <code>false</code>
*
* @return the result of a call to
* {@link LinkedBindingAction#getFinalResult()} on the given action
* after running
* {@link LinkedBindingAction#performAction(SourceTypeBinding)} on
* each of the linked bindings or until
* {@link LinkedBindingAction#performAction(SourceTypeBinding)}
* returned <code>false</code>
*
* @see LinkedBindingAction
*/
Object performActionOnLinkedBindings(LinkedBindingAction action) {
SourceTypeBinding currBinding = this;
/* perform the given action each linked type stopping either
* when looped back to the beginning or #performAction returns false */
boolean keepProcessing = true;
do {
keepProcessing = action.performAction(currBinding);
currBinding = currBinding.fNextType;
} while(currBinding != this && keepProcessing);
return action.getFinalResult();
}
/**
* <p>
* An action to perform on a set of linked {@link SourceTypeBinding}s
* </p>
*
* @see #performAction(SourceTypeBinding)
*/
static abstract class LinkedBindingAction {
/**
* <p>
* Performs an action on the given {@link SourceTypeBinding}.
* </p>
*
* @param linkedBinding
* {@link SourceTypeBinding} to perform the action on
*
* @return <code>true</code> if the next linked binding should be
* passed to this function, <code>false</code> if the loop
* should stop prematurely
*/
public abstract boolean performAction(SourceTypeBinding linkedBinding);
/**
* <p>
* Default implementation is to return <code>null</code> assuming no
* accumulative result was gathered.
* </p>
*
* @return final result gathered after calling
* {@link #performAction(SourceTypeBinding)} on the linked
* bindings, default result is <code>null</code>
*/
public Object getFinalResult() {
return null;
}
}
}