| /******************************************************************************* |
| * 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.jdt.internal.compiler.lookup; |
| |
| import com.google.gwt.dev.util.collect.HashMap; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeParameter; |
| import org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; |
| import org.eclipse.jdt.internal.compiler.util.Util; |
| |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| |
| public class SourceTypeBinding extends ReferenceBinding { |
| public ReferenceBinding superclass; |
| public ReferenceBinding[] superInterfaces; |
| private FieldBinding[] fields; |
| private MethodBinding[] methods; |
| public ReferenceBinding[] memberTypes; |
| public TypeVariableBinding[] typeVariables; |
| |
| public ClassScope scope; |
| |
| // Synthetics are separated into 5 categories: methods, super methods, fields, class literals, changed declaring type bindings and bridge methods |
| // if a new category is added, also increment MAX_SYNTHETICS |
| private final static int METHOD_EMUL = 0; |
| private final static int FIELD_EMUL = 1; |
| private final static int CLASS_LITERAL_EMUL = 2; |
| private final static int RECEIVER_TYPE_EMUL = 3; |
| |
| private final static int MAX_SYNTHETICS = 4; |
| |
| HashMap[] synthetics; |
| char[] genericReferenceTypeSignature; |
| |
| private SimpleLookupTable storedAnnotations = null; // keys are this ReferenceBinding & its fields and methods, value is an AnnotationHolder |
| |
| public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassScope scope) { |
| this.compoundName = compoundName; |
| this.fPackage = fPackage; |
| this.fileName = scope.referenceCompilationUnit().getFileName(); |
| this.modifiers = scope.referenceContext.modifiers; |
| this.sourceName = scope.referenceContext.name; |
| this.scope = scope; |
| |
| // expect the fields & methods to be initialized correctly later |
| this.fields = Binding.UNINITIALIZED_FIELDS; |
| this.methods = Binding.UNINITIALIZED_METHODS; |
| |
| computeId(); |
| } |
| |
| 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) { |
| MethodBinding[] 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()) { |
| MethodBinding[] superMethods = superType.methods(); |
| nextAbstractMethod: for (int m = superMethods.length; --m >= 0;) { |
| MethodBinding method = superMethods[m]; |
| // explicitly implemented ? |
| if (implementsMethod(method)) |
| continue nextAbstractMethod; |
| if (defaultAbstractsCount == 0) { |
| defaultAbstracts = new MethodBinding[5]; |
| } else { |
| // already added as default abstract ? |
| for (int k = 0; k < defaultAbstractsCount; k++) { |
| MethodBinding alreadyAdded = defaultAbstracts[k]; |
| if (CharOperation.equals(alreadyAdded.selector, method.selector) && alreadyAdded.areParametersEqual(method)) |
| continue nextAbstractMethod; |
| } |
| } |
| MethodBinding defaultAbstract = new MethodBinding( |
| method.modifiers | ExtraCompilerModifiers.AccDefaultAbstract | ClassFileConstants.AccSynthetic, |
| method.selector, |
| method.returnType, |
| method.parameters, |
| method.thrownExceptions, |
| this); |
| if (defaultAbstractsCount == defaultAbstracts.length) |
| System.arraycopy(defaultAbstracts, 0, defaultAbstracts = new MethodBinding[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 MethodBinding[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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(); |
| |
| 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.scope.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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(); |
| |
| 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.scope.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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] == null) |
| this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] = new HashMap(); |
| |
| // 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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(); |
| |
| 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.scope.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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(); |
| |
| 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.scope.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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(); |
| |
| 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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(); |
| |
| 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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(); |
| |
| 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.scope.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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(); |
| |
| 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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(); |
| |
| 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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) { |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(); |
| } 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; |
| } |
| boolean areFieldsInitialized() { |
| return this.fields != Binding.UNINITIALIZED_FIELDS; |
| } |
| boolean areMethodsInitialized() { |
| return this.methods != Binding.UNINITIALIZED_METHODS; |
| } |
| 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) |
| return this.fields; |
| |
| 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; |
| return this.fields; |
| } |
| /** |
| * @see org.eclipse.jdt.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.jdt.internal.compiler.lookup.Binding#getAnnotationTagBits() |
| */ |
| public long getAnnotationTagBits() { |
| if ((this.tagBits & TagBits.AnnotationResolved) == 0 && this.scope != null) { |
| TypeDeclaration typeDecl = this.scope.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; |
| } |
| // NOTE: the return type, arg & exception types of each method of a source type are resolved when needed |
| public MethodBinding getExactConstructor(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 null; |
| } |
| |
| //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. |
| public MethodBinding getExactMethod(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]) |
| 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(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 |
| } |
| } |
| } |
| nextMethod: for (int imethod = start; imethod <= end; imethod++) { |
| MethodBinding 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 (isInterface()) { |
| if (this.superInterfaces.length == 1) { |
| if (refScope != null) |
| refScope.recordTypeReference(this.superInterfaces[0]); |
| return this.superInterfaces[0].getExactMethod(selector, argumentTypes, refScope); |
| } |
| } else if (this.superclass != null) { |
| if (refScope != null) |
| refScope.recordTypeReference(this.superclass); |
| return this.superclass.getExactMethod(selector, argumentTypes, refScope); |
| } |
| } |
| return null; |
| } |
| |
| //NOTE: the type of a field of a source type is resolved when needed |
| public FieldBinding getField(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; |
| } |
| |
| // NOTE: the return type, arg & exception types of each method of a source type are resolved when needed |
| public MethodBinding[] getMethods(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.findSuperTypeOriginatingFrom(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.jdt.internal.compiler.lookup.Binding#initializeDeprecatedAnnotationTagBits() |
| */ |
| public void initializeDeprecatedAnnotationTagBits() { |
| if ((this.tagBits & TagBits.DeprecatedAnnotationResolved) == 0) { |
| TypeDeclaration typeDecl = this.scope.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; |
| } |
| } |
| } |
| |
| // ensure the receiver knows its hierarchy & fields/methods so static imports can be resolved correctly |
| // see bug 230026 |
| void initializeForStaticImports() { |
| if (this.scope == null) return; // already initialized |
| |
| if (this.superInterfaces == null) |
| this.scope.connectTypeHierarchy(); |
| this.scope.buildFields(); |
| this.scope.buildMethods(); |
| } |
| |
| /** |
| * 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 : |
| case Binding.INTERSECTION_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() { |
| return this.memberTypes; |
| } |
| public FieldBinding getUpdatedFieldBinding(FieldBinding targetField, ReferenceBinding newDeclaringClass) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL] == null) |
| this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL] = new HashMap(); |
| |
| 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[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL] == null) |
| this.synthetics[SourceTypeBinding.RECEIVER_TYPE_EMUL] = new HashMap(); |
| |
| 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); |
| methodMap.put(newDeclaringClass, updatedMethod); |
| } |
| return updatedMethod; |
| } |
| public boolean hasMemberTypes() { |
| return this.memberTypes.length > 0; |
| } |
| // 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) |
| return this.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; |
| } |
| |
| 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.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, method.areParametersEqual(method2)); |
| } |
| 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, method.areParametersEqual(method2)); |
| } |
| 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; |
| } |
| return this.methods; |
| } |
| private FieldBinding resolveTypeFor(FieldBinding field) { |
| if ((field.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0) |
| return field; |
| |
| if (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; |
| FieldDeclaration[] fieldDecls = this.scope.referenceContext.fields; |
| for (int f = 0, length = fieldDecls.length; f < length; f++) { |
| if (fieldDecls[f].binding != field) |
| continue; |
| |
| MethodScope initializationScope = field.isStatic() |
| ? this.scope.referenceContext.staticInitializerScope |
| : this.scope.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, false /*do not force conversion of enclosing types*/) // 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; |
| } |
| if ((fieldType.tagBits & TagBits.HasMissingType) != 0) { |
| field.tagBits |= TagBits.HasMissingType; |
| } |
| 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.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 (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.findSuperTypeOriginatingFrom(TypeIds.T_JavaLangThrowable, true) == null) { |
| if (resolvedExceptionType.isValidBinding()) { |
| methodDecl.scope.problemReporter().cannotThrowType(exceptionTypes[i], resolvedExceptionType); |
| continue; |
| } |
| } |
| if ((resolvedExceptionType.tagBits & TagBits.HasMissingType) != 0) { |
| method.tagBits |= TagBits.HasMissingType; |
| } |
| method.modifiers |= (resolvedExceptionType.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]; |
| if (arg.annotations != null) { |
| method.tagBits |= TagBits.HasParameterAnnotations; |
| } |
| TypeBinding parameterType = arg.type.resolveType(methodDecl.scope, true /* check bounds*/); |
| if (parameterType == null) { |
| foundArgProblem = true; |
| } else if (parameterType == TypeBinding.VOID) { |
| methodDecl.scope.problemReporter().argumentTypeCannotBeVoid(this, methodDecl, arg); |
| foundArgProblem = true; |
| } else { |
| if ((parameterType.tagBits & TagBits.HasMissingType) != 0) { |
| method.tagBits |= TagBits.HasMissingType; |
| } |
| 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.scope.problemReporter().missingReturnType(methodDecl); |
| method.returnType = null; |
| foundReturnTypeProblem = true; |
| } else { |
| TypeBinding methodType = returnType.resolveType(methodDecl.scope, true /* check bounds*/); |
| if (methodType == null) { |
| foundReturnTypeProblem = true; |
| } else if (methodType.isArrayType() && ((ArrayBinding) methodType).leafComponentType == TypeBinding.VOID) { |
| methodDecl.scope.problemReporter().returnTypeCannotBeVoidArray((MethodDeclaration) methodDecl); |
| foundReturnTypeProblem = true; |
| } else { |
| if ((methodType.tagBits & TagBits.HasMissingType) != 0) { |
| method.tagBits |= TagBits.HasMissingType; |
| } |
| 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 (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) { |
| this.fields = fields; |
| } |
| public void setMethods(MethodBinding[] methods) { |
| this.methods = methods; |
| } |
| public final int sourceEnd() { |
| return this.scope.referenceContext.sourceEnd; |
| } |
| public final int sourceStart() { |
| return this.scope.referenceContext.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() { |
| return this.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("<"); //$NON-NLS-1$ |
| for (int i = 0, length = this.typeVariables.length; i < length; i++) { |
| if (i > 0) buffer.append(", "); //$NON-NLS-1$ |
| if (this.typeVariables[i] == null) { |
| buffer.append("NULL TYPE VARIABLE"); //$NON-NLS-1$ |
| continue; |
| } |
| char[] varChars = this.typeVariables[i].toString().toCharArray(); |
| buffer.append(varChars, 1, varChars.length - 2); |
| } |
| 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); |
| } |
| } |