/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Fraunhofer FIRST - extended API and implementation * Technical University Berlin - extended API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; import java.util.HashSet; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies; import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates; import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor; import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding; import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel; import org.eclipse.objectteams.otdt.internal.core.compiler.model.TypeModel; /** * OTDT changes: * * Support for "playedBy" declaration: * =================================== * API: Method baseclass() and hasBaseclassProblem() * * What: Field baseclass is actually a quad-state * null : not resolved yet * NoBaseClass : Resolved to no baseclass. * ProblemBaseClass : Resolve failed. * : resolved, valid baseclass. * */ public final class MemberTypeBinding extends NestedTypeBinding { public MemberTypeBinding(char[][] compoundName, ClassScope scope, SourceTypeBinding enclosingType) { super(compoundName, scope, enclosingType); this.tagBits |= TagBits.MemberTypeMask; } public MemberTypeBinding(MemberTypeBinding prototype) { super(prototype); //{ObjectTeams: role? if (prototype.roleModel != null) this.roleModel = new RoleModel(this); // SH} } void checkSyntheticArgsAndFields() { if (!isPrototype()) throw new IllegalStateException(); //{ObjectTeams: role ifcs require synth args too /* orig: if (isStatic()) return; if (isInterface()) return; :giro */ if(isInterface()) { if (isRole()) addSyntheticArgument(this.enclosingType); return; } if (isStatic()) return; // SH} if (!isPrototype()) { ((MemberTypeBinding) this.prototype).checkSyntheticArgsAndFields(); return; } this.addSyntheticArgumentAndField(this.enclosingType); } /* Answer the receiver's constant pool name. * * NOTE: This method should only be used during/after code gen. */ @Override public char[] constantPoolName() /* java/lang/Object */ { if (this.constantPoolName != null) return this.constantPoolName; if (!isPrototype()) { return this.prototype.constantPoolName(); } return this.constantPoolName = CharOperation.concat(enclosingType().constantPoolName(), this.sourceName, '$'); } @Override public TypeBinding clone(TypeBinding outerType) { MemberTypeBinding copy = new MemberTypeBinding(this); copy.enclosingType = (SourceTypeBinding) outerType; return copy; } /** * @see org.eclipse.jdt.internal.compiler.lookup.Binding#initializeDeprecatedAnnotationTagBits() */ @Override public void initializeDeprecatedAnnotationTagBits() { if (!isPrototype()) { this.prototype.initializeDeprecatedAnnotationTagBits(); return; } if ((this.tagBits & TagBits.DeprecatedAnnotationResolved) == 0) { super.initializeDeprecatedAnnotationTagBits(); if ((this.tagBits & TagBits.AnnotationDeprecated) == 0) { // check enclosing type ReferenceBinding enclosing; if (((enclosing = enclosingType()).tagBits & TagBits.DeprecatedAnnotationResolved) == 0) { enclosing.initializeDeprecatedAnnotationTagBits(); } if (enclosing.isViewedAsDeprecated()) { this.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly; this.tagBits |= (enclosing.tagBits & TagBits.AnnotationTerminallyDeprecated); } } } } //{ObjectTeams: search baseclass on demand private static final ReferenceBinding NoBaseclass = new SourceTypeBinding(); private static final ReferenceBinding ProblemBaseClass = new SourceTypeBinding(); static { NoBaseclass.compoundName = new char[][]{"No Baseclass".toCharArray()}; //$NON-NLS-1$ NoBaseclass.model = new TypeModel(NoBaseclass); NoBaseclass.model.setState(ITranslationStates.STATE_FINAL); ProblemBaseClass.compoundName = new char[][]{"Problem Baseclass".toCharArray()}; //$NON-NLS-1$ ProblemBaseClass.model = new TypeModel(ProblemBaseClass); ProblemBaseClass.model.setState(ITranslationStates.STATE_FINAL); } private boolean hasCheckedBaseclassCircularity = false; // TODO(SH): convert to a tagbit? @Override public ReferenceBinding baseclass() { if (!isDirectRole()) return null; // BinaryTypeBindings have their PlayedBy in the Attribute if (this.baseclass == null) { this.baseclass = NoBaseclass; // mark that we have searched if (this.roleModel != null) { // if superinterfaces where not setup during role splitting, // but only after copy inheritance (setupInterfaceFromExtends), // the base class must be searched in the class part. if (isSynthInterface()) { ReferenceBinding classPart = getRealClass(); if (classPart != null && classPart.baseclass != null) this.baseclass = classPart.baseclass; if (this.baseclass != NoBaseclass && this.baseclass != ProblemBaseClass) //$IDENTITY-COMPARISON$ return this.baseclass; } if (!Dependencies.ensureRoleState(this.roleModel, ITranslationStates.STATE_LENV_DONE_FIELDS_AND_METHODS)) { this.scope.problemReporter().searchingBaseclassTooEarly(this); RoleModel.setTagBit(this, RoleModel.BaseclassHasProblems); this.baseclass = ProblemBaseClass; return null; } } if ( superclass() != null && !CharOperation.equals(superclass().compoundName, TypeConstants.JAVA_LANG_OBJECT)) { if (superclass().isSourceRole()) this.baseclass = superclass().baseclass(); } if (this.baseclass == null) this.baseclass= NoBaseclass; checkRefineBaseFromSuperInterfaces(); } if (!this.roleModel._playedByEnclosing && !isSynthInterface()) { String circle = hasBaseclassCircularity(); if (circle != null) { circle = new String(readableName())+circle; this.scope.problemReporter().baseclassCircularity(circle, this.scope.referenceContext); this.roleModel._playedByEnclosing = true; } } return rawBaseclass(); } // do not perform any lookup/analysis but filter out NobBaseclass/ProblemBaseClass @Override public ReferenceBinding rawBaseclass() { return ((this.baseclass == NoBaseclass) || (this.baseclass == ProblemBaseClass)) //$IDENTITY-COMPARISON$ ? null: this.baseclass; } private String hasBaseclassCircularity() { if (this.hasCheckedBaseclassCircularity) return null; this.hasCheckedBaseclassCircularity = true; if (this.baseclass == null) return null; HashSet upEnclosing = new HashSet(); // set of all enclosings and their supers ReferenceBinding out = this; while ((out = out.enclosingType()) != null) { ReferenceBinding up = out; while (up != null && up.id != TypeIds.T_JavaLangObject) { upEnclosing.add(up); up = up.superclass(); } } return internalHasBaseclassCircularity(upEnclosing, new HashSet(), this.baseclass); } @SuppressWarnings("nls") private String internalHasBaseclassCircularity( HashSet upEnclosing, HashSet visitedTypes, ReferenceBinding type) { if (type == null || type == NoBaseclass || type == ProblemBaseClass) //$IDENTITY-COMPARISON$ return null; // completing a cycle that started from the focus role? if (visitedTypes.contains(type)) return ""; // completing a cycle that started from "above" the focus role (enclosing types and their supers)? if (upEnclosing.contains(type)) return "->" + new String(type.readableName()); visitedTypes.add(type); String circle = null; if (type.rawBaseclass() != null) { circle = internalHasBaseclassCircularity(upEnclosing, visitedTypes, type.baseclass); if (circle != null) return "->"+new String(type.shortReadableName())+circle; visitedTypes.remove(type.baseclass); } ReferenceBinding[] members = type.memberTypes(); if (members == null) return null; for (ReferenceBinding member : members) { circle = internalHasBaseclassCircularity(upEnclosing, visitedTypes, member); if (circle != null) return "->"+new String(type.shortReadableName())+circle; visitedTypes.remove(member); } return null; } public void checkRefineBaseFromSuperInterfaces() { TypeDeclaration roleDecl = this.roleModel.getAst(); if (roleDecl == null) return; // loop over superinterfaces in order to find the most specific baseclass if (this.superInterfaces != null) { for (int i=0; i i)) { sStart = roleDecl.superInterfaces[i].sourceStart; sEnd = roleDecl.superInterfaces[i].sourceEnd; } if (superIfc.isDirectRole()) { superIfc.baseclass(); // ensure initialized this.baseclass = checkRefineBase( roleDecl.scope, sStart, sEnd, this.baseclass, superIfc.baseclass); // raw field access! if (this.baseclass == ProblemBaseClass) //$IDENTITY-COMPARISON$ RoleModel.setTagBit(this, RoleModel.BaseclassHasProblems); // detect situation of OTJLD 2.4.3: if ( this.superclass != null && this.superclass.isRole() && rawBaseclass() != null && TypeBinding.equalsEquals(this.baseclass, this.superclass.baseclass)) this.superclass.roleModel._supercededBy = this; } } } } /** Helper for the above. */ private ReferenceBinding checkRefineBase( ClassScope classScope, int sStart, int sEnd, ReferenceBinding current, ReferenceBinding next) { if (next == null || next == NoBaseclass) return current; //$IDENTITY-COMPARISON$ if (current == null || current == NoBaseclass) return next; //$IDENTITY-COMPARISON$ if (current == ProblemBaseClass || next == ProblemBaseClass) return ProblemBaseClass; //$IDENTITY-COMPARISON$ if (current.isCompatibleWith(next)) return current; if (next.isCompatibleWith(current)) return next; if (current.isRole() && current.roleModel.hasTSuperRole(next.getRealType())) return current; if ( RoleTypeBinding.isRoleWithExplicitAnchor(current) && RoleTypeBinding.isRoleWithExplicitAnchor(next)) { // check for implicit refinement of base anchored baseclasses (OTJLD 2.7(d)): ITeamAnchor currentAnchor = ((RoleTypeBinding)current).getAnchor(); ITeamAnchor nextAnchor = ((RoleTypeBinding)next).getAnchor(); if (currentAnchor.isBaseAnchor() && nextAnchor.isBaseAnchor()) { ReferenceBinding nextDeclaringType = ((FieldBinding)nextAnchor).getDeclaringClass().getRealType(); ReferenceBinding currentDeclaringType = ((FieldBinding)currentAnchor).getDeclaringClass().getRealType(); if ( current.getRealType().isCompatibleWith(next.getRealType()) && currentDeclaringType.isCompatibleWith(nextDeclaringType)) return current; if ( next.getRealType().isCompatibleWith(current.getRealType()) && nextDeclaringType.isCompatibleWith(currentDeclaringType)) return next; } } classScope.problemReporter().incompatibleBaseclasses( classScope.referenceContext, sStart, sEnd, current, next); RoleModel.setTagBit(this, RoleModel.BaseclassHasProblems); if (isSynthInterface()) RoleModel.setTagBit(this.roleModel.getClassPartBinding(), RoleModel.BaseclassHasProblems); return ProblemBaseClass; } //SH} @Override public String toString() { if (this.hasTypeAnnotations()) { return annotatedDebugName(); } else { return "Member type : " + new String(sourceName()) + " " + super.toString(); //$NON-NLS-2$ //$NON-NLS-1$ } } @Override public ModuleBinding module() { return this.enclosingType.module(); } }