| /******************************************************************************* |
| * Copyright (c) 2000, 2014 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 |
| * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for |
| * bug 328281 - visibility leaks not detected when analyzing unused field in private class |
| * bug 349326 - [1.7] new warning for missing try-with-resources |
| * bug 186342 - [compiler][null] Using annotations for null checking |
| * bug 365836 - [compiler][null] Incomplete propagation of null defaults. |
| * bug 365519 - editorial cleanup after bug 186342 and bug 365387 |
| * bug 365662 - [compiler][null] warn on contradictory and redundant null annotations |
| * bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults |
| * bug 366063 - Compiler should not add synthetic @NonNull annotations |
| * bug 384663 - Package Based Annotation Compilation Error in JDT 3.8/4.2 (works in 3.7.2) |
| * bug 386356 - Type mismatch error with annotations and generics |
| * bug 388281 - [compiler][null] inheritance of null annotations as an option |
| * bug 331649 - [compiler][null] consider null annotations for fields |
| * bug 380896 - [compiler][null] Enum constants not recognised as being NonNull. |
| * bug 391376 - [1.8] check interaction of default methods with bridge methods and generics |
| * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis |
| * Bug 415043 - [1.8][null] Follow-up re null type annotations after bug 392099 |
| * Bug 392238 - [1.8][compiler][null] Detect semantically invalid null type annotations |
| * Bug 415850 - [1.8] Ensure RunJDTCoreTests can cope with null annotations enabled |
| * Bug 416172 - [1.8][compiler][null] null type annotation not evaluated on method return type |
| * Bug 417295 - [1.8[[null] Massage type annotated null analysis to gel well with deep encoded type bindings. |
| * Bug 426048 - [1.8] NPE in TypeVariableBinding.internalBoundCheck when parentheses are not balanced |
| * Bug 392238 - [1.8][compiler][null] Detect semantically invalid null type annotations |
| * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault |
| * Bug 432348 - [1.8] Internal compiler error (NPE) after upgrade to 1.8 |
| * Jesper S Moller <jesper@selskabet.org> - Contributions for |
| * Bug 412153 - [1.8][compiler] Check validity of annotations which may be repeatable |
| * Till Brychcy - Contributions for |
| * bug 415269 - NonNullByDefault is not always inherited to nested classes |
| * Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for |
| * Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas |
| *******************************************************************************/ |
| package org.aspectj.org.eclipse.jdt.internal.compiler.lookup; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| |
| import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.LambdaExpression; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeParameter; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.Util; |
| |
| @SuppressWarnings({ "rawtypes", "unchecked" }) |
| public class SourceTypeBinding extends ReferenceBinding { |
| public ReferenceBinding superclass; // MUST NOT be modified directly, use setter ! |
| public ReferenceBinding[] superInterfaces; // MUST NOT be modified directly, use setter ! |
| // AspectJ Extension - raised visibility to public of these fields |
| public FieldBinding[] fields; // MUST NOT be modified directly, use setter ! |
| public MethodBinding[] methods; // MUST NOT be modified directly, use setter ! |
| // End AspectJ Extension - raised visibility to public of these fields |
| public ReferenceBinding[] memberTypes; // MUST NOT be modified directly, use setter ! |
| public TypeVariableBinding[] typeVariables; // MUST NOT be modified directly, use setter ! |
| |
| |
| // AspectJ Extension |
| |
| // These store the original superclass and superinterfaces. This means if decp processing changes them, |
| // we can still write out the original correct ones at code gen time. |
| public ReferenceBinding originalSuperclass = null; |
| public ReferenceBinding[] originalSuperInterfaces = null; |
| |
| public IPrivilegedHandler privilegedHandler = null; |
| public IMemberFinder memberFinder = null; |
| public ITypeFinder typeFinder = null; |
| // End AspectJ Extension |
| |
| public ClassScope scope; |
| protected SourceTypeBinding prototype; |
| LookupEnvironment environment; |
| |
| // Synthetics are separated into 4 categories: methods, super methods, fields, class literals 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 MAX_SYNTHETICS = 3; |
| |
| HashMap[] synthetics; |
| char[] genericReferenceTypeSignature; |
| |
| private SimpleLookupTable storedAnnotations = null; // keys are this ReferenceBinding & its fields and methods, value is an AnnotationHolder |
| |
| public int defaultNullness; |
| private int nullnessDefaultInitialized = 0; // 0: nothing; 1: type; 2: package |
| private int lambdaOrdinal = 0; |
| private ReferenceBinding containerAnnotationType = null; |
| // AspectJ Extension |
| // for AspectJ... (because we extend this type with BinaryTypeBinding) |
| // (and yes, binary source is a bit odd...) |
| public SourceTypeBinding() { |
| this.prototype = this; |
| } |
| // End AspectJ Extension |
| |
| |
| 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; |
| this.environment = scope.environment(); |
| |
| // expect the fields & methods to be initialized correctly later |
| this.fields = Binding.UNINITIALIZED_FIELDS; |
| this.methods = Binding.UNINITIALIZED_METHODS; |
| this.prototype = this; |
| computeId(); |
| } |
| |
| public SourceTypeBinding(SourceTypeBinding prototype) { |
| super(prototype); |
| |
| this.prototype = prototype.prototype; |
| this.prototype.tagBits |= TagBits.HasAnnotatedVariants; |
| this.tagBits &= ~TagBits.HasAnnotatedVariants; |
| |
| this.superclass = prototype.superclass; |
| this.superInterfaces = prototype.superInterfaces; |
| this.fields = prototype.fields; |
| this.methods = prototype.methods; |
| this.memberTypes = prototype.memberTypes; |
| this.typeVariables = prototype.typeVariables; |
| this.environment = prototype.environment; |
| |
| // this.scope = prototype.scope; // Will defeat CompilationUnitDeclaration.cleanUp(TypeDeclaration) && CompilationUnitDeclaration.cleanUp(), so not copied, not an issue for JSR 308. |
| |
| this.synthetics = prototype.synthetics; |
| this.genericReferenceTypeSignature = prototype.genericReferenceTypeSignature; |
| this.storedAnnotations = prototype.storedAnnotations; |
| this.defaultNullness = prototype.defaultNullness; |
| this.nullnessDefaultInitialized= prototype.nullnessDefaultInitialized; |
| this.lambdaOrdinal = prototype.lambdaOrdinal; |
| this.containerAnnotationType = prototype.containerAnnotationType; |
| this.tagBits |= TagBits.HasUnresolvedMemberTypes; // see memberTypes() |
| } |
| |
| private void addDefaultAbstractMethods() { |
| |
| if (!isPrototype()) throw new IllegalStateException(); |
| |
| 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]; |
| // AspectJ Extension - some methods on interfaces provide default implementations - these are |
| // not considered abstract (pr271704) |
| if (!method.isAbstract()) { |
| continue nextAbstractMethod; |
| } |
| // End AspectJ Extension |
| // 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 (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface; |
| interfacesToVisit[nextPosition++] = next; |
| } |
| } |
| } |
| } |
| if (defaultAbstractsCount > 0) { |
| int length = this.methods.length; |
| System.arraycopy(this.methods, 0, setMethods(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 (!isPrototype()) throw new IllegalStateException(); |
| |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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 = getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.scope.referenceContext; |
| FieldDeclaration[] fieldDeclarations = typeDecl.fields; |
| int max = fieldDeclarations == null ? 0 : fieldDeclarations.length; |
| for (int i = 0; i < max; i++) { |
| FieldDeclaration fieldDecl = fieldDeclarations[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 (!isPrototype()) throw new IllegalStateException(); |
| |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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 = getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.scope.referenceContext; |
| FieldDeclaration[] fieldDeclarations = typeDecl.fields; |
| int max = fieldDeclarations == null ? 0 : fieldDeclarations.length; |
| for (int i = 0; i < max; i++) { |
| FieldDeclaration fieldDecl = fieldDeclarations[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 (!isPrototype()) throw new IllegalStateException(); |
| |
| 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(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 = getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = blockScope.referenceType(); |
| FieldDeclaration[] typeDeclarationFields = typeDecl.fields; |
| int max = typeDeclarationFields == null ? 0 : typeDeclarationFields.length; |
| for (int i = 0; i < max; i++) { |
| FieldDeclaration fieldDecl = typeDeclarationFields[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 (!isPrototype()) throw new IllegalStateException(); |
| |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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 = getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.scope.referenceContext; |
| int max = (typeDecl.fields == null) ? 0 : typeDecl.fields.length; |
| for (int i = 0; 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 (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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 = getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.scope.referenceContext; |
| FieldDeclaration[] fieldDeclarations = typeDecl.fields; |
| int max = fieldDeclarations == null ? 0 : fieldDeclarations.length; |
| for (int i = 0; i < max; i++) { |
| FieldDeclaration fieldDecl = fieldDeclarations[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; |
| } |
| |
| // AspectJ Extension |
| public FieldBinding addSyntheticField(SyntheticFieldBinding sfb) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[4]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(5); |
| String key = new String(sfb.name); |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL].get(key)!=null) throw new RuntimeException("You are trying to add this twice?? "+key);//$NON-NLS-1$ |
| sfb.index=this.synthetics[SourceTypeBinding.FIELD_EMUL].size(); |
| this.synthetics[SourceTypeBinding.FIELD_EMUL].put(key,sfb); |
| // Skip the check for a clash... naughty! |
| return sfb; |
| } |
| // End AspectJ Extension |
| |
| /* 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, boolean isSuperAccess) { |
| if (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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, isSuperAccess, 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, isSuperAccess, 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 (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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 (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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 = getField(synthField.name, true /*resolve*/)) != null) { |
| TypeDeclaration typeDecl = this.scope.referenceContext; |
| FieldDeclaration[] fieldDeclarations = typeDecl.fields; |
| int max = fieldDeclarations == null ? 0 : fieldDeclarations.length; |
| for (int i = 0; i < max; i++) { |
| FieldDeclaration fieldDecl = fieldDeclarations[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 (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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 = 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 = addSyntheticFieldForSwitchEnum(selector, key); |
| accessMethod = new SyntheticMethodBinding(fieldBinding, this, enumBinding, selector); |
| accessors[0] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| public SyntheticMethodBinding addSyntheticMethodForEnumInitialization(int begin, int end) { |
| if (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| |
| SyntheticMethodBinding accessMethod = new SyntheticMethodBinding(this, begin, end); |
| SyntheticMethodBinding[] accessors = new SyntheticMethodBinding[2]; |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(accessMethod.selector, accessors); |
| accessors[0] = accessMethod; |
| return accessMethod; |
| } |
| public SyntheticMethodBinding addSyntheticMethod(LambdaExpression lambda) { |
| if (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| |
| SyntheticMethodBinding lambdaMethod = null; |
| SyntheticMethodBinding[] lambdaMethods = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(lambda); |
| if (lambdaMethods == null) { |
| lambdaMethod = new SyntheticMethodBinding(lambda, CharOperation.concat(TypeConstants.ANONYMOUS_METHOD, Integer.toString(this.lambdaOrdinal++).toCharArray()), this); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(lambda, lambdaMethods = new SyntheticMethodBinding[1]); |
| lambdaMethods[0] = lambdaMethod; |
| } else { |
| lambdaMethod = lambdaMethods[0]; |
| } |
| |
| // Create a $deserializeLambda$ method if necessary, one is shared amongst all lambdas |
| if (lambda.isSerializable) { |
| SyntheticMethodBinding[] deserializeLambdaMethods = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(TypeConstants.DESERIALIZE_LAMBDA); |
| if (deserializeLambdaMethods == null) { |
| SyntheticMethodBinding deserializeLambdaMethod = new SyntheticMethodBinding(this); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(TypeConstants.DESERIALIZE_LAMBDA,deserializeLambdaMethods = new SyntheticMethodBinding[1]); |
| deserializeLambdaMethods[0] = deserializeLambdaMethod; |
| } |
| } |
| |
| return lambdaMethod; |
| } |
| |
| /* 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 (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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; |
| } |
| } |
| if (targetMethod.declaringClass.isStatic()) { |
| if ((targetMethod.isConstructor() && targetMethod.parameters.length >= 0xFE) |
| || targetMethod.parameters.length >= 0xFF) { |
| this.scope.problemReporter().tooManyParametersForSyntheticMethod(targetMethod.sourceMethod()); |
| } |
| } else if ((targetMethod.isConstructor() && targetMethod.parameters.length >= 0xFD) |
| || targetMethod.parameters.length >= 0xFE) { |
| this.scope.problemReporter().tooManyParametersForSyntheticMethod(targetMethod.sourceMethod()); |
| } |
| return accessMethod; |
| } |
| public SyntheticMethodBinding addSyntheticArrayMethod(ArrayBinding arrayType, int purpose) { |
| if (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| |
| SyntheticMethodBinding arrayMethod = null; |
| SyntheticMethodBinding[] arrayMethods = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(arrayType); |
| if (arrayMethods == null) { |
| char [] selector = CharOperation.concat(TypeConstants.ANONYMOUS_METHOD, Integer.toString(this.lambdaOrdinal++).toCharArray()); |
| arrayMethod = new SyntheticMethodBinding(purpose, arrayType, selector, this); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(arrayType, arrayMethods = new SyntheticMethodBinding[2]); |
| arrayMethods[purpose == SyntheticMethodBinding.ArrayConstructor ? 0 : 1] = arrayMethod; |
| } else { |
| if ((arrayMethod = arrayMethods[purpose == SyntheticMethodBinding.ArrayConstructor ? 0 : 1]) == null) { |
| char [] selector = CharOperation.concat(TypeConstants.ANONYMOUS_METHOD, Integer.toString(this.lambdaOrdinal++).toCharArray()); |
| arrayMethod = new SyntheticMethodBinding(purpose, arrayType, selector, this); |
| arrayMethods[purpose == SyntheticMethodBinding.ArrayConstructor ? 0 : 1] = arrayMethod; |
| } |
| } |
| return arrayMethod; |
| } |
| public SyntheticMethodBinding addSyntheticFactoryMethod(MethodBinding privateConstructor, MethodBinding publicConstructor, TypeBinding [] enclosingInstances) { |
| if (!isPrototype()) throw new IllegalStateException(); |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| |
| char [] selector = CharOperation.concat(TypeConstants.ANONYMOUS_METHOD, Integer.toString(this.lambdaOrdinal++).toCharArray()); |
| SyntheticMethodBinding factory = new SyntheticMethodBinding(privateConstructor, publicConstructor, selector, enclosingInstances, this); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(selector, new SyntheticMethodBinding[] { factory }); |
| return factory; |
| } |
| /* |
| * Record the fact that bridge methods need to be generated to override certain inherited methods |
| */ |
| public SyntheticMethodBinding addSyntheticBridgeMethod(MethodBinding inheritedMethodToBridge, MethodBinding targetMethod) { |
| if (!isPrototype()) throw new IllegalStateException(); |
| if (isInterface()) return null; // only classes & enums get bridge methods |
| // targetMethod may be inherited |
| if (TypeBinding.equalsEquals(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(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) |
| && TypeBinding.equalsEquals(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; |
| } |
| /* |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=288658. Generate a bridge method if a public method is inherited |
| * from a non-public class into a public class (only in 1.6 or greater) |
| * https://bugs.eclipse.org/404690 : this doesn't apply to inherited interface methods (i.e., default methods) |
| */ |
| public SyntheticMethodBinding addSyntheticBridgeMethod(MethodBinding inheritedMethodToBridge) { |
| if (!isPrototype()) throw new IllegalStateException(); |
| if (this.scope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_5) { |
| return null; |
| } |
| if (isInterface() && !inheritedMethodToBridge.isDefaultMethod()) return null; |
| if (inheritedMethodToBridge.isAbstract() || inheritedMethodToBridge.isFinal() || inheritedMethodToBridge.isStatic()) { |
| return null; |
| } |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| 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) |
| && TypeBinding.equalsEquals(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, this); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(inheritedMethodToBridge, accessors = new SyntheticMethodBinding[2]); |
| accessors[0] = accessMethod; |
| } else { |
| if ((accessMethod = accessors[0]) == null) { |
| accessMethod = new SyntheticMethodBinding(inheritedMethodToBridge, this); |
| accessors[0] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| boolean areFieldsInitialized() { |
| if (!isPrototype()) |
| return this.prototype.areFieldsInitialized(); |
| return this.fields != Binding.UNINITIALIZED_FIELDS; |
| } |
| boolean areMethodsInitialized() { |
| if (!isPrototype()) |
| return this.prototype.areMethodsInitialized(); |
| return this.methods != Binding.UNINITIALIZED_METHODS; |
| } |
| public int kind() { |
| if (!isPrototype()) |
| return this.prototype.kind(); |
| if (this.typeVariables != Binding.NO_TYPE_VARIABLES) return Binding.GENERIC_TYPE; |
| return Binding.TYPE; |
| } |
| |
| public TypeBinding clone(TypeBinding immaterial) { |
| return new SourceTypeBinding(this); |
| } |
| |
| public char[] computeUniqueKey(boolean isLeaf) { |
| if (!isPrototype()) |
| return this.prototype.computeUniqueKey(); |
| 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 |
| if (this.isMemberType()) { |
| end = CharOperation.indexOf('$', uniqueKey, start); |
| } else { |
| // '$' is part of the type name |
| end = -1; |
| } |
| 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() { |
| if (!isPrototype()) throw new IllegalStateException(); |
| // check @Deprecated annotation |
| getAnnotationTagBits(); // marks as deprecated by side effect |
| ReferenceBinding enclosingType = enclosingType(); |
| if (enclosingType != null && enclosingType.isViewedAsDeprecated() && !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 (!isPrototype()) { |
| if ((this.tagBits & TagBits.AreFieldsComplete) != 0) |
| return this.fields; |
| this.tagBits |= TagBits.AreFieldsComplete; |
| return this.fields = this.prototype.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 setFields(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]; |
| } |
| setFields(newFields); |
| } |
| } |
| this.tagBits |= TagBits.AreFieldsComplete; |
| return this.fields; |
| } |
| /** |
| * @see org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding#genericTypeSignature() |
| */ |
| public char[] genericTypeSignature() { |
| if (!isPrototype()) |
| return this.prototype.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() { |
| if (!isPrototype()) |
| return this.prototype.genericSignature(); |
| |
| // AspectJ Extension |
| // messy messy. We need to use the 'originalSuperclass/SuperInterfaces' if there are any |
| // rather than what they might have become due to declare parents being applied. |
| // Unfortunately here this means changing 'this.superclass' and 'this.superInterfaces' |
| // through the rest of the method... |
| |
| // next 4 lines are new code, rest is changed from the original method body, marked 'AJ was' |
| ReferenceBinding supclass = this.superclass; |
| ReferenceBinding[] supinterfaces = this.superInterfaces; |
| if (this.originalSuperclass!=null) supclass = this.originalSuperclass; |
| if (this.originalSuperInterfaces!=null) supinterfaces = this.originalSuperInterfaces; |
| |
| 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 (supclass/*AJ was this.superclass*/ == null || !supclass/*AJ was this.superclass*/.isParameterizedType()) { |
| for (int i = 0, length = supinterfaces/*AJ was this.superInterfaces*/.length; i < length; i++) |
| if (supinterfaces/*AJ was this.superInterfaces*/[i].isParameterizedType()) |
| break noSignature; |
| return null; |
| } |
| sig = new StringBuffer(10); |
| } |
| if (supclass/*AJ was this.superclass*/ != null) |
| sig.append(supclass/*AJ was this.superclass*/.genericTypeSignature()); |
| else // interface scenario only (as Object cannot be generic) - 65953 |
| sig.append(this.scope.getJavaLangObject().genericTypeSignature()); |
| for (int i = 0, length = supinterfaces/*AJ was this.superInterfaces*/.length; i < length; i++) |
| sig.append(supinterfaces/*AJ was this.superInterfaces*/[i].genericTypeSignature()); |
| return sig.toString().toCharArray(); |
| |
| // End AspectJ Extension |
| } |
| |
| /** |
| * Compute the tagbits for standard annotations. For source types, these could require |
| * lazily resolving corresponding annotation nodes, in case of forward references. |
| * For type use bindings, this method still returns the tagbits corresponding to the type |
| * declaration binding. |
| * @see org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding#getAnnotationTagBits() |
| */ |
| public long getAnnotationTagBits() { |
| if (!isPrototype()) |
| return this.prototype.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; |
| evaluateNullAnnotations(this.tagBits); |
| } |
| return this.tagBits; |
| } |
| public MethodBinding[] getDefaultAbstractMethods() { |
| if (!isPrototype()) |
| return this.prototype.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) { |
| if (!isPrototype()) |
| return this.prototype.getExactConstructor(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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(toMatch[iarg], argumentTypes[iarg])) |
| continue nextMethod; |
| return method; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| // AspectJ Extension - renamed original method getExactMethodBase and added this one |
| //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) { |
| if (memberFinder != null) return memberFinder.getExactMethod(this, selector, argumentTypes, refScope); |
| else return getExactMethodBase(selector, argumentTypes, refScope); |
| } |
| |
| //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 getExactMethodBase(char[] selector, TypeBinding[] argumentTypes, CompilationUnitScope refScope) { |
| // End AspectJ Extension |
| if (!isPrototype()) |
| return this.prototype.getExactMethod(selector, argumentTypes, 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 (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 |
| // AspectJ Extension - replaced original impl with this |
| public FieldBinding getField(char[] fieldName, boolean needResolve) { |
| if (memberFinder != null) return memberFinder.getField(this, fieldName, null, null); |
| else return this.getFieldBase(fieldName, needResolve); |
| } |
| |
| public FieldBinding getField(char[] fieldName, boolean needResolve, InvocationSite site, Scope scope) { |
| if (memberFinder != null) return memberFinder.getField(this, fieldName, site, scope); |
| else return this.getFieldBase(fieldName, needResolve); |
| } |
| |
| public FieldBinding getFieldBase(char[] fieldName, boolean needResolve) { |
| // End AspectJ Extension |
| |
| if (!isPrototype()) |
| return this.prototype.getField(fieldName, 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) { |
| setFields(Binding.NO_FIELDS); |
| } else { |
| FieldBinding[] newFields = new FieldBinding[newSize]; |
| int index = 0; |
| for (int i = 0, length = this.fields.length; i < length; i++) { |
| FieldBinding f = this.fields[i]; |
| if (f == field) continue; |
| newFields[index++] = f; |
| } |
| setFields(newFields); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| // AspectJ Extension - replaced original impl with this |
| public MethodBinding[] getMethods(char[] selector) { |
| if (memberFinder != null) return memberFinder.getMethods(this, selector); |
| else return getMethodsBase(selector); |
| } |
| |
| // overrides superclass method to consult ITD finder |
| public MethodBinding[] getMethods(char[] selector, int suggestedParameterLength) { |
| if (memberFinder != null) return memberFinder.getMethods(this, selector); |
| else return getMethodsBase(selector); |
| } |
| |
| // NOTE: the return type, arg & exception types of each method of a source type are resolved when needed |
| public MethodBinding[] getMethodsBase(char[] selector) { |
| // End AspectJ Extension |
| if (!isPrototype()) |
| return this.prototype.getMethods(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 (!isPrototype()) throw new IllegalStateException(); |
| 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 (!isPrototype()) throw new IllegalStateException(); |
| 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 (!isPrototype()) throw new IllegalStateException(); |
| 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]; |
| } |
| |
| public boolean hasTypeBit(int bit) { |
| if (!isPrototype()) { |
| return this.prototype.hasTypeBit(bit); |
| } |
| // source types initialize type bits during connectSuperclass/interfaces() |
| return (this.typeBits & bit) != 0; |
| } |
| |
| /** |
| * @see org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding#initializeDeprecatedAnnotationTagBits() |
| */ |
| public void initializeDeprecatedAnnotationTagBits() { |
| if (!isPrototype()) { |
| this.prototype.initializeDeprecatedAnnotationTagBits(); |
| return; |
| } |
| 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 (!isPrototype()) { |
| this.prototype.initializeForStaticImports(); |
| return; |
| } |
| if (this.scope == null) return; // already initialized |
| |
| if (this.superInterfaces == null) |
| this.scope.connectTypeHierarchy(); |
| this.scope.buildFields(); |
| this.scope.buildMethods(); |
| } |
| |
| int getNullDefault() { |
| |
| if (!isPrototype()) { |
| return this.prototype.getNullDefault(); |
| } |
| // ensure nullness defaults are initialized at all enclosing levels: |
| switch (this.nullnessDefaultInitialized) { |
| case 0: |
| getAnnotationTagBits(); // initialize |
| //$FALL-THROUGH$ |
| case 1: |
| getPackage().isViewedAsDeprecated(); // initialize annotations |
| this.nullnessDefaultInitialized = 2; |
| } |
| return this.defaultNullness; |
| } |
| |
| /** |
| * 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 (!isPrototype()) |
| return this.prototype.isEquivalentTo(otherType); |
| |
| if (TypeBinding.equalsEquals(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 && (!isMemberType() || !otherType.isMemberType())) |
| return false; // should have been identical |
| ParameterizedTypeBinding otherParamType = (ParameterizedTypeBinding) otherType; |
| if (TypeBinding.notEquals(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 (TypeBinding.notEquals(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 TypeBinding.equalsEquals(otherType.erasure(), this); |
| } |
| return false; |
| } |
| public boolean isGenericType() { |
| if (!isPrototype()) |
| return this.prototype.isGenericType(); |
| return this.typeVariables != Binding.NO_TYPE_VARIABLES; |
| } |
| public boolean isHierarchyConnected() { |
| if (!isPrototype()) |
| return this.prototype.isHierarchyConnected(); |
| return (this.tagBits & TagBits.EndHierarchyCheck) != 0; |
| } |
| public boolean isRepeatableAnnotationType() { |
| if (!isPrototype()) throw new IllegalStateException(); |
| return this.containerAnnotationType != null; |
| } |
| |
| public boolean isTaggedRepeatable() { // tagged but not necessarily repeatable. see isRepeatableAnnotationType. |
| return (this.tagBits & TagBits.AnnotationRepeatable) != 0; |
| } |
| |
| public ReferenceBinding[] memberTypes() { |
| if (!isPrototype()) { |
| if ((this.tagBits & TagBits.HasUnresolvedMemberTypes) == 0) |
| return this.memberTypes; |
| ReferenceBinding [] members = this.memberTypes = this.prototype.memberTypes(); |
| int membersLength = members == null ? 0 : members.length; |
| this.memberTypes = new ReferenceBinding[membersLength]; |
| for (int i = 0; i < membersLength; i++) { |
| this.memberTypes[i] = this.environment.createMemberType(members[i], this); |
| } |
| this.tagBits &= ~TagBits.HasUnresolvedMemberTypes; |
| } |
| return this.memberTypes; |
| } |
| |
| public boolean hasMemberTypes() { |
| if (!isPrototype()) |
| return this.prototype.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[] methodsBase() { // AspectJ Extension - added Base suffix, see methods() |
| |
| if (!isPrototype()) { |
| if ((this.tagBits & TagBits.AreMethodsComplete) != 0) |
| return this.methods; |
| this.tagBits |= TagBits.AreMethodsComplete; |
| return this.methods = this.prototype.methods(); |
| } |
| |
| if ((this.tagBits & TagBits.AreMethodsComplete) != 0) |
| return this.methods; |
| |
| if (!areMethodsInitialized()) { // https://bugs.eclipse.org/384663 |
| this.scope.buildMethods(); |
| } |
| |
| // 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 ((this.tagBits & TagBits.AreMethodsComplete) != 0) { |
| // recursive call to methods() from resolveTypesFor(..) resolved the methods |
| return this.methods; |
| } |
| |
| 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 complyTo15OrAbove = this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; |
| boolean compliance16 = this.scope.compilerOptions().complianceLevel == ClassFileConstants.JDK1_6; |
| |
| for (int i = 0, length = this.methods.length; i < length; i++) { |
| int severity = ProblemSeverities.Error; |
| 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 (complyTo15OrAbove) { |
| if (method.areParameterErasuresEqual(method2)) { |
| // we now ignore return types in 1.7 when detecting duplicates, just as we did before 1.5 |
| // Only in 1.6, we have to make sure even return types are different |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=317719 |
| if (compliance16 && method.returnType != null && method2.returnType != null) { |
| if (TypeBinding.notEquals(method.returnType.erasure(), method2.returnType.erasure())) { |
| // check to see if the erasure of either method is equal to the other |
| // if not, then change severity to WARNING |
| TypeBinding[] params1 = method.parameters; |
| TypeBinding[] params2 = method2.parameters; |
| int pLength = params1.length; |
| 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 (vars != Binding.NO_TYPE_VARIABLES && vars2 != Binding.NO_TYPE_VARIABLES) { |
| // both have type arguments. Erasure of signature of one cannot be equal to signature of other |
| severity = ProblemSeverities.Warning; |
| } else if (pLength > 0) { |
| int index = pLength; |
| // is erasure of signature of m2 same as signature of m1? |
| for (; --index >= 0;) { |
| if (TypeBinding.notEquals(params1[index], params2[index].erasure())) { |
| // If one of them is a raw type |
| if (params1[index] instanceof RawTypeBinding) { |
| if (TypeBinding.notEquals(params2[index].erasure(), ((RawTypeBinding)params1[index]).actualType())) { |
| break; |
| } |
| } else { |
| break; |
| } |
| } |
| if (TypeBinding.equalsEquals(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) { |
| // is erasure of signature of m1 same as signature of m2? |
| for (index = pLength; --index >= 0;) |
| if (TypeBinding.notEquals(params1[index].erasure(), params2[index])) { |
| // If one of them is a raw type |
| if (params2[index] instanceof RawTypeBinding) { |
| if (TypeBinding.notEquals(params1[index].erasure(), ((RawTypeBinding)params2[index]).actualType())) { |
| break; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| } |
| if (index >= 0) { |
| // erasure of neither is equal to signature of other |
| severity = ProblemSeverities.Warning; |
| } |
| } else if (pLength != 0){ |
| severity = ProblemSeverities.Warning; |
| } // pLength = 0 automatically makes erasure of arguments one equal to arguments of other. |
| } |
| // else return types also equal. All conditions satisfied |
| // to give error in 1.6 compliance as well. |
| } |
| } else { |
| continue nextSibling; |
| } |
| } else if (!method.areParametersEqual(method2)) { |
| // prior to 1.5, parameters identical meant a collision case |
| continue nextSibling; |
| } |
| // otherwise duplicates / name clash |
| boolean isEnumSpecialMethod = isEnum() && (CharOperation.equals(selector,TypeConstants.VALUEOF) || CharOperation.equals(selector,TypeConstants.VALUES)); |
| // report duplicate |
| boolean removeMethod2 = (severity == ProblemSeverities.Error) ? true : false; // do not remove if in 1.6 and just a warning given |
| 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 |
| boolean removeMethod = method.returnType == null && method2.returnType != null; |
| if (isEnumSpecialMethod) { |
| this.scope.problemReporter().duplicateEnumSpecialMethod(this, methodDecl); |
| // remove user defined methods & keep the synthetic |
| removeMethod = true; |
| } else { |
| this.scope.problemReporter().duplicateMethodInType(methodDecl, method.areParametersEqual(method2), severity); |
| } |
| if (removeMethod) { |
| removeMethod2 = false; |
| 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); |
| removeMethod2 = true; |
| } else { |
| this.scope.problemReporter().duplicateMethodInType(method2Decl, method.areParametersEqual(method2), severity); |
| } |
| if (removeMethod2) { |
| 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 && resolvedMethods[i] != 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 ((this.tagBits & TagBits.AreMethodsComplete) != 0) { |
| // recursive call to methods() from resolveTypesFor(..) resolved the methods |
| return this.methods; |
| } |
| if (failed > 0) { |
| int newSize = resolvedMethods.length - failed; |
| if (newSize == 0) { |
| setMethods(Binding.NO_METHODS); |
| } else { |
| MethodBinding[] newMethods = new MethodBinding[newSize]; |
| for (int i = 0, j = 0, length = resolvedMethods.length; i < length; i++) |
| if (resolvedMethods[i] != null) |
| newMethods[j++] = resolvedMethods[i]; |
| setMethods(newMethods); |
| } |
| } |
| |
| // handle forward references to potential default abstract methods |
| addDefaultAbstractMethods(); |
| this.tagBits |= TagBits.AreMethodsComplete; |
| } |
| return this.methods; |
| } |
| |
| public TypeBinding prototype() { |
| return this.prototype; |
| } |
| |
| public boolean isPrototype() { |
| return this == this.prototype; //$IDENTITY-COMPARISON$ |
| } |
| |
| public ReferenceBinding containerAnnotationType() { |
| |
| if (!isPrototype()) throw new IllegalStateException(); |
| |
| if (this.containerAnnotationType instanceof UnresolvedReferenceBinding) { |
| this.containerAnnotationType = (ReferenceBinding)BinaryTypeBinding.resolveType(this.containerAnnotationType, this.scope.environment(), false); |
| } |
| return this.containerAnnotationType; |
| } |
| |
| public FieldBinding resolveTypeFor(FieldBinding field) { |
| |
| if (!isPrototype()) |
| return this.prototype.resolveTypeFor(field); |
| |
| if ((field.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0) |
| return field; |
| |
| long sourceLevel = this.scope.compilerOptions().sourceLevel; |
| if (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; |
| int length = fieldDecls == null ? 0 : fieldDecls.length; |
| for (int f = 0; 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; |
| } |
| |
| if (sourceLevel >= ClassFileConstants.JDK1_8) { |
| Annotation [] annotations = fieldDecl.annotations; |
| if (annotations != null && annotations.length != 0) { |
| ASTNode.copySE8AnnotationsToType(initializationScope, field, annotations, |
| fieldDecl.getKind() != AbstractVariableDeclaration.ENUM_CONSTANT); // type annotation is illegal on enum constant |
| } |
| Annotation.isTypeUseCompatible(fieldDecl.type, this.scope, annotations); |
| } |
| // apply null default: |
| if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) { |
| // TODO(SH): different strategy for 1.8, or is "repair" below enough? |
| if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) { |
| // enum constants neither have a type declaration nor can they be null |
| field.tagBits |= TagBits.AnnotationNonNull; |
| } else { |
| if (hasNonNullDefaultFor(DefaultLocationField, sourceLevel >= ClassFileConstants.JDK1_8)) { |
| field.fillInDefaultNonNullness(fieldDecl, initializationScope); |
| } |
| // validate null annotation: |
| if (!this.scope.validateNullAnnotation(field.tagBits, fieldDecl.type, fieldDecl.annotations)) |
| field.tagBits &= ~TagBits.AnnotationNullMASK; |
| } |
| } |
| } finally { |
| initializationScope.initializedField = previousField; |
| } |
| return field; |
| } |
| return null; // should never reach this point |
| } |
| public MethodBinding resolveTypesFor(MethodBinding method) { |
| |
| if (!isPrototype()) |
| return this.prototype.resolveTypesFor(method); |
| |
| if ((method.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0) |
| return method; |
| |
| final long sourceLevel = this.scope.compilerOptions().sourceLevel; |
| if (sourceLevel >= ClassFileConstants.JDK1_5) { |
| ReferenceBinding object = this.scope.getJavaLangObject(); |
| TypeVariableBinding[] tvb = method.typeVariables; |
| for (int i = 0; i < tvb.length; i++) |
| tvb[i].superclass = object; // avoid null (see https://bugs.eclipse.org/426048) |
| |
| 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 |
| |
| |
| methodDecl.ensureScopeSetup(); // AspectJ extension |
| |
| 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; |
| } |
| if (resolvedExceptionType.hasNullTypeAnnotations()) { |
| methodDecl.scope.problemReporter().nullAnnotationUnsupportedLocation(exceptionTypes[i]); |
| } |
| 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); |
| } |
| |
| if (methodDecl.receiver != null) { |
| method.receiver = methodDecl.receiver.type.resolveType(methodDecl.scope, true /* check bounds*/); |
| } |
| final boolean reportUnavoidableGenericTypeProblems = this.scope.compilerOptions().reportUnavoidableGenericTypeProblems; |
| 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; |
| } |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=322817 |
| boolean deferRawTypeCheck = !reportUnavoidableGenericTypeProblems && !method.isConstructor() && (arg.type.bits & ASTNode.IgnoreRawTypeCheck) == 0; |
| TypeBinding parameterType; |
| if (deferRawTypeCheck) { |
| arg.type.bits |= ASTNode.IgnoreRawTypeCheck; |
| } |
| try { |
| parameterType = arg.type.resolveType(methodDecl.scope, true /* check bounds*/); |
| } finally { |
| if (deferRawTypeCheck) { |
| arg.type.bits &= ~ASTNode.IgnoreRawTypeCheck; |
| } |
| } |
| |
| if (parameterType == null) { |
| foundArgProblem = true; |
| } else if (parameterType == TypeBinding.VOID) { |
| methodDecl.scope.problemReporter().argumentTypeCannotBeVoid(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, methodDecl.scope); |
| } |
| } |
| // only assign parameters if no problems are found |
| if (!foundArgProblem) { |
| method.parameters = newParameters; |
| } |
| } |
| |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=337799 |
| if (sourceLevel >= ClassFileConstants.JDK1_7) { |
| if ((method.tagBits & TagBits.AnnotationSafeVarargs) != 0) { |
| if (!method.isVarargs()) { |
| methodDecl.scope.problemReporter().safeVarargsOnFixedArityMethod(method); |
| } else if (!method.isStatic() && !method.isFinal() && !method.isConstructor()) { |
| methodDecl.scope.problemReporter().safeVarargsOnNonFinalInstanceMethod(method); |
| } |
| } else if (method.parameters != null && method.parameters.length > 0 && method.isVarargs()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=337795 |
| if (!method.parameters[method.parameters.length - 1].isReifiable()) { |
| methodDecl.scope.problemReporter().possibleHeapPollutionFromVararg(methodDecl.arguments[methodDecl.arguments.length - 1]); |
| } |
| } |
| } |
| |
| 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 { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=322817 |
| boolean deferRawTypeCheck = !reportUnavoidableGenericTypeProblems && (returnType.bits & ASTNode.IgnoreRawTypeCheck) == 0; |
| TypeBinding methodType; |
| if (deferRawTypeCheck) { |
| returnType.bits |= ASTNode.IgnoreRawTypeCheck; |
| } |
| try { |
| methodType = returnType.resolveType(methodDecl.scope, true /* check bounds*/); |
| } finally { |
| if (deferRawTypeCheck) { |
| returnType.bits &= ~ASTNode.IgnoreRawTypeCheck; |
| } |
| } |
| if (methodType == null) { |
| foundReturnTypeProblem = true; |
| } else { |
| if ((methodType.tagBits & TagBits.HasMissingType) != 0) { |
| method.tagBits |= TagBits.HasMissingType; |
| } |
| method.returnType = methodType; |
| if (sourceLevel >= ClassFileConstants.JDK1_8 && !method.isVoidMethod()) { |
| Annotation [] annotations = methodDecl.annotations; |
| if (annotations != null && annotations.length != 0) { |
| ASTNode.copySE8AnnotationsToType(methodDecl.scope, method, methodDecl.annotations, true); |
| } |
| Annotation.isTypeUseCompatible(returnType, this.scope, methodDecl.annotations); |
| } |
| TypeBinding leafType = methodType.leafComponentType(); |
| if (leafType instanceof ReferenceBinding && (((ReferenceBinding) leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) |
| method.modifiers |= ExtraCompilerModifiers.AccGenericSignature; |
| else if (leafType == TypeBinding.VOID && methodDecl.annotations != null) |
| rejectTypeAnnotatedVoidMethod(methodDecl); |
| } |
| } |
| } |
| 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; |
| } |
| CompilerOptions compilerOptions = this.scope.compilerOptions(); |
| if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) { |
| if (!method.isConstructor() && method.returnType != null) { |
| long nullTagBits = method.tagBits & TagBits.AnnotationNullMASK; |
| if (nullTagBits != 0) { |
| TypeReference returnTypeRef = ((MethodDeclaration)methodDecl).returnType; |
| if (compilerOptions.sourceLevel < ClassFileConstants.JDK1_8) { |
| if (!this.scope.validateNullAnnotation(nullTagBits, returnTypeRef, methodDecl.annotations)) |
| method.tagBits &= ~TagBits.AnnotationNullMASK; |
| } else { |
| if (nullTagBits != (method.returnType.tagBits & TagBits.AnnotationNullMASK)) { |
| if (!this.scope.validateNullAnnotation(nullTagBits, returnTypeRef, methodDecl.annotations)) { |
| method.returnType.tagBits &= ~TagBits.AnnotationNullMASK; |
| } |
| method.tagBits &= ~TagBits.AnnotationNullMASK; |
| } |
| } |
| } |
| } |
| } |
| if (compilerOptions.storeAnnotations) |
| createArgumentBindings(method, compilerOptions); // need annotations resolved already at this point |
| 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; |
| } |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=391108 |
| private static void rejectTypeAnnotatedVoidMethod(AbstractMethodDeclaration methodDecl) { |
| Annotation[] annotations = methodDecl.annotations; |
| int length = annotations == null ? 0 : annotations.length; |
| for (int i = 0; i < length; i++) { |
| ReferenceBinding binding = (ReferenceBinding) annotations[i].resolvedType; |
| if (binding != null |
| && (binding.tagBits & TagBits.AnnotationForTypeUse) != 0 |
| && (binding.tagBits & TagBits.AnnotationForMethod) == 0) { |
| methodDecl.scope.problemReporter().illegalUsageOfTypeAnnotations(annotations[i]); |
| } |
| } |
| } |
| |
| private void createArgumentBindings(MethodBinding method, CompilerOptions compilerOptions) { |
| |
| if (!isPrototype()) throw new IllegalStateException(); |
| if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) |
| getNullDefault(); // ensure initialized |
| AbstractMethodDeclaration methodDecl = method.sourceMethod(); |
| if (methodDecl != null) { |
| // while creating argument bindings we also collect explicit null annotations: |
| if (method.parameters != Binding.NO_PARAMETERS) |
| methodDecl.createArgumentBindings(); |
| // add implicit annotations (inherited(?) & default): |
| if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) { |
| new ImplicitNullAnnotationVerifier(this.scope.environment()).checkImplicitNullAnnotations(method, methodDecl, true, this.scope); |
| } |
| } |
| } |
| |
| private void evaluateNullAnnotations(long annotationTagBits) { |
| |
| if (!isPrototype()) throw new IllegalStateException(); |
| |
| if (this.nullnessDefaultInitialized > 0 || !this.scope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) |
| return; |
| boolean isPackageInfo = CharOperation.equals(this.sourceName, TypeConstants.PACKAGE_INFO_NAME); |
| PackageBinding pkg = getPackage(); |
| boolean isInDefaultPkg = (pkg.compoundName == CharOperation.NO_CHAR_CHAR); |
| if (!isPackageInfo) { |
| boolean isInNullnessAnnotationPackage = |
| pkg == this.scope.environment().nonnullAnnotationPackage |
| || pkg == this.scope.environment().nullableAnnotationPackage |
| || pkg == this.scope.environment().nonnullByDefaultAnnotationPackage; |
| if (pkg.defaultNullness == NO_NULL_DEFAULT && !isInDefaultPkg && !isInNullnessAnnotationPackage && !(this instanceof NestedTypeBinding)) { |
| ReferenceBinding packageInfo = pkg.getType(TypeConstants.PACKAGE_INFO_NAME); |
| if (packageInfo == null) { |
| // no pkgInfo - complain |
| this.scope.problemReporter().missingNonNullByDefaultAnnotation(this.scope.referenceContext); |
| pkg.defaultNullness = NULL_UNSPECIFIED_BY_DEFAULT; |
| } else { |
| // if pkgInfo has no default annot. - complain |
| packageInfo.getAnnotationTagBits(); |
| } |
| } |
| } |
| this.nullnessDefaultInitialized = 1; |
| boolean isJdk18 = this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8; |
| if (isJdk18) { |
| if (this.defaultNullness != 0) { |
| if (isPackageInfo) { |
| pkg.defaultNullness = this.defaultNullness; |
| } else { |
| TypeDeclaration typeDecl = this.scope.referenceContext; |
| checkRedundantNullnessDefaultRecurse(typeDecl, typeDecl.annotations, this.defaultNullness, isJdk18); |
| } |
| } else if (isPackageInfo || (isInDefaultPkg && !(this instanceof NestedTypeBinding))) { |
| this.scope.problemReporter().missingNonNullByDefaultAnnotation(this.scope.referenceContext); |
| if (!isInDefaultPkg) |
| pkg.defaultNullness = NULL_UNSPECIFIED_BY_DEFAULT; |
| } |
| } else { |
| // transfer nullness info from tagBits to this.nullnessDefaultAnnotation |
| int newDefaultNullness = NO_NULL_DEFAULT; |
| if ((annotationTagBits & TagBits.AnnotationNullUnspecifiedByDefault) != 0) |
| newDefaultNullness = NULL_UNSPECIFIED_BY_DEFAULT; |
| else if ((annotationTagBits & TagBits.AnnotationNonNullByDefault) != 0) |
| newDefaultNullness = NONNULL_BY_DEFAULT; |
| if (newDefaultNullness != NO_NULL_DEFAULT) { |
| if (isPackageInfo) { |
| pkg.defaultNullness = newDefaultNullness; |
| } else { |
| this.defaultNullness = newDefaultNullness; |
| TypeDeclaration typeDecl = this.scope.referenceContext; |
| long nullDefaultBits = annotationTagBits & (TagBits.AnnotationNullUnspecifiedByDefault|TagBits.AnnotationNonNullByDefault); |
| checkRedundantNullnessDefaultRecurse(typeDecl, typeDecl.annotations, nullDefaultBits, false); |
| } |
| } else if (isPackageInfo || (isInDefaultPkg && !(this instanceof NestedTypeBinding))) { |
| this.scope.problemReporter().missingNonNullByDefaultAnnotation(this.scope.referenceContext); |
| if (!isInDefaultPkg) |
| pkg.defaultNullness = NULL_UNSPECIFIED_BY_DEFAULT; |
| } |
| } |
| maybeMarkTypeParametersNonNull(); |
| } |
| |
| private void maybeMarkTypeParametersNonNull() { |
| // when creating type variables we didn't yet have the defaultNullness, fill it in now: |
| if (this.scope == null || !this.scope.hasDefaultNullnessFor(DefaultLocationTypeParameter)) |
| return; |
| if (this.typeVariables != null && this.typeVariables.length > 0) { |
| AnnotationBinding[] annots = new AnnotationBinding[]{ this.environment.getNonNullAnnotation() }; |
| for (int i = 0; i < this.typeVariables.length; i++) { |
| TypeVariableBinding tvb = this.typeVariables[i]; |
| if ((tvb.tagBits & TagBits.AnnotationNullMASK) == 0) |
| this.typeVariables[i] = (TypeVariableBinding) this.environment.createAnnotatedType(tvb, annots); |
| } |
| } |
| } |
| |
| /** |
| * Recursively check if the given annotations are redundant with equal annotations at an enclosing level. |
| * @param location fallback location to report the warning against (if we can't blame a specific annotation) |
| * @param annotations search these for the annotation that should be blamed in warning messages |
| * @param nullBits in 1.7- times these are the annotationTagBits, in 1.8+ the bitvector from {@link Binding#NullnessDefaultMASK} |
| * @param isJdk18 toggles the interpretation of 'nullBits' |
| * |
| * @pre null annotation analysis is enabled |
| */ |
| protected void checkRedundantNullnessDefaultRecurse(ASTNode location, Annotation[] annotations, long nullBits, boolean isJdk18) { |
| |
| if (!isPrototype()) throw new IllegalStateException(); |
| |
| if (this.fPackage.defaultNullness != NO_NULL_DEFAULT) { |
| boolean isRedundant = isJdk18 |
| ? this.fPackage.defaultNullness == nullBits |
| : (this.fPackage.defaultNullness == NONNULL_BY_DEFAULT |
| && ((nullBits & TagBits.AnnotationNonNullByDefault) != 0)); |
| if (isRedundant) { |
| this.scope.problemReporter().nullDefaultAnnotationIsRedundant(location, annotations, this.fPackage); |
| } |
| return; |
| } |
| } |
| |
| // return: should caller continue searching? |
| protected boolean checkRedundantNullnessDefaultOne(ASTNode location, Annotation[] annotations, long nullBits, boolean isJdk18) { |
| |
| if (!isPrototype()) throw new IllegalStateException(); |
| |
| int thisDefault = getNullDefault(); |
| if (thisDefault != NO_NULL_DEFAULT) { |
| boolean isRedundant = isJdk18 |
| ? thisDefault == nullBits |
| : (nullBits & TagBits.AnnotationNonNullByDefault) != 0; |
| if (isRedundant) { |
| this.scope.problemReporter().nullDefaultAnnotationIsRedundant(location, annotations, this); |
| } |
| return false; // different default means inner default is not redundant -> we're done |
| } |
| return true; |
| } |
| |
| boolean hasNonNullDefaultFor(int location, boolean useTypeAnnotations) { |
| |
| if (!isPrototype()) throw new IllegalStateException(); |
| |
| // 1.8: |
| if (useTypeAnnotations) { |
| if (this.scope == null) { |
| return (this.defaultNullness & location) != 0; |
| } |
| return this.scope.hasDefaultNullnessFor(location); |
| } |
| |
| // find the applicable default inside->out: |
| |
| SourceTypeBinding currentType = null; |
| Scope currentScope = this.scope; |
| while (currentScope != null) { |
| switch (currentScope.kind) { |
| case Scope.METHOD_SCOPE: |
| AbstractMethodDeclaration referenceMethod = ((MethodScope)currentScope).referenceMethod(); |
| if (referenceMethod != null && referenceMethod.binding != null) { |
| long methodTagBits = referenceMethod.binding.tagBits; |
| if ((methodTagBits & TagBits.AnnotationNonNullByDefault) != 0) |
| return true; |
| if ((methodTagBits & TagBits.AnnotationNullUnspecifiedByDefault) != 0) |
| return false; |
| } |
| break; |
| case Scope.CLASS_SCOPE: |
| currentType = ((ClassScope)currentScope).referenceContext.binding; |
| if (currentType != null) { |
| int foundDefaultNullness = currentType.getNullDefault(); |
| if ((foundDefaultNullness & NullnessDefaultMASK) > NULL_UNSPECIFIED_BY_DEFAULT) { |
| return true; |
| } |
| if (foundDefaultNullness != NO_NULL_DEFAULT) { |
| return foundDefaultNullness == NONNULL_BY_DEFAULT; |
| } |
| } |
| break; |
| } |
| currentScope = currentScope.parent; |
| } |
| |
| // package |
| if (currentType != null) { |
| return currentType.getPackage().defaultNullness == NONNULL_BY_DEFAULT; |
| } |
| |
| return false; |
| } |
| |
| public AnnotationHolder retrieveAnnotationHolder(Binding binding, boolean forceInitialization) { |
| if (!isPrototype()) |
| return this.prototype.retrieveAnnotationHolder(binding, forceInitialization); |
| if (forceInitialization) |
| binding.getAnnotationTagBits(); // ensure annotations are up to date |
| return super.retrieveAnnotationHolder(binding, false); |
| } |
| |
| public void setContainerAnnotationType(ReferenceBinding value) { |
| if (!isPrototype()) throw new IllegalStateException(); |
| this.containerAnnotationType = value; |
| } |
| |
| public void tagAsHavingDefectiveContainerType() { |
| if (!isPrototype()) throw new IllegalStateException(); |
| if (this.containerAnnotationType != null && this.containerAnnotationType.isValidBinding()) |
| this.containerAnnotationType = new ProblemReferenceBinding(this.containerAnnotationType.compoundName, this.containerAnnotationType, ProblemReasons.DefectiveContainerAnnotationType); |
| } |
| |
| // Propagate writes to all annotated variants so the clones evolve along. |
| public FieldBinding [] setFields(FieldBinding[] fields) { |
| |
| if (!isPrototype()) |
| return this.prototype.setFields(fields); |
| |
| if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) { |
| TypeBinding [] annotatedTypes = this.scope.environment().getAnnotatedTypes(this); |
| for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { |
| SourceTypeBinding annotatedType = (SourceTypeBinding) annotatedTypes[i]; |
| annotatedType.fields = fields; |
| } |
| } |
| return this.fields = fields; |
| } |
| |
| // We need to specialize member types, can't just propagate. Can't specialize here, clones could created post setMemberTypes() |
| public ReferenceBinding [] setMemberTypes(ReferenceBinding[] memberTypes) { |
| |
| if (!isPrototype()) |
| return this.prototype.setMemberTypes(memberTypes); |
| |
| this.memberTypes = memberTypes; |
| if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) { |
| TypeBinding [] annotatedTypes = this.scope.environment().getAnnotatedTypes(this); |
| for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { |
| SourceTypeBinding annotatedType = (SourceTypeBinding) annotatedTypes[i]; |
| annotatedType.tagBits |= TagBits.HasUnresolvedMemberTypes; |
| annotatedType.memberTypes(); // recompute. |
| } |
| } |
| return this.memberTypes; |
| } |
| |
| // Propagate writes to all annotated variants so the clones evolve along. |
| public MethodBinding [] setMethods(MethodBinding[] methods) { |
| |
| if (!isPrototype()) |
| return this.prototype.setMethods(methods); |
| |
| if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) { |
| TypeBinding [] annotatedTypes = this.scope.environment().getAnnotatedTypes(this); |
| for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { |
| SourceTypeBinding annotatedType = (SourceTypeBinding) annotatedTypes[i]; |
| annotatedType.methods = methods; |
| } |
| } |
| return this.methods = methods; |
| } |
| |
| // Propagate writes to all annotated variants so the clones evolve along. |
| public ReferenceBinding setSuperClass(ReferenceBinding superClass) { |
| |
| if (!isPrototype()) |
| return this.prototype.setSuperClass(superClass); |
| |
| if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) { |
| TypeBinding [] annotatedTypes = this.scope.environment().getAnnotatedTypes(this); |
| for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { |
| SourceTypeBinding annotatedType = (SourceTypeBinding) annotatedTypes[i]; |
| annotatedType.superclass = superClass; |
| } |
| } |
| return this.superclass = superClass; |
| } |
| |
| // Propagate writes to all annotated variants so the clones evolve along. |
| public ReferenceBinding [] setSuperInterfaces(ReferenceBinding [] superInterfaces) { |
| |
| if (!isPrototype()) |
| return this.prototype.setSuperInterfaces(superInterfaces); |
| |
| if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) { |
| TypeBinding [] annotatedTypes = this.scope.environment().getAnnotatedTypes(this); |
| for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { |
| SourceTypeBinding annotatedType = (SourceTypeBinding) annotatedTypes[i]; |
| annotatedType.superInterfaces = superInterfaces; |
| } |
| } |
| return this.superInterfaces = superInterfaces; |
| } |
| |
| // Propagate writes to all annotated variants so the clones evolve along. |
| public TypeVariableBinding [] setTypeVariables(TypeVariableBinding [] typeVariables) { |
| |
| if (!isPrototype()) |
| return this.prototype.setTypeVariables(typeVariables); |
| |
| if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) { |
| TypeBinding [] annotatedTypes = this.scope.environment().getAnnotatedTypes(this); |
| for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { |
| SourceTypeBinding annotatedType = (SourceTypeBinding) annotatedTypes[i]; |
| annotatedType.typeVariables = typeVariables; |
| } |
| } |
| return this.typeVariables = typeVariables; |
| } |
| |
| public final int sourceEnd() { |
| if (!isPrototype()) |
| return this.prototype.sourceEnd(); |
| |
| return this.scope.referenceContext.sourceEnd; |
| } |
| public final int sourceStart() { |
| if (!isPrototype()) |
| return this.prototype.sourceStart(); |
| |
| return this.scope.referenceContext.sourceStart; |
| } |
| SimpleLookupTable storedAnnotations(boolean forceInitialize) { |
| if (!isPrototype()) |
| return this.prototype.storedAnnotations(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; |
| final CompilerOptions globalOptions = this.scope.environment().globalOptions; |
| if (!globalOptions.storeAnnotations) |
| return null; // not supported during this compile |
| this.storedAnnotations = new SimpleLookupTable(3); |
| } |
| return this.storedAnnotations; |
| } |
| |
| public ReferenceBinding superclass() { |
| if (!isPrototype()) |
| return this.superclass = this.prototype.superclass(); |
| return this.superclass; |
| } |
| |
| public ReferenceBinding[] superInterfaces() { |
| if (!isPrototype()) |
| return this.superInterfaces = this.prototype.superInterfaces(); |
| return this.superInterfaces != null ? this.superInterfaces : isAnnotationType() ? this.superInterfaces = new ReferenceBinding [] { this.scope.getJavaLangAnnotationAnnotation() } : null; |
| } |
| |
| public SyntheticMethodBinding[] syntheticMethods() { |
| |
| if (!isPrototype()) throw new IllegalStateException(); |
| |
| 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 methodArrayIterator = this.synthetics[SourceTypeBinding.METHOD_EMUL].values().iterator(); |
| while (methodArrayIterator.hasNext()) { |
| SyntheticMethodBinding[] methodAccessors = (SyntheticMethodBinding[]) methodArrayIterator.next(); |
| for (int i = 0, max = methodAccessors.length; i < max; i++) { |
| if (methodAccessors[i] != null) { |
| if (index+1 > bindings.length) { |
| System.arraycopy(bindings, 0, (bindings = new SyntheticMethodBinding[index + 1]), 0, index); |
| } |
| bindings[index++] = methodAccessors[i]; |
| } |
| } |
| } |
| // 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 (!isPrototype()) throw new IllegalStateException(); |
| |
| 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() { |
| if (this.hasTypeAnnotations()) { |
| return annotatedDebugName(); |
| } |
| |
| 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() { |
| if (!isPrototype()) |
| return this.typeVariables = this.prototype.typeVariables(); |
| return this.typeVariables != null ? this.typeVariables : Binding.NO_TYPE_VARIABLES; |
| } |
| void verifyMethods(MethodVerifier verifier) { |
| |
| if (!isPrototype()) throw new IllegalStateException(); |
| |
| verifier.verify(this); |
| |
| for (int i = this.memberTypes.length; --i >= 0;) |
| ((SourceTypeBinding) this.memberTypes[i]).verifyMethods(verifier); |
| } |
| |
| public TypeBinding unannotated() { |
| return this.prototype; |
| } |
| |
| public FieldBinding[] unResolvedFields() { |
| if (!isPrototype()) |
| return this.prototype.unResolvedFields(); |
| return this.fields; |
| } |
| |
| public void tagIndirectlyAccessibleMembers() { |
| if (!isPrototype()) { |
| this.prototype.tagIndirectlyAccessibleMembers(); |
| return; |
| } |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=328281 |
| for (int i = 0; i < this.fields.length; i++) { |
| if (!this.fields[i].isPrivate()) |
| this.fields[i].modifiers |= ExtraCompilerModifiers.AccLocallyUsed; |
| } |
| for (int i = 0; i < this.memberTypes.length; i++) { |
| if (!this.memberTypes[i].isPrivate()) |
| this.memberTypes[i].modifiers |= ExtraCompilerModifiers.AccLocallyUsed; |
| } |
| if (this.superclass.isPrivate()) |
| if (this.superclass instanceof SourceTypeBinding) // should always be true because private super type can only be accessed in same CU |
| ((SourceTypeBinding) this.superclass).tagIndirectlyAccessibleMembers(); |
| } |
| //AspectJ Extension |
| public void addField(FieldBinding binding) { |
| if (fields == null) { |
| fields = new FieldBinding[] {binding}; |
| } else { |
| //??? inefficient |
| ArrayList l = new ArrayList(Arrays.asList(fields)); |
| l.add(binding); |
| fields = (FieldBinding[])l.toArray(new FieldBinding[l.size()]); |
| } |
| } |
| public void addMethod(MethodBinding binding) { |
| int len = 1; |
| if (methods != null) len = methods.length + 1; |
| MethodBinding[] newMethods = new MethodBinding[len]; |
| if (len > 1) { |
| System.arraycopy(methods, 0, newMethods, 0, len-1); |
| } |
| newMethods[len-1] = binding; |
| methods = newMethods; |
| tagBits &= ~(TagBits.AreMethodsSorted | TagBits.AreMethodsComplete); // New AspectJ Extension |
| //System.out.println("bindings: " + Arrays.asList(methods)); |
| } |
| |
| public void removeMethod(int index) { |
| int len = methods.length; |
| MethodBinding[] newMethods = new MethodBinding[len-1]; |
| System.arraycopy(methods, 0, newMethods, 0, index); |
| System.arraycopy(methods, index+1, newMethods, index, len-index-1); |
| methods = newMethods; |
| } |
| |
| public void rememberTypeHierarchy() { |
| if (originalSuperclass==null) originalSuperclass = superclass; |
| if (originalSuperInterfaces==null) { |
| originalSuperInterfaces = new ReferenceBinding[superInterfaces.length]; |
| System.arraycopy(superInterfaces,0,originalSuperInterfaces,0,superInterfaces.length); |
| } |
| } |
| |
| public MethodBinding[] methods() { |
| if (memberFinder!=null) return memberFinder.methods(this); |
| else return methodsBase(); |
| } |
| |
| public ReferenceBinding getMemberType(char[] typeName) { |
| ReferenceBinding rb = super.getMemberType(typeName); |
| if (rb==null && typeFinder!=null) { |
| rb = typeFinder.getMemberType(typeName); |
| } |
| return rb; |
| } |
| //End AspectJ Extension |
| } |