/*******************************************************************************
* Copyright (c) 2000, 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.internal.compiler.lookup;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.zip.CRC32;
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.MethodDeclaration;
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 {
public ReferenceBinding superclass;
protected FieldBinding[] fields;
protected MethodBinding[] methods;
public ReferenceBinding[] memberTypes = Binding.NO_MEMBER_TYPES;
public Scope scope;
public ClassScope classScope;
char[] genericReferenceTypeSignature;
public SourceTypeBinding nextType;
private static final CRC32 checksumCalculator = new CRC32();
public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage,
Scope scope) {
this.compoundName = compoundName;
this.fPackage = fPackage;
this.fileName = 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() {
}
void buildFieldsAndMethods() {
buildFields();
buildMethods();
}
/**
* <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;
}
private void buildFields() {
FieldBinding prototype = new FieldBinding(TypeConstants.PROTOTYPE,
TypeBinding.UNKNOWN, modifiers
| ExtraCompilerModifiers.AccUnresolved, this);
InferredType inferredType = this.classScope.inferredType;
int size = inferredType.numberAttributes;
if (size == 0) {
setFields(new FieldBinding[] { prototype });
return;
}
// 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];
int modifiers = 0;
if (field.isStatic)
modifiers |= ClassFileConstants.AccStatic;
InferredType fieldType = field.type;
TypeBinding fieldTypeBinding = null;
if (fieldType != null) {
// fieldTypeBinding = BaseTypeBinding.UNKNOWN;
// fieldTypeBinding = scope.getType(fieldType.getName());
fieldTypeBinding = fieldType.resolveType(scope, field.node);
}
if (fieldTypeBinding == null)
fieldTypeBinding = TypeBinding.UNKNOWN;
FieldBinding fieldBinding = new FieldBinding(field,
fieldTypeBinding, modifiers
| ExtraCompilerModifiers.AccUnresolved, this);
fieldBinding.id = count;
// field's type will be resolved when needed for top level types
// checkAndSetModifiersForField(fieldBinding, field);
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;
}
}
}
knownFieldNames.put(field.name, null); // ensure that the
// duplicate field is
// found & removed
scope.problemReporter().duplicateFieldInType(this, field);
field.binding = null;
} else {
knownFieldNames.put(field.name, fieldBinding);
// remember that we have seen a field with this name
if (fieldBinding != null)
fieldBindings[count++] = fieldBinding;
}
}
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;
}
if (count != fieldBindings.length)
System.arraycopy(fieldBindings, 0,
fieldBindings = new FieldBinding[count], 0, count);
setFields(fieldBindings);
}
private void buildMethods() {
InferredType inferredType = this.classScope.inferredType;
int size = (inferredType.methods != null) ? inferredType.methods.size()
: 0;
if (size == 0) {
setMethods(Binding.NO_METHODS);
return;
}
int count = 0;
MethodBinding[] methodBindings = new MethodBinding[size];
// create bindings for source methods
for (int i = 0; i < size; i++) {
InferredMethod method = (InferredMethod) inferredType.methods.get(i);
//determine if the method already has a resolved scope or not
boolean doesNotHaveResolvedScope = method.getFunctionDeclaration() instanceof AbstractMethodDeclaration &&
((AbstractMethodDeclaration)method.getFunctionDeclaration()).scope == null;
//build method scope
MethodDeclaration methDec = (MethodDeclaration) method.getFunctionDeclaration();
MethodScope scope = new MethodScope(this.scope, methDec, false);
MethodBinding methodBinding = scope.createMethod(method, this);
//bind arguments
method.methodBinding = methodBinding;
methDec.binding = methodBinding;
methDec.bindArguments();
if (methodBinding != null) // is null if binding could not be
// created
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);
tagBits &= ~TagBits.AreMethodsSorted; // in case some static imports
// reached already into this
// type
setMethods(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);
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();
// for (int i = 0, length = this.memberTypes.length; i < length; i++)
// ((SourceTypeBinding)
// this.memberTypes[i]).faultInTypesForFieldsAndMethods();
}
// NOTE: the type of each field of a source type is resolved when needed
public FieldBinding[] fields() {
Map fieldCache = new HashMap();
if ((this.tagBits & TagBits.AreFieldsComplete) == 0) {
int failed = 0;
FieldBinding[] resolvedFields = this.fields;
try {
// 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;
}
for (int i = 0, length = this.fields.length; i < length; i++) {
if (resolveTypeFor(this.fields[i]) == null) {
// do not alter original field array until resolution is
// over, due to reentrance (143259)
if (resolvedFields == this.fields) {
System.arraycopy(this.fields, 0,
resolvedFields = new FieldBinding[length],
0, length);
}
resolvedFields[i] = null;
failed++;
}
fieldCache.put(this.fields[i].name, this.fields[i]);
}
} finally {
if (failed > 0) {
// ensure fields are consistent reqardless of the error
int newSize = resolvedFields.length - failed;
if (newSize == 0)
return this.fields = Binding.NO_FIELDS;
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];
}
this.fields = newFields;
}
}
this.tagBits |= TagBits.AreFieldsComplete;
} else {
for(int i = 0; i < this.fields.length; i++) {
if(this.fields[i] != null)
fieldCache.put(this.fields[i].name, this.fields[i]);
}
}
if (this.nextType != null) {
FieldBinding[] moreFields = this.nextType.fields();
for(int i = 0; i < moreFields.length; i++) {
if(fieldCache.get(moreFields[i].name) == null) {
fieldCache.put(moreFields[i].name, moreFields[i]);
}
}
// FieldBinding[] combinedFields = new FieldBinding[this.fields.length
// + moreFields.length];
// System.arraycopy(this.fields, 0, combinedFields, 0,
// this.fields.length);
// System.arraycopy(moreFields, 0, combinedFields, this.fields.length,
// moreFields.length);
return (FieldBinding[]) fieldCache.values().toArray(new FieldBinding[0]);
//return combinedFields;
} else
return this.fields;
}
public MethodBinding[] getDefaultAbstractMethods() {
int count = 0;
for (int i = this.methods.length; --i >= 0;)
if (this.methods[i].isDefaultAbstract())
count++;
if (count == 0)
return Binding.NO_METHODS;
MethodBinding[] result = new MethodBinding[count];
count = 0;
for (int i = this.methods.length; --i >= 0;)
if (this.methods[i].isDefaultAbstract())
result[count++] = this.methods[i];
return result;
}
public MethodBinding getExactConstructor(TypeBinding[] argumentTypes) {
MethodBinding exactConstructor = getExactConstructor0(argumentTypes);
if (exactConstructor == null && this.nextType != null)
exactConstructor = this.nextType.getExactConstructor(argumentTypes);
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) {
int argCount = argumentTypes.length;
if ((this.tagBits & TagBits.AreMethodsComplete) != 0) { // have resolved
// all arg types
// & return type
// of the
// methods
long range;
if ((range = ReferenceBinding.binarySearch(TypeConstants.INIT,
this.methods)) >= 0) {
// nextMethod:
for (int imethod = (int) range, end = (int) (range >> 32); imethod <= end; imethod++) {
MethodBinding method = this.methods[imethod];
// if (method.parameters.length == argCount) {
// TypeBinding[] toMatch = method.parameters;
// for (int iarg = 0; iarg < argCount; iarg++)
// if (toMatch[iarg] != argumentTypes[iarg])
// continue nextMethod;
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) {
// nextMethod:
for (int imethod = (int) range, end = (int) (range >> 32); imethod <= end; imethod++) {
MethodBinding method = this.methods[imethod];
if (resolveTypesFor(method) == null
|| method.returnType == null) {
methods();
return getExactConstructor(argumentTypes); // try again
// since the
// problem
// methods
// have been
// removed
}
// if (method.parameters.length == argCount) {
// TypeBinding[] toMatch = method.parameters;
// for (int iarg = 0; iarg < argCount; iarg++)
// if (toMatch[iarg] != argumentTypes[iarg])
// continue nextMethod;
// return method;
// }
return method;
}
}
}
return null;
}
public MethodBinding getExactMethod(char[] selector,
TypeBinding[] argumentTypes, CompilationUnitScope refScope) {
MethodBinding exactMethod = getExactMethod0(selector, argumentTypes,
refScope);
if (exactMethod == null && this.nextType != null)
exactMethod = this.nextType.getExactMethod(selector, argumentTypes,
refScope);
return exactMethod;
}
// NOTE: the return type, arg & exception types of each method of a source
// type are resolved when needed
// searches up the hierarchy as long as no potential (but not exact) match
// was found.
private MethodBinding getExactMethod0(char[] selector,
TypeBinding[] argumentTypes, CompilationUnitScope refScope) {
// sender from refScope calls recordTypeReference(this)
// int argCount = argumentTypes.length;
boolean foundNothing = true;
if ((this.tagBits & TagBits.AreMethodsComplete) != 0) { // have resolved
// all arg types
// & return type
// of the
// methods
long range;
if ((range = ReferenceBinding.binarySearch(selector, this.methods)) >= 0) {
// nextMethod:
for (int imethod = (int) range, end = (int) (range >> 32); imethod <= end; imethod++) {
MethodBinding method = this.methods[imethod];
foundNothing = false; // inner type lookups must know that a
// method with this name exists
// if (method.parameters.length == argCount) {
// TypeBinding[] toMatch = method.parameters;
// for (int iarg = 0; iarg < argCount; iarg++)
// if (toMatch[iarg] != argumentTypes[iarg])
// {
// if (toMatch[iarg].id!=TypeIds.T_any &&
// argumentTypes[iarg].id!=TypeIds.T_any)
// continue nextMethod;
// }
// return method;
// }
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();
return getExactMethod(selector, argumentTypes, refScope); // try
// again
// since
// the
// problem
// methods
// have
// been
// removed
}
}
// 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();
return getExactMethod(selector, argumentTypes,
refScope); // try again since the problem
// methods have been removed
}
}
}
return this.methods[start];
// nextMethod: for (int imethod = start; imethod <= end;
// imethod++) {
// FunctionBinding method = this.methods[imethod];
// TypeBinding[] toMatch = method.parameters;
// if (toMatch.length == argCount) {
// for (int iarg = 0; iarg < argCount; iarg++)
// if (toMatch[iarg] != argumentTypes[iarg])
// continue nextMethod;
// return method;
// }
// }
}
}
if (foundNothing) {
if (this.superclass != null && this.superclass != this) {
if (refScope != null)
refScope.recordTypeReference(this.superclass);
MethodBinding exactMethod = this.superclass.getExactMethod(
selector, argumentTypes, refScope);
if (exactMethod != null && exactMethod.isValidBinding())
return exactMethod;
}
}
return null;
}
public FieldBinding getField(char[] fieldName, boolean needResolve) {
FieldBinding field = getField0(fieldName, needResolve);
if (field == null && this.nextType != null)
field = this.nextType.getField(fieldName, needResolve);
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.superclass();
}
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.fields = 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.fields = newFields;
}
}
}
}
return null;
}
public MethodBinding[] getMethods(char[] selector) {
MethodBinding[] meths = getMethods0(selector);
if (this.nextType == null)
return meths;
MethodBinding[] moreMethods = this.nextType.getMethods(selector);
MethodBinding[] combinedMethods = new MethodBinding[meths.length
+ moreMethods.length];
System.arraycopy(meths, 0, combinedMethods, 0, meths.length);
System.arraycopy(moreMethods, 0, combinedMethods, meths.length,
moreMethods.length);
return combinedMethods;
}
// NOTE: the return type, arg & exception types of each method of a source
// type are resolved when needed
private MethodBinding[] getMethods0(char[] selector) {
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;
}
}
// 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;
}
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) {
methods();
return getMethods(selector); // try again since the problem
// methods have been removed
}
}
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();
return getMethods(selector); // try again since the
// duplicate methods have
// been removed
}
}
}
return result;
}
/**
* Returns true if a type is identical to another one, or for generic types,
* true if compared to its raw type.
*/
public boolean isEquivalentTo(TypeBinding otherType) {
if (this == otherType)
return true;
if (otherType == null)
return false;
return false;
}
public ReferenceBinding[] memberTypes() {
if (this.nextType == null)
return this.memberTypes;
ReferenceBinding[] moreTypes = this.nextType.memberTypes();
ReferenceBinding[] combinedTypes = new ReferenceBinding[this.memberTypes.length
+ moreTypes.length];
System.arraycopy(this.memberTypes, 0, combinedTypes, 0,
this.memberTypes.length);
System.arraycopy(moreTypes, 0, combinedTypes, this.memberTypes.length,
moreTypes.length);
return combinedTypes;
}
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;
}
public boolean hasMemberTypes() {
boolean hasMembers = this.memberTypes != null
&& this.memberTypes.length > 0;
if (!hasMembers && this.nextType != null)
hasMembers = this.nextType.hasMemberTypes();
return hasMembers;
}
// NOTE: the return type, arg & exception types of each method of a source
// type are resolved when needed
public MethodBinding[] methods() {
if ((this.tagBits & TagBits.AreMethodsComplete) == 0) {
// 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;
}
int failed = 0;
MethodBinding[] resolvedMethods = this.methods;
try {
for (int i = 0, length = this.methods.length; i < length; i++) {
if (resolveTypesFor(this.methods[i]) == null) {
// do not alter original method array until resolution
// is over, due to reentrance (143259)
if (resolvedMethods == this.methods) {
System
.arraycopy(
this.methods,
0,
resolvedMethods = new MethodBinding[length],
0, length);
}
resolvedMethods[i] = null; // unable to resolve
// parameters
failed++;
}
}
// find & report collision cases
boolean complyTo15 = (this.scope != null && this.scope
.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5);
for (int i = 0, length = this.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 (!CharOperation.equals(selector, method2.selector))
break nextSibling; // methods with same selector are
// contiguous
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;
}
} else if (!method.areParametersEqual(method2)) { // prior
// to
// 1.5,
// parameter
// identity
// meant
// a
// collision
// case
continue nextSibling;
}
// report duplicate
if (methodDecl == null) {
methodDecl = method.sourceMethod(); // cannot be
// retrieved
// after binding
// is lost & may
// still be null
// if method is
// special
if (methodDecl != null
&& methodDecl.binding != null) { // ensure
// its a
// valid
// user
// defined
// method
this.scope
.problemReporter()
.duplicateMethodInType(this, methodDecl);
methodDecl.binding = null;
// do not alter original method array until
// resolution is over, due to reentrance
// (143259)
if (resolvedMethods == this.methods) {
System
.arraycopy(
this.methods,
0,
resolvedMethods = new MethodBinding[length],
0, length);
}
resolvedMethods[i] = null;
failed++;
}
}
AbstractMethodDeclaration method2Decl = method2
.sourceMethod();
if (method2Decl != null && method2Decl.binding != null) { // ensure
// its
// a
// valid
// user
// defined
// method
this.scope.problemReporter().duplicateMethodInType(
this, method2Decl);
method2Decl.binding = null;
// do not alter original method array until
// resolution is over, due to reentrance (143259)
if (resolvedMethods == this.methods) {
System
.arraycopy(
this.methods,
0,
resolvedMethods = new MethodBinding[length],
0, length);
}
resolvedMethods[j] = null;
failed++;
}
}
if (method.returnType == null && methodDecl == null) { // forget
// method
// with
// invalid
// return
// type...
// was
// kept
// to
// detect
// possible
// collisions
methodDecl = method.sourceMethod();
if (methodDecl != null) {
methodDecl.binding = null;
}
// do not alter original method array until resolution
// is over, due to reentrance (143259)
if (resolvedMethods == this.methods) {
System
.arraycopy(
this.methods,
0,
resolvedMethods = new MethodBinding[length],
0, length);
}
resolvedMethods[i] = null;
failed++;
}
}
} finally {
if (failed > 0) {
int newSize = resolvedMethods.length - failed;
if (newSize == 0) {
this.methods = 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];
this.methods = newMethods;
}
}
// handle forward references to potential default abstract
// methods
// addDefaultAbstractMethods();
this.tagBits |= TagBits.AreMethodsComplete;
}
}
if (this.nextType != null) {
MethodBinding[] moreMethods = this.nextType.methods();
MethodBinding[] combinedMethods = new MethodBinding[this.methods.length
+ moreMethods.length];
System.arraycopy(this.methods, 0, combinedMethods, 0,
this.methods.length);
System.arraycopy(moreMethods, 0, combinedMethods,
this.methods.length, moreMethods.length);
return combinedMethods;
} else
return this.methods;
}
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;
// FieldDeclaration[] fieldDecls =
// this.classScope.referenceContext.fields;
// for (int f = 0, length = fieldDecls.length; f < length; f++) {
// if (fieldDecls[f].binding != field)
// continue;
//
// MethodScope initializationScope = field.isStatic()
// ? this.classScope.referenceContext.staticInitializerScope
// : this.classScope.referenceContext.initializerScope;
// FieldBinding previousField = initializationScope.initializedField;
// try {
// initializationScope.initializedField = field;
// FieldDeclaration fieldDecl = fieldDecls[f];
// TypeBinding fieldType =
// fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT
// ? initializationScope.environment().convertToRawType(this) // enum
// constant is implicitly of declaring enum type
// : fieldDecl.type.resolveType(initializationScope, true /* check
// bounds*/);
// field.type = fieldType;
// field.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
// if (fieldType == null) {
// fieldDecl.binding = null;
// return null;
// }
// if (fieldType == TypeBinding.VOID) {
// this.scope.problemReporter().variableTypeCannotBeVoid(fieldDecl);
// fieldDecl.binding = null;
// return null;
// }
// if (fieldType.isArrayType() && ((ArrayBinding)
// fieldType).leafComponentType == TypeBinding.VOID) {
// this.scope.problemReporter().variableTypeCannotBeVoidArray(fieldDecl);
// fieldDecl.binding = null;
// return null;
// }
// TypeBinding leafType = fieldType.leafComponentType();
// if (leafType instanceof ReferenceBinding &&
// (((ReferenceBinding)leafType).modifiers &
// ExtraCompilerModifiers.AccGenericSignature) != 0) {
// field.modifiers |= ExtraCompilerModifiers.AccGenericSignature;
// }
// } finally {
// initializationScope.initializedField = previousField;
// }
// return field;
// }
// return null; // should never reach this point
}
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.parameters = 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.scope, true /* check bounds */);
else if (arg.inferredType != null)
parameterType = arg.inferredType.resolveType(
methodDecl.scope, arg);
if (parameterType == null) {
// foundArgProblem = true;
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.parameters = newParameters;
}
boolean foundReturnTypeProblem = false;
if (!method.isConstructor()) {
TypeReference returnType = methodDecl instanceof MethodDeclaration ? ((MethodDeclaration) methodDecl).returnType
: null;
if (returnType == null
&& !(methodDecl instanceof MethodDeclaration)) {
methodDecl.scope.problemReporter()
.missingReturnType(methodDecl);
method.returnType = null;
foundReturnTypeProblem = true;
} else {
TypeBinding methodType = (returnType != null) ? returnType
.resolveType(methodDecl.scope, true /* check bounds */)
: null;
if (methodType == null)
methodType = (methodDecl.inferredType != null) ? methodDecl.inferredType
.resolveType(methodDecl.scope, methodDecl)
: TypeBinding.UNKNOWN;
if (methodType == null) {
foundReturnTypeProblem = true;
} else {
method.returnType = methodType;
TypeBinding leafType = methodType.leafComponentType();
}
}
}
if (foundArgProblem) {
methodDecl.binding = null;
method.parameters = 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) {
// if (this.nextType!=null)
// throw new UnimplementedException("should not get here"); //$NON-NLS-1$
this.fields = fields;
}
public void setMethods(MethodBinding[] methods) {
// if (this.nextType!=null)
// throw new UnimplementedException("should not get here"); //$NON-NLS-1$
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;
}
public ReferenceBinding superclass() {
if (this.nextType == null) {
//fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=282372
if(this == this.superclass)
return null;
return this.superclass;
}
if (this.superclass != null
&& this.superclass.id != TypeIds.T_JavaLangObject) {
//fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=282372
if(this == this.superclass)
return null;
return this.superclass;
}
return this.nextType.superclass();
}
public String toString() {
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.superclass != null) ? this.superclass.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$
}
buffer.append("\n\n"); //$NON-NLS-1$
return buffer.toString();
}
void verifyMethods(MethodVerifier verifier) {
//verifier.verify(this);
}
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) {
int length = this.methods.length;
System.arraycopy(this.methods, 0,
this.methods = new MethodBinding[length + 1], 0, length);
this.methods[length] = binding;
}
public void cleanup() {
this.scope = null;
this.classScope = null;
}
public boolean contains(ReferenceBinding binding) {
if (binding == this)
return true;
if (this.nextType != null)
return this.nextType.contains(binding);
return false;
}
public void addNextType(SourceTypeBinding type) {
SourceTypeBinding binding = this;
// attempt to remove duplicates
boolean isDuplicate = checkIfDuplicateType(binding, type);
while (!isDuplicate && binding.nextType != null) {
binding = binding.nextType;
if(binding != null && checkIfDuplicateType(binding, type))
isDuplicate = true;
}
if(!isDuplicate)
binding.nextType = type;
}
public boolean checkIfDuplicateType(SourceTypeBinding binding1, SourceTypeBinding binding2) {
InferredType type2 = binding2.classScope.inferredType;
if(binding1.classScope == null) {
if(binding1.superclass == null && type2.superClass != null)
return false;
if(binding1.superclass != null && type2.superClass == null)
return false;
if(binding1.superclass != null && type2.superClass != null &&
!CharOperation.equals(binding1.superclass.sourceName, type2.superClass.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.superClass == null && type2.superClass != null)
return false;
if(type1.superClass != null && type2.superClass == null)
return false;
if(type1.superClass != null && type2.superClass != null &&
!CharOperation.equals(type1.superClass.getName(), type2.superClass.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(); //$NON-NLS-1$
StringBuffer checkSumString2 = new StringBuffer(); //$NON-NLS-1$
for(int i = 0; i < type1.attributes.length; i++) {
checkSumString1.append((type1.attributes[i] == null ? "" : new String(type1.attributes[i].name)));
checkSumString2.append((type2.attributes[i] == null ? "" : new String(type2.attributes[i].name)));
}
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;
}
}