| /******************************************************************************* |
| * Copyright (c) 2000, 2013 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * This is an implementation of an early-draft specification developed under the Java |
| * Community Process (JCP) and is made available for testing and evaluation purposes |
| * only. The code is not compatible with any specification of the JCP. |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Fraunhofer FIRST - extended API and implementation |
| * Technical University Berlin - extended 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 |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.lookup; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.CompilationResult.CheckPoint; |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Annotation; |
| import org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeParameter; |
| import org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.Expression.DecapsulationState; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.eclipse.jdt.internal.compiler.parser.Parser; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; |
| import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; |
| import org.eclipse.jdt.internal.compiler.util.SimpleSetOfCharArray; |
| import org.eclipse.jdt.internal.compiler.util.Util; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.LiftingTypeReference; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.RoleFileCache; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeAnchorReference; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeValueParameter; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.*; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.AnchorMapping; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticBaseCallSurrogate; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticRoleBridgeMethodBinding; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticRoleFieldAccess; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.*; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel.FakeKind; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.copyinheritance.CopyInheritance; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.MethodSignatureEnhancer; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.PredicateGenerator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.RoleMigrationImplementor; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.RoleSplitter; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer; |
| |
| /** |
| * OTDT changes: |
| * |
| * What: Team packages |
| * How: Set in constructor, use in getMemberType() to lookup role files. |
| * |
| * What: Creation of byte code attribute FieldTypeAnchor: |
| * How: Right after resolving the type of a field, if it is an externalized role, |
| * create a byte code attribute that stores the anchor path. |
| * |
| * What: Respect STATE_FINAL |
| * Why: In that state the scope is lost => some lookup cannot be performed any more. |
| * |
| * What: Let Dependencies control faultInTypesForFieldsAndMethods() |
| * How: Make it public, respect side effect (on demand role loading) |
| * |
| * What: Create type of base arg in base-guard. |
| * Why: During parsing the base type is not yet known. Fill in this |
| * information resolveTypesFor(MethodBinding) |
| * |
| * What: Resolve anchored types in arguments |
| * Why: Special treatment because arguments can be anchored to each other. |
| * |
| * What: resolveGeneratedMethod() lets a generated method catch up. |
| * |
| * What: Some more small changes... |
| * |
| */ |
| @SuppressWarnings("unchecked") |
| public class SourceTypeBinding extends ReferenceBinding { |
| public ReferenceBinding superclass; |
| public ReferenceBinding[] superInterfaces; |
| private FieldBinding[] fields; |
| private MethodBinding[] methods; |
| public ReferenceBinding[] memberTypes; |
| //{ObjectTeams: initialization added |
| // readableName() will otherwise not work before Scope.buildTypeVariables()! |
| public TypeVariableBinding[] typeVariables = Binding.NO_TYPE_VARIABLES; |
| // SH} |
| |
| public ClassScope scope; |
| |
| // 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; |
| //{ObjectTeams: these bridges are not to be found by addSyntheticMethod(MethodBinding,boolean), use only from callout: |
| private final static int ROLE_BRIDGE = 3; |
| |
| private final static int MAX_SYNTHETICS = 4; |
| /* orig: |
| private final static int MAX_SYNTHETICS = 3; |
| :giro */ |
| // SH} |
| |
| HashMap[] synthetics; |
| char[] genericReferenceTypeSignature; |
| |
| //{ObjectTeams: managing membertypes |
| public final static ReferenceBinding MultipleCasts = new SourceTypeBinding(); |
| static { |
| MultipleCasts.compoundName = new char[][] { "multiple".toCharArray(), "casts".toCharArray(), "required".toCharArray() }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| |
| protected SourceTypeBinding() { super(null); } // default ctor for Singleton membertypes NoBaseclass, ProblemBaseclass |
| |
| //Markus Witte} |
| |
| private SimpleLookupTable storedAnnotations = null; // keys are this ReferenceBinding & its fields and methods, value is an AnnotationHolder |
| |
| private int defaultNullness; |
| private int nullnessDefaultInitialized = 0; // 0: nothing; 1: type; 2: package |
| |
| public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassScope scope) { |
| //{ObjectTeams: // share model from TypeDeclaration: |
| super(scope.referenceContext.getModel()); |
| // SH} |
| this.compoundName = compoundName; |
| this.fPackage = fPackage; |
| this.fileName = scope.referenceCompilationUnit().getFileName(); |
| this.modifiers = scope.referenceContext.modifiers; |
| //{ObjectTeams: when compiling org.objectteams.Team: set flag 'team' |
| if (CharOperation.equals(compoundName, IOTConstants.ORG_OBJECTTEAMS_TEAM)) { |
| this.modifiers |= ClassFileConstants.AccTeam; |
| scope.referenceContext.modifiers |= ClassFileConstants.AccTeam; |
| } |
| // SH} |
| this.sourceName = scope.referenceContext.name; |
| this.scope = scope; |
| //{ObjectTeams: ROFI create a package binding for our role files (if any): |
| maybeSetTeamPackage(compoundName, fPackage, scope.environment()); |
| // SH} |
| |
| // expect the fields & methods to be initialized correctly later |
| this.fields = Binding.UNINITIALIZED_FIELDS; |
| this.methods = Binding.UNINITIALIZED_METHODS; |
| |
| computeId(); |
| } |
| |
| private void addDefaultAbstractMethods() { |
| if ((this.tagBits & TagBits.KnowsDefaultAbstractMethods) != 0) return; |
| |
| this.tagBits |= TagBits.KnowsDefaultAbstractMethods; |
| if (isClass() && isAbstract()) { |
| if (this.scope.compilerOptions().targetJDK >= ClassFileConstants.JDK1_2) |
| return; // no longer added for post 1.2 targets |
| |
| ReferenceBinding[] itsInterfaces = superInterfaces(); |
| if (itsInterfaces != Binding.NO_SUPERINTERFACES) { |
| MethodBinding[] defaultAbstracts = null; |
| int defaultAbstractsCount = 0; |
| ReferenceBinding[] interfacesToVisit = itsInterfaces; |
| int nextPosition = interfacesToVisit.length; |
| for (int i = 0; i < nextPosition; i++) { |
| ReferenceBinding superType = interfacesToVisit[i]; |
| if (superType.isValidBinding()) { |
| MethodBinding[] superMethods = superType.methods(); |
| nextAbstractMethod: for (int m = superMethods.length; --m >= 0;) { |
| MethodBinding method = superMethods[m]; |
| // explicitly implemented ? |
| if (implementsMethod(method)) |
| continue nextAbstractMethod; |
| if (defaultAbstractsCount == 0) { |
| defaultAbstracts = new MethodBinding[5]; |
| } else { |
| // already added as default abstract ? |
| for (int k = 0; k < defaultAbstractsCount; k++) { |
| MethodBinding alreadyAdded = defaultAbstracts[k]; |
| if (CharOperation.equals(alreadyAdded.selector, method.selector) && alreadyAdded.areParametersEqual(method)) |
| continue nextAbstractMethod; |
| } |
| } |
| MethodBinding defaultAbstract = new MethodBinding( |
| method.modifiers | ExtraCompilerModifiers.AccDefaultAbstract | ClassFileConstants.AccSynthetic, |
| method.selector, |
| method.returnType, |
| method.parameters, |
| method.thrownExceptions, |
| this); |
| if (defaultAbstractsCount == defaultAbstracts.length) |
| System.arraycopy(defaultAbstracts, 0, defaultAbstracts = new MethodBinding[2 * defaultAbstractsCount], 0, defaultAbstractsCount); |
| defaultAbstracts[defaultAbstractsCount++] = defaultAbstract; |
| } |
| |
| if ((itsInterfaces = superType.superInterfaces()) != Binding.NO_SUPERINTERFACES) { |
| int itsLength = itsInterfaces.length; |
| if (nextPosition + itsLength >= interfacesToVisit.length) |
| System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition); |
| nextInterface : for (int a = 0; a < itsLength; a++) { |
| ReferenceBinding next = itsInterfaces[a]; |
| for (int b = 0; b < nextPosition; b++) |
| if (next == interfacesToVisit[b]) continue nextInterface; |
| interfacesToVisit[nextPosition++] = next; |
| } |
| } |
| } |
| } |
| if (defaultAbstractsCount > 0) { |
| int length = this.methods.length; |
| System.arraycopy(this.methods, 0, this.methods = new MethodBinding[length + defaultAbstractsCount], 0, length); |
| System.arraycopy(defaultAbstracts, 0, this.methods, length, defaultAbstractsCount); |
| // re-sort methods |
| length = length + defaultAbstractsCount; |
| if (length > 1) |
| ReferenceBinding.sortMethods(this.methods, 0, length); |
| // this.tagBits |= TagBits.AreMethodsSorted; -- already set in #methods() |
| } |
| } |
| } |
| } |
| /* Add a new synthetic field for <actualOuterLocalVariable>. |
| * Answer the new field or the existing field if one already existed. |
| */ |
| public FieldBinding addSyntheticFieldForInnerclass(LocalVariableBinding actualOuterLocalVariable) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.FIELD_EMUL] = new HashMap(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 (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()); |
| |
| //{ObjectTeams: adjust modifiers? |
| // may need to access enclosing team of role across packages, changed Default to Public |
| if (enclosingType.isTeam()) |
| synthField.modifiers |= ClassFileConstants.AccPublic; |
| // for migratable roles this$n is not final! |
| if (isRole() && this.isCompatibleWith(this.scope.getOrgObjectteamsITeamMigratable())) { |
| synthField.modifiers &= ~ClassFileConstants.AccFinal; |
| // also, add the very method that leverages this non-finalness: |
| RoleMigrationImplementor.addMigrateToTeamMethod(this.scope.referenceContext); |
| } |
| // SH} |
| |
| 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; |
| } |
| |
| //{ObjectTeams: synthetic arguments for value parameters: |
| private SyntheticArgumentBinding[] valueParameters = NO_SYNTH_ARGUMENTS; |
| public SyntheticArgumentBinding[] valueParamSynthArgs() { |
| return this.valueParameters; |
| } |
| /* Add a new synthetic argument for <parameterType> (type of a value parameter). |
| */ |
| public void addSyntheticArgForValParam(TypeValueParameter param) { |
| ReferenceBinding parameterType = (ReferenceBinding)param.type.resolvedType; |
| SyntheticArgumentBinding synthLocal = null; |
| if (this.valueParameters == null) { |
| synthLocal = new SyntheticArgumentBinding(param.name, parameterType); |
| this.valueParameters = new SyntheticArgumentBinding[] {synthLocal}; |
| } else { |
| int size = this.valueParameters.length; |
| int newArgIndex = size; |
| for (int i = size; --i >= 0;) { |
| if (this.valueParameters[i].type == parameterType) |
| return; // already exists |
| if (enclosingType() == parameterType) |
| newArgIndex = 0; |
| } |
| SyntheticArgumentBinding[] newInstances = new SyntheticArgumentBinding[size + 1]; |
| System.arraycopy(this.valueParameters, 0, newInstances, newArgIndex == 0 ? 1 : 0, size); |
| newInstances[newArgIndex] = synthLocal = new SyntheticArgumentBinding(param.name, parameterType); |
| this.valueParameters = newInstances; |
| } |
| synthLocal.matchingField = param.fieldBinding; |
| } |
| |
| /** |
| * Compute the resolved positions for all the value parameter arguments |
| */ |
| final public void computeValueParameterSlotSizes() { |
| |
| int slotSize = 0; |
| // insert enclosing instances first, followed by the outerLocals |
| int enclosingInstancesCount = this.valueParameters == null ? 0 : this.valueParameters.length; |
| for (int i = 0; i < enclosingInstancesCount; i++){ |
| SyntheticArgumentBinding argument = this.valueParameters[i]; |
| // position the enclosing instance synthetic arg |
| argument.resolvedPosition = slotSize + 1; // shift by 1 to leave room for aload0==this |
| if (slotSize + 1 > 0xFF) { // no more than 255 words of arguments |
| this.scope.problemReporter().noMoreAvailableSpaceForArgument(argument, this.scope.referenceType()); |
| } |
| if ((argument.type == TypeBinding.LONG) || (argument.type == TypeBinding.DOUBLE)){ |
| slotSize += 2; |
| } else { |
| slotSize ++; |
| } |
| } |
| } |
| // SH} |
| |
| /* Add a new synthetic field for a class literal access. |
| * Answer the new field or the existing field if one already existed. |
| */ |
| public FieldBinding addSyntheticFieldForClassLiteral(TypeBinding targetType, BlockScope blockScope) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] == null) |
| this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] = new HashMap(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 (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 (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; |
| } |
| //{ObjectTeams: set copied synthetic field: |
| public void addCopiedSyntheticFied(FieldBinding field) { |
| if (CharOperation.prefixEquals(TypeConstants.SYNTHETIC_ENCLOSING_INSTANCE_PREFIX, field.name)) |
| { |
| int d = field.declaringClass.depth() - ((ReferenceBinding)field.type).depth(); |
| ReferenceBinding outer = enclosingType(); |
| while (d-- > 1) { |
| outer = outer.enclosingType(); |
| } |
| addSyntheticFieldForInnerclass(outer); |
| } else if (CharOperation.prefixEquals(TypeConstants.SYNTHETIC_CLASS, field.name)) |
| { |
| MethodScope dummyScope = new MethodScope(this.scope, null, false); |
| ReferenceBinding dummyType = new SourceTypeBinding(new char[0][0], null, this.scope); |
| FieldBinding newField = addSyntheticFieldForClassLiteral(dummyType, dummyScope); |
| this.roleModel.addSyntheticFieldMapping(field, newField); |
| } else if (CharOperation.prefixEquals(TypeConstants.SYNTHETIC_OUTER_LOCAL_PREFIX, field.name)) |
| { |
| char[] localName = CharOperation.subarray(field.name, TypeConstants.SYNTHETIC_OUTER_LOCAL_PREFIX.length, -1); |
| LocalVariableBinding local = new LocalVariableBinding(localName, field.type, field.modifiers, false/*isArgument*/); |
| SyntheticArgumentBinding synthArg = ((NestedTypeBinding)this).addSyntheticArgumentAndField(local); |
| FieldBinding newField = synthArg.matchingField; |
| this.roleModel.addSyntheticFieldMapping(field, newField); |
| } |
| } |
| //SH} |
| /* Add a new synthetic access method for read/write access to <targetField>. |
| Answer the new method or the existing method if one already existed. |
| */ |
| //{ObjectTeams: last parameter added: |
| /*orig: |
| public SyntheticMethodBinding addSyntheticMethod(FieldBinding targetField, boolean isReadAccess, boolean isSuperAccess) { |
| :giro*/ |
| public SyntheticMethodBinding addSyntheticMethod(FieldBinding targetField, boolean isReadAccess, boolean isSuperAccess, boolean externalizedReceiver) { |
| // SH} |
| 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); |
| |
| //{ObjectTeams: role field? |
| ReferenceBinding enclosingTeam = null; |
| if ( externalizedReceiver // must use role field accessor! |
| || targetField.declaringClass != this) // if declaringClass==this: use regular accessor |
| { |
| enclosingTeam = SyntheticRoleFieldAccess.getTeamOfRoleField(targetField); |
| } |
| if (enclosingTeam != null) { |
| if (externalizedReceiver |
| ? (this != enclosingTeam) // be strict for externalized access |
| : !this.isCompatibleWith(enclosingTeam)) // within instance scope compatibility suffices |
| { |
| // found a team but its not the current team. |
| // indirection: synth method to be fetched from another team: |
| SyntheticMethodBinding accessor = SyntheticRoleFieldAccess.getAccessorFor(enclosingTeam, targetField, isReadAccess, externalizedReceiver); |
| if (accessor != null) |
| return accessor; |
| else if (this.scope != null) |
| this.scope.problemReporter().missingAccessorInBinary(this, targetField); |
| else |
| throw new InternalCompilerError("Missing synthetic accessor for "+new String(targetField.name)); //$NON-NLS-1$ |
| } |
| } |
| // SH} |
| SyntheticMethodBinding accessMethod = null; |
| SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(targetField); |
| if (accessors == null) { |
| //{ObjectTeams: role field? |
| if (enclosingTeam != null) // this team is responsible |
| accessMethod = new SyntheticRoleFieldAccess(targetField, isReadAccess, this); |
| else |
| // orig: |
| accessMethod = new SyntheticMethodBinding(targetField, isReadAccess, isSuperAccess, this); |
| // SH} |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(targetField, accessors = new SyntheticMethodBinding[2]); |
| accessors[isReadAccess ? 0 : 1] = accessMethod; |
| } else { |
| if ((accessMethod = accessors[isReadAccess ? 0 : 1]) == null) { |
| //{ObjectTeams: role field? |
| if (enclosingTeam != null) // this team is responsible |
| accessMethod = new SyntheticRoleFieldAccess(targetField, isReadAccess, this); |
| else |
| // orig: |
| accessMethod = new SyntheticMethodBinding(targetField, isReadAccess, isSuperAccess, this); |
| // SH} |
| accessors[isReadAccess ? 0 : 1] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| /* Add a new synthetic method the enum type. Selector can either be 'values' or 'valueOf'. |
| * char[] constants from TypeConstants must be used: TypeConstants.VALUES/VALUEOF |
| */ |
| public SyntheticMethodBinding addSyntheticEnumMethod(char[] selector) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) |
| this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); |
| |
| SyntheticMethodBinding accessMethod = null; |
| SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(selector); |
| if (accessors == null) { |
| accessMethod = new SyntheticMethodBinding(this, selector); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(selector, accessors = new SyntheticMethodBinding[2]); |
| accessors[0] = accessMethod; |
| } else { |
| if ((accessMethod = accessors[0]) == null) { |
| accessMethod = new SyntheticMethodBinding(this, selector); |
| accessors[0] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| /* |
| * Add a synthetic field to handle the cache of the switch translation table for the corresponding enum type |
| */ |
| public SyntheticFieldBinding addSyntheticFieldForSwitchEnum(char[] fieldName, String key) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[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 (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 (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; |
| } |
| /* 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) { |
| //{ObjectTeams: role interfaces: use role class instead |
| if (isSynthInterface()) { |
| SourceTypeBinding classPart = (SourceTypeBinding)this.roleModel.getClassPartBinding(); |
| return classPart.addSyntheticMethod( |
| new MethodBinding(targetMethod, classPart), |
| isSuperAccess); |
| } |
| // SH} |
| 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; |
| } |
| //{ObjectTeams: add OT-specific synthetic bridges: |
| // (mostly empty) basecall surrogate |
| public SyntheticMethodBinding addSyntheticBaseCallSurrogate(MethodBinding callinMethod) { |
| 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(callinMethod); |
| if (accessors == null) { |
| accessMethod = new SyntheticBaseCallSurrogate(callinMethod, this); |
| this.synthetics[SourceTypeBinding.METHOD_EMUL].put(callinMethod, accessors = new SyntheticMethodBinding[2]); |
| accessors[1/*not super*/] = accessMethod; |
| } else { |
| if ((accessMethod = accessors[1]) == null) { |
| accessMethod = new SyntheticBaseCallSurrogate(callinMethod, this); |
| accessors[1] = accessMethod; |
| } |
| } |
| return accessMethod; |
| } |
| // bridges towards a private role method |
| public SyntheticMethodBinding addSyntheticRoleMethodBridge(SourceTypeBinding declaringRole, ReferenceBinding originalRole, MethodBinding targetMethod, int bridgeKind) { |
| if (this.synthetics == null) |
| this.synthetics = new HashMap[MAX_SYNTHETICS]; |
| if (this.synthetics[SourceTypeBinding.ROLE_BRIDGE] == null) |
| this.synthetics[SourceTypeBinding.ROLE_BRIDGE] = new HashMap(5); |
| |
| SyntheticMethodBinding accessMethod = (SyntheticMethodBinding) this.synthetics[SourceTypeBinding.ROLE_BRIDGE].get(targetMethod); |
| if (accessMethod == null) { |
| accessMethod = new SyntheticRoleBridgeMethodBinding(declaringRole, originalRole, targetMethod, bridgeKind); |
| this.synthetics[SourceTypeBinding.ROLE_BRIDGE].put(targetMethod, accessMethod); |
| } |
| return accessMethod; |
| } |
| // and retrieve an existing accessor: |
| public SyntheticMethodBinding findOuterRoleMethodSyntheticAccessor(MethodBinding targetMethod) { |
| SyntheticMethodBinding accessor; |
| if (this.synthetics != null && this.synthetics[ROLE_BRIDGE] != null) { |
| accessor = (SyntheticMethodBinding) this.synthetics[ROLE_BRIDGE].get(targetMethod); |
| if (accessor != null) { |
| if (accessor.isStatic() && isRole()) |
| return ((SourceTypeBinding)enclosingType()).findOuterRoleMethodSyntheticAccessor(accessor); |
| else |
| return accessor; |
| } |
| } |
| if (MethodModel.isFakedMethod(targetMethod, FakeKind.BASE_FIELD_ACCESSOR)) |
| return ((SourceTypeBinding)enclosingType()).findOuterRoleMethodSyntheticAccessor(targetMethod); |
| return null; |
| } |
| // SH} |
| /* |
| * Record the fact that bridge methods need to be generated to override certain inherited methods |
| */ |
| public SyntheticMethodBinding addSyntheticBridgeMethod(MethodBinding inheritedMethodToBridge, MethodBinding targetMethod) { |
| if (isInterface()) return null; // only classes & enums get bridge methods |
| // targetMethod may be inherited |
| //{ObjectTeams: retrieve callin method's real return type: |
| TypeBinding inheritedReturn= MethodModel.getReturnType(inheritedMethodToBridge); |
| TypeBinding targetReturn= MethodModel.getReturnType(targetMethod); |
| if (inheritedReturn.erasure() == targetReturn.erasure() |
| /* orig: |
| if (inheritedMethodToBridge.returnType.erasure() == targetMethod.returnType.erasure() |
| :giro */ |
| // SH} |
| && 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) |
| && 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) |
| */ |
| public SyntheticMethodBinding addSyntheticBridgeMethod(MethodBinding inheritedMethodToBridge) { |
| 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) |
| && 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() { |
| return this.fields != Binding.UNINITIALIZED_FIELDS; |
| } |
| boolean areMethodsInitialized() { |
| return this.methods != Binding.UNINITIALIZED_METHODS; |
| } |
| public int kind() { |
| if (this.typeVariables != Binding.NO_TYPE_VARIABLES) return Binding.GENERIC_TYPE; |
| return Binding.TYPE; |
| } |
| |
| public char[] computeUniqueKey(boolean isLeaf) { |
| //{ObjectTeams: don't use __OT__R names for keys (TODO(SH): nested roles?) |
| if (isRole()) { |
| if (isClass()) { |
| ReferenceBinding realType = getRealType(); |
| if (realType != null && realType != this) |
| return realType.computeUniqueKey(isLeaf); |
| } |
| // role file is also a main type |
| if (this.roleModel.isRoleFile()) |
| return super.computeUniqueKey(isLeaf); |
| } |
| // SH} |
| 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; |
| } |
| |
| //{ObjectTeams: allow access from Dependencies: |
| public |
| // SH} |
| void faultInTypesForFieldsAndMethods() { |
| // 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(); |
| |
| //{ObjectTeams: do not cache memberTypes.length! |
| // During faultInTypesForFieldsAndMethods(), memberTypes may be added (role files, on demand) |
| // process those new members as well. |
| /* orig: |
| for (int i = 0, length = this.memberTypes.length; i < length; i++) |
| :giro */ |
| for (int i = 0; i < this.memberTypes.length; i++) |
| if (!this.memberTypes[i].isBinaryBinding()) // roles could be binary contained in source |
| //carp} |
| ((SourceTypeBinding) this.memberTypes[i]).faultInTypesForFieldsAndMethods(); |
| } |
| // NOTE: the type of each field of a source type is resolved when needed |
| public FieldBinding[] fields() { |
| if ((this.tagBits & TagBits.AreFieldsComplete) != 0) |
| return this.fields; |
| |
| int failed = 0; |
| FieldBinding[] resolvedFields = this.fields; |
| try { |
| // lazily sort fields |
| if ((this.tagBits & TagBits.AreFieldsSorted) == 0) { |
| int length = this.fields.length; |
| if (length > 1) |
| ReferenceBinding.sortFields(this.fields, 0, length); |
| this.tagBits |= TagBits.AreFieldsSorted; |
| } |
| //{ObjectTeams: don't cache length, may shrink in re-entrant executions |
| /*orig |
| for (int i = 0, length = this.fields.length; i < length; i++) { |
| */ |
| for (int i = 0; i < this.fields.length; i++) { |
| // check discouraged field in @Instantation(ALWAYS) roles: |
| if ( this.scope != null |
| && (this.tagBits & TagBits.AnnotationInstantiation) != 0 |
| && !this.fields[i].isStatic()) |
| this.scope.problemReporter().fieldInRoleWithInstantiationPolicy(this, this.fields[i]); |
| |
| // after compilation is finished we have no scope, can't resolve any better |
| // resolveTypeFor would NPE! |
| int length = this.fields.length; |
| if ( this.model!=null |
| && this.model.getState() == ITranslationStates.STATE_FINAL |
| && this.fields[i].type == null) |
| { |
| this.fields[i] = null; |
| failed++; |
| } else |
| //SH} |
| if (resolveTypeFor(this.fields[i]) == null) { |
| // do not alter original field array until resolution is over, due to reentrance (143259) |
| if (resolvedFields == this.fields) { |
| System.arraycopy(this.fields, 0, resolvedFields = new FieldBinding[length], 0, length); |
| } |
| resolvedFields[i] = null; |
| failed++; |
| } |
| } |
| } finally { |
| if (failed > 0) { |
| // ensure fields are consistent reqardless of the error |
| int newSize = resolvedFields.length - failed; |
| if (newSize == 0) |
| return this.fields = Binding.NO_FIELDS; |
| |
| FieldBinding[] newFields = new FieldBinding[newSize]; |
| for (int i = 0, j = 0, length = resolvedFields.length; i < length; i++) { |
| if (resolvedFields[i] != null) |
| newFields[j++] = resolvedFields[i]; |
| } |
| this.fields = newFields; |
| } |
| } |
| this.tagBits |= TagBits.AreFieldsComplete; |
| return this.fields; |
| } |
| /** |
| * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#genericTypeSignature() |
| */ |
| public char[] genericTypeSignature() { |
| if (this.genericReferenceTypeSignature == null) |
| this.genericReferenceTypeSignature = computeGenericTypeSignature(this.typeVariables); |
| return this.genericReferenceTypeSignature; |
| } |
| /** |
| * <param1 ... paramN>superclass superinterface1 ... superinterfaceN |
| * <T:LY<TT;>;U:Ljava/lang/Object;V::Ljava/lang/Runnable;:Ljava/lang/Cloneable;:Ljava/util/Map;>Ljava/lang/Exception;Ljava/lang/Runnable; |
| */ |
| public char[] genericSignature() { |
| StringBuffer sig = null; |
| if (this.typeVariables != Binding.NO_TYPE_VARIABLES) { |
| sig = new StringBuffer(10); |
| sig.append('<'); |
| for (int i = 0, length = this.typeVariables.length; i < length; i++) |
| sig.append(this.typeVariables[i].genericSignature()); |
| sig.append('>'); |
| } else { |
| // could still need a signature if any of supertypes is parameterized |
| noSignature: if (this.superclass == null || !this.superclass.isParameterizedType()) { |
| for (int i = 0, length = this.superInterfaces.length; i < length; i++) |
| if (this.superInterfaces[i].isParameterizedType()) |
| break noSignature; |
| return null; |
| } |
| sig = new StringBuffer(10); |
| } |
| if (this.superclass != null) |
| sig.append(this.superclass.genericTypeSignature()); |
| else // interface scenario only (as Object cannot be generic) - 65953 |
| sig.append(this.scope.getJavaLangObject().genericTypeSignature()); |
| for (int i = 0, length = this.superInterfaces.length; i < length; i++) |
| sig.append(this.superInterfaces[i].genericTypeSignature()); |
| return sig.toString().toCharArray(); |
| } |
| |
| /** |
| * Compute the tagbits for standard annotations. For source types, these could require |
| * lazily resolving corresponding annotation nodes, in case of forward references. |
| * @see org.eclipse.jdt.internal.compiler.lookup.Binding#getAnnotationTagBits() |
| */ |
| public long getAnnotationTagBits() { |
| if ((this.tagBits & TagBits.AnnotationResolved) == 0 && this.scope != null) { |
| TypeDeclaration typeDecl = this.scope.referenceContext; |
| boolean old = typeDecl.staticInitializerScope.insideTypeAnnotation; |
| try { |
| typeDecl.staticInitializerScope.insideTypeAnnotation = true; |
| ASTNode.resolveAnnotations(typeDecl.staticInitializerScope, typeDecl.annotations, this); |
| } finally { |
| typeDecl.staticInitializerScope.insideTypeAnnotation = old; |
| } |
| if ((this.tagBits & TagBits.AnnotationDeprecated) != 0) |
| this.modifiers |= ClassFileConstants.AccDeprecated; |
| //{ObjectTeams: @Instantation can only be applied to role classes |
| if ( (this.tagBits & TagBits.AnnotationInstantiation) != 0 |
| && (!isRole() || isInterface())) |
| this.scope.problemReporter().instantiationAnnotationInNonRole(typeDecl); |
| // SH} |
| evaluateNullAnnotations(this.tagBits); |
| } |
| return this.tagBits; |
| } |
| public MethodBinding[] getDefaultAbstractMethods() { |
| int count = 0; |
| for (int i = this.methods.length; --i >= 0;) |
| if (this.methods[i].isDefaultAbstract()) |
| count++; |
| if (count == 0) return Binding.NO_METHODS; |
| |
| MethodBinding[] result = new MethodBinding[count]; |
| count = 0; |
| for (int i = this.methods.length; --i >= 0;) |
| if (this.methods[i].isDefaultAbstract()) |
| result[count++] = this.methods[i]; |
| return result; |
| } |
| // NOTE: the return type, arg & exception types of each method of a source type are resolved when needed |
| public MethodBinding getExactConstructor(TypeBinding[] argumentTypes) { |
| int argCount = argumentTypes.length; |
| if ((this.tagBits & TagBits.AreMethodsComplete) != 0) { // have resolved all arg types & return type of the methods |
| long range; |
| if ((range = ReferenceBinding.binarySearch(TypeConstants.INIT, this.methods)) >= 0) { |
| nextMethod: for (int imethod = (int)range, end = (int)(range >> 32); imethod <= end; imethod++) { |
| MethodBinding method = this.methods[imethod]; |
| if (method.parameters.length == argCount) { |
| TypeBinding[] toMatch = method.parameters; |
| for (int iarg = 0; iarg < argCount; iarg++) |
| //{ObjectTeams: weaker form of equality: |
| /* orig: |
| if (toMatch[iarg] != argumentTypes[iarg]) |
| :giro*/ |
| if (!AnchorMapping.areTypesEqual(toMatch[iarg], argumentTypes[iarg], method)) |
| // SH} |
| continue nextMethod; |
| return method; |
| } |
| } |
| } |
| } else { |
| // lazily sort methods |
| if ((this.tagBits & TagBits.AreMethodsSorted) == 0) { |
| int length = this.methods.length; |
| if (length > 1) |
| ReferenceBinding.sortMethods(this.methods, 0, length); |
| this.tagBits |= TagBits.AreMethodsSorted; |
| } |
| long range; |
| if ((range = ReferenceBinding.binarySearch(TypeConstants.INIT, this.methods)) >= 0) { |
| nextMethod: for (int imethod = (int)range, end = (int)(range >> 32); imethod <= end; imethod++) { |
| MethodBinding method = this.methods[imethod]; |
| if (resolveTypesFor(method) == null || method.returnType == null) { |
| methods(); |
| return getExactConstructor(argumentTypes); // try again since the problem methods have been removed |
| } |
| if (method.parameters.length == argCount) { |
| TypeBinding[] toMatch = method.parameters; |
| for (int iarg = 0; iarg < argCount; iarg++) |
| if (toMatch[iarg] != argumentTypes[iarg]) |
| continue nextMethod; |
| return method; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| //NOTE: the return type, arg & exception types of each method of a source type are resolved when needed |
| //searches up the hierarchy as long as no potential (but not exact) match was found. |
| public MethodBinding getExactMethod(char[] selector, TypeBinding[] argumentTypes, CompilationUnitScope refScope) { |
| // sender from refScope calls recordTypeReference(this) |
| int argCount = argumentTypes.length; |
| boolean foundNothing = true; |
| |
| if ((this.tagBits & TagBits.AreMethodsComplete) != 0) { // have resolved all arg types & return type of the methods |
| long range; |
| if ((range = ReferenceBinding.binarySearch(selector, this.methods)) >= 0) { |
| nextMethod: for (int imethod = (int)range, end = (int)(range >> 32); imethod <= end; imethod++) { |
| MethodBinding method = this.methods[imethod]; |
| foundNothing = false; // inner type lookups must know that a method with this name exists |
| if (method.parameters.length == argCount) { |
| TypeBinding[] toMatch = method.parameters; |
| for (int iarg = 0; iarg < argCount; iarg++) |
| //{ObjectTeams: weaker form of equality: |
| /* orig: |
| if (toMatch[iarg] != argumentTypes[iarg]) |
| :giro*/ |
| if (!AnchorMapping.areTypesEqual(toMatch[iarg], argumentTypes[iarg], method)) |
| // SH} |
| 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++) |
| //{ObjectTeams: weaker form of equality: |
| /* orig: |
| if (toMatch[iarg] != argumentTypes[iarg]) |
| :giro*/ |
| if (!AnchorMapping.areTypesEqual(toMatch[iarg], argumentTypes[iarg], method)) |
| // SH} |
| continue nextMethod; |
| return method; |
| } |
| } |
| } |
| } |
| |
| if (foundNothing) { |
| if (isInterface()) { |
| if (this.superInterfaces.length == 1) { |
| if (refScope != null) |
| refScope.recordTypeReference(this.superInterfaces[0]); |
| return this.superInterfaces[0].getExactMethod(selector, argumentTypes, refScope); |
| } |
| } else if (this.superclass != null) { |
| if (refScope != null) |
| refScope.recordTypeReference(this.superclass); |
| return this.superclass.getExactMethod(selector, argumentTypes, refScope); |
| } |
| } |
| return null; |
| } |
| |
| //NOTE: the type of a field of a source type is resolved when needed |
| public FieldBinding getField(char[] fieldName, boolean needResolve) { |
| //{ObjectTeams: could be called before fields are present |
| // (from ClassScope.findSupertype if supertyperef is a QualifiedTypeReference), |
| // can't find any fields, but mustn't prematurely set NO_FIELDS. |
| if (this.fields == Binding.UNINITIALIZED_FIELDS) |
| return null; |
| // SH} |
| |
| 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; |
| } |
| //{ObjectTeams: special case introduced by re-entrance of fields() et al: |
| // be especially careful while fields are sorted but not yet complete: |
| // may have nulls in this.fields, therefor cannot use binarySearch |
| else { |
| FieldBinding field = null; |
| FieldBinding result = null; |
| try { |
| for (FieldBinding f : this.fields) { |
| if (f != null && CharOperation.equals(f.name, fieldName)) { |
| field = f; |
| result = resolveTypeFor(f); |
| return result; |
| } |
| } |
| } finally { |
| // copied from below with adaptations: |
| // SH: for generated fields we must not assume that the field actually exists in fields. |
| if (result == null) { |
| // ensure fields are consistent reqardless of the error |
| int newSize = this.fields.length - 1; |
| if (newSize < 0) { // don't delete before finding |
| this.fields = Binding.NO_FIELDS; |
| } else { |
| FieldBinding[] newFields = new FieldBinding[newSize]; |
| int index = 0; |
| for (int i = 0, length = this.fields.length; i < length; i++) { |
| FieldBinding f = this.fields[i]; |
| if (f == field) continue; |
| if (index == newSize) return result; // already full => none deleted |
| newFields[index++] = f; |
| } |
| this.fields = newFields; |
| } |
| } |
| } |
| return null; |
| } |
| // SH} |
| // always resolve anyway on source types |
| FieldBinding field = ReferenceBinding.binarySearch(fieldName, this.fields); |
| if (field != null) { |
| FieldBinding result = null; |
| try { |
| result = resolveTypeFor(field); |
| return result; |
| } finally { |
| if (result == null) { |
| // ensure fields are consistent reqardless of the error |
| int newSize = this.fields.length - 1; |
| if (newSize == 0) { |
| this.fields = Binding.NO_FIELDS; |
| } else { |
| FieldBinding[] newFields = new FieldBinding[newSize]; |
| int index = 0; |
| for (int i = 0, length = this.fields.length; i < length; i++) { |
| FieldBinding f = this.fields[i]; |
| if (f == field) continue; |
| newFields[index++] = f; |
| } |
| this.fields = newFields; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| // NOTE: the return type, arg & exception types of each method of a source type are resolved when needed |
| public MethodBinding[] getMethods(char[] selector) { |
| if ((this.tagBits & TagBits.AreMethodsComplete) != 0) { |
| long range; |
| if ((range = ReferenceBinding.binarySearch(selector, this.methods)) >= 0) { |
| int start = (int) range, end = (int) (range >> 32); |
| int length = end - start + 1; |
| MethodBinding[] result; |
| System.arraycopy(this.methods, start, result = new MethodBinding[length], 0, length); |
| return result; |
| } else { |
| return Binding.NO_METHODS; |
| } |
| } |
| // lazily sort methods |
| if ((this.tagBits & TagBits.AreMethodsSorted) == 0) { |
| int length = this.methods.length; |
| if (length > 1) |
| ReferenceBinding.sortMethods(this.methods, 0, length); |
| this.tagBits |= TagBits.AreMethodsSorted; |
| } |
| MethodBinding[] result; |
| long range; |
| if ((range = ReferenceBinding.binarySearch(selector, this.methods)) >= 0) { |
| int start = (int) range, end = (int) (range >> 32); |
| for (int i = start; i <= end; i++) { |
| MethodBinding method = this.methods[i]; |
| if (resolveTypesFor(method) == null || method.returnType == null) { |
| methods(); |
| return getMethods(selector); // try again since the problem methods have been removed |
| } |
| } |
| int length = end - start + 1; |
| System.arraycopy(this.methods, start, result = new MethodBinding[length], 0, length); |
| } else { |
| return Binding.NO_METHODS; |
| } |
| boolean isSource15 = this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; |
| for (int i = 0, length = result.length - 1; i < length; i++) { |
| MethodBinding method = result[i]; |
| for (int j = length; j > i; j--) { |
| boolean paramsMatch = isSource15 |
| ? method.areParameterErasuresEqual(result[j]) |
| : method.areParametersEqual(result[j]); |
| if (paramsMatch) { |
| methods(); |
| return getMethods(selector); // try again since the duplicate methods have been removed |
| } |
| } |
| } |
| return result; |
| } |
| //{ObjectTeams: ROFI if it is a team, the member might be a role file still to be found: |
| public ReferenceBinding getMemberType(char[] name) { |
| if (this.notFoundMemberNames != null && this.notFoundMemberNames.includes(name)) |
| return null; |
| |
| ReferenceBinding result = super.getMemberType(name); |
| if (result != null) { |
| // is it a member in a different file? |
| if ( result.enclosingType() == this |
| && isTeam() |
| && !CharOperation.equals(result.getFileName(), this.fileName) |
| && !RoleFileCache.isRoFiCache(result)) // don't record the cache itself |
| { |
| // record this role file: |
| getTeamModel().addKnownRoleFile(name, result); |
| } |
| return result; |
| } |
| if (isTeam() && !StateHelper.hasState(this, ITranslationStates.STATE_LENV_CONNECT_TYPE_HIERARCHY)) { |
| if (StateHelper.isReadyToProcess(this, ITranslationStates.STATE_LENV_CONNECT_TYPE_HIERARCHY)) { |
| if ( Dependencies.ensureBindingState(this, ITranslationStates.STATE_LENV_CONNECT_TYPE_HIERARCHY) |
| && StateHelper.hasState(this, ITranslationStates.STATE_LENV_CONNECT_TYPE_HIERARCHY)) |
| { |
| ReferenceBinding member = getMemberType(name); // try again for copied roles. |
| if (member != null && (member.tagBits & TagBits.HasMissingType) == 0) |
| return member; |
| } |
| } |
| ReferenceBinding member = CopyInheritance.checkCopyLateRoleFile(this, name); |
| if (member != null) |
| return member; |
| } |
| if (isTeam() && this.teamPackage != null) { |
| ReferenceBinding member = findTypeInTeamPackage(name); |
| if (member != null) |
| return member; |
| } |
| if (StateHelper.hasState(this, ITranslationStates.STATE_LENV_CONNECT_TYPE_HIERARCHY)) { |
| if (this.notFoundMemberNames == null) |
| this.notFoundMemberNames = new SimpleSetOfCharArray(); |
| this.notFoundMemberNames.add(name); |
| } |
| return null; |
| } |
| SimpleSetOfCharArray notFoundMemberNames; |
| |
| ReferenceBinding findTypeInTeamPackage(char[] name) { |
| |
| ReferenceBinding result = null; |
| if (this.teamPackage != null) { |
| // we might be called without setting up Dependencies, i.e. when resolving the DOM AST |
| // TODO (carp): what else can we do instead of giving up? See TPX171 |
| if (!Dependencies.isSetup()) |
| return null; |
| |
| TeamModel teamModel = getTeamModel(); |
| |
| // need to set Parser.currentTeam, for enclosingType to be set! |
| TypeDeclaration previousTeam = null; |
| Parser parser = Config.getParser(); |
| if (parser != null) { |
| previousTeam = parser.currentTeam; |
| parser.currentTeam = this._teamModel.getAst(); |
| } |
| |
| // indirect recursive calls should not translated newly loaded roles: |
| boolean oldFlag = teamModel._blockCatchup; |
| teamModel._blockCatchup = true; |
| try { |
| // if name contains __OT__ trigger loading using the source name: |
| if (RoleSplitter.isClassPartName(name)) { |
| char[] srcName = RoleSplitter.getInterfacePartName(name); |
| if (super.getMemberType(srcName) == null) |
| this.teamPackage.getRoleType(srcName); // discard result, triggering was all we wanted. |
| result = ((PackageBinding)this.teamPackage).getType0(name); |
| // If role is source, the above getRoleType should have triggered |
| // loading of class part, so that getType0() return a valid type. |
| // If role is binary, class part must be requested separately below. |
| } |
| if (result == null || result == LookupEnvironment.TheNotFoundType) { |
| // this might trigger Compiler.accept -> parse and start processing! |
| result = this.teamPackage.getRoleType(name); |
| } |
| } finally { |
| // end critical section which might load new role file. |
| teamModel._blockCatchup = oldFlag; |
| } |
| |
| if (parser != null) |
| parser.currentTeam = previousTeam; |
| if ( result != null |
| && result.isValidBinding()) |
| { |
| if (result.isRole()) { |
| // record the role file: |
| teamModel.addKnownRoleFile(name, result); |
| |
| if ( result instanceof BinaryTypeBinding // already processed |
| || teamModel._blockCatchup) // process later |
| return result; |
| |
| |
| // newly accepted role must catch up! |
| Dependencies.lateRolesCatchup(teamModel); |
| |
| // TODO(SH): enable when needed ;-) [ tests currently pass without ] |
| // if (teamModel.liftingEnv != null) |
| // teamModel.liftingEnv.init(teamModel.getAst()); |
| } |
| } else if (teamModel.getState() >= ITranslationStates.STATE_LENV_CONNECT_TYPE_HIERARCHY) { |
| TypeDeclaration roleDecl = CopyInheritance.internalCheckCopyLateRoleFile(this, name); |
| if (roleDecl != null) |
| return roleDecl.binding; |
| } |
| } |
| return result; |
| } |
| //SH} |
| /* Answer the synthetic field for <actualOuterLocalVariable> |
| * or null if one does not exist. |
| */ |
| public FieldBinding getSyntheticField(LocalVariableBinding actualOuterLocalVariable) { |
| if (this.synthetics == null || this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) return null; |
| return (FieldBinding) this.synthetics[SourceTypeBinding.FIELD_EMUL].get(actualOuterLocalVariable); |
| } |
| //{ObjectTeams: retrieve synthetic field by its name |
| /** synthetic field for outer local */ |
| public FieldBinding getSyntheticOuterLocal(char[] fieldName) { |
| if (this.synthetics == null || this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) return null; |
| HashMap outerLocalMap = this.synthetics[SourceTypeBinding.FIELD_EMUL]; |
| for(Object element : outerLocalMap.values()) { |
| FieldBinding field = (FieldBinding)element; |
| if (CharOperation.equals(field.name, fieldName)) |
| return field; |
| } |
| return null; |
| } |
| /** synthetic field for class literal */ |
| public FieldBinding getSyntheticClassLiteral(char[] fieldName) { |
| if (this.synthetics == null || this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] == null) return null; |
| HashMap classLiteralMap = this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL]; |
| for(Object element : classLiteralMap.values()) { |
| FieldBinding field = (FieldBinding)element; |
| if (CharOperation.equals(field.name, fieldName)) |
| return field; |
| } |
| return null; |
| } |
| // SH} |
| /* Answer the synthetic field for <targetEnclosingType> |
| * or null if one does not exist. |
| */ |
| public FieldBinding getSyntheticField(ReferenceBinding targetEnclosingType, boolean onlyExactMatch) { |
| |
| if (this.synthetics == null || this.synthetics[SourceTypeBinding.FIELD_EMUL] == null) return null; |
| FieldBinding field = (FieldBinding) this.synthetics[SourceTypeBinding.FIELD_EMUL].get(targetEnclosingType); |
| if (field != null) return field; |
| |
| // type compatibility : to handle cases such as |
| // class T { class M{}} |
| // class S extends T { class N extends M {}} --> need to use S as a default enclosing instance for the super constructor call in N(). |
| if (!onlyExactMatch){ |
| Iterator accessFields = this.synthetics[SourceTypeBinding.FIELD_EMUL].values().iterator(); |
| while (accessFields.hasNext()) { |
| field = (FieldBinding) accessFields.next(); |
| if (CharOperation.prefixEquals(TypeConstants.SYNTHETIC_ENCLOSING_INSTANCE_PREFIX, field.name) |
| && field.type.findSuperTypeOriginatingFrom(targetEnclosingType) != null) |
| return field; |
| } |
| } |
| return null; |
| } |
| /* |
| * Answer the bridge method associated for an inherited methods or null if one does not exist |
| */ |
| public SyntheticMethodBinding getSyntheticBridgeMethod(MethodBinding inheritedMethodToBridge) { |
| if (this.synthetics == null) return null; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) return null; |
| SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(inheritedMethodToBridge); |
| if (accessors == null) return null; |
| return accessors[1]; |
| } |
| |
| public boolean hasTypeBit(int bit) { |
| // source types initialize type bits during connectSuperclass/interfaces() |
| return (this.typeBits & bit) != 0; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.internal.compiler.lookup.Binding#initializeDeprecatedAnnotationTagBits() |
| */ |
| public void initializeDeprecatedAnnotationTagBits() { |
| if ((this.tagBits & TagBits.DeprecatedAnnotationResolved) == 0) { |
| TypeDeclaration typeDecl = this.scope.referenceContext; |
| boolean old = typeDecl.staticInitializerScope.insideTypeAnnotation; |
| try { |
| typeDecl.staticInitializerScope.insideTypeAnnotation = true; |
| ASTNode.resolveDeprecatedAnnotations(typeDecl.staticInitializerScope, typeDecl.annotations, this); |
| this.tagBits |= TagBits.DeprecatedAnnotationResolved; |
| } finally { |
| typeDecl.staticInitializerScope.insideTypeAnnotation = old; |
| } |
| if ((this.tagBits & TagBits.AnnotationDeprecated) != 0) { |
| this.modifiers |= ClassFileConstants.AccDeprecated; |
| } |
| } |
| } |
| |
| // ensure the receiver knows its hierarchy & fields/methods so static imports can be resolved correctly |
| // see bug 230026 |
| //{ObjectTeams: accessible across AbstractOTReferenceBinding (sitting in a different package): |
| protected |
| // SH} |
| void initializeForStaticImports() { |
| if (this.scope == null) return; // already initialized |
| |
| if (this.superInterfaces == null) |
| this.scope.connectTypeHierarchy(); |
| this.scope.buildFields(); |
| this.scope.buildMethods(); |
| } |
| |
| private void initializeNullDefault() { |
| // 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; |
| } |
| } |
| |
| /** |
| * Returns true if a type is identical to another one, |
| * or for generic types, true if compared to its raw type. |
| */ |
| public boolean isEquivalentTo(TypeBinding otherType) { |
| |
| if (this == otherType) return true; |
| if (otherType == null) return false; |
| switch(otherType.kind()) { |
| |
| case Binding.WILDCARD_TYPE : |
| case Binding.INTERSECTION_TYPE: |
| return ((WildcardBinding) otherType).boundCheck(this); |
| |
| case Binding.PARAMETERIZED_TYPE : |
| if ((otherType.tagBits & TagBits.HasDirectWildcard) == 0 && (!isMemberType() || !otherType.isMemberType())) |
| return false; // should have been identical |
| ParameterizedTypeBinding otherParamType = (ParameterizedTypeBinding) otherType; |
| if (this != otherParamType.genericType()) |
| return false; |
| if (!isStatic()) { // static member types do not compare their enclosing |
| ReferenceBinding enclosing = enclosingType(); |
| if (enclosing != null) { |
| ReferenceBinding otherEnclosing = otherParamType.enclosingType(); |
| if (otherEnclosing == null) return false; |
| if ((otherEnclosing.tagBits & TagBits.HasDirectWildcard) == 0) { |
| if (enclosing != otherEnclosing) return false; |
| } else { |
| if (!enclosing.isEquivalentTo(otherParamType.enclosingType())) return false; |
| } |
| } |
| } |
| int length = this.typeVariables == null ? 0 : this.typeVariables.length; |
| TypeBinding[] otherArguments = otherParamType.arguments; |
| int otherLength = otherArguments == null ? 0 : otherArguments.length; |
| if (otherLength != length) |
| return false; |
| for (int i = 0; i < length; i++) |
| if (!this.typeVariables[i].isTypeArgumentContainedBy(otherArguments[i])) |
| return false; |
| return true; |
| |
| case Binding.RAW_TYPE : |
| return otherType.erasure() == this; |
| } |
| return false; |
| } |
| public boolean isGenericType() { |
| return this.typeVariables != Binding.NO_TYPE_VARIABLES; |
| } |
| public boolean isHierarchyConnected() { |
| return (this.tagBits & TagBits.EndHierarchyCheck) != 0; |
| } |
| public ReferenceBinding[] memberTypes() { |
| return this.memberTypes; |
| } |
| |
| public boolean hasMemberTypes() { |
| return this.memberTypes.length > 0; |
| } |
| |
| // NOTE: the return type, arg & exception types of each method of a source type are resolved when needed |
| public MethodBinding[] methods() { |
| if ((this.tagBits & TagBits.AreMethodsComplete) != 0) |
| return this.methods; |
| |
| 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 { |
| //{ObjectTeams: don't cache length, resolving callin creates base call surrogate |
| /* orig: |
| for (int i = 0, length = this.methods.length; i < length; i++) { |
| :giro */ |
| for (int i = 0; i < this.methods.length; i++) { |
| int length = this.methods.length; |
| // SH} |
| 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); |
| } |
| //{ObjectTeams: may need to grow resolvedMethods: |
| if (i >= resolvedMethods.length) { |
| int l = resolvedMethods.length; |
| System.arraycopy(resolvedMethods, 0, resolvedMethods = new MethodBinding[length], 0, l); |
| } |
| // SH} |
| resolvedMethods[i] = null; // unable to resolve parameters |
| failed++; |
| } |
| } |
| //{ObjectTeams: in state final we lost the scope, cannot use code below any more. |
| if (isStateFinal()) |
| return this.methods; // relies on finally block below. |
| // SH} |
| |
| // find & report collision cases |
| boolean complyTo15OrAbove = this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; |
| boolean compliance16 = this.scope.compilerOptions().complianceLevel == ClassFileConstants.JDK1_6; |
| |
| //{ObjectTeams: this.methods may have changed size, use the appropriate length: |
| /* orig: |
| for (int i = 0, length = this.methods.length; i < length; i++) { |
| :giro */ |
| for (int i = 0, length = resolvedMethods.length; i < length; i++) { |
| // SH} |
| 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 (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 (params1[index] != params2[index].erasure()) { |
| // If one of them is a raw type |
| if (params1[index] instanceof RawTypeBinding) { |
| if (params2[index].erasure() != ((RawTypeBinding)params1[index]).actualType()) { |
| break; |
| } |
| } else { |
| break; |
| } |
| } |
| if (params1[index] == params2[index]) { |
| TypeBinding type = params1[index].leafComponentType(); |
| if (type instanceof SourceTypeBinding && type.typeVariables() != Binding.NO_TYPE_VARIABLES) { |
| index = pLength; // handle comparing identical source types like X<T>... its erasure is itself BUT we need to answer false |
| break; |
| } |
| } |
| } |
| if (index >= 0 && index < pLength) { |
| // is erasure of signature of m1 same as signature of m2? |
| for (index = pLength; --index >= 0;) |
| if (params1[index].erasure() != params2[index]) { |
| // If one of them is a raw type |
| if (params2[index] instanceof RawTypeBinding) { |
| if (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(this, 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(this, 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) { |
| this.methods = Binding.NO_METHODS; |
| } else { |
| MethodBinding[] newMethods = new MethodBinding[newSize]; |
| for (int i = 0, j = 0, length = resolvedMethods.length; i < length; i++) |
| if (resolvedMethods[i] != null) |
| newMethods[j++] = resolvedMethods[i]; |
| this.methods = newMethods; |
| } |
| } |
| |
| // handle forward references to potential default abstract methods |
| addDefaultAbstractMethods(); |
| this.tagBits |= TagBits.AreMethodsComplete; |
| } |
| return this.methods; |
| } |
| public FieldBinding resolveTypeFor(FieldBinding field) { |
| if ((field.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0) |
| return field; |
| |
| if (this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { |
| if ((field.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0) |
| field.modifiers |= ClassFileConstants.AccDeprecated; |
| } |
| if (isViewedAsDeprecated() && !field.isDeprecated()) |
| field.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly; |
| if (hasRestrictedAccess()) |
| field.modifiers |= ExtraCompilerModifiers.AccRestrictedAccess; |
| FieldDeclaration[] fieldDecls = this.scope.referenceContext.fields; |
| 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; |
| } |
| |
| // apply null default: |
| LookupEnvironment environment = this.scope.environment(); |
| if (environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) { |
| if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) { |
| // enum constants neither have a type declaration nor can they be null |
| field.tagBits |= TagBits.AnnotationNonNull; |
| } else { |
| initializeNullDefault(); |
| if (hasNonNullDefault()) { |
| field.fillInDefaultNonNullness(fieldDecl, initializationScope); |
| } |
| // validate null annotation: |
| this.scope.validateNullAnnotation(field.tagBits, fieldDecl.type, fieldDecl.annotations); |
| } |
| } |
| } finally { |
| initializationScope.initializedField = previousField; |
| } |
| //{ObjectTeams: copy-inherited fields and anchored types: |
| if (fieldDecls[f].getKind() != AbstractVariableDeclaration.ENUM_CONSTANT) { |
| if (fieldDecls[f].type == null) // should not happen for non-enum types |
| throw new InternalCompilerError("Field "+fieldDecls[f]+" has no type in "+this); |
| |
| field.copyInheritanceSrc = fieldDecls[f].copyInheritanceSrc; |
| field.maybeSetFieldTypeAnchorAttribute(); |
| // anchored to tthis? |
| field.type = RoleTypeCreator.maybeWrapUnqualifiedRoleType(this.scope, field.type, fieldDecls[f].type); |
| if (field.couldBeTeamAnchor()) { |
| // link decl and binding via model |
| // for early resolving from TeamAnchor.hasSameBestNameAs() |
| FieldModel.getModel(fieldDecls[f]).setBinding(field); |
| } |
| } |
| // need role field bridges? |
| if ( isRole() |
| && ((field.modifiers & ClassFileConstants.AccPrivate) != 0) |
| && !CharOperation.prefixEquals(IOTConstants.OT_DOLLAR_NAME, field.name)) |
| { |
| MethodBinding inner; |
| ReferenceBinding originalRole = field.declaringClass; |
| if (field.copyInheritanceSrc != null) |
| originalRole = field.copyInheritanceSrc.declaringClass; |
| inner = FieldModel.getDecapsulatingFieldAccessor(this, field, true/*isGetter*/); |
| ((SourceTypeBinding) enclosingType()).addSyntheticRoleMethodBridge(this, originalRole, inner, SyntheticMethodBinding.RoleMethodBridgeOuter); |
| if (!field.isFinal()) { // no setter for final (includes all static role fields) |
| // otherwise we would have to handle different signatures (w/ w/o role arg), which we currently don't |
| inner = FieldModel.getDecapsulatingFieldAccessor(this, field, false/*isGetter*/); |
| ((SourceTypeBinding) enclosingType()).addSyntheticRoleMethodBridge(this, originalRole, inner, SyntheticMethodBinding.RoleMethodBridgeOuter); |
| } |
| } |
| // SH} |
| return field; |
| } |
| return null; // should never reach this point |
| } |
| public MethodBinding resolveTypesFor(MethodBinding method) { |
| //{ObjectTeams: overload with one more parameter: |
| return resolveTypesFor(method, false); |
| } |
| public MethodBinding resolveTypesFor(MethodBinding method, boolean fromSynthetic) { |
| // SH} |
| if ((method.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0) |
| return method; |
| //{ObjectTeams: in state final we lost the scope, cannot use code below any more. |
| if (isStateFinal()) |
| return method; |
| //SH} |
| |
| if (this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { |
| if ((method.getAnnotationTagBits() & TagBits.AnnotationDeprecated) != 0) |
| method.modifiers |= ClassFileConstants.AccDeprecated; |
| } |
| if (isViewedAsDeprecated() && !method.isDeprecated()) |
| method.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly; |
| if (hasRestrictedAccess()) |
| method.modifiers |= ExtraCompilerModifiers.AccRestrictedAccess; |
| |
| AbstractMethodDeclaration methodDecl = method.sourceMethod(); |
| if (methodDecl == null) return null; // method could not be resolved in previous iteration |
| |
| //{ObjectTeams: pre-fetch (resolve) potential type anchors: |
| boolean[] anchorFlags = null; // keep track so we don't doubly resolve |
| if (methodDecl.arguments != null) { |
| anchorFlags = TypeAnchorReference.fetchAnchorFlags(methodDecl.arguments, methodDecl.typeParameters()); |
| for (int i = 0; i < anchorFlags.length; i++) |
| if (anchorFlags[i]) |
| methodDecl.arguments[i].type.resolveType(methodDecl.scope, true /* check bounds*/); |
| } |
| // SH} |
| |
| 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++) |
| //{ObjectTeams: more to connect: |
| { |
| // orig: |
| typeParameters[i].checkBounds(methodDecl.scope); |
| // :giro |
| typeParameters[i].connectTypeAnchors(methodDecl.scope); |
| } |
| // SH} |
| } |
| TypeReference[] exceptionTypes = methodDecl.thrownExceptions; |
| if (exceptionTypes != null) { |
| int size = exceptionTypes.length; |
| method.thrownExceptions = new ReferenceBinding[size]; |
| int count = 0; |
| ReferenceBinding resolvedExceptionType; |
| for (int i = 0; i < size; i++) { |
| resolvedExceptionType = (ReferenceBinding) exceptionTypes[i].resolveType(methodDecl.scope, true /* check bounds*/); |
| if (resolvedExceptionType == null) |
| continue; |
| if (resolvedExceptionType.isBoundParameterizedType()) { |
| methodDecl.scope.problemReporter().invalidParameterizedExceptionType(resolvedExceptionType, exceptionTypes[i]); |
| continue; |
| } |
| if (resolvedExceptionType.findSuperTypeOriginatingFrom(TypeIds.T_JavaLangThrowable, true) == null) { |
| if (resolvedExceptionType.isValidBinding()) { |
| methodDecl.scope.problemReporter().cannotThrowType(exceptionTypes[i], resolvedExceptionType); |
| continue; |
| } |
| } |
| if ((resolvedExceptionType.tagBits & TagBits.HasMissingType) != 0) { |
| method.tagBits |= TagBits.HasMissingType; |
| } |
| method.modifiers |= (resolvedExceptionType.modifiers & ExtraCompilerModifiers.AccGenericSignature); |
| method.thrownExceptions[count++] = resolvedExceptionType; |
| } |
| if (count < size) |
| System.arraycopy(method.thrownExceptions, 0, method.thrownExceptions = new ReferenceBinding[count], 0, count); |
| } |
| |
| 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; |
| } |
| //{ObjectTeams: update special argument: |
| // first arg (base) in base predicates: |
| final boolean isBaseGuard = CharOperation.prefixEquals(IOTConstants.BASE_PREDICATE_PREFIX, method.selector); |
| if ( i == 0 |
| && isBaseGuard) |
| { |
| if (!PredicateGenerator.createBaseArgType(methodDecl, arg)) { |
| foundArgProblem = true; |
| continue; |
| } |
| } |
| // SH} |
| //{ObjectTeams: option to roll back some problems |
| CheckPoint cp = this.scope.referenceContext.compilationResult.getCheckPoint(methodDecl); |
| // prepare baseclass decapsulation if its |
| // - the first arg in a role constructor |
| if ( isFirstRoleCtorArg(methodDecl, method, i) |
| && !arg.type.getBaseclassDecapsulation().isAllowed()) |
| { |
| arg.type.setBaseclassDecapsulation(DecapsulationState.ALLOWED); |
| } |
| DecapsulationState previousDecapsulation = arg.type.getBaseclassDecapsulation(); |
| // check if this argument was checked early as a type anchor for another argument: |
| try { |
| parameterType = |
| (anchorFlags[i] && arg.type.resolvedType.isValidBinding()) ? |
| arg.type.resolvedType : |
| arg.type.resolveType(methodDecl.scope, true /* check bounds*/); |
| // orig: |
| // parameterType = arg.type.resolveType(methodDecl.scope, true /* check bounds*/); |
| } finally { |
| if (deferRawTypeCheck) { |
| arg.type.bits &= ~ASTNode.IgnoreRawTypeCheck; |
| } |
| } |
| // :giro |
| // try anchored types (var.Type arg): |
| if ( (parameterType == null || parameterType instanceof MissingTypeBinding) |
| && !(arg.type instanceof LiftingTypeReference)) |
| { |
| TypeBinding anchoredType = RoleTypeCreator.getTypeAnchoredToParameter( |
| arg.type, |
| arguments, i, |
| methodDecl.scope, |
| cp); |
| if (anchoredType != null && anchoredType.isValidBinding()) |
| parameterType = anchoredType; // only use if no error |
| // in this case getTypeAnchoredToParameter() has performed a roleBack. |
| } |
| // if decapsulation was actually used, try if that was legal: |
| if (arg.type.getBaseclassDecapsulation().isAllowed() != previousDecapsulation.isAllowed()) |
| { |
| ReferenceBinding declaredBaseclass = method.declaringClass.baseclass(); |
| if ( declaredBaseclass == null |
| || !parameterType.isCompatibleWith(declaredBaseclass)) |
| { |
| ProblemReferenceBinding problemBinding = new ProblemReferenceBinding( |
| parameterType.readableName(), (ReferenceBinding)parameterType, ProblemReasons.NotVisible); |
| this.scope.problemReporter().invalidType(arg.type, problemBinding); |
| } |
| } |
| // fix for NPE of https://bugs.eclipse.org/403396 |
| if (parameterType != null && !parameterType.isValidBinding() && ((parameterType.tagBits & TagBits.HasMissingType) == 0)) |
| foundArgProblem = true; |
| // SH} |
| 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; |
| //{ObjectTeams: don't overwrite existing binding (only fill in detail) |
| // Note: possibly a binding has been created along this call chain: |
| // TypeParameter.connectTypeAnchors() -> TypeAnchorReference.[resolveType()->findVariable()] -> RoleTypeCreator.resolveTypeAnchoredToArgument() -> Argument.bind() |
| if (arg.binding != null) |
| arg.binding.type = parameterType; // only add this missing info |
| else |
| // SH} |
| arg.binding = new LocalVariableBinding(arg, parameterType, arg.modifiers, true /*isArgument*/); |
| } |
| } |
| // only assign parameters if no problems are found |
| if (!foundArgProblem) { |
| method.parameters = newParameters; |
| } |
| } |
| |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=337799 |
| if (this.scope.compilerOptions().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 { |
| //{ObjectTeams: option to roll back problems: |
| CheckPoint cp = this.scope.referenceContext.compilationResult.getCheckPoint(methodDecl); |
| if (this.isTeam() && !method.isPrivate() && returnType.getBaseclassDecapsulation() == DecapsulationState.NONE) |
| returnType.setBaseclassDecapsulation(DecapsulationState.TOLERATED); |
| // SH} |
| // 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; |
| } |
| } |
| //{ObjectTeams: might need a method parameter as type anchor: |
| if ( arguments != null |
| && ( methodType == null |
| || methodType.problemId() == ProblemReasons.NotFound)) // FIXME(SH): shouldn't occur (only null or valid). |
| { |
| TypeBinding adjustedMethodType = RoleTypeCreator.getTypeAnchoredToParameter( |
| returnType, |
| arguments, arguments.length, |
| methodDecl.scope, |
| cp); |
| if (adjustedMethodType != null && adjustedMethodType.isValidBinding()) |
| methodType = adjustedMethodType; // adopt alternative proposal |
| // in this case getTypeAnchoredToParameter() has performed a roleBack. |
| } |
| // SH} |
| if (methodType == null) { |
| foundReturnTypeProblem = true; |
| } else { |
| //{ObjectTeams: generalize return of callin method? |
| if (method.isCallin() && methodType.isBaseType()) |
| methodType = MethodSignatureEnhancer.generalizeReturnType((MethodDeclaration)methodDecl, methodType); |
| // SH} |
| if ((methodType.tagBits & TagBits.HasMissingType) != 0) { |
| method.tagBits |= TagBits.HasMissingType; |
| } |
| method.returnType = methodType; |
| TypeBinding leafType = methodType.leafComponentType(); |
| if (leafType instanceof ReferenceBinding && (((ReferenceBinding) leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) |
| method.modifiers |= ExtraCompilerModifiers.AccGenericSignature; |
| 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; |
| } |
| //{ObjectTeams: all types in resolved signatures of non-synthetic methods may require wrapping: |
| if (fromSynthetic) { |
| method.modifiers |= ClassFileConstants.AccSynthetic; |
| } else { |
| if (this.isRole() || this.isTeam()) |
| RoleTypeCreator.wrapTypesInMethodDeclSignature(method, methodDecl); |
| } |
| // SH} |
| CompilerOptions compilerOptions = this.scope.compilerOptions(); |
| if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) { |
| 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; |
| //{ObjectTeams: need role method bridges? |
| int abstractStatic = ClassFileConstants.AccAbstract | ClassFileConstants.AccStatic; |
| if ( isRole() |
| && ( (method.modifiers & ClassFileConstants.AccPrivate) != 0 |
| || ((method.modifiers & abstractStatic) == abstractStatic) && !method.declaringClass.isInterface()) |
| && !CharOperation.prefixEquals(IOTConstants.OT_DOLLAR_NAME, method.selector) |
| && !methodDecl.isConstructor()) |
| { |
| ReferenceBinding originalRole = this; |
| if (method.copyInheritanceSrc != null) |
| originalRole = method.copyInheritanceSrc.declaringClass; |
| else if (method.overriddenTSupers != null) |
| originalRole = method.overriddenTSupers[0].declaringClass; |
| MethodBinding inner = addSyntheticRoleMethodBridge(this, originalRole, method, SyntheticMethodBinding.RoleMethodBridgeInner); |
| ((SourceTypeBinding) enclosingType()).addSyntheticRoleMethodBridge(this, originalRole, inner, SyntheticMethodBinding.RoleMethodBridgeOuter); |
| } |
| // SH} |
| return method; |
| } |
| //https://bugs.eclipse.org/bugs/show_bug.cgi?id=391108 |
| private 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) { |
| initializeNullDefault(); |
| 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) { |
| //{ObjectTeams: added 2nd ctor arg: |
| new ImplicitNullAnnotationVerifier(compilerOptions.inheritNullAnnotations, this.scope.environment()).checkImplicitNullAnnotations(method, methodDecl, true, this.scope); |
| // SH} |
| } |
| } |
| } |
| private void evaluateNullAnnotations(long annotationTagBits) { |
| 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; |
| // 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); |
| } |
| } else if (isPackageInfo || (isInDefaultPkg && !(this instanceof NestedTypeBinding))) { |
| this.scope.problemReporter().missingNonNullByDefaultAnnotation(this.scope.referenceContext); |
| if (!isInDefaultPkg) |
| pkg.defaultNullness = NULL_UNSPECIFIED_BY_DEFAULT; |
| } |
| } |
| |
| protected void checkRedundantNullnessDefaultRecurse(ASTNode location, Annotation[] annotations, long annotationTagBits) { |
| if (this.fPackage.defaultNullness != NO_NULL_DEFAULT) { |
| if ((this.fPackage.defaultNullness == NONNULL_BY_DEFAULT |
| && ((annotationTagBits & TagBits.AnnotationNonNullByDefault) != 0))) { |
| this.scope.problemReporter().nullDefaultAnnotationIsRedundant(location, annotations, this.fPackage); |
| } |
| return; |
| } |
| } |
| |
| // return: should caller continue searching? |
| protected boolean checkRedundantNullnessDefaultOne(ASTNode location, Annotation[] annotations, long annotationTagBits) { |
| int thisDefault = this.defaultNullness; |
| if (thisDefault == NONNULL_BY_DEFAULT) { |
| if ((annotationTagBits & TagBits.AnnotationNonNullByDefault) != 0) { |
| this.scope.problemReporter().nullDefaultAnnotationIsRedundant(location, annotations, this); |
| } |
| return false; // different default means inner default is not redundant -> we're done |
| } |
| return true; |
| } |
| |
| boolean hasNonNullDefault() { |
| // 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.defaultNullness; |
| 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; |
| } |
| |
| //{ObjectTeams: helper to find args allowing baseclass decapsulation: |
| private boolean isFirstRoleCtorArg(AbstractMethodDeclaration methodDecl, MethodBinding method, int i) |
| { |
| return methodDecl.isConstructor() |
| && i==0 |
| && method.declaringClass.isRole(); |
| } |
| // SH} |
| public AnnotationHolder retrieveAnnotationHolder(Binding binding, boolean forceInitialization) { |
| if (forceInitialization) |
| binding.getAnnotationTagBits(); // ensure annotations are up to date |
| return super.retrieveAnnotationHolder(binding, false); |
| } |
| public void setFields(FieldBinding[] fields) { |
| this.fields = fields; |
| //{ObjectTeams: // integrate pendingField |
| if (this.pendingField != null) { |
| int l= fields.length; |
| System.arraycopy(fields, 0, this.fields= new FieldBinding[l+1], 1, l); |
| this.fields[0]= this.pendingField; |
| this.pendingField= null; |
| } |
| // in case getField() (or similar func) has already been called reset flag: |
| this.tagBits &= ~(TagBits.AreFieldsSorted | TagBits.AreFieldsComplete); |
| // SH} |
| } |
| public void setMethods(MethodBinding[] methods) { |
| //{ObjectTeams: baseCallSurrogate might be stored before regular methods are created |
| // happens when binary role with static callin method is loaded before STATE_BINDINGS_COMPLETE |
| if (this.pendingMethods != null) { |
| assert (this.tagBits & TagBits.AreMethodsSorted) == 0 : "setMethods after sorting"; //$NON-NLS-1$ |
| int len1 = this.pendingMethods.size(); |
| int len2 = methods.length; |
| this.methods = new MethodBinding[len1+len2]; |
| this.pendingMethods.toArray(this.methods); |
| System.arraycopy(methods, 0, this.methods, len1, len2); |
| this.pendingMethods = null; |
| } else |
| // SH} |
| this.methods = methods; |
| } |
| //{ObjectTeams: adding generated elements |
| FieldBinding pendingField= null; |
| public void addField(FieldBinding fieldBinding) { |
| if (this.fields == Binding.UNINITIALIZED_FIELDS) { |
| assert this.pendingField == null: "Can only have one pending field"; //$NON-NLS-1$ |
| this.pendingField= fieldBinding; |
| return; |
| } |
| int size = this.fields.length; |
| if ((this.tagBits & TagBits.AreFieldsSorted) != 0) { |
| this.fields = ReferenceBinding.sortedInsert(this.fields, fieldBinding); |
| if ((this.tagBits & TagBits.AreFieldsComplete) != 0) |
| resolveTypeFor(fieldBinding); |
| } else { |
| //grow array |
| System.arraycopy(this.fields, 0, this.fields = new FieldBinding[size + 1], 0, size); |
| |
| this.fields[size] = fieldBinding; |
| } |
| fieldBinding.id = size; |
| } |
| // base call surrogate could possibly be added very early, store it here first: |
| private List<MethodBinding> pendingMethods = null; |
| public void addMethod(MethodBinding methodBinding) { |
| // adding before methods are set? |
| if (this.methods == Binding.UNINITIALIZED_METHODS) { |
| if (this.pendingMethods == null) |
| this.pendingMethods = new ArrayList<MethodBinding>(); |
| this.pendingMethods.add(methodBinding); |
| return; |
| } |
| // differentiate between sorted and unsorted state: |
| int size = this.methods.length; |
| if ((this.tagBits & TagBits.AreMethodsComplete) != 0) { |
| if (resolveTypesFor(methodBinding) == null) |
| return; // don't add erroenous method |
| } |
| if ((this.tagBits & TagBits.AreMethodsSorted) != 0) { |
| this.methods= ReferenceBinding.sortedInsert(this.methods, methodBinding); |
| } else { |
| //grow array |
| System.arraycopy(this.methods, 0, this.methods= new MethodBinding[size + 1], 0, size); |
| |
| this.methods[size] = methodBinding; |
| } |
| } |
| public void reduceMethods(HashSet<MethodBinding> toRemove, int len) { |
| if (len < 0) |
| len = 0; |
| MethodBinding[] newMethods = new MethodBinding[len]; |
| if (len > 0) |
| CopyInheritance.reduceArray(this.methods, newMethods, toRemove); |
| this.methods = newMethods; |
| } |
| public void removeMethod(MethodBinding method) { |
| int length = this.methods.length; |
| MethodBinding[] methods = new MethodBinding[length - 1]; |
| int pos = -1; |
| if (this.methods != null) { |
| for (int i = 0; i < this.methods.length; i++) { |
| if (method == this.methods[i]) { |
| pos = i; |
| break; |
| } |
| } |
| } |
| if (pos >= 0) { |
| System.arraycopy(this.methods, 0, methods, 0, pos); |
| System.arraycopy( |
| this.methods, pos + 1, |
| methods, pos, |
| length - (pos + 1)); |
| this.methods = methods; |
| } |
| } |
| |
| public MethodBinding resolveGeneratedMethod(MethodBinding mb) { |
| return resolveTypesFor(mb); |
| } |
| /** |
| * Create and link binding for a generated method. |
| * @param methodDeclaration |
| * @param wasSynthetic |
| * @param copyInheritanceSrc |
| */ |
| public void resolveGeneratedMethod(AbstractMethodDeclaration methodDeclaration, boolean wasSynthetic, MethodBinding copyInheritanceSrc) { |
| if (this.scope != null) { |
| MethodBinding binding = this.scope.createMethod(methodDeclaration); |
| binding.setCopyInheritanceSrc(copyInheritanceSrc); |
| } else { |
| // in STATE_FINAL we can only fake this method: |
| // (this way, dependent classes can already be fully translated). |
| // TODO (SH): should this be reported as an error -> trigger recompilation? |
| MethodBinding binding = new MethodBinding( |
| methodDeclaration.modifiers |
| | ExtraCompilerModifiers.AccUnresolved |
| | ExtraCompilerModifiers.AccLocallyUsed, // suppress all "unused" warnings for generated methods |
| methodDeclaration.selector, |
| null, null, null, |
| this); |
| binding.setCopyInheritanceSrc(copyInheritanceSrc); |
| methodDeclaration.binding = binding; |
| resolveTypesFor(binding); |
| return; |
| } |
| |
| CheckPoint cp = null; |
| if (this.scope.referenceContext.ignoreFurtherInvestigation) |
| // for types with errors be more forgiving wrt to generated methods |
| cp = this.scope.referenceContext.compilationResult.getCheckPoint(methodDeclaration); |
| |
| try { |
| MethodBinding resolvedMethod = methodDeclaration.binding; |
| if (!methodDeclaration.isCopied) |
| resolvedMethod.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; // suppress all "unused" warnings for generated methods |
| // FIXME(SH): does this improve robustness? |
| // if ((this.tagBits & TagBits.AreMethodsComplete) != 0) |
| // { |
| if (resolveTypesFor(resolvedMethod, wasSynthetic) == null) { |
| // other parts may rely on finding generated methods, give back a binding: |
| if (methodDeclaration.binding == null) { |
| methodDeclaration.binding = new ProblemMethodBinding( |
| resolvedMethod, |
| resolvedMethod.selector, |
| resolvedMethod.parameters, |
| ProblemReasons.NoError); |
| if (!methodDeclaration.compilationResult.hasErrors()) // accept all cases where an error has already been reported. |
| throw new InternalCompilerError("Cannot resolve types for generated method: "+methodDeclaration); //$NON-NLS-1$ |
| // avoid deletion of this binding during resolveTypesFor(): |
| methodDeclaration.modifiers &= ~ExtraCompilerModifiers.AccUnresolved; |
| methodDeclaration.binding.returnType = resolvedMethod.returnType; |
| methodDeclaration.binding.thrownExceptions = Binding.NO_EXCEPTIONS; |
| } |
| // try to keep going with the code below! |
| } |
| // } |
| // don't add more than one binding for a method |
| // (CopyInheritance) |
| MethodBinding methodBinding = getExactMethod( |
| methodDeclaration.selector, resolvedMethod.parameters, |
| this.scope.compilationUnitScope()); |
| if ( (methodBinding == null) |
| || (methodBinding.declaringClass != this)) |
| { |
| this.scope.addGeneratedMethod(methodDeclaration.binding); |
| } |
| else |
| { |
| TypeBinding declarationReturn= methodDeclaration.binding.returnType; |
| // replace any existing binding |
| methodDeclaration.binding = methodBinding; |
| // but use the new return type |
| // (might be more specific, e.g., when a callout refines an inherited). |
| methodDeclaration.binding.returnType= declarationReturn; |
| } |
| //FIXME(SH): is something like the following needed? |
| // scope.connectTypeVariables(methodDeclaration.typeParameters(), true); |
| if ( StateMemento.hasMethodResolveStarted(this) |
| && methodDeclaration.binding.isValidBinding()) |
| { |
| // manually detect overriding, if we're past the MethodVerifier: |
| if (StateHelper.hasState(this, ITranslationStates.STATE_METHODS_VERIFIED)) |
| if (methodBinding != null && methodBinding.isValidBinding() && methodBinding.declaringClass != this) |
| methodDeclaration.binding.modifiers |= methodBinding.declaringClass.isInterface() |
| ? ExtraCompilerModifiers.AccImplementing |
| : ExtraCompilerModifiers.AccOverriding; |
| methodDeclaration.resolve(this.scope); |
| } |
| } finally { |
| if (cp != null) |
| // type already had errors, roll back errors in generated methods |
| this.scope.referenceContext.compilationResult.rollBack(cp); |
| } |
| } |
| |
| //Markus Witte} |
| public final int sourceEnd() { |
| return this.scope.referenceContext.sourceEnd; |
| } |
| public final int sourceStart() { |
| return this.scope.referenceContext.sourceStart; |
| } |
| SimpleLookupTable storedAnnotations(boolean forceInitialize) { |
| if (forceInitialize && this.storedAnnotations == null && this.scope != null) { // scope null when no annotation cached, and type got processed fully (159631) |
| this.scope.referenceCompilationUnit().compilationResult.hasAnnotations = true; |
| //{ObjectTeams: do support annotations for roles for the sake of copying: |
| if (!this.isRole()) |
| // SH} |
| if (!this.scope.environment().globalOptions.storeAnnotations) |
| return null; // not supported during this compile |
| this.storedAnnotations = new SimpleLookupTable(3); |
| } |
| return this.storedAnnotations; |
| } |
| public ReferenceBinding superclass() { |
| return this.superclass; |
| } |
| public ReferenceBinding[] superInterfaces() { |
| return this.superInterfaces; |
| } |
| public SyntheticMethodBinding[] syntheticMethods() { |
| //{ObjectTeams: two different kinds of synthetics: |
| /* orig: |
| 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]; |
| :giro */ |
| if (this.synthetics == null) return null; |
| int index = 0; |
| SyntheticMethodBinding[] bindings = new SyntheticMethodBinding[1]; |
| if (this.synthetics[SourceTypeBinding.METHOD_EMUL] != null && this.synthetics[METHOD_EMUL].size() > 0) |
| { |
| //orig: |
| 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]; |
| } |
| } |
| } |
| } |
| // :giro |
| // more synthetics to generate: |
| if (this.synthetics[SourceTypeBinding.ROLE_BRIDGE] != null && this.synthetics[SourceTypeBinding.ROLE_BRIDGE].size() > 0) |
| { |
| Iterator methodArrayIterator = this.synthetics[SourceTypeBinding.ROLE_BRIDGE].values().iterator(); |
| while (methodArrayIterator.hasNext()) { |
| SyntheticMethodBinding methodAccessor = (SyntheticMethodBinding) methodArrayIterator.next(); |
| if (methodAccessor != null) { |
| if (index+1 > bindings.length) { |
| System.arraycopy(bindings, 0, (bindings = new SyntheticMethodBinding[index + 1]), 0, index); |
| } |
| bindings[index++] = methodAccessor; |
| } |
| } |
| } |
| if (index == 0) |
| return null; // nothing found |
| // SH} |
| // sort them in according to their own indexes |
| int length; |
| SyntheticMethodBinding[] sortedBindings = new SyntheticMethodBinding[length = bindings.length]; |
| for (int i = 0; i < length; i++){ |
| SyntheticMethodBinding binding = bindings[i]; |
| sortedBindings[binding.index] = binding; |
| } |
| return sortedBindings; |
| } |
| /** |
| * Answer the collection of synthetic fields to append into the classfile |
| */ |
| public FieldBinding[] syntheticFields() { |
| if (this.synthetics == null) return null; |
| int fieldSize = this.synthetics[SourceTypeBinding.FIELD_EMUL] == null ? 0 : this.synthetics[SourceTypeBinding.FIELD_EMUL].size(); |
| int literalSize = this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] == null ? 0 :this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL].size(); |
| int totalSize = fieldSize + literalSize; |
| if (totalSize == 0) return null; |
| FieldBinding[] bindings = new FieldBinding[totalSize]; |
| |
| // add innerclass synthetics |
| if (this.synthetics[SourceTypeBinding.FIELD_EMUL] != null){ |
| Iterator elements = this.synthetics[SourceTypeBinding.FIELD_EMUL].values().iterator(); |
| for (int i = 0; i < fieldSize; i++) { |
| SyntheticFieldBinding synthBinding = (SyntheticFieldBinding) elements.next(); |
| bindings[synthBinding.index] = synthBinding; |
| } |
| } |
| // add class literal synthetics |
| if (this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL] != null){ |
| Iterator elements = this.synthetics[SourceTypeBinding.CLASS_LITERAL_EMUL].values().iterator(); |
| for (int i = 0; i < literalSize; i++) { |
| SyntheticFieldBinding synthBinding = (SyntheticFieldBinding) elements.next(); |
| bindings[fieldSize+synthBinding.index] = synthBinding; |
| } |
| } |
| return bindings; |
| } |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(30); |
| buffer.append("(id="); //$NON-NLS-1$ |
| if (this.id == TypeIds.NoId) |
| buffer.append("NoId"); //$NON-NLS-1$ |
| else |
| buffer.append(this.id); |
| buffer.append(")\n"); //$NON-NLS-1$ |
| if (isDeprecated()) buffer.append("deprecated "); //$NON-NLS-1$ |
| if (isPublic()) buffer.append("public "); //$NON-NLS-1$ |
| if (isProtected()) buffer.append("protected "); //$NON-NLS-1$ |
| if (isPrivate()) buffer.append("private "); //$NON-NLS-1$ |
| if (isAbstract() && isClass()) buffer.append("abstract "); //$NON-NLS-1$ |
| if (isStatic() && isNestedType()) buffer.append("static "); //$NON-NLS-1$ |
| if (isFinal()) buffer.append("final "); //$NON-NLS-1$ |
| //{ObjectTeams |
| if (isTeam()) buffer.append("team "); //$NON-NLS-1$ |
| // Markus Witte} |
| |
| 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$ |
| } |
| |
| // {ObjectTeams |
| if(isDirectRole()) |
| { |
| buffer.append("\n\tplayedBy "); //$NON-NLS-1$ |
| buffer.append((this.baseclass != null) ? this.baseclass.debugName() : "NULL TYPE"); //$NON-NLS-1$ |
| } |
| // Markus Witte} |
| if (enclosingType() != null) { |
| buffer.append("\n\tenclosing type : "); //$NON-NLS-1$ |
| buffer.append(enclosingType().debugName()); |
| } |
| |
| if (this.fields != null) { |
| if (this.fields != Binding.NO_FIELDS) { |
| buffer.append("\n/* fields */"); //$NON-NLS-1$ |
| for (int i = 0, length = this.fields.length; i < length; i++) |
| buffer.append('\n').append((this.fields[i] != null) ? this.fields[i].toString() : "NULL FIELD"); //$NON-NLS-1$ |
| } |
| } else { |
| buffer.append("NULL FIELDS"); //$NON-NLS-1$ |
| } |
| |
| if (this.methods != null) { |
| if (this.methods != Binding.NO_METHODS) { |
| buffer.append("\n/* methods */"); //$NON-NLS-1$ |
| for (int i = 0, length = this.methods.length; i < length; i++) |
| buffer.append('\n').append((this.methods[i] != null) ? this.methods[i].toString() : "NULL METHOD"); //$NON-NLS-1$ |
| } |
| } else { |
| buffer.append("NULL METHODS"); //$NON-NLS-1$ |
| } |
| |
| if (this.memberTypes != null) { |
| if (this.memberTypes != Binding.NO_MEMBER_TYPES) { |
| buffer.append("\n/* members */"); //$NON-NLS-1$ |
| for (int i = 0, length = this.memberTypes.length; i < length; i++) |
| buffer.append('\n').append((this.memberTypes[i] != null) ? this.memberTypes[i].toString() : "NULL TYPE"); //$NON-NLS-1$ |
| } |
| } else { |
| buffer.append("NULL MEMBER TYPES"); //$NON-NLS-1$ |
| } |
| |
| buffer.append("\n\n"); //$NON-NLS-1$ |
| return buffer.toString(); |
| } |
| public TypeVariableBinding[] typeVariables() { |
| return this.typeVariables != null ? this.typeVariables : Binding.NO_TYPE_VARIABLES; |
| } |
| void verifyMethods(MethodVerifier verifier) { |
| //{ObjectTeams: shortcut for predefined confined types (override final methods???) |
| if (TypeAnalyzer.isTopConfined(this)) |
| return; |
| // SH} |
| verifier.verify(this); |
| |
| for (int i = this.memberTypes.length; --i >= 0;) |
| //{ObjectTeams: roles can be binary contained in source type: |
| if (!this.memberTypes[i].isBinaryBinding()) |
| // SH} |
| ((SourceTypeBinding) this.memberTypes[i]).verifyMethods(verifier); |
| //{ObjectTeams: one more structure to connect: |
| // (requires inherited callin bindings (from STATE_LATE_ATTRIBUTES_EVALUATED), but must happend |
| // before resolve() so resolving BaseCallMessageSends will find inherited surrogates) |
| if (this.isDirectRole() && !this.isInterface()) |
| SyntheticBaseCallSurrogate.addFakedBaseCallSurrogates(this); |
| // SH} |
| } |
| |
| public FieldBinding[] unResolvedFields() { |
| return this.fields; |
| } |
| |
| public void tagIndirectlyAccessibleMembers() { |
| // 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(); |
| } |
| |
| //{ObjectTeams: in state final we lost the scope, cannot use some code. |
| private boolean isStateFinal() { |
| if (this.roleModel != null) |
| return this.roleModel.getState() == ITranslationStates.STATE_FINAL; |
| if (this._teamModel != null) |
| return this._teamModel.getState() == ITranslationStates.STATE_FINAL; |
| return (this.scope == null); |
| } |
| //SH} |
| } |