| /******************************************************************************* |
| * Copyright (c) 2000, 2008 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.Iterator; |
| |
| import org.eclipse.wst.jsdt.core.JavaScriptCore; |
| import org.eclipse.wst.jsdt.core.UnimplementedException; |
| 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.FieldDeclaration; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.MethodDeclaration; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.TypeParameter; |
| 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.impl.Constant; |
| import org.eclipse.wst.jsdt.internal.compiler.util.HashtableOfObject; |
| import org.eclipse.wst.jsdt.internal.compiler.util.SimpleLookupTable; |
| import org.eclipse.wst.jsdt.internal.compiler.util.Util; |
| |
| public class SourceTypeBinding extends ReferenceBinding { |
| public ReferenceBinding superclass; |
| public ReferenceBinding[] superInterfaces= Binding.NO_SUPERINTERFACES; |
| protected FieldBinding[] fields; |
| protected MethodBinding[] methods; |
| public ReferenceBinding[] memberTypes=Binding.NO_MEMBER_TYPES; |
| public TypeVariableBinding[] typeVariables=Binding.NO_TYPE_VARIABLES; |
| |
| public Scope scope; |
| public ClassScope classScope; |
| |
| |
| // Synthetics are separated into 5 categories: methods, super methods, fields, class literals, changed declaring type bindings and bridge methods |
| public final static int METHOD_EMUL = 0; |
| public final static int FIELD_EMUL = 1; |
| public final static int CLASS_LITERAL_EMUL = 2; |
| public final static int RECEIVER_TYPE_EMUL = 3; |
| HashMap[] synthetics; |
| char[] genericReferenceTypeSignature; |
| |
| public SourceTypeBinding nextType; |
| |
| private SimpleLookupTable storedAnnotations = null; // keys are this ReferenceBinding & its fields and methods, value is an AnnotationHolder |
| |
| 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(); |
| |
| } |
| |
| public InferredType getInferredType() { |
| if (this.nextType!=null) |
| throw new UnimplementedException("should not get here"); //$NON-NLS-1$ |
| ClassScope classScope = scope.classScope(); |
| return classScope.inferredType; |
| } |
| |
| private void buildFields() { |
| FieldBinding prototype = new FieldBinding(TypeConstants.PROTOTYPE, TypeBinding.UNKNOWN, modifiers | ExtraCompilerModifiers.AccUnresolved, this,null); |
| 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); |
| MethodScope scope = new MethodScope(this.scope, (MethodDeclaration)method.getFunctionDeclaration(), false); |
| MethodBinding methodBinding = scope.createMethod(method,this); |
| method.methodBinding=methodBinding; |
| if (methodBinding != null) // is null if binding could not be created |
| methodBindings[count++] = methodBinding; |
| } |
| 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); |
| } |
| |
| |
| |
| //private void addDefaultAbstractMethods() { |
| // if ((this.tagBits & TagBits.KnowsDefaultAbstractMethods) != 0) return; |
| // |
| // this.tagBits |= TagBits.KnowsDefaultAbstractMethods; |
| // if (isClass() && isAbstract()) { |
| // if (this.scope.compilerOptions().targetJDK >= ClassFileConstants.JDK1_2) |
| // return; // no longer added for post 1.2 targets |
| // |
| // ReferenceBinding[] itsInterfaces = superInterfaces(); |
| // if (itsInterfaces != Binding.NO_SUPERINTERFACES) { |
| // FunctionBinding[] defaultAbstracts = null; |
| // int defaultAbstractsCount = 0; |
| // ReferenceBinding[] interfacesToVisit = itsInterfaces; |
| // int nextPosition = interfacesToVisit.length; |
| // for (int i = 0; i < nextPosition; i++) { |
| // ReferenceBinding superType = interfacesToVisit[i]; |
| // if (superType.isValidBinding()) { |
| // FunctionBinding[] superMethods = superType.methods(); |
| // nextAbstractMethod: for (int m = superMethods.length; --m >= 0;) { |
| // FunctionBinding method = superMethods[m]; |
| // // explicitly implemented ? |
| // if (implementsMethod(method)) |
| // continue nextAbstractMethod; |
| // if (defaultAbstractsCount == 0) { |
| // defaultAbstracts = new FunctionBinding[5]; |
| // } else { |
| // // already added as default abstract ? |
| // for (int k = 0; k < defaultAbstractsCount; k++) { |
| // FunctionBinding alreadyAdded = defaultAbstracts[k]; |
| // if (CharOperation.equals(alreadyAdded.selector, method.selector) && alreadyAdded.areParametersEqual(method)) |
| // continue nextAbstractMethod; |
| // } |
| // } |
| // FunctionBinding defaultAbstract = new FunctionBinding( |
| // method.modifiers | ExtraCompilerModifiers.AccDefaultAbstract, |
| // method.selector, |
| // method.returnType, |
| // method.parameters, |
| // method.thrownExceptions, |
| // this); |
| // if (defaultAbstractsCount == defaultAbstracts.length) |
| // System.arraycopy(defaultAbstracts, 0, defaultAbstracts = new FunctionBinding[2 * defaultAbstractsCount], 0, defaultAbstractsCount); |
| // defaultAbstracts[defaultAbstractsCount++] = defaultAbstract; |
| // } |
| // |
| // if ((itsInterfaces = superType.superInterfaces()) != Binding.NO_SUPERINTERFACES) { |
| // int itsLength = itsInterfaces.length; |
| // if (nextPosition + itsLength >= interfacesToVisit.length) |
| // System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition); |
| // nextInterface : for (int a = 0; a < itsLength; a++) { |
| // ReferenceBinding next = itsInterfaces[a]; |
| // for (int b = 0; b < nextPosition; b++) |
| // if (next == interfacesToVisit[b]) continue nextInterface; |
| // interfacesToVisit[nextPosition++] = next; |
| // } |
| // } |
| // } |
| // } |
| // if (defaultAbstractsCount > 0) { |
| // int length = this.methods.length; |
| // System.arraycopy(this.methods, 0, this.methods = new FunctionBinding[length + defaultAbstractsCount], 0, length); |
| // System.arraycopy(defaultAbstracts, 0, this.methods, length, defaultAbstractsCount); |
| // // re-sort methods |
| // length = length + defaultAbstractsCount; |
| // if (length > 1) |
| // ReferenceBinding.sortMethods(this.methods, 0, length); |
| // // this.tagBits |= TagBits.AreMethodsSorted; -- already set in #methods() |
| // } |
| // } |
| // } |
| //} |
| /* Add a new synthetic field for <actualOuterLocalVariable>. |
| * Answer the new field or the existing field if one already existed. |
| */ |
| public FieldBinding addSyntheticFieldForInnerclass(LocalVariableBinding actualOuterLocalVariable) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(5); |
| |
| FieldBinding synthField = (FieldBinding) this.synthetics[SourceTypeBinding.FIELD_EMUL].get(actualOuterLocalVariable); |
| if (synthField == null) { |
| synthField = new SyntheticFieldBinding( |
| CharOperation.concat(TypeConstants.SYNTHETIC_OUTER_LOCAL_PREFIX, actualOuterLocalVariable.name), |
| actualOuterLocalVariable.type, |
| ClassFileConstants.AccPrivate | ClassFileConstants.AccFinal | ClassFileConstants.AccSynthetic, |
| this, |
| Constant.NotAConstant, |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].size()); |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].put(actualOuterLocalVariable, synthField); |
| } |
| |
| // ensure there is not already such a field defined by the user |
| boolean needRecheck; |
| int index = 1; |
| do { |
| needRecheck = false; |
| FieldBinding existingField; |
| if ((existingField = this.getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.classScope.referenceContext; |
| for (int i = 0, max = typeDecl.fields.length; i < max; i++) { |
| FieldDeclaration fieldDecl = typeDecl.fields[i]; |
| if (fieldDecl.binding == existingField) { |
| synthField.name = CharOperation.concat( |
| TypeConstants.SYNTHETIC_OUTER_LOCAL_PREFIX, |
| actualOuterLocalVariable.name, |
| ("$" + String.valueOf(index++)).toCharArray()); //$NON-NLS-1$ |
| needRecheck = true; |
| break; |
| } |
| } |
| } |
| } while (needRecheck); |
| return synthField; |
| } |
| /* Add a new synthetic field for <enclosingType>. |
| * Answer the new field or the existing field if one already existed. |
| */ |
| public FieldBinding addSyntheticFieldForInnerclass(ReferenceBinding enclosingType) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(5); |
| |
| FieldBinding synthField = (FieldBinding) this.synthetics[SourceTypeBinding.FIELD_EMUL].get(enclosingType); |
| if (synthField == null) { |
| synthField = new SyntheticFieldBinding( |
| CharOperation.concat( |
| TypeConstants.SYNTHETIC_ENCLOSING_INSTANCE_PREFIX, |
| String.valueOf(enclosingType.depth()).toCharArray()), |
| enclosingType, |
| ClassFileConstants.AccDefault | ClassFileConstants.AccFinal | ClassFileConstants.AccSynthetic, |
| this, |
| Constant.NotAConstant, |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].size()); |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].put(enclosingType, synthField); |
| } |
| // ensure there is not already such a field defined by the user |
| boolean needRecheck; |
| do { |
| needRecheck = false; |
| FieldBinding existingField; |
| if ((existingField = this.getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.classScope.referenceContext; |
| for (int i = 0, max = typeDecl.fields.length; i < max; i++) { |
| FieldDeclaration fieldDecl = typeDecl.fields[i]; |
| if (fieldDecl.binding == existingField) { |
| if (this.scope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_5) { |
| synthField.name = CharOperation.concat( |
| synthField.name, |
| "$".toCharArray()); //$NON-NLS-1$ |
| needRecheck = true; |
| } else { |
| this.scope.problemReporter().duplicateFieldInType(this, fieldDecl); |
| } |
| break; |
| } |
| } |
| } |
| } while (needRecheck); |
| return synthField; |
| } |
| /* Add a new synthetic field for a class literal access. |
| * Answer the new field or the existing field if one already existed. |
| */ |
| public FieldBinding addSyntheticFieldForClassLiteral(TypeBinding targetType, BlockScope blockScope) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] == null) |
| this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] = new HashMap(5); |
| |
| // use a different table than FIELDS, given there might be a collision between emulation of X.this$0 and X.class. |
| FieldBinding synthField = (FieldBinding) this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL].get(targetType); |
| if (synthField == null) { |
| synthField = new SyntheticFieldBinding( |
| CharOperation.concat( |
| TypeConstants.SYNTHETIC_CLASS, |
| String.valueOf(this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL].size()).toCharArray()), |
| blockScope.getJavaLangClass(), |
| ClassFileConstants.AccDefault | ClassFileConstants.AccStatic | ClassFileConstants.AccSynthetic, |
| this, |
| Constant.NotAConstant, |
| this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL].size()); |
| this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL].put(targetType, synthField); |
| } |
| // ensure there is not already such a field defined by the user |
| FieldBinding existingField; |
| if ((existingField = this.getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = blockScope.referenceType(); |
| for (int i = 0, max = typeDecl.fields.length; i < max; i++) { |
| FieldDeclaration fieldDecl = typeDecl.fields[i]; |
| if (fieldDecl.binding == existingField) { |
| blockScope.problemReporter().duplicateFieldInType(this, fieldDecl); |
| break; |
| } |
| } |
| } |
| return synthField; |
| } |
| /* Add a new synthetic field for the emulation of the assert statement. |
| * Answer the new field or the existing field if one already existed. |
| */ |
| public FieldBinding addSyntheticFieldForAssert(BlockScope blockScope) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(5); |
| |
| FieldBinding synthField = (FieldBinding) this.synthetics[SourceTypeBinding.FIELD_EMUL].get("assertionEmulation"); //$NON-NLS-1$ |
| if (synthField == null) { |
| synthField = new SyntheticFieldBinding( |
| TypeConstants.SYNTHETIC_ASSERT_DISABLED, |
| TypeBinding.BOOLEAN, |
| ClassFileConstants.AccDefault | ClassFileConstants.AccStatic | ClassFileConstants.AccSynthetic | ClassFileConstants.AccFinal, |
| this, |
| Constant.NotAConstant, |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].size()); |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].put("assertionEmulation", synthField); //$NON-NLS-1$ |
| } |
| // ensure there is not already such a field defined by the user |
| // ensure there is not already such a field defined by the user |
| boolean needRecheck; |
| int index = 0; |
| do { |
| needRecheck = false; |
| FieldBinding existingField; |
| if ((existingField = this.getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.classScope.referenceContext; |
| for (int i = 0, max = typeDecl.fields.length; i < max; i++) { |
| FieldDeclaration fieldDecl = typeDecl.fields[i]; |
| if (fieldDecl.binding == existingField) { |
| synthField.name = CharOperation.concat( |
| TypeConstants.SYNTHETIC_ASSERT_DISABLED, |
| ("_" + String.valueOf(index++)).toCharArray()); //$NON-NLS-1$ |
| needRecheck = true; |
| break; |
| } |
| } |
| } |
| } while (needRecheck); |
| return synthField; |
| } |
| /* Add a new synthetic field for recording all enum constant values |
| * Answer the new field or the existing field if one already existed. |
| */ |
| public FieldBinding addSyntheticFieldForEnumValues() { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(5); |
| |
| FieldBinding synthField = (FieldBinding) this.synthetics[SourceTypeBinding.FIELD_EMUL].get("enumConstantValues"); //$NON-NLS-1$ |
| if (synthField == null) { |
| synthField = new SyntheticFieldBinding( |
| TypeConstants.SYNTHETIC_ENUM_VALUES, |
| this.scope.createArrayType(this,1), |
| ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccSynthetic | ClassFileConstants.AccFinal, |
| this, |
| Constant.NotAConstant, |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].size()); |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].put("enumConstantValues", synthField); //$NON-NLS-1$ |
| } |
| // ensure there is not already such a field defined by the user |
| // ensure there is not already such a field defined by the user |
| boolean needRecheck; |
| int index = 0; |
| do { |
| needRecheck = false; |
| FieldBinding existingField; |
| if ((existingField = this.getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.classScope.referenceContext; |
| for (int i = 0, max = typeDecl.fields.length; i < max; i++) { |
| FieldDeclaration fieldDecl = typeDecl.fields[i]; |
| if (fieldDecl.binding == existingField) { |
| synthField.name = CharOperation.concat( |
| TypeConstants.SYNTHETIC_ENUM_VALUES, |
| ("_" + String.valueOf(index++)).toCharArray()); //$NON-NLS-1$ |
| needRecheck = true; |
| break; |
| } |
| } |
| } |
| } while (needRecheck); |
| return synthField; |
| } |
| /* Add a new synthetic access method for read/write access to <targetField>. |
| Answer the new method or the existing method if one already existed. |
| */ |
| public SyntheticMethodBinding addSyntheticMethod(FieldBinding targetField, boolean isReadAccess) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| |
| SyntheticMethodBinding accessMethod = null; |
| SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(targetField); |
| if (accessors == null) { |
| accessMethod = new SyntheticMethodBinding(targetField, isReadAccess, this); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(targetField, accessors = new SyntheticMethodBinding[2]); |
| accessors[isReadAccess ? 0 : 1] = accessMethod; |
| } else { |
| if ((accessMethod = accessors[isReadAccess ? 0 : 1]) == null) { |
| accessMethod = new SyntheticMethodBinding(targetField, isReadAccess, this); |
| accessors[isReadAccess ? 0 : 1] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| /* Add a new synthetic method the enum type. Selector can either be 'values' or 'valueOf'. |
| * char[] constants from TypeConstants must be used: TypeConstants.VALUES/VALUEOF |
| */ |
| public SyntheticMethodBinding addSyntheticEnumMethod(char[] selector) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| |
| SyntheticMethodBinding accessMethod = null; |
| SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(selector); |
| if (accessors == null) { |
| accessMethod = new SyntheticMethodBinding(this, selector); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(selector, accessors = new SyntheticMethodBinding[2]); |
| accessors[0] = accessMethod; |
| } else { |
| if ((accessMethod = accessors[0]) == null) { |
| accessMethod = new SyntheticMethodBinding(this, selector); |
| accessors[0] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| /* |
| * Add a synthetic field to handle the cache of the switch translation table for the corresponding enum type |
| */ |
| public SyntheticFieldBinding addSyntheticFieldForSwitchEnum(char[] fieldName, String key) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(5); |
| |
| SyntheticFieldBinding synthField = (SyntheticFieldBinding) this.synthetics[SourceTypeBinding.FIELD_EMUL].get(key); |
| if (synthField == null) { |
| synthField = new SyntheticFieldBinding( |
| fieldName, |
| this.scope.createArrayType(TypeBinding.INT,1), |
| ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccSynthetic, |
| this, |
| Constant.NotAConstant, |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].size()); |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].put(key, synthField); |
| } |
| // ensure there is not already such a field defined by the user |
| boolean needRecheck; |
| int index = 0; |
| do { |
| needRecheck = false; |
| FieldBinding existingField; |
| if ((existingField = this.getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.classScope.referenceContext; |
| for (int i = 0, max = typeDecl.fields.length; i < max; i++) { |
| FieldDeclaration fieldDecl = typeDecl.fields[i]; |
| if (fieldDecl.binding == existingField) { |
| synthField.name = CharOperation.concat( |
| fieldName, |
| ("_" + String.valueOf(index++)).toCharArray()); //$NON-NLS-1$ |
| needRecheck = true; |
| break; |
| } |
| } |
| } |
| } while (needRecheck); |
| return synthField; |
| } |
| /* Add a new synthetic method the enum type. Selector can either be 'values' or 'valueOf'. |
| * char[] constants from TypeConstants must be used: TypeConstants.VALUES/VALUEOF |
| */ |
| public SyntheticMethodBinding addSyntheticMethodForSwitchEnum(TypeBinding enumBinding) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| |
| SyntheticMethodBinding accessMethod = null; |
| char[] selector = CharOperation.concat(TypeConstants.SYNTHETIC_SWITCH_ENUM_TABLE, enumBinding.constantPoolName()); |
| CharOperation.replace(selector, '/', '$'); |
| final String key = new String(selector); |
| SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(key); |
| // first add the corresponding synthetic field |
| if (accessors == null) { |
| // then create the synthetic method |
| final SyntheticFieldBinding fieldBinding = this.addSyntheticFieldForSwitchEnum(selector, key); |
| accessMethod = new SyntheticMethodBinding(fieldBinding, this, enumBinding, selector); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(key, accessors = new SyntheticMethodBinding[2]); |
| accessors[0] = accessMethod; |
| } else { |
| if ((accessMethod = accessors[0]) == null) { |
| final SyntheticFieldBinding fieldBinding = this.addSyntheticFieldForSwitchEnum(selector, key); |
| accessMethod = new SyntheticMethodBinding(fieldBinding, this, enumBinding, selector); |
| accessors[0] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| /* Add a new synthetic access method for access to <targetMethod>. |
| * Must distinguish access method used for super access from others (need to use invokespecial bytecode) |
| Answer the new method or the existing method if one already existed. |
| */ |
| public SyntheticMethodBinding addSyntheticMethod(MethodBinding targetMethod, boolean isSuperAccess) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| |
| SyntheticMethodBinding accessMethod = null; |
| SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(targetMethod); |
| if (accessors == null) { |
| accessMethod = new SyntheticMethodBinding(targetMethod, isSuperAccess, this); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(targetMethod, accessors = new SyntheticMethodBinding[2]); |
| accessors[isSuperAccess ? 0 : 1] = accessMethod; |
| } else { |
| if ((accessMethod = accessors[isSuperAccess ? 0 : 1]) == null) { |
| accessMethod = new SyntheticMethodBinding(targetMethod, isSuperAccess, this); |
| accessors[isSuperAccess ? 0 : 1] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| /* |
| * Record the fact that bridge methods need to be generated to override certain inherited methods |
| */ |
| public SyntheticMethodBinding addSyntheticBridgeMethod(MethodBinding inheritedMethodToBridge, MethodBinding targetMethod) { |
| if (isInterface()) return null; // only classes & enums get bridge methods |
| // targetMethod may be inherited |
| if (inheritedMethodToBridge.returnType.erasure() == targetMethod.returnType.erasure() |
| && inheritedMethodToBridge.areParameterErasuresEqual(targetMethod)) { |
| return null; // do not need bridge method |
| } |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) { |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| } else { |
| // check to see if there is another equivalent inheritedMethod already added |
| Iterator synthMethods = this.synthetics[SourceTypeBinding.METHOD_EMUL].keySet().iterator(); |
| while (synthMethods.hasNext()) { |
| Object synthetic = synthMethods.next(); |
| if (synthetic instanceof MethodBinding) { |
| MethodBinding method = (MethodBinding) synthetic; |
| if (CharOperation.equals(inheritedMethodToBridge.selector, method.selector) |
| && inheritedMethodToBridge.returnType.erasure() == method.returnType.erasure() |
| && inheritedMethodToBridge.areParameterErasuresEqual(method)) { |
| return null; |
| } |
| } |
| } |
| } |
| |
| SyntheticMethodBinding accessMethod = null; |
| SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(inheritedMethodToBridge); |
| if (accessors == null) { |
| accessMethod = new SyntheticMethodBinding(inheritedMethodToBridge, targetMethod, this); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(inheritedMethodToBridge, accessors = new SyntheticMethodBinding[2]); |
| accessors[1] = accessMethod; |
| } else { |
| if ((accessMethod = accessors[1]) == null) { |
| accessMethod = new SyntheticMethodBinding(inheritedMethodToBridge, targetMethod, this); |
| accessors[1] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| public int kind() { |
| if (this.typeVariables != Binding.NO_TYPE_VARIABLES) return Binding.GENERIC_TYPE; |
| 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)) 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() { |
| 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++; |
| } |
| } |
| } 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; |
| } |
| if (this.nextType!=null) |
| { |
| FieldBinding[] moreFields=this.nextType.fields(); |
| 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 combinedFields; |
| |
| } |
| else |
| return this.fields; |
| } |
| /** |
| * @see org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding#genericTypeSignature() |
| */ |
| public char[] genericTypeSignature() { |
| if (this.genericReferenceTypeSignature == null) |
| this.genericReferenceTypeSignature = computeGenericTypeSignature(this.typeVariables); |
| return this.genericReferenceTypeSignature; |
| } |
| /** |
| * <param1 ... paramN>superclass superinterface1 ... superinterfaceN |
| * <T:LY<TT;>;U:Ljava/lang/Object;V::Ljava/lang/Runnable;:Ljava/lang/Cloneable;:Ljava/util/Map;>Ljava/lang/Exception;Ljava/lang/Runnable; |
| */ |
| public char[] genericSignature() { |
| StringBuffer sig = null; |
| if (this.typeVariables != Binding.NO_TYPE_VARIABLES) { |
| sig = new StringBuffer(10); |
| sig.append('<'); |
| for (int i = 0, length = this.typeVariables.length; i < length; i++) |
| sig.append(this.typeVariables[i].genericSignature()); |
| sig.append('>'); |
| } else { |
| // could still need a signature if any of supertypes is parameterized |
| noSignature: if (this.superclass == null || !this.superclass.isParameterizedType()) { |
| for (int i = 0, length = this.superInterfaces.length; i < length; i++) |
| if (this.superInterfaces[i].isParameterizedType()) |
| break noSignature; |
| return null; |
| } |
| sig = new StringBuffer(10); |
| } |
| if (this.superclass != null) |
| sig.append(this.superclass.genericTypeSignature()); |
| else // interface scenario only (as Object cannot be generic) - 65953 |
| sig.append(this.scope.getJavaLangObject().genericTypeSignature()); |
| for (int i = 0, length = this.superInterfaces.length; i < length; i++) |
| sig.append(this.superInterfaces[i].genericTypeSignature()); |
| return sig.toString().toCharArray(); |
| } |
| |
| /** |
| * Compute the tagbits for standard annotations. For source types, these could require |
| * lazily resolving corresponding annotation nodes, in case of forward references. |
| * @see org.eclipse.wst.jsdt.internal.compiler.lookup.Binding#getAnnotationTagBits() |
| */ |
| public long getAnnotationTagBits() { |
| // if ((this.tagBits & TagBits.AnnotationResolved) == 0 && this.scope != null) { |
| // TypeDeclaration typeDecl = this.classScope.referenceContext; |
| // boolean old = typeDecl.staticInitializerScope.insideTypeAnnotation; |
| // try { |
| // typeDecl.staticInitializerScope.insideTypeAnnotation = true; |
| // ASTNode.resolveAnnotations(typeDecl.staticInitializerScope, typeDecl.annotations, this); |
| // } finally { |
| // typeDecl.staticInitializerScope.insideTypeAnnotation = old; |
| // } |
| // if ((this.tagBits & TagBits.AnnotationDeprecated) != 0) |
| // this.modifiers |= ClassFileConstants.AccDeprecated; |
| // } |
| return this.tagBits; |
| } |
| 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.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.areParameterErasuresEqual(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 (JavaScriptCore.IS_ECMASCRIPT4 && isInterface()) { |
| if (this.superInterfaces.length == 1) { |
| if (refScope != null) |
| refScope.recordTypeReference(this.superInterfaces[0]); |
| return this.superInterfaces[0].getExactMethod(selector, argumentTypes, refScope); |
| } |
| /* BC- Added cycle check BUG 200501 */ |
| } else if (this.superclass != null && this.superclass!=this) { |
| if (refScope != null) |
| refScope.recordTypeReference(this.superclass); |
| return this.superclass.getExactMethod(selector, argumentTypes, refScope); |
| } |
| } |
| 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; |
| } |
| //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.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.areParameterErasuresEqual(result[j]) |
| : method.areParametersEqual(result[j]); |
| if (paramsMatch) { |
| methods(); |
| return getMethods(selector); // try again since the duplicate methods have been removed |
| } |
| } |
| } |
| return result; |
| } |
| /* Answer the synthetic field for <actualOuterLocalVariable> |
| * or null if one does not exist. |
| */ |
| public FieldBinding getSyntheticField(LocalVariableBinding actualOuterLocalVariable) { |
| if (this.synthetics == null || this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) return null; |
| return (FieldBinding) this.synthetics[SourceTypeBinding.FIELD_EMUL].get(actualOuterLocalVariable); |
| } |
| /* Answer the synthetic field for <targetEnclosingType> |
| * or null if one does not exist. |
| */ |
| public FieldBinding getSyntheticField(ReferenceBinding targetEnclosingType, boolean onlyExactMatch) { |
| |
| if (this.synthetics == null || this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) return null; |
| FieldBinding field = (FieldBinding) this.synthetics[SourceTypeBinding.FIELD_EMUL].get(targetEnclosingType); |
| if (field != null) return field; |
| |
| // type compatibility : to handle cases such as |
| // class T { class M{}} |
| // class S extends T { class N extends M {}} --> need to use S as a default enclosing instance for the super constructor call in N(). |
| if (!onlyExactMatch){ |
| Iterator accessFields = this.synthetics[SourceTypeBinding.FIELD_EMUL].values().iterator(); |
| while (accessFields.hasNext()) { |
| field = (FieldBinding) accessFields.next(); |
| if (CharOperation.prefixEquals(TypeConstants.SYNTHETIC_ENCLOSING_INSTANCE_PREFIX, field.name) |
| && field.type.findSuperTypeWithSameErasure(targetEnclosingType) != null) |
| return field; |
| } |
| } |
| return null; |
| } |
| /* |
| * Answer the bridge method associated for an inherited methods or null if one does not exist |
| */ |
| public SyntheticMethodBinding getSyntheticBridgeMethod(MethodBinding inheritedMethodToBridge) { |
| if (this.synthetics == null) return null; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) return null; |
| SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(inheritedMethodToBridge); |
| if (accessors == null) return null; |
| return accessors[1]; |
| } |
| |
| /** |
| * @see org.eclipse.wst.jsdt.internal.compiler.lookup.Binding#initializeDeprecatedAnnotationTagBits() |
| */ |
| public void initializeDeprecatedAnnotationTagBits() { |
| // if ((this.tagBits & TagBits.DeprecatedAnnotationResolved) == 0) { |
| // TypeDeclaration typeDecl = this.classScope.referenceContext; |
| // boolean old = typeDecl.staticInitializerScope.insideTypeAnnotation; |
| // try { |
| // typeDecl.staticInitializerScope.insideTypeAnnotation = true; |
| // ASTNode.resolveDeprecatedAnnotations(typeDecl.staticInitializerScope, typeDecl.annotations, this); |
| // this.tagBits |= TagBits.DeprecatedAnnotationResolved; |
| // } finally { |
| // typeDecl.staticInitializerScope.insideTypeAnnotation = old; |
| // } |
| // if ((this.tagBits & TagBits.AnnotationDeprecated) != 0) { |
| // this.modifiers |= ClassFileConstants.AccDeprecated; |
| // } |
| // } |
| } |
| |
| /** |
| * 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; |
| switch(otherType.kind()) { |
| |
| case Binding.WILDCARD_TYPE : |
| return ((WildcardBinding) otherType).boundCheck(this); |
| |
| case Binding.PARAMETERIZED_TYPE : |
| if ((otherType.tagBits & TagBits.HasDirectWildcard) == 0 && (!this.isMemberType() || !otherType.isMemberType())) |
| return false; // should have been identical |
| ParameterizedTypeBinding otherParamType = (ParameterizedTypeBinding) otherType; |
| if (this != otherParamType.genericType()) |
| return false; |
| if (!isStatic()) { // static member types do not compare their enclosing |
| ReferenceBinding enclosing = enclosingType(); |
| if (enclosing != null) { |
| ReferenceBinding otherEnclosing = otherParamType.enclosingType(); |
| if (otherEnclosing == null) return false; |
| if ((otherEnclosing.tagBits & TagBits.HasDirectWildcard) == 0) { |
| if (enclosing != otherEnclosing) return false; |
| } else { |
| if (!enclosing.isEquivalentTo(otherParamType.enclosingType())) return false; |
| } |
| } |
| } |
| int length = this.typeVariables == null ? 0 : this.typeVariables.length; |
| TypeBinding[] otherArguments = otherParamType.arguments; |
| int otherLength = otherArguments == null ? 0 : otherArguments.length; |
| if (otherLength != length) |
| return false; |
| for (int i = 0; i < length; i++) |
| if (!this.typeVariables[i].isTypeArgumentContainedBy(otherArguments[i])) |
| return false; |
| return true; |
| |
| case Binding.RAW_TYPE : |
| return otherType.erasure() == this; |
| } |
| return false; |
| } |
| public boolean isGenericType() { |
| return this.typeVariables != Binding.NO_TYPE_VARIABLES; |
| } |
| 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) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL] == null) |
| this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL] = new HashMap(5); |
| |
| Hashtable fieldMap = (Hashtable) this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL].get(targetField); |
| if (fieldMap == null) { |
| fieldMap = new Hashtable(5); |
| this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL].put(targetField, fieldMap); |
| } |
| FieldBinding updatedField = (FieldBinding) fieldMap.get(newDeclaringClass); |
| if (updatedField == null){ |
| updatedField = new FieldBinding(targetField, newDeclaringClass); |
| fieldMap.put(newDeclaringClass, updatedField); |
| } |
| return updatedField; |
| } |
| public MethodBinding getUpdatedMethodBinding(MethodBinding targetMethod, ReferenceBinding newDeclaringClass) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL] == null) |
| this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL] = new HashMap(5); |
| |
| Hashtable methodMap = (Hashtable) this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL].get(targetMethod); |
| if (methodMap == null) { |
| methodMap = new Hashtable(5); |
| this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL].put(targetMethod, methodMap); |
| } |
| MethodBinding updatedMethod = (MethodBinding) methodMap.get(newDeclaringClass); |
| if (updatedMethod == null){ |
| updatedMethod = new MethodBinding(targetMethod, newDeclaringClass); |
| updatedMethod.createFunctionTypeBinding(scope); |
| methodMap.put(newDeclaringClass, updatedMethod); |
| } |
| 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; |
| |
| TypeVariableBinding[] vars = method.typeVariables; |
| TypeVariableBinding[] vars2 = method2.typeVariables; |
| boolean equalTypeVars = vars == vars2; |
| MethodBinding subMethod = method2; |
| if (!equalTypeVars) { |
| MethodBinding temp = method |
| .computeSubstitutedMethod(method2, |
| this.scope.environment()); |
| if (temp != null) { |
| equalTypeVars = true; |
| subMethod = temp; |
| } |
| } |
| boolean equalParams = method |
| .areParametersEqual(subMethod); |
| if (equalParams && equalTypeVars) { |
| // duplicates regardless of return types |
| } else if (method.returnType.erasure() == subMethod.returnType |
| .erasure() |
| && (equalParams || method |
| .areParameterErasuresEqual(method2))) { |
| // name clash for sure if not duplicates, report as duplicates |
| } else if (!equalTypeVars |
| && vars != Binding.NO_TYPE_VARIABLES |
| && vars2 != Binding.NO_TYPE_VARIABLES) { |
| // type variables are different so we can distinguish between methods |
| continue nextSibling; |
| } 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].erasure()) |
| break; |
| if (params1[index] == params2[index]) { |
| TypeBinding type = params1[index] |
| .leafComponentType(); |
| if (type instanceof SourceTypeBinding |
| && type.typeVariables() != Binding.NO_TYPE_VARIABLES) { |
| index = pLength; // handle comparing identical source types like X<T>... its erasure is itself BUT we need to answer false |
| break; |
| } |
| } |
| } |
| if (index >= 0 && index < pLength) { |
| for (index = pLength; --index >= 0;) |
| if (params1[index].erasure() != 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; |
| } |
| boolean isEnumSpecialMethod = isEnum() |
| && (CharOperation.equals(selector, |
| TypeConstants.VALUEOF) || CharOperation |
| .equals(selector, TypeConstants.VALUES)); |
| // 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 |
| if (isEnumSpecialMethod) { |
| this.scope.problemReporter() |
| .duplicateEnumSpecialMethod(this, |
| methodDecl); |
| } else { |
| 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 |
| if (isEnumSpecialMethod) { |
| this.scope.problemReporter() |
| .duplicateEnumSpecialMethod(this, |
| method2Decl); |
| } else { |
| 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 (this.scope!=null && this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { |
| if ((field.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0) |
| field.modifiers |= ClassFileConstants.AccDeprecated; |
| } |
| 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) { |
| if ((method.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0) |
| return method; |
| |
| if (this.scope!=null && this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { |
| if ((method.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0) |
| method.modifiers |= ClassFileConstants.AccDeprecated; |
| } |
| if (isViewedAsDeprecated() && !method.isDeprecated()) |
| method.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly; |
| if (hasRestrictedAccess()) |
| method.modifiers |= ExtraCompilerModifiers.AccRestrictedAccess; |
| |
| AbstractMethodDeclaration methodDecl = method.sourceMethod(); |
| if (methodDecl == null) return null; // method could not be resolved in previous iteration |
| |
| TypeParameter[] typeParameters = methodDecl.typeParameters(); |
| if (JavaScriptCore.IS_ECMASCRIPT4) |
| { |
| if (typeParameters != null) { |
| methodDecl.scope.connectTypeVariables(typeParameters, true); |
| // Perform deferred bound checks for type variables (only done after type variable hierarchy is connected) |
| for (int i = 0, paramLength = typeParameters.length; i < paramLength; i++) |
| typeParameters[i].checkBounds(methodDecl.scope); |
| } |
| TypeReference[] exceptionTypes = methodDecl.thrownExceptions; |
| if (exceptionTypes != null) { |
| int size = exceptionTypes.length; |
| method.thrownExceptions = new ReferenceBinding[size]; |
| int count = 0; |
| ReferenceBinding resolvedExceptionType; |
| for (int i = 0; i < size; i++) { |
| resolvedExceptionType = (ReferenceBinding) exceptionTypes[i].resolveType(methodDecl.scope, true /* check bounds*/); |
| if (resolvedExceptionType == null) |
| continue; |
| if (resolvedExceptionType.isBoundParameterizedType()) { |
| methodDecl.scope.problemReporter().invalidParameterizedExceptionType(resolvedExceptionType, exceptionTypes[i]); |
| continue; |
| } |
| if (resolvedExceptionType.findSuperTypeErasingTo(TypeIds.T_JavaLangThrowable, true) == null) { |
| methodDecl.scope.problemReporter().cannotThrowType(exceptionTypes[i], resolvedExceptionType); |
| continue; |
| } |
| if ((resolvedExceptionType.modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) |
| method.modifiers |= ExtraCompilerModifiers.AccGenericSignature; |
| method.thrownExceptions[count++] = resolvedExceptionType; |
| } |
| if (count < size) |
| System.arraycopy(method.thrownExceptions, 0, method.thrownExceptions = new ReferenceBinding[count], 0, count); |
| } |
| } |
| |
| 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; |
| } |
| // else |
| if (parameterType == TypeBinding.VOID) { |
| methodDecl.scope.problemReporter().argumentTypeCannotBeVoid(this, methodDecl, arg); |
| foundArgProblem = true; |
| } else { |
| TypeBinding leafType = parameterType.leafComponentType(); |
| if (leafType instanceof ReferenceBinding && (((ReferenceBinding) leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) |
| method.modifiers |= ExtraCompilerModifiers.AccGenericSignature; |
| newParameters[i] = parameterType; |
| 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 if (methodType.isArrayType() && ((ArrayBinding) methodType).leafComponentType == TypeBinding.VOID) { |
| methodDecl.scope.problemReporter().returnTypeCannotBeVoidArray((MethodDeclaration) methodDecl); |
| foundReturnTypeProblem = true; |
| } else { |
| method.returnType = methodType; |
| TypeBinding leafType = methodType.leafComponentType(); |
| if (leafType instanceof ReferenceBinding && (((ReferenceBinding) leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) |
| method.modifiers |= ExtraCompilerModifiers.AccGenericSignature; |
| } |
| } |
| } |
| 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) |
| if (JavaScriptCore.IS_ECMASCRIPT4) |
| { |
| if (typeParameters != null) |
| for (int i = 0, length = typeParameters.length; i < length; i++) |
| typeParameters[i].binding = null; |
| } |
| 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 AnnotationHolder retrieveAnnotationHolder(Binding binding, boolean forceInitialization) { |
| if (forceInitialization) |
| binding.getAnnotationTagBits(); // ensure annotations are up to date |
| return super.retrieveAnnotationHolder(binding, false); |
| } |
| 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; |
| } |
| SimpleLookupTable storedAnnotations(boolean forceInitialize) { |
| if (forceInitialize && this.storedAnnotations == null && this.scope != null) { // scope null when no annotation cached, and type got processed fully (159631) |
| this.scope.referenceCompilationUnit().compilationResult.hasAnnotations = true; |
| if (!this.scope.environment().globalOptions.storeAnnotations) |
| return null; // not supported during this compile |
| this.storedAnnotations = new SimpleLookupTable(3); |
| } |
| return this.storedAnnotations; |
| } |
| public ReferenceBinding superclass() { |
| if (this.nextType==null) |
| return this.superclass; |
| if (this.superclass!=null && this.superclass.id!=TypeIds.T_JavaLangObject) |
| return this.superclass; |
| return this.nextType.superclass(); |
| |
| } |
| public ReferenceBinding[] superInterfaces() { |
| return this.superInterfaces; |
| } |
| // TODO (philippe) could be a performance issue since some senders are building the list just to count them |
| public SyntheticMethodBinding[] syntheticMethods() { |
| |
| if (this.synthetics == null || this.synthetics[SourceTypeBinding.METHOD_EMUL] == null || this.synthetics[SourceTypeBinding.METHOD_EMUL].size() == 0) return null; |
| |
| // difficult to compute size up front because of the embedded arrays so assume there is only 1 |
| int index = 0; |
| SyntheticMethodBinding[] bindings = new SyntheticMethodBinding[1]; |
| Iterator fieldsOrMethods = this.synthetics[SourceTypeBinding.METHOD_EMUL].keySet().iterator(); |
| while (fieldsOrMethods.hasNext()) { |
| |
| Object fieldOrMethod = fieldsOrMethods.next(); |
| |
| if (fieldOrMethod instanceof MethodBinding) { |
| |
| SyntheticMethodBinding[] methodAccessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(fieldOrMethod); |
| int numberOfAccessors = 0; |
| if (methodAccessors[0] != null) numberOfAccessors++; |
| if (methodAccessors[1] != null) numberOfAccessors++; |
| if (index + numberOfAccessors > bindings.length) |
| System.arraycopy(bindings, 0, (bindings = new SyntheticMethodBinding[index + numberOfAccessors]), 0, index); |
| if (methodAccessors[0] != null) |
| bindings[index++] = methodAccessors[0]; // super access |
| if (methodAccessors[1] != null) |
| bindings[index++] = methodAccessors[1]; // normal access or bridge |
| |
| } else { |
| |
| SyntheticMethodBinding[] fieldAccessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(fieldOrMethod); |
| int numberOfAccessors = 0; |
| if (fieldAccessors[0] != null) numberOfAccessors++; |
| if (fieldAccessors[1] != null) numberOfAccessors++; |
| if (index + numberOfAccessors > bindings.length) |
| System.arraycopy(bindings, 0, (bindings = new SyntheticMethodBinding[index + numberOfAccessors]), 0, index); |
| if (fieldAccessors[0] != null) |
| bindings[index++] = fieldAccessors[0]; // read access |
| if (fieldAccessors[1] != null) |
| bindings[index++] = fieldAccessors[1]; // write access |
| } |
| } |
| |
| // sort them in according to their own indexes |
| int length; |
| SyntheticMethodBinding[] sortedBindings = new SyntheticMethodBinding[length = bindings.length]; |
| for (int i = 0; i < length; i++){ |
| SyntheticMethodBinding binding = bindings[i]; |
| sortedBindings[binding.index] = binding; |
| } |
| return sortedBindings; |
| } |
| /** |
| * Answer the collection of synthetic fields to append into the classfile |
| */ |
| public FieldBinding[] syntheticFields() { |
| |
| if (this.synthetics == null) return null; |
| |
| int fieldSize = this.synthetics[SourceTypeBinding.FIELD_EMUL] == null ? 0 : this.synthetics[SourceTypeBinding.FIELD_EMUL].size(); |
| int literalSize = this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] == null ? 0 :this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL].size(); |
| int totalSize = fieldSize + literalSize; |
| if (totalSize == 0) return null; |
| FieldBinding[] bindings = new FieldBinding[totalSize]; |
| |
| // add innerclass synthetics |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] != null){ |
| Iterator elements = this.synthetics[SourceTypeBinding.FIELD_EMUL].values().iterator(); |
| for (int i = 0; i < fieldSize; i++) { |
| SyntheticFieldBinding synthBinding = (SyntheticFieldBinding) elements.next(); |
| bindings[synthBinding.index] = synthBinding; |
| } |
| } |
| // add class literal synthetics |
| if (this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] != null){ |
| Iterator elements = this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL].values().iterator(); |
| for (int i = 0; i < literalSize; i++) { |
| SyntheticFieldBinding synthBinding = (SyntheticFieldBinding) elements.next(); |
| bindings[fieldSize+synthBinding.index] = synthBinding; |
| } |
| } |
| return bindings; |
| } |
| 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 (isProtected()) buffer.append("protected "); //$NON-NLS-1$ |
| if (isPrivate()) buffer.append("private "); //$NON-NLS-1$ |
| if (isAbstract() && isClass()) buffer.append("abstract "); //$NON-NLS-1$ |
| if (isStatic() && isNestedType()) buffer.append("static "); //$NON-NLS-1$ |
| if (isFinal()) buffer.append("final "); //$NON-NLS-1$ |
| |
| if (isEnum()) buffer.append("enum "); //$NON-NLS-1$ |
| else if (isAnnotationType()) buffer.append("@interface "); //$NON-NLS-1$ |
| else 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$ |
| |
| if (this.typeVariables == null) { |
| buffer.append("<NULL TYPE VARIABLES>"); //$NON-NLS-1$ |
| } else if (this.typeVariables != Binding.NO_TYPE_VARIABLES) { |
| buffer.append("\n\t<"); //$NON-NLS-1$ |
| for (int i = 0, length = this.typeVariables.length; i < length; i++) { |
| if (i > 0) |
| buffer.append(", "); //$NON-NLS-1$ |
| buffer.append((this.typeVariables[i] != null) ? this.typeVariables[i].toString() : "NULL TYPE VARIABLE"); //$NON-NLS-1$ |
| } |
| buffer.append(">"); //$NON-NLS-1$ |
| } |
| buffer.append("\n\textends "); //$NON-NLS-1$ |
| buffer.append((this.superclass != null) ? this.superclass.debugName() : "NULL TYPE"); //$NON-NLS-1$ |
| |
| if (this.superInterfaces != null) { |
| if (this.superInterfaces != Binding.NO_SUPERINTERFACES) { |
| buffer.append("\n\timplements : "); //$NON-NLS-1$ |
| for (int i = 0, length = this.superInterfaces.length; i < length; i++) { |
| if (i > 0) |
| buffer.append(", "); //$NON-NLS-1$ |
| buffer.append((this.superInterfaces[i] != null) ? this.superInterfaces[i].debugName() : "NULL TYPE"); //$NON-NLS-1$ |
| } |
| } |
| } else { |
| buffer.append("NULL SUPERINTERFACES"); //$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(); |
| } |
| public TypeVariableBinding[] typeVariables() { |
| return this.typeVariables; |
| } |
| void verifyMethods(MethodVerifier verifier) { |
| verifier.verify(this); |
| |
| // for (int i = this.memberTypes.length; --i >= 0;) |
| // ((SourceTypeBinding) this.memberTypes[i]).verifyMethods(verifier); |
| } |
| |
| 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(); |
| // AbstractMethodDeclaration[] methods = classScope.referenceContext.methods; |
| // for (int i = methods.length; --i >= 0;) |
| // if (binding == methods[i].binding) |
| // return methods[i]; |
| 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; |
| // clean up should be called for each compilation unit, so it shouldnt be necessary to chain the cleanups |
| //if (this.nextType!=null) |
| // this.nextType.cleanup(); |
| } |
| |
| |
| 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; |
| while (binding.nextType!=null) |
| binding=binding.nextType; |
| binding.nextType=type; |
| |
| } |
| |
| } |