blob: b8051a1e4d91a7637bc4be5f1a8fd1aba33570a9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2020 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
* Stephan Herrmann - Contributions for
* Bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop)
* Bug 388630 - @NonNull diagnostics at line 0
* Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Bug 416176 - [1.8][compiler][null] null type annotations cause grief on type variables
* Bug 424727 - [compiler][null] NullPointerException in nullAnnotationUnsupportedLocation(ProblemReporter.java:5708)
* Bug 457210 - [1.8][compiler][null] Wrong Nullness errors given on full build build but not on incremental build?
* Keigo Imai - Contribution for bug 388903 - Cannot extend inner class as an anonymous class when it extends the outer class
* Pierre-Yves B. <pyvesdev@gmail.com> - Contributions for
* Bug 542520 - [JUnit 5] Warning The method xxx from the type X is never used locally is shown when using MethodSource
* Bug 546084 - Using Junit 5s MethodSource leads to ClassCastException
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding.TeamPackageBinding;
import org.eclipse.jdt.internal.compiler.parser.*;
import org.eclipse.jdt.internal.compiler.problem.*;
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.compiler.ISMAPConstants;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.AbstractMethodMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CallinMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.GuardPredicateDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.PrecedenceDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeValueParameter;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.BytecodeTransformer;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.CallinPrecedenceAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.InlineAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.WordValueAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
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.control.StateHelper;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.StateMemento;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.CallinCalloutBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.OTClassScope;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.PrecedenceBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.*;
import org.eclipse.objectteams.otdt.internal.core.compiler.smap.AbstractSmapGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.smap.RoleSmapGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.smap.TeamSmapGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.copyinheritance.CopyInheritance;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.RoleSplitter;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.Sorting;
/**
* OTDT changes:
*
* New structural elements:
* + baseclass for "playedBy"
* + precedences
* + callinCallouts for method mappings
* + predicate
*
* + additional flags: isGenerated, isPurelyCopied
*
* Linkage to various models, which store additional information:
* + TypeModel, RoleModel, TeamModel
*
* Role files (ROFI):
* + store the role compilation unit
*
* ----
* What: copy-inherited local types need to set constantPoolName in analyseCode already.
* Why: TODO(SH) document the reason
*
* What: avoid generation of default constructor for bound role
* Why: bound roles have a default lifting constructor instead.
* Where:createsInternalConstructor()
*
* What: fine-grained dependency control in generateCode()
* Why: tsupers need to be generated first (including synthetic fields and methods),
* even if they are within the same compilation unit (nested teams)
*
* What: conclude base call analysis in conjunction with replace bindings
* Where:internalAnalyseCode; See also MethodDeclaration (class comment).
*
* @version $Id: TypeDeclaration.java 23405 2010-02-03 17:02:18Z stephan $
*/
public class TypeDeclaration extends Statement implements ProblemSeverities, ReferenceContext,
//{ObjectTeams:
ClassFileConstants
// SH}
{
// Type decl kinds
public static final int CLASS_DECL = 1;
public static final int INTERFACE_DECL = 2;
public static final int ENUM_DECL = 3;
public static final int ANNOTATION_TYPE_DECL = 4;
/*
* @noreference This field is not intended to be referenced by clients as it is a part of Java preview feature.
*/
public static final int RECORD_DECL = 5;
public int modifiers = ClassFileConstants.AccDefault;
public int modifiersSourceStart;
public int functionalExpressionsCount = 0;
public Annotation[] annotations;
public char[] name;
public TypeReference superclass;
public TypeReference[] superInterfaces;
public FieldDeclaration[] fields;
public AbstractMethodDeclaration[] methods;
public TypeDeclaration[] memberTypes;
public SourceTypeBinding binding;
public ClassScope scope;
public MethodScope initializerScope;
public MethodScope staticInitializerScope;
public boolean ignoreFurtherInvestigation = false;
public int maxFieldCount;
public int declarationSourceStart;
public int declarationSourceEnd;
public int restrictedIdentifierStart = -1; // used only for record and permits restricted keywords.
public int bodyStart;
public int bodyEnd; // doesn't include the trailing comment if any.
public CompilationResult compilationResult;
public MethodDeclaration[] missingAbstractMethods;
public Javadoc javadoc;
public QualifiedAllocationExpression allocation; // for anonymous only
public TypeDeclaration enclosingType; // for member types only
public FieldBinding enumValuesSyntheticfield; // for enum
public int enumConstantsCounter;
// 1.5 support
public TypeParameter[] typeParameters;
// 14 Records preview support
public RecordComponent[] recordComponents;
public int nRecordComponents;
public static Set<String> disallowedComponentNames;
// 15 Sealed Type preview support
public TypeReference[] permittedTypes;
static {
disallowedComponentNames = new HashSet<>(6);
disallowedComponentNames.add("clone"); //$NON-NLS-1$
disallowedComponentNames.add("finalize"); //$NON-NLS-1$
disallowedComponentNames.add("getClass"); //$NON-NLS-1$
disallowedComponentNames.add("hashCode"); //$NON-NLS-1$
disallowedComponentNames.add("notify"); //$NON-NLS-1$
disallowedComponentNames.add("notifyAll");//$NON-NLS-1$
disallowedComponentNames.add("toString"); //$NON-NLS-1$
disallowedComponentNames.add("wait"); //$NON-NLS-1$
}
//{ObjectTeams: added fields and their accessors:
// ==== New Main Structural Fields:
public AbstractMethodMappingDeclaration[] callinCallouts;
public PrecedenceDeclaration[] precedences;
public TypeReference baseclass;
// (stored by Parser.consumePredicate(), copied to methods from Parser.dispatchDeclarationInto())
public GuardPredicateDeclaration predicate = null;
/** Copy all predicates within this type into this.methods. */
public void copyPredicates () {
int count = 0;
if (this.predicate != null)
count++;
int max = count
+ ((this.methods == null) ? 0 : this.methods.length)
+ ((this.callinCallouts == null) ? 0 : this.callinCallouts.length);
GuardPredicateDeclaration[] guardMethods = new GuardPredicateDeclaration[max];
if (this.predicate != null)
guardMethods[0] = this.predicate;
if (this.methods != null) {
for (AbstractMethodDeclaration method : this.methods) {
if (method.isMethod()) {
MethodDeclaration md = (MethodDeclaration)method;
if (md.predicate != null)
guardMethods[count++] = md.predicate;
}
}
}
if (this.callinCallouts != null) {
for (AbstractMethodMappingDeclaration mapping : this.callinCallouts) {
if (mapping.isCallin()) {
CallinMappingDeclaration callinDecl = (CallinMappingDeclaration)mapping;
if (callinDecl.predicate != null)
guardMethods[count++] = callinDecl.predicate;
}
}
}
if (count == 0)
return;
int oldLen = 0;
if (this.methods != null)
oldLen = this.methods.length;
AbstractMethodDeclaration[] newMethods = new AbstractMethodDeclaration[oldLen+count];
if (oldLen > 0)
System.arraycopy(this.methods, 0, newMethods, 0, oldLen);
if (count > 0)
System.arraycopy(guardMethods, 0, newMethods, oldLen, count);
this.methods = newMethods;
}
/** role files link back to their compilationUnit: */
public CompilationUnitDeclaration compilationUnit = null;
/** has this type been converted from ISourceType? */
public boolean isConverted = false;
/** Is this a role declaration defined in its on file (and compilation unit)? */
public boolean isRoleFile() {
if (this.compilationUnit == null)
return false;
for (int i = 0; i < this.compilationUnit.types.length; i++) {
if (this.compilationUnit.types[i] == this)
return true;
}
return false; // nested within a role file
}
/**
* Given that this is a role file, retreive the package from the enclosing team
* (and check for consistency with this role file's team package).
*/
public PlainPackageBinding getPackageOfTeam(ImportReference teamPackage, Scope aScope)
{
TypeDeclaration teamDecl = this.enclosingType;
if (teamDecl == null)
return null;
// prefer name manipulation over RoleModel.getInterfaceAst(),
// because we might be called before copied roles have connected class/ifc parts.
char[] teamName= teamDecl.name;
if (RoleSplitter.isClassPartName(teamName))
teamName= RoleSplitter.getInterfacePartName(teamName);
boolean match;
if (teamDecl.binding != null)
match = CharOperation.equals(teamPackage.tokens,
TeamPackageBinding.adjustTeamPackageName(teamDecl.binding.compoundName));
else
match = CharOperation.equals(
teamPackage.tokens[teamPackage.tokens.length-1],
teamName);
if (!match)
{
aScope.problemReporter().mismatchingPackageForRole(
teamPackage.tokens,
teamName,
this.name,
teamPackage.sourceStart,
teamPackage.sourceEnd);
return null;
}
return (PlainPackageBinding) teamDecl.scope.getCurrentPackage();
}
/** If this is a role file answer the number of nesting levels to the
* outermost team. This is useful for converting source paths
* <code>some/pack/Team/Nested/Role.java</code>
* to binary paths
* <code>some/pack/Team$Nested$Role.class</code>
* In the given example the depth would be 2 to signal the the class file
* is stored two levels up from the source file's folder.
*/
public int getRoleFileDepth() {
if (!isRoleFile())
return 0;
int depth = 0;
TypeDeclaration current = this.enclosingType;
while (current != null && current.isTeam()) {
depth ++;
current = current.enclosingType;
}
return depth;
}
// ==== Handling of models: TeamModel, TypeModel, RoleModel
private TeamModel teamModel = null;
public TeamModel getTeamModel() {
if (this.teamModel == null)
this.teamModel = new TeamModel(this);
if ((this.teamModel.getBinding() == null) && (this.binding != null))
this.teamModel.setBinding(this.binding);
return this.teamModel;
}
private TypeModel model = null;
public TypeModel getModel() {
if (this.model == null)
this.model = new TypeModel(this);
return this.model;
}
private RoleModel roleModel = null;
public RoleModel getRoleModel() {
// NOTE (SH): classes nested in a role have a role model but not AccRole.
// assert(((modifiers & AccRole) != 0));
assert(isRole());
if (this.roleModel == null)
this.roleModel = new RoleModel(this);
if ((this.roleModel.getBinding() == null) && (this.binding != null))
this.roleModel.setBinding(this.binding);
return this.roleModel;
}
public RoleModel getRoleModel(TeamModel aTeamModel) {
getRoleModel();
this.roleModel.setTeamModel(aTeamModel);
return this.roleModel;
}
public void setBinding(SourceTypeBinding binding) {
this.binding = binding;
getModel(); // intended side-effect
if (isTeam())
getTeamModel(); // intended side-effect
if (isRole())
getRoleModel(); // intended side-effect
}
/**
* Parsing of org.objectteams.Team did not recognize the team,
* adjust it and its roles now.
*/
public void adjustOrgObjectteamsTeam() {
this.modifiers |= ExtraCompilerModifiers.AccTeam;
TypeDeclaration confined = null;
TypeDeclaration otconfined = null;
if (this.memberTypes != null) {
for (int i = 0; i < this.memberTypes.length; i++) {
TypeDeclaration memberType = this.memberTypes[i];
memberType.modifiers |= ExtraCompilerModifiers.AccRole;
if (CharOperation.equals(memberType.name, IOTConstants.OTCONFINED))
otconfined = memberType;
else if (CharOperation.equals(memberType.name, IOTConstants.CONFINED))
confined = memberType;
}
if (confined != null && otconfined != null) {
// link roles that are split manually (without using RoleSplitter).
RoleModel ifcModel = confined.getRoleModel(getTeamModel());
RoleModel clsModel = otconfined.getRoleModel(getTeamModel());
ifcModel._classPart = otconfined;
clsModel._interfacePart = confined;
}
}
}
// ==== new flags and boolean queries: ====
/** not created from source but generated for one of the following reasons:
* - it's a role interface created by RoleSplitter
* - it's copied by CopyInheritance
* - it's a marker interface
* - it's a RoFi cache
*/
public boolean isGenerated = false;
/** copy inheritence without an overriding source role in the current team? */
public boolean isPurelyCopied = false;
public boolean willCatchAbort = false;
public final boolean isTeam() {
return (this.modifiers & ExtraCompilerModifiers.AccTeam) != 0;
}
/** Is this an actual role (not a generated interface)? */
public boolean isSourceRole() {
if ((this.modifiers & ClassFileConstants.AccEnum) != 0)
return false;
if(!(this.isGenerated && isInterface())) {
if((this.modifiers & ExtraCompilerModifiers.AccRole) != 0)
return true;
}
return false;
}
/** Is this class either a role or nested in a role? */
public boolean isRole() {
if ((this.modifiers & ExtraCompilerModifiers.AccRole) != 0)
return true;
if ((this.modifiers & ClassFileConstants.AccEnum) != 0)
return false;
if ( this.enclosingType == null
&& this.scope != null
&& this.scope.parent != null)
{
if (this.scope.parent.methodScope() != null)
// self healing: ;-)
this.enclosingType = this.scope.parent.methodScope().referenceType();
}
if (this.enclosingType != null)
return this.enclosingType.isRole();
if (this.binding != null)
return this.binding.isRole();
return false;
}
public boolean isDirectRole() {
return (this.modifiers & ExtraCompilerModifiers.AccRole) != 0;
}
public boolean isInterface() {
return kind(this.modifiers) == INTERFACE_DECL;
}
/** Is this an interface that was not created by role splitting? */
public boolean isRegularInterface() {
return (this.modifiers & (AccInterface|AccSynthetic)) == AccInterface;
}
/** Beautified name for roles (identical to ReferenceBinding.sourceName()). */
public char[] sourceName() {
if (isSourceRole() && RoleSplitter.isClassPartName(this.name))
return RoleSplitter.getInterfacePartName(this.name);
return this.name;
}
// Markus Witte+SH}
public TypeDeclaration(CompilationResult compilationResult){
this.compilationResult = compilationResult;
}
/*
* We cause the compilation task to abort to a given extent.
*/
@Override
public void abort(int abortLevel, CategorizedProblem problem) {
//{ObjectTeams: also mark in the state that we're done:
if (!this.willCatchAbort) { // only on exceptions that will actually fly
switch (abortLevel) {
case AbortCompilation :
case AbortCompilationUnit :
StateHelper.setStateRecursive(this.scope.referenceCompilationUnit(), ITranslationStates.STATE_FINAL, false);
break;
case AbortMethod:
break;
default :
StateHelper.setStateRecursive(this, ITranslationStates.STATE_FINAL, false);
}
}
// SH}
switch (abortLevel) {
case AbortCompilation :
throw new AbortCompilation(this.compilationResult, problem);
case AbortCompilationUnit :
throw new AbortCompilationUnit(this.compilationResult, problem);
case AbortMethod :
throw new AbortMethod(this.compilationResult, problem);
default :
throw new AbortType(this.compilationResult, problem);
}
}
/**
* This method is responsible for adding a <clinit> method declaration to the type method collections.
* Note that this implementation is inserting it in first place (as VAJ or javac), and that this
* impacts the behavior of the method ConstantPool.resetForClinit(int. int), in so far as
* the latter will have to reset the constant pool state accordingly (if it was added first, it does
* not need to preserve some of the method specific cached entries since this will be the first method).
* inserts the clinit method declaration in the first position.
*
* @see org.eclipse.jdt.internal.compiler.codegen.ConstantPool#resetForClinit(int, int)
*/
public final void addClinit() {
//see comment on needClassInitMethod
if (needClassInitMethod()) {
int length;
AbstractMethodDeclaration[] methodDeclarations;
if ((methodDeclarations = this.methods) == null) {
length = 0;
methodDeclarations = new AbstractMethodDeclaration[1];
} else {
length = methodDeclarations.length;
System.arraycopy(
methodDeclarations,
0,
(methodDeclarations = new AbstractMethodDeclaration[length + 1]),
1,
length);
}
Clinit clinit = new Clinit(this.compilationResult);
methodDeclarations[0] = clinit;
// clinit is added in first location, so as to minimize the use of ldcw (big consumer of constant inits)
clinit.declarationSourceStart = clinit.sourceStart = this.sourceStart;
clinit.declarationSourceEnd = clinit.sourceEnd = this.sourceEnd;
clinit.bodyEnd = this.sourceEnd;
this.methods = methodDeclarations;
}
}
/*
* INTERNAL USE ONLY - Creates a fake method declaration for the corresponding binding.
* It is used to report errors for missing abstract methods.
*/
public MethodDeclaration addMissingAbstractMethodFor(MethodBinding methodBinding) {
TypeBinding[] argumentTypes = methodBinding.parameters;
int argumentsLength = argumentTypes.length;
//the constructor
MethodDeclaration methodDeclaration = new MethodDeclaration(this.compilationResult);
methodDeclaration.selector = methodBinding.selector;
methodDeclaration.sourceStart = this.sourceStart;
methodDeclaration.sourceEnd = this.sourceEnd;
methodDeclaration.modifiers = methodBinding.getAccessFlags() & ~ClassFileConstants.AccAbstract;
if (argumentsLength > 0) {
String baseName = "arg";//$NON-NLS-1$
Argument[] arguments = (methodDeclaration.arguments = new Argument[argumentsLength]);
for (int i = argumentsLength; --i >= 0;) {
arguments[i] = new Argument((baseName + i).toCharArray(), 0L, null /*type ref*/, ClassFileConstants.AccDefault);
}
}
//adding the constructor in the methods list
if (this.missingAbstractMethods == null) {
this.missingAbstractMethods = new MethodDeclaration[] { methodDeclaration };
} else {
MethodDeclaration[] newMethods;
System.arraycopy(
this.missingAbstractMethods,
0,
newMethods = new MethodDeclaration[this.missingAbstractMethods.length + 1],
1,
this.missingAbstractMethods.length);
newMethods[0] = methodDeclaration;
this.missingAbstractMethods = newMethods;
}
//============BINDING UPDATE==========================
methodDeclaration.binding = new MethodBinding(
methodDeclaration.modifiers | ClassFileConstants.AccSynthetic, //methodDeclaration
methodBinding.selector,
methodBinding.returnType,
argumentsLength == 0 ? Binding.NO_PARAMETERS : argumentTypes, //arguments bindings
methodBinding.thrownExceptions, //exceptions
this.binding); //declaringClass
methodDeclaration.scope = new MethodScope(this.scope, methodDeclaration, true);
methodDeclaration.bindArguments();
/* if (binding.methods == null) {
binding.methods = new MethodBinding[] { methodDeclaration.binding };
} else {
MethodBinding[] newMethods;
System.arraycopy(
binding.methods,
0,
newMethods = new MethodBinding[binding.methods.length + 1],
1,
binding.methods.length);
newMethods[0] = methodDeclaration.binding;
binding.methods = newMethods;
}*/
//===================================================
return methodDeclaration;
}
/**
* Flow analysis for a local innertype
*
*/
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
if (this.ignoreFurtherInvestigation)
return flowInfo;
try {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) {
this.bits |= ASTNode.IsReachable;
LocalTypeBinding localType = (LocalTypeBinding) this.binding;
//{ObjectTeams: constantPoolName is set in advance for copy-inherited local types:
if (localType.constantPoolName() == null)
// SH}
localType.setConstantPoolName(currentScope.compilationUnitScope().computeConstantPoolName(localType));
}
manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
updateMaxFieldCount(); // propagate down the max field count
internalAnalyseCode(flowContext, flowInfo);
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
}
return flowInfo;
}
/**
* Flow analysis for a member innertype
*
*/
public void analyseCode(ClassScope enclosingClassScope) {
if (this.ignoreFurtherInvestigation)
return;
try {
// propagate down the max field count
updateMaxFieldCount();
internalAnalyseCode(null, FlowInfo.initial(this.maxFieldCount));
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
}
}
/**
* Flow analysis for a local member innertype
*
*/
public void analyseCode(ClassScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
if (this.ignoreFurtherInvestigation)
return;
try {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) {
this.bits |= ASTNode.IsReachable;
LocalTypeBinding localType = (LocalTypeBinding) this.binding;
localType.setConstantPoolName(currentScope.compilationUnitScope().computeConstantPoolName(localType));
}
manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
updateMaxFieldCount(); // propagate down the max field count
internalAnalyseCode(flowContext, flowInfo);
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
}
}
/**
* Flow analysis for a package member type
*
*/
public void analyseCode(CompilationUnitScope unitScope) {
if (this.ignoreFurtherInvestigation)
return;
try {
//{ObjectTeams: make sure role units fetch the maxFieldCount from the outermost team:
/* orig:
internalAnalyseCode(null, FlowInfo.initial(this.maxFieldCount));
:giro */
if (this.isRoleFile()) {
if (this.binding.enclosingType() != null)
Dependencies.ensureBindingState(this.binding.enclosingType(), ITranslationStates.STATE_RESOLVED);
else if (!this.compilationResult.hasErrors())
throw new InternalCompilerError("Role file unexpectedly has no enclosing team"); //$NON-NLS-1$
}
int myMaxFieldCount = this.scope.outerMostClassScope().referenceType().maxFieldCount;
internalAnalyseCode(null, FlowInfo.initial(myMaxFieldCount));
// SH}
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
}
}
/**
* Check for constructor vs. method with no return type.
* Answers true if at least one constructor is defined
*/
public boolean checkConstructors(Parser parser) {
//if a constructor has not the name of the type,
//convert it into a method with 'null' as its return type
boolean hasConstructor = false;
if (this.methods != null) {
for (int i = this.methods.length; --i >= 0;) {
AbstractMethodDeclaration am;
if ((am = this.methods[i]).isConstructor()) {
if (!CharOperation.equals(am.selector, this.name)) {
// the constructor was in fact a method with no return type
// unless an explicit constructor call was supplied
ConstructorDeclaration c = (ConstructorDeclaration) am;
if (c.constructorCall == null || c.constructorCall.isImplicitSuper()) { //changed to a method
MethodDeclaration m = parser.convertToMethodDeclaration(c, this.compilationResult);
this.methods[i] = m;
}
} else {
switch (kind(this.modifiers)) {
case TypeDeclaration.INTERFACE_DECL :
// report the problem and continue the parsing
parser.problemReporter().interfaceCannotHaveConstructors((ConstructorDeclaration) am);
break;
case TypeDeclaration.ANNOTATION_TYPE_DECL :
// report the problem and continue the parsing
parser.problemReporter().annotationTypeDeclarationCannotHaveConstructor((ConstructorDeclaration) am);
break;
}
hasConstructor = true;
}
}
}
}
return hasConstructor;
}
@Override
public CompilationResult compilationResult() {
return this.compilationResult;
}
public ConstructorDeclaration createDefaultConstructorForRecord(boolean needExplicitConstructorCall, boolean needToInsert) {
//Add to method'set, the default constuctor that just recall the
//super constructor with no arguments
//The arguments' type will be positionned by the TC so just use
//the default int instead of just null (consistency purpose)
ConstructorDeclaration constructor = new ConstructorDeclaration(this.compilationResult);
constructor.bits |= ASTNode.IsCanonicalConstructor | ASTNode.IsImplicit;
constructor.selector = this.name;
constructor.modifiers = this.modifiers & ExtraCompilerModifiers.AccVisibilityMASK;
// constructor.modifiers = this.modifiers & ClassFileConstants.AccPublic;
// constructor.modifiers |= ClassFileConstants.AccPublic; // JLS 14 8.10.5
constructor.arguments = getArgumentsFromComponents(this.recordComponents);
constructor.declarationSourceStart = constructor.sourceStart =
constructor.bodyStart = this.sourceStart;
constructor.declarationSourceEnd =
constructor.sourceEnd = constructor.bodyEnd = this.sourceStart - 1;
//the super call inside the constructor
if (needExplicitConstructorCall) {
constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
constructor.constructorCall.sourceStart = this.sourceStart;
constructor.constructorCall.sourceEnd = this.sourceEnd;
}
/* The body of the implicitly declared canonical constructor initializes each field corresponding
* to a record component with the corresponding formal parameter in the order that they appear
* in the record component list.*/
List<Statement> statements = new ArrayList<>();
int l = this.recordComponents != null ? this.recordComponents.length : 0;
if (l > 0 && this.fields != null) {
List<String> fNames = Arrays.stream(this.fields)
.filter(f -> f.isARecordComponent)
.map(f ->new String(f.name))
.collect(Collectors.toList());
for (int i = 0; i < l; ++i) {
RecordComponent component = this.recordComponents[i];
if (!fNames.contains(new String(component.name)))
continue;
FieldReference lhs = new FieldReference(component.name, 0);
lhs.receiver = ThisReference.implicitThis();
statements.add(new Assignment(lhs, new SingleNameReference(component.name, 0), 0));
}
}
constructor.statements = statements.toArray(new Statement[0]);
//adding the constructor in the methods list: rank is not critical since bindings will be sorted
if (needToInsert) {
if (this.methods == null) {
this.methods = new AbstractMethodDeclaration[] { constructor };
} else {
AbstractMethodDeclaration[] newMethods;
System.arraycopy(
this.methods,
0,
newMethods = new AbstractMethodDeclaration[this.methods.length + 1],
1,
this.methods.length);
newMethods[0] = constructor;
this.methods = newMethods;
}
}
return constructor;
}
private Argument[] getArgumentsFromComponents(RecordComponent[] comps) {
Argument[] args2 = comps == null || comps.length == 0 ? ASTNode.NO_ARGUMENTS :
new Argument[comps.length];
int count = 0;
for (RecordComponent comp : comps) {
Argument argument = new Argument(comp.name, ((long)comp.sourceStart) << 32 | comp.sourceEnd,
comp.type, 0); // no modifiers allowed for record components - enforce
args2[count++] = argument;
}
return args2;
}
public ConstructorDeclaration createDefaultConstructor( boolean needExplicitConstructorCall, boolean needToInsert) {
if (this.isRecord())
return createDefaultConstructorForRecord(needExplicitConstructorCall, needToInsert);
//Add to method'set, the default constuctor that just recall the
//super constructor with no arguments
//The arguments' type will be positionned by the TC so just use
//the default int instead of just null (consistency purpose)
//{ObjectTeams: no default constructor for bound roles
// (cf. also AstEdit.removeDefaultConstructor())
// also unbound roles defer constructor creation to Dependencies.establishMethodsCreated(RoleModel)
// if roleModel != null assume were called later than during parsing:
// - from CopyInheritance.copyMethod(OTConfined)
// - crom Dependencies.establishMethodsCreated(RoleModel)
if (this.isDirectRole() && this.roleModel == null)
return null;
// SH}
//the constructor
ConstructorDeclaration constructor = new ConstructorDeclaration(this.compilationResult);
constructor.bits |= ASTNode.IsDefaultConstructor;
constructor.selector = this.name;
constructor.modifiers = this.modifiers & ExtraCompilerModifiers.AccVisibilityMASK;
//if you change this setting, please update the
//SourceIndexer2.buildTypeDeclaration(TypeDeclaration,char[]) method
constructor.declarationSourceStart = constructor.sourceStart = this.sourceStart;
constructor.declarationSourceEnd =
constructor.sourceEnd = constructor.bodyEnd = this.sourceEnd;
//the super call inside the constructor
if (needExplicitConstructorCall) {
constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
constructor.constructorCall.sourceStart = this.sourceStart;
constructor.constructorCall.sourceEnd = this.sourceEnd;
}
//adding the constructor in the methods list: rank is not critical since bindings will be sorted
if (needToInsert) {
if (this.methods == null) {
this.methods = new AbstractMethodDeclaration[] { constructor };
} else {
AbstractMethodDeclaration[] newMethods;
System.arraycopy(
this.methods,
0,
newMethods = new AbstractMethodDeclaration[this.methods.length + 1],
1,
this.methods.length);
newMethods[0] = constructor;
this.methods = newMethods;
}
}
return constructor;
}
// anonymous type constructor creation: rank is important since bindings already got sorted
public MethodBinding createDefaultConstructorWithBinding(MethodBinding inheritedConstructorBinding, boolean eraseThrownExceptions) {
//Add to method'set, the default constuctor that just recall the
//super constructor with the same arguments
String baseName = "$anonymous"; //$NON-NLS-1$
TypeBinding[] argumentTypes = inheritedConstructorBinding.parameters;
int argumentsLength = argumentTypes.length;
//the constructor
ConstructorDeclaration constructor = new ConstructorDeclaration(this.compilationResult);
constructor.selector = new char[] { 'x' }; //no maining
constructor.sourceStart = this.sourceStart;
constructor.sourceEnd = this.sourceEnd;
int newModifiers = this.modifiers & ExtraCompilerModifiers.AccVisibilityMASK;
if (inheritedConstructorBinding.isVarargs()) {
newModifiers |= ClassFileConstants.AccVarargs;
}
constructor.modifiers = newModifiers;
constructor.bits |= ASTNode.IsDefaultConstructor;
if (argumentsLength > 0) {
Argument[] arguments = (constructor.arguments = new Argument[argumentsLength]);
for (int i = argumentsLength; --i >= 0;) {
arguments[i] = new Argument((baseName + i).toCharArray(), 0L, null /*type ref*/, ClassFileConstants.AccDefault);
}
}
//the super call inside the constructor
constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
constructor.constructorCall.sourceStart = this.sourceStart;
constructor.constructorCall.sourceEnd = this.sourceEnd;
if (argumentsLength > 0) {
Expression[] args1;
args1 = constructor.constructorCall.arguments = new Expression[argumentsLength];
for (int i = argumentsLength; --i >= 0;) {
args1[i] = new SingleNameReference((baseName + i).toCharArray(), 0L);
}
}
//adding the constructor in the methods list
if (this.methods == null) {
this.methods = new AbstractMethodDeclaration[] { constructor };
} else {
AbstractMethodDeclaration[] newMethods;
System.arraycopy(this.methods, 0, newMethods = new AbstractMethodDeclaration[this.methods.length + 1], 1, this.methods.length);
newMethods[0] = constructor;
this.methods = newMethods;
}
//============BINDING UPDATE==========================
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6
ReferenceBinding[] thrownExceptions = eraseThrownExceptions
? this.scope.environment().convertToRawTypes(inheritedConstructorBinding.thrownExceptions, true, true)
: inheritedConstructorBinding.thrownExceptions;
SourceTypeBinding sourceType = this.binding;
constructor.binding = new MethodBinding(
constructor.modifiers, //methodDeclaration
argumentsLength == 0 ? Binding.NO_PARAMETERS : argumentTypes, //arguments bindings
thrownExceptions, //exceptions
sourceType); //declaringClass
constructor.binding.tagBits |= (inheritedConstructorBinding.tagBits & TagBits.HasMissingType);
constructor.binding.modifiers |= ExtraCompilerModifiers.AccIsDefaultConstructor;
if (inheritedConstructorBinding.parameterNonNullness != null // this implies that annotation based null analysis is enabled
&& argumentsLength > 0)
{
// copy nullness info from inherited constructor to the new constructor:
int len = inheritedConstructorBinding.parameterNonNullness.length;
System.arraycopy(inheritedConstructorBinding.parameterNonNullness, 0,
constructor.binding.parameterNonNullness = new Boolean[len], 0, len);
}
// TODO(stephan): do argument types already carry sufficient info about type annotations?
constructor.scope = new MethodScope(this.scope, constructor, true);
constructor.bindArguments();
constructor.constructorCall.resolve(constructor.scope);
//{ObjectTeams: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=140643
/* orig:
MethodBinding[] methodBindings = sourceType.methods(); // trigger sorting
int length;
System.arraycopy(methodBindings, 0, methodBindings = new MethodBinding[(length = methodBindings.length) + 1], 1, length);
methodBindings[0] = constructor.binding;
if (++length > 1)
ReferenceBinding.sortMethods(methodBindings, 0, length); // need to resort, since could be valid methods ahead (140643) - DOM needs eager sorting
sourceType.setMethods(methodBindings);
:giro*/
// we have the infrastructure so use it:
this.binding.addMethod(constructor.binding);
// SH}
//===================================================
return constructor.binding;
}
/**
* Find the matching parse node, answers null if nothing found
*/
public FieldDeclaration declarationOf(FieldBinding fieldBinding) {
if (fieldBinding != null && this.fields != null) {
for (int i = 0, max = this.fields.length; i < max; i++) {
FieldDeclaration fieldDecl;
if ((fieldDecl = this.fields[i]).binding == fieldBinding)
return fieldDecl;
}
}
return null;
}
/**
* Find the matching parse node, answers null if nothing found
*/
public TypeDeclaration declarationOf(MemberTypeBinding memberTypeBinding) {
if (memberTypeBinding != null && this.memberTypes != null) {
for (int i = 0, max = this.memberTypes.length; i < max; i++) {
TypeDeclaration memberTypeDecl;
if (TypeBinding.equalsEquals((memberTypeDecl = this.memberTypes[i]).binding, memberTypeBinding))
return memberTypeDecl;
}
}
return null;
}
/**
* Find the matching parse node, answers null if nothing found
*/
public AbstractMethodDeclaration declarationOf(MethodBinding methodBinding) {
if (methodBinding != null && this.methods != null) {
for (int i = 0, max = this.methods.length; i < max; i++) {
AbstractMethodDeclaration methodDecl;
if ((methodDecl = this.methods[i]).binding == methodBinding)
return methodDecl;
}
}
return null;
}
/**
* Find the matching parse node, answers null if nothing found
*/
public RecordComponent declarationOf(RecordComponentBinding recordComponentBinding) {
if (recordComponentBinding != null && this.recordComponents != null) {
for (int i = 0, max = this.fields.length; i < max; i++) {
RecordComponent recordComponent;
if ((recordComponent = this.recordComponents[i]).binding == recordComponentBinding)
return recordComponent;
}
}
return null;
}
//{ObjectTeams: find source of a method mapping
public AbstractMethodMappingDeclaration declarationOf(CallinCalloutBinding mappingBinding) {
if (mappingBinding != null && this.callinCallouts != null) {
for (int i = 0, max = this.callinCallouts.length; i < max; i++) {
AbstractMethodMappingDeclaration mappingDecl;
if ((mappingDecl = this.callinCallouts[i]).binding == mappingBinding)
return mappingDecl;
}
}
return null;
}
// SH}
/**
* Finds the matching type amoung this type's member types.
* Returns null if no type with this name is found.
* The type name is a compound name relative to this type
* e.g. if this type is X and we're looking for Y.X.A.B
* then a type name would be {X, A, B}
*/
public TypeDeclaration declarationOfType(char[][] typeName) {
int typeNameLength = typeName.length;
if (typeNameLength < 1 || !CharOperation.equals(typeName[0], this.name)) {
return null;
}
if (typeNameLength == 1) {
return this;
}
char[][] subTypeName = new char[typeNameLength - 1][];
System.arraycopy(typeName, 1, subTypeName, 0, typeNameLength - 1);
for (int i = 0; i < this.memberTypes.length; i++) {
TypeDeclaration typeDecl = this.memberTypes[i].declarationOfType(subTypeName);
if (typeDecl != null) {
return typeDecl;
}
}
return null;
}
@Override
public CompilationUnitDeclaration getCompilationUnitDeclaration() {
if (this.scope != null) {
return this.scope.compilationUnitScope().referenceContext;
}
return null;
}
/* only for records */
public ConstructorDeclaration getConstructor(Parser parser) {
if (this.methods != null) {
for (int i = this.methods.length; --i >= 0;) {
AbstractMethodDeclaration am;
if ((am = this.methods[i]).isConstructor()) {
if (!CharOperation.equals(am.selector, this.name)) {
// the constructor was in fact a method with no return type
// unless an explicit constructor call was supplied
ConstructorDeclaration c = (ConstructorDeclaration) am;
if (c.constructorCall == null || c.constructorCall.isImplicitSuper()) { //changed to a method
MethodDeclaration m = parser.convertToMethodDeclaration(c, this.compilationResult);
this.methods[i] = m;
}
} else {
if (am instanceof CompactConstructorDeclaration) {
CompactConstructorDeclaration ccd = (CompactConstructorDeclaration) am;
ccd.recordDeclaration = this;
if (ccd.arguments == null)
ccd.arguments = getArgumentsFromComponents(this.recordComponents);
return ccd;
}
// now we are looking at a "normal" constructor
if (this.recordComponents == null && am.arguments == null)
return (ConstructorDeclaration) am;
}
}
}
}
/* At this point we can only say that there is high possibility that there is a constructor
* If it is a CCD, then definitely it is there (except for empty one); else we need to check
* the bindings to say that there is a canonical constructor. To take care at binding resolution time.
*/
return null;
}
/**
* Generic bytecode generation for type
*/
public void generateCode(ClassFile enclosingClassFile) {
if ((this.bits & ASTNode.HasBeenGenerated) != 0)
return;
this.bits |= ASTNode.HasBeenGenerated;
//{ObjectTeams: don't generate code from types that have entered the compilation process
// via SourceTypeConverter (at least line number positions are missing).
// There should be no need to generate this code (except for copy inheritance??).
if (this.isConverted && !(isTeam() && getTeamModel().containsRoFi(true)))
return;
// from now on treat base class problem as fatal:
if (RoleModel.isRoleWithBaseProblem(this))
this.ignoreFurtherInvestigation= true;
// SH}
if (this.ignoreFurtherInvestigation) {
if (this.binding == null)
return;
//{ObjectTeams: don't let tsub-roles search for byte code of this or members:
markMissingBytecode();
// SH}
ClassFile.createProblemType(
this,
this.scope.referenceCompilationUnit().compilationResult);
return;
}
//{ObjectTeams: ensure tsuper role has byte code generated,
// even in nested team of the same compilation unit:
if (isRole()) {
// search common enclosing team:
ReferenceBinding superTeam = this.binding.enclosingType().superclass();
ReferenceBinding prevSuper = superTeam;
ReferenceBinding currentSuper = superTeam.enclosingType();
ClassFile enclosingSuperClassFile = null;
superLoop: while (currentSuper != null) {
ReferenceBinding current = this.binding.enclosingType().enclosingType();
enclosingSuperClassFile = enclosingClassFile.enclosingClassFile;
while (current != null) {
if (TypeBinding.equalsEquals(current, currentSuper))
break superLoop;
current = current.enclosingType();
enclosingSuperClassFile = enclosingSuperClassFile.enclosingClassFile;
}
prevSuper = currentSuper;
currentSuper = currentSuper.enclosingType();
}
// if source type found then generate
if (currentSuper != null && !prevSuper.isBinaryBinding()) {
TypeDeclaration superType = ((SourceTypeBinding)prevSuper.erasure()).scope.referenceContext;
superType.generateCode((ClassScope)null, enclosingSuperClassFile);
}
}
if (isTeam()) {
CopyInheritance.copySyntheticTeamMethods(this);
getTeamModel().updateDecapsAccessIds();
}
// value paramters:
this.binding.computeValueParameterSlotSizes();
// SH}
try {
// create the result for a compiled type
ClassFile classFile = ClassFile.getNewInstance(this.binding);
classFile.initialize(this.binding, enclosingClassFile, false);
if (this.binding.isMemberType()) {
classFile.recordInnerClasses(this.binding);
} else if (this.binding.isLocalType()) {
enclosingClassFile.recordInnerClasses(this.binding);
classFile.recordInnerClasses(this.binding);
}
TypeVariableBinding[] typeVariables = this.binding.typeVariables();
for (int i = 0, max = typeVariables.length; i < max; i++) {
TypeVariableBinding typeVariableBinding = typeVariables[i];
if ((typeVariableBinding.tagBits & TagBits.ContainsNestedTypeReferences) != 0) {
Util.recordNestedType(classFile, typeVariableBinding);
}
}
// generate all fiels
classFile.addFieldInfos();
//{ObjectTeams: public role fields need accessors:
if (isRole() && this.fields != null) {
for (FieldDeclaration field : this.fields) {
if ( !(field instanceof Initializer) // initializer doesn't have a binding set.
&& !field.isGenerated
&& field.binding.isPublic())
{
// prepare for accessed via externalized receiver:
SourceTypeBinding enclosingTeam = (SourceTypeBinding)this.binding.enclosingType();
enclosingTeam.addSyntheticMethod(field.binding, true, false/*superAccess*/, true/*externalizedReceiver*/);
enclosingTeam.addSyntheticMethod(field.binding, false, false/*superAccess*/, true/*externalizedReceiver*/);
}
}
}
// SH}
//{ObjectTeams: for teams get all members, including binary roles read via the RoFiCache
if (isTeam()) {
ReferenceBinding[] members = getTeamModel().getKnownRoles();
for (int i = 0; i < members.length; i++)
classFile.recordInnerClasses(members[i]);
}
new BytecodeTransformer().checkCopyNonWideConstants(this.scope, classFile);
// SH}
if (this.memberTypes != null) {
//{ObjectTeams: FIXME(SH): should we need sorting? [need super-types first for copy inheritance in nested teams]
// if (this.isTeam())
// sortMemberTypes();
// SH}
for (int i = 0, max = this.memberTypes.length; i < max; i++) {
TypeDeclaration memberType = this.memberTypes[i];
//{ObjectTeams: roles are already handled above
if (!memberType.isRole())
// SH}
classFile.recordInnerClasses(memberType.binding);
memberType.generateCode(this.scope, classFile);
}
}
// generate all methods
classFile.setForMethodInfos();
if (this.methods != null) {
for (int i = 0, max = this.methods.length; i < max; i++) {
//{ObjectTeams: not all methods have statements:
if (needToProcess(this.methods[i]))
// SH}
this.methods[i].generateCode(this.scope, classFile);
}
}
// generate all synthetic and abstract methods
classFile.addSpecialMethods(this);
if (this.ignoreFurtherInvestigation) { // trigger problem type generation for code gen errors
throw new AbortType(this.scope.referenceCompilationUnit().compilationResult, null);
}
//{ObjectTeams: role files and copy inheritance:
// store a role file cache?
if (isTeam())
getTeamModel().generateRoFiCache(classFile);
// support for JSR-045:
char[] smap = getSMAP();
if (smap != null)
{
getModel().addAttribute(InlineAttribute.sourceDebugExtensionAttribute(smap));
}
// SH}
// finalize the compiled type result
classFile.addAttributes();
this.scope.referenceCompilationUnit().compilationResult.record(
this.binding.constantPoolName(),
classFile);
} catch (AbortType e) {
if (this.binding == null)
return;
ClassFile.createProblemType(
this,
this.scope.referenceCompilationUnit().compilationResult);
}
//{ObjectTeams: mark as done:
finally {
if (this.isTeam())
this.teamModel.setState(ITranslationStates.STATE_BYTE_CODE_GENERATED);
}
// SH}
}
//{ObjectTeams: don't let tsub-roles search for byte code of this or members:
private void markMissingBytecode() {
// descend into all methods and nested types (members and local types)
traverse(new ASTVisitor() {
@Override
public boolean visit(MethodDeclaration method, ClassScope classScope) {
if (method.binding != null)
method.binding.bytecodeMissing = true;
return true;
}
@Override
public boolean visit(ConstructorDeclaration ctor, ClassScope classScope) {
if (ctor.binding != null)
ctor.binding.bytecodeMissing = true;
return true;
}
@Override
public boolean visit(TypeDeclaration type, ClassScope classScope) {
type.tagAsHavingErrors();
return true;
}
@Override
public boolean visit(TypeDeclaration type, BlockScope blockScope) {
type.tagAsHavingErrors();
return true;
}
}, this.scope);
}
// SH}
//{ObjectTeams: trigger for smap generation
private char[] getSMAP() {
//if type is an interface don't generate anything
if (isInterface())
return null;
AbstractSmapGenerator generator = createSmapGenerator();
if (generator == null)
return null;
generator.addStratum(ISMAPConstants.OTJ_STRATUM_NAME);
generator.setDefaultStratum(ISMAPConstants.OTJ_STRATUM_NAME);
return generator.generate();
}
protected AbstractSmapGenerator createSmapGenerator()
{
if (isRole())
return new RoleSmapGenerator(this);
if (isTeam())
return new TeamSmapGenerator(this);
// NOTE: currently no special treatment for nested team (team&role)
return null;
}
//ike}
/**
* Bytecode generation for a local inner type (API as a normal statement code gen)
*/
@Override
public void generateCode(BlockScope blockScope, CodeStream codeStream) {
if ((this.bits & ASTNode.IsReachable) == 0) {
return;
}
if ((this.bits & ASTNode.HasBeenGenerated) != 0) return;
int pc = codeStream.position;
if (this.binding != null) {
SyntheticArgumentBinding[] enclosingInstances = ((NestedTypeBinding) this.binding).syntheticEnclosingInstances();
for (int i = 0, slotSize = 0, count = enclosingInstances == null ? 0 : enclosingInstances.length; i < count; i++){
SyntheticArgumentBinding enclosingInstance = enclosingInstances[i];
enclosingInstance.resolvedPosition = ++slotSize; // shift by 1 to leave room for aload0==this
if (slotSize > 0xFF) { // no more than 255 words of arguments
blockScope.problemReporter().noMoreAvailableSpaceForArgument(enclosingInstance, blockScope.referenceType());
}
}
}
generateCode(codeStream.classFile);
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
/**
* Bytecode generation for a member inner type
*/
public void generateCode(ClassScope classScope, ClassFile enclosingClassFile) {
if ((this.bits & ASTNode.HasBeenGenerated) != 0) return;
//{ObjectTeams: ensure intermediate steps for role files:
if (this.binding != null)
Dependencies.ensureBindingState(this.binding, ITranslationStates.STATE_BYTE_CODE_GENERATED-1);
// SH}
if (this.binding != null) {
SyntheticArgumentBinding[] enclosingInstances = ((NestedTypeBinding) this.binding).syntheticEnclosingInstances();
for (int i = 0, slotSize = 0, count = enclosingInstances == null ? 0 : enclosingInstances.length; i < count; i++){
SyntheticArgumentBinding enclosingInstance = enclosingInstances[i];
enclosingInstance.resolvedPosition = ++slotSize; // shift by 1 to leave room for aload0==this
if (slotSize > 0xFF) { // no more than 255 words of arguments
classScope.problemReporter().noMoreAvailableSpaceForArgument(enclosingInstance, classScope.referenceType());
}
}
}
generateCode(enclosingClassFile);
}
/**
* Bytecode generation for a package member
*/
public void generateCode(CompilationUnitScope unitScope) {
generateCode((ClassFile) null);
}
@Override
public boolean hasErrors() {
return this.ignoreFurtherInvestigation;
}
/**
* Common flow analysis for all types
*/
private void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) {
checkYieldUsage();
//{ObjectTeams: postponed from resolve() to also catch import usage from late statement generators.
if (isRoleFile() && !this.binding.isSynthInterface())
if (!this.compilationResult.hasSyntaxError)
this.scope.referenceCompilationUnit().checkUnusedImports();
if (this.scope instanceof OTClassScope)
((OTClassScope)this.scope).checkUnusedImports();
// SH}
if (!this.binding.isUsed() && this.binding.isOrEnclosedByPrivateType()) {
if (!this.scope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
this.scope.problemReporter().unusedPrivateType(this);
}
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=385780
if (this.typeParameters != null &&
!this.scope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
for (int i = 0, length = this.typeParameters.length; i < length; ++i) {
TypeParameter typeParameter = this.typeParameters[i];
//{ObjectTeams: exclude:
if (!(typeParameter instanceof TypeValueParameter))
// SH}
if ((typeParameter.binding.modifiers & ExtraCompilerModifiers.AccLocallyUsed) == 0) {
this.scope.problemReporter().unusedTypeParameter(typeParameter);
}
}
}
// for local classes we use the flowContext as our parent, but never use an initialization context for this purpose
// see Bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop)
FlowContext parentContext = (flowContext instanceof InitializationFlowContext) ? null : flowContext;
InitializationFlowContext initializerContext = new InitializationFlowContext(parentContext, this, flowInfo, flowContext, this.initializerScope);
// no static initializer in local classes, thus no need to set parent:
InitializationFlowContext staticInitializerContext = new InitializationFlowContext(null, this, flowInfo, flowContext, this.staticInitializerScope);
FlowInfo nonStaticFieldInfo = flowInfo.unconditionalFieldLessCopy();
FlowInfo staticFieldInfo = flowInfo.unconditionalFieldLessCopy();
if (this.fields != null) {
for (int i = 0, count = this.fields.length; i < count; i++) {
FieldDeclaration field = this.fields[i];
if (field.isStatic()) {
if ((staticFieldInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0)
field.bits &= ~ASTNode.IsReachable;
/*if (field.isField()){
staticInitializerContext.handledExceptions = NoExceptions; // no exception is allowed jls8.3.2
} else {*/
staticInitializerContext.handledExceptions = Binding.ANY_EXCEPTION; // tolerate them all, and record them
/*}*/
staticFieldInfo = field.analyseCode(this.staticInitializerScope, staticInitializerContext, staticFieldInfo);
// in case the initializer is not reachable, use a reinitialized flowInfo and enter a fake reachable
// branch, since the previous initializer already got the blame.
if (staticFieldInfo == FlowInfo.DEAD_END) {
this.staticInitializerScope.problemReporter().initializerMustCompleteNormally(field);
staticFieldInfo = FlowInfo.initial(this.maxFieldCount).setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
} else {
if ((nonStaticFieldInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0)
field.bits &= ~ASTNode.IsReachable;
/*if (field.isField()){
initializerContext.handledExceptions = NoExceptions; // no exception is allowed jls8.3.2
} else {*/
initializerContext.handledExceptions = Binding.ANY_EXCEPTION; // tolerate them all, and record them
/*}*/
nonStaticFieldInfo = field.analyseCode(this.initializerScope, initializerContext, nonStaticFieldInfo);
// in case the initializer is not reachable, use a reinitialized flowInfo and enter a fake reachable
// branch, since the previous initializer already got the blame.
if (nonStaticFieldInfo == FlowInfo.DEAD_END) {
this.initializerScope.problemReporter().initializerMustCompleteNormally(field);
nonStaticFieldInfo = FlowInfo.initial(this.maxFieldCount).setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
}
}
}
//{ObjectTeams: manage synthetics for value parameters:
if (this.typeParameters != null) {
// type value parameters are non static fields, which are a-priori assigned.
nonStaticFieldInfo = TypeValueParameter.analyseCode(this.typeParameters, this.initializerScope, flowContext, nonStaticFieldInfo);
}
// analyse member types in topological order (super before sub):
Sorting.sortMemberTypes(this);
// SH}
if (this.memberTypes != null) {
for (int i = 0, count = this.memberTypes.length; i < count; i++) {
if (flowContext != null){ // local type
this.memberTypes[i].analyseCode(this.scope, flowContext, nonStaticFieldInfo.copy().setReachMode(flowInfo.reachMode())); // reset reach mode in case initializers did abrupt completely
} else {
this.memberTypes[i].analyseCode(this.scope);
}
}
}
if (this.scope.compilerOptions().complianceLevel >= ClassFileConstants.JDK9) {
// synthesize <clinit> if one is not present. Required to initialize
// synthetic final fields as modifying final fields outside of a <clinit>
// is disallowed in Java 9
//{ObjectTeams: robustness:
/* orig:
if (this.methods == null || !this.methods[0].isClinit()) {
:giro */
if (this.methods == null || this.methods.length == 0 || !this.methods[0].isClinit()) {
// SH}
Clinit clinit = new Clinit(this.compilationResult);
clinit.declarationSourceStart = clinit.sourceStart = this.sourceStart;
clinit.declarationSourceEnd = clinit.sourceEnd = this.sourceEnd;
clinit.bodyEnd = this.sourceEnd;
int length = this.methods == null ? 0 : this.methods.length;
AbstractMethodDeclaration[] methodDeclarations = new AbstractMethodDeclaration[length + 1];
methodDeclarations[0] = clinit;
if (this.methods != null)
System.arraycopy(this.methods, 0, methodDeclarations, 1, length);
}
}
//{ObjectTeams: RoFi with incomplete method bodies don't analyze:
if (this.compilationUnit == null || this.compilationUnit.parseMethodBodies)
// SH}
if (this.methods != null) {
UnconditionalFlowInfo outerInfo = flowInfo.unconditionalFieldLessCopy();
FlowInfo constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo);
SimpleSetOfCharArray jUnitMethodSourceValues = getJUnitMethodSourceValues();
for (int i = 0, count = this.methods.length; i < count; i++) {
AbstractMethodDeclaration method = this.methods[i];
if (method.ignoreFurtherInvestigation)
continue;
//{ObjectTeams: have statements to analyze?
if (!(needToProcess(method)))
continue;
// SH}
if (method.isInitializationMethod()) {
// pass down the appropriate initializerContext:
if (method.isStatic()) { // <clinit>
((Clinit)method).analyseCode(
this.scope,
staticInitializerContext,
staticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo));
} else { // constructor
((ConstructorDeclaration)method).analyseCode(this.scope, initializerContext, constructorInfo.copy(), flowInfo.reachMode());
}
} else { // regular method
// JUnit 5 only accepts methods without arguments for method sources
if (method.arguments == null && jUnitMethodSourceValues.includes(method.selector) && method.binding != null) {
method.binding.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
}
// pass down the parentContext (NOT an initializer context, see above):
((MethodDeclaration)method).analyseCode(this.scope, parentContext, flowInfo.copy());
}
}
}
// enable enum support ?
if (this.binding.isEnum() && !this.binding.isAnonymousType()) {
this.enumValuesSyntheticfield = this.binding.addSyntheticFieldForEnumValues();
}
//{ObjectTeams: callins and precedence:
mergePrecedences();
// now check for replace callin bindings with missing base call result:
if (this.callinCallouts != null) {
WeavingScheme weavingScheme = this.initializerScope.compilerOptions().weavingScheme;
for (int i = 0; i < this.callinCallouts.length; i++) {
if (this.callinCallouts[i].isReplaceCallin())
((CallinMappingDeclaration)this.callinCallouts[i]).analyseDetails(this, weavingScheme);
}
}
// SH}
}
//{ObjectTeams:
// analyseCode and generateCode only process methods with statements plus default ctors
private boolean needToProcess(AbstractMethodDeclaration method) {
return method.hasParsedStatements
// different reasons, why it may be normal for a method to have no statements:
|| method.isDefaultConstructor() || method.isCanonicalConstructor() || method.isClinit() || method.isAbstract()
|| method.isCopied;
}
/**
* After precedences of this type and super types are resolved,
* try to merge them (inherited & defined in different roles)
* and check for duplicates.
*/
private void mergePrecedences() {
// merge precedences:
PrecedenceBinding[] precedenceBindings
= this.binding.precedences
= PrecedenceDeclaration.mergePrecedences(this);
// outermost team writes the byte code attribute:
if (precedenceBindings != PrecedenceBinding.NoPrecedences) {
if ( isTeam()
&& ( this.enclosingType == null
|| !this.enclosingType.isTeam()))
{
this.model = getTeamModel();
for (int i = 0; i < precedenceBindings.length; i++) {
// includes flattening of class based precedences and elimination of overrides:
CallinCalloutBinding[] callins = precedenceBindings[i].callins(true);
this.model.addAttribute(new CallinPrecedenceAttribute(this.binding, callins));
}
}
}
// check for success only after everything is copied, merged and flattened:
if (isTeam()
&& ( this.enclosingType == null
|| !this.enclosingType.isTeam())) // only check outer-most team.
PrecedenceBinding.checkDuplicates(this);
}
// SH}
private void checkYieldUsage() {
long sourceLevel = this.scope.compilerOptions().sourceLevel;
if (sourceLevel < ClassFileConstants.JDK14 || this.name == null ||
!("yield".equals(new String(this.name)))) //$NON-NLS-1$
return;
if (sourceLevel >= ClassFileConstants.JDK14) {
this.scope.problemReporter().switchExpressionsYieldTypeDeclarationError(this);
} else {
this.scope.problemReporter().switchExpressionsYieldTypeDeclarationWarning(this);
}
}
private SimpleSetOfCharArray getJUnitMethodSourceValues() {
SimpleSetOfCharArray junitMethodSourceValues = new SimpleSetOfCharArray();
for (AbstractMethodDeclaration methodDeclaration : this.methods) {
if (methodDeclaration.annotations != null) {
for (Annotation annotation : methodDeclaration.annotations) {
if (annotation.resolvedType != null && annotation.resolvedType.id == TypeIds.T_OrgJunitJupiterParamsProviderMethodSource) {
addJUnitMethodSourceValues(junitMethodSourceValues, annotation, methodDeclaration.selector);
}
}
}
}
return junitMethodSourceValues;
}
private void addJUnitMethodSourceValues(SimpleSetOfCharArray junitMethodSourceValues, Annotation annotation, char[] methodName) {
for (MemberValuePair memberValuePair : annotation.memberValuePairs()) {
if (CharOperation.equals(memberValuePair.name, TypeConstants.VALUE)) {
Expression value = memberValuePair.value;
if (value instanceof ArrayInitializer) { // e.g. @MethodSource({ "someMethod" })
ArrayInitializer arrayInitializer = (ArrayInitializer) value;
for (Expression arrayValue : arrayInitializer.expressions) {
junitMethodSourceValues.add(getValueAsChars(arrayValue));
}
} else {
junitMethodSourceValues.add(getValueAsChars(value));
}
return;
}
}
// value member not specified (i.e. marker annotation): JUnit 5 defaults to the test method's name
junitMethodSourceValues.add(methodName);
}
private char[] getValueAsChars(Expression value) {
if (value instanceof StringLiteral) { // e.g. "someMethod"
return ((StringLiteral) value).source;
} else if (value.constant instanceof StringConstant) { // e.g. SOME_CONSTANT + "value"
return ((StringConstant) value.constant).stringValue().toCharArray();
}
return CharOperation.NO_CHAR;
}
public final static int kind(int flags) {
switch (flags & (ClassFileConstants.AccInterface|ClassFileConstants.AccAnnotation|ClassFileConstants.AccEnum|ExtraCompilerModifiers.AccRecord)) {
case ClassFileConstants.AccInterface :
return TypeDeclaration.INTERFACE_DECL;
case ClassFileConstants.AccInterface|ClassFileConstants.AccAnnotation :
return TypeDeclaration.ANNOTATION_TYPE_DECL;
case ClassFileConstants.AccEnum :
return TypeDeclaration.ENUM_DECL;
case ExtraCompilerModifiers.AccRecord :
return TypeDeclaration.RECORD_DECL;
default :
return TypeDeclaration.CLASS_DECL;
}
}
public boolean isRecord() {
return (this.modifiers & ExtraCompilerModifiers.AccRecord) != 0;
}
/*
* Access emulation for a local type
* force to emulation of access to direct enclosing instance.
* By using the initializer scope, we actually only request an argument emulation, the
* field is not added until actually used. However we will force allocations to be qualified
* with an enclosing instance.
* 15.9.2
*/
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) return;
NestedTypeBinding nestedType = (NestedTypeBinding) this.binding;
MethodScope methodScope = currentScope.methodScope();
if (!methodScope.isStatic && !methodScope.isConstructorCall){
nestedType.addSyntheticArgumentAndField(nestedType.enclosingType());
}
// add superclass enclosing instance arg for anonymous types (if necessary)
if (nestedType.isAnonymousType()) {
ReferenceBinding superclassBinding = (ReferenceBinding)nestedType.superclass.erasure();
if (superclassBinding.enclosingType() != null && !superclassBinding.isStatic()) {
if (!superclassBinding.isLocalType()
|| ((NestedTypeBinding)superclassBinding).getSyntheticField(superclassBinding.enclosingType(), true) != null
|| superclassBinding.isMemberType()){
nestedType.addSyntheticArgument(superclassBinding.enclosingType());
}
}
// From 1.5 on, provide access to enclosing instance synthetic constructor argument when declared inside constructor call
// only for direct anonymous type
//public class X {
// void foo() {}
// class M {
// M(Object o) {}
// M() { this(new Object() { void baz() { foo(); }}); } // access to #foo() indirects through constructor synthetic arg: val$this$0
// }
//}
if (!methodScope.isStatic && methodScope.isConstructorCall && currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_5) {
ReferenceBinding enclosing = nestedType.enclosingType();
if (enclosing.isNestedType()) {
NestedTypeBinding nestedEnclosing = (NestedTypeBinding)enclosing;
// if (nestedEnclosing.findSuperTypeErasingTo(nestedEnclosing.enclosingType()) == null) { // only if not inheriting
SyntheticArgumentBinding syntheticEnclosingInstanceArgument = nestedEnclosing.getSyntheticArgument(nestedEnclosing.enclosingType(), true, false);
if (syntheticEnclosingInstanceArgument != null) {
nestedType.addSyntheticArgumentAndField(syntheticEnclosingInstanceArgument);
}
}
// }
}
}
}
/**
* Access emulation for a local member type
* force to emulation of access to direct enclosing instance.
* By using the initializer scope, we actually only request an argument emulation, the
* field is not added until actually used. However we will force allocations to be qualified
* with an enclosing instance.
*
* Local member cannot be static.
*/
public void manageEnclosingInstanceAccessIfNecessary(ClassScope currentScope, FlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) {
NestedTypeBinding nestedType = (NestedTypeBinding) this.binding;
nestedType.addSyntheticArgumentAndField(this.binding.enclosingType());
}
}
/**
* A <clinit> will be requested as soon as static fields or assertions are present. It will be eliminated during
* classfile creation if no bytecode was actually produced based on some optimizations/compiler settings.
*/
public final boolean needClassInitMethod() {
// always need a <clinit> when assertions are present
if ((this.bits & ASTNode.ContainsAssertion) != 0)
return true;
switch (kind(this.modifiers)) {
case TypeDeclaration.INTERFACE_DECL:
case TypeDeclaration.ANNOTATION_TYPE_DECL:
return this.fields != null; // fields are implicitly statics
case TypeDeclaration.ENUM_DECL:
return true; // even if no enum constants, need to set $VALUES array
}
if (this.fields != null) {
for (int i = this.fields.length; --i >= 0;) {
FieldDeclaration field = this.fields[i];
//need to test the modifier directly while there is no binding yet
if ((field.modifiers & ClassFileConstants.AccStatic) != 0)
return true; // TODO (philippe) shouldn't it check whether field is initializer or has some initial value ?
}
}
return false;
}
public void parseMethods(Parser parser, CompilationUnitDeclaration unit) {
//connect method bodies
if (unit.ignoreMethodBodies)
return;
//{ObjectTeams: enable OT keywords?
if (isTeam() || isRoleFile())
parser.scanner.enterOTSource();
// SH}
//members
if (this.memberTypes != null) {
int length = this.memberTypes.length;
for (int i = 0; i < length; i++) {
TypeDeclaration typeDeclaration = this.memberTypes[i];
//{ObjectTeams: don't parse role files via containing team, but let Dependencies do the job.
if (!typeDeclaration.isRoleFile()) {
// orig:
typeDeclaration.parseMethods(parser, unit);
this.bits |= (typeDeclaration.bits & ASTNode.HasSyntaxErrors);
// :giro
}
// SH}
}
}
//methods
if (this.methods != null) {
int length = this.methods.length;
for (int i = 0; i < length; i++) {
AbstractMethodDeclaration abstractMethodDeclaration = this.methods[i];
abstractMethodDeclaration.parseStatements(parser, unit);
this.bits |= (abstractMethodDeclaration.bits & ASTNode.HasSyntaxErrors);
}
}
//initializers
if (this.fields != null) {
int length = this.fields.length;
for (int i = 0; i < length; i++) {
final FieldDeclaration fieldDeclaration = this.fields[i];
switch(fieldDeclaration.getKind()) {
case AbstractVariableDeclaration.INITIALIZER:
((Initializer) fieldDeclaration).parseStatements(parser, this, unit);
this.bits |= (fieldDeclaration.bits & ASTNode.HasSyntaxErrors);
break;
}
}
}
//{ObjectTeams: method mappings:
if (this.callinCallouts != null) {
int length = this.callinCallouts.length;
for (int i = 0; i < length; i++) {
this.callinCallouts[i].parseParamMappings(parser, unit);
}
}
// SH}
}
@Override
public StringBuffer print(int indent, StringBuffer output) {
if (this.javadoc != null) {
this.javadoc.print(indent, output);
}
if ((this.bits & ASTNode.IsAnonymousType) == 0) {
printIndent(indent, output);
printHeader(0, output);
}
return printBody(indent, output);
}
public StringBuffer printBody(int indent, StringBuffer output) {
output.append(" {"); //$NON-NLS-1$
//{ObjectTeams: precedence declarations:
if (this.precedences != null) {
for (int i = 0; i < this.precedences.length; i++) {
if (this.precedences[i] != null) {
output.append('\n');
this.precedences[i].print(indent + 1, output);
}
}
}
// SH}
if (this.memberTypes != null) {
for (int i = 0; i < this.memberTypes.length; i++) {
if (this.memberTypes[i] != null) {
output.append('\n');
this.memberTypes[i].print(indent + 1, output);
}
}
}
if (this.fields != null) {
for (int fieldI = 0; fieldI < this.fields.length; fieldI++) {
if (this.fields[fieldI] != null) {
output.append('\n');
this.fields[fieldI].print(indent + 1, output);
}
}
}
if (this.methods != null) {
for (int i = 0; i < this.methods.length; i++) {
if (this.methods[i] != null) {
output.append('\n');
this.methods[i].print(indent + 1, output);
}
}
}
//{ObjectTeams: print method mappings:
if (this.callinCallouts != null) {
for (int i = 0; i < this.callinCallouts.length; i++) {
if (this.callinCallouts[i] != null) {
output.append("\n"); //$NON-NLS-1$
this.callinCallouts[i].print(indent + 1,output);
}
}
}
//Markus Witte}
output.append('\n');
return printIndent(indent, output).append('}');
}
public StringBuffer printHeader(int indent, StringBuffer output) {
//{ObjectTeams: workaround conflict wrt AccDefaultMethod vs. AccTeam:
/* orig:
printModifiers(this.modifiers, output);
:giro */
printModifiers(this.modifiers & ExtraCompilerModifiers.AccJustFlag, output);
// SH}
if (this.annotations != null) {
printAnnotations(this.annotations, output);
output.append(' ');
}
//{ObjectTeams: role/team:
printTypeKind(this.modifiers, output);
// SH}
switch (kind(this.modifiers)) {
case TypeDeclaration.CLASS_DECL :
output.append("class "); //$NON-NLS-1$
break;
case TypeDeclaration.INTERFACE_DECL :
output.append("interface "); //$NON-NLS-1$
break;
case TypeDeclaration.ENUM_DECL :
output.append("enum "); //$NON-NLS-1$
break;
case TypeDeclaration.ANNOTATION_TYPE_DECL :
output.append("@interface "); //$NON-NLS-1$
break;
case TypeDeclaration.RECORD_DECL :
output.append("record "); //$NON-NLS-1$
break;
}
output.append(this.name);
if (this.isRecord()) {
output.append('(');
if (this.nRecordComponents > 0 && this.fields != null) {
for (int i = 0; i < this.nRecordComponents; i++) {
if (i > 0) output.append(", "); //$NON-NLS-1$
output.append(this.fields[i].type.getTypeName()[0]);
output.append(' ');
output.append(this.fields[i].name);
}
}
output.append(')');
}
if (this.typeParameters != null) {
output.append("<");//$NON-NLS-1$
for (int i = 0; i < this.typeParameters.length; i++) {
if (i > 0) output.append( ", "); //$NON-NLS-1$
this.typeParameters[i].print(0, output);
}
output.append(">");//$NON-NLS-1$
}
if (!this.isRecord() && this.superclass != null) {
output.append(" extends "); //$NON-NLS-1$
this.superclass.print(0, output);
}
if (this.superInterfaces != null && this.superInterfaces.length > 0) {
switch (kind(this.modifiers)) {
case TypeDeclaration.CLASS_DECL :
case TypeDeclaration.ENUM_DECL :
case TypeDeclaration.RECORD_DECL :
output.append(" implements "); //$NON-NLS-1$
break;
case TypeDeclaration.INTERFACE_DECL :
case TypeDeclaration.ANNOTATION_TYPE_DECL :
output.append(" extends "); //$NON-NLS-1$
break;
}
for (int i = 0; i < this.superInterfaces.length; i++) {
if (i > 0) output.append( ", "); //$NON-NLS-1$
this.superInterfaces[i].print(0, output);
}
}
//{ObjectTeams: print playedBy
if (this.baseclass != null)
{
output.append(" playedBy "); //$NON-NLS-1$
this.baseclass.print(0,output);
}
//}
if (this.permittedTypes != null && this.permittedTypes.length > 0) {
output.append(" permits "); //$NON-NLS-1$
for (int i = 0; i < this.permittedTypes.length; i++) {
if (i > 0) output.append( ", "); //$NON-NLS-1$
this.permittedTypes[i].print(0, output);
}
}
return output;
}
@Override
public StringBuffer printStatement(int tab, StringBuffer output) {
return print(tab, output);
}
/*
* Keep track of number of lambda/method reference expressions in this type declaration.
* Return the 0 based "ordinal" in the TypeDeclaration.
*/
public int record(FunctionalExpression expression) {
return this.functionalExpressionsCount++;
}
public void resolve() {
//{ObjectTeams:
// signaling:
StateHelper.startProcessing(this, ITranslationStates.STATE_RESOLVED, 0);
// warnings in ROFI:
if (isRoleFile() && isSourceRole())
this.compilationUnit.reportNLSProblems();
// SH}
SourceTypeBinding sourceType = this.binding;
if (sourceType == null) {
this.ignoreFurtherInvestigation = true;
return;
}
try {
if (CharOperation.equals(this.name, TypeConstants.VAR)) {
if (this.scope.compilerOptions().sourceLevel < ClassFileConstants.JDK10) {
this.scope.problemReporter().varIsReservedTypeNameInFuture(this);
} else {
this.scope.problemReporter().varIsReservedTypeName(this);
}
}
this.scope.problemReporter().validateRestrictedKeywords(this.name, this);
// resolve annotations and check @Deprecated annotation
long annotationTagBits = sourceType.getAnnotationTagBits();
if ((annotationTagBits & TagBits.AnnotationDeprecated) == 0
&& (sourceType.modifiers & ClassFileConstants.AccDeprecated) != 0
&& this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
this.scope.problemReporter().missingDeprecatedAnnotationForType(this);
}
if ((annotationTagBits & TagBits.AnnotationFunctionalInterface) != 0) {
if(!this.binding.isFunctionalInterface(this.scope)) {
this.scope.problemReporter().notAFunctionalInterface(this);
}
}
//{ObjectTeams: check @Override annotation:
boolean hasOverrideAnnotation = (this.binding.tagBits & TagBits.AnnotationOverride) != 0;
if (isSourceRole()) {
boolean hasTSuper = (this.binding.modifiers & ExtraCompilerModifiers.AccOverriding) != 0
|| this.roleModel.getTSuperRoleBindings().length > 0;
if (hasOverrideAnnotation && !hasTSuper)
this.scope.problemReporter().roleMustOverride(this);
else if (hasTSuper && !hasOverrideAnnotation)
this.scope.problemReporter().missingOverrideAnnotationForRole(this);
} // @Override on non-role types is detected by Annotation.resolveType
// SH}
if ((this.bits & ASTNode.UndocumentedEmptyBlock) != 0) {
this.scope.problemReporter().undocumentedEmptyBlock(this.bodyStart-1, this.bodyEnd);
}
boolean needSerialVersion =
this.scope.compilerOptions().getSeverity(CompilerOptions.MissingSerialVersion) != ProblemSeverities.Ignore
&& sourceType.isClass()
&& !sourceType.isRecord()
&& sourceType.findSuperTypeOriginatingFrom(TypeIds.T_JavaIoExternalizable, false /*Externalizable is not a class*/) == null
&& sourceType.findSuperTypeOriginatingFrom(TypeIds.T_JavaIoSerializable, false /*Serializable is not a class*/) != null;
if (needSerialVersion) {
// if Object writeReplace() throws java.io.ObjectStreamException is present, then no serialVersionUID is needed
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=101476
CompilationUnitScope compilationUnitScope = this.scope.compilationUnitScope();
MethodBinding methodBinding = sourceType.getExactMethod(TypeConstants.WRITEREPLACE, Binding.NO_TYPES, compilationUnitScope);
ReferenceBinding[] throwsExceptions;
needSerialVersion =
methodBinding == null
|| !methodBinding.isValidBinding()
|| methodBinding.returnType.id != TypeIds.T_JavaLangObject
|| (throwsExceptions = methodBinding.thrownExceptions).length != 1
|| throwsExceptions[0].id != TypeIds.T_JavaIoObjectStreamException;
if (needSerialVersion) {
// check the presence of an implementation of the methods
// private void writeObject(java.io.ObjectOutputStream out) throws IOException
// private void readObject(java.io.ObjectInputStream out) throws IOException
boolean hasWriteObjectMethod = false;
boolean hasReadObjectMethod = false;
TypeBinding argumentTypeBinding = this.scope.getType(TypeConstants.JAVA_IO_OBJECTOUTPUTSTREAM, 3);
if (argumentTypeBinding.isValidBinding()) {
methodBinding = sourceType.getExactMethod(TypeConstants.WRITEOBJECT, new TypeBinding[] { argumentTypeBinding }, compilationUnitScope);
hasWriteObjectMethod = methodBinding != null
&& methodBinding.isValidBinding()
&& methodBinding.modifiers == ClassFileConstants.AccPrivate
&& methodBinding.returnType == TypeBinding.VOID
&& (throwsExceptions = methodBinding.thrownExceptions).length == 1
&& throwsExceptions[0].id == TypeIds.T_JavaIoException;
}
argumentTypeBinding = this.scope.getType(TypeConstants.JAVA_IO_OBJECTINPUTSTREAM, 3);
if (argumentTypeBinding.isValidBinding()) {
methodBinding = sourceType.getExactMethod(TypeConstants.READOBJECT, new TypeBinding[] { argumentTypeBinding }, compilationUnitScope);
hasReadObjectMethod = methodBinding != null
&& methodBinding.isValidBinding()
&& methodBinding.modifiers == ClassFileConstants.AccPrivate
&& methodBinding.returnType == TypeBinding.VOID
&& (throwsExceptions = methodBinding.thrownExceptions).length == 1
&& throwsExceptions[0].id == TypeIds.T_JavaIoException;
}
needSerialVersion = !hasWriteObjectMethod || !hasReadObjectMethod;
}
}
// generics (and non static generic members) cannot extend Throwable
if (sourceType.findSuperTypeOriginatingFrom(TypeIds.T_JavaLangThrowable, true) != null) {
ReferenceBinding current = sourceType;
checkEnclosedInGeneric : do {
if (current.isGenericType()) {
this.scope.problemReporter().genericTypeCannotExtendThrowable(this);
break checkEnclosedInGeneric;
}
if (current.isStatic()) break checkEnclosedInGeneric;
if (current.isLocalType()) {
NestedTypeBinding nestedType = (NestedTypeBinding) current.erasure();
if (nestedType.scope.methodScope().isStatic) break checkEnclosedInGeneric;
}
} while ((current = current.enclosingType()) != null);
}
//{ObjectTeams: resolve precedence declarations
if (this.precedences != null && this.precedences.length > 0)
{
if (isTeam() || isRole()) {
this.binding.precedences = new PrecedenceBinding[this.precedences.length];
// process back to front because addResolvedPrecedenceDeclaration will always insert at front:
for (int i = this.precedences.length-1; i>=0; i--)
this.binding.precedences[i] = this.precedences[i].resolve(this);
} else {
this.scope.problemReporter().precedenceInRegularClass(this, this.precedences);
}
}
// SH}
// this.maxFieldCount might already be set
int localMaxFieldCount = 0;
int lastVisibleFieldID = -1;
boolean hasEnumConstants = false;
FieldDeclaration[] enumConstantsWithoutBody = null;
//{ObjectTeams: don't cache count, array may grow during this loop!
int resolvedMemberCount = 0; // but remember for later :)
// orig:
if (this.memberTypes != null) {
/*
for (int i = 0, count = this.memberTypes.length; i < count; i++) {
:giro */
resolvedMemberCount = this.memberTypes.length;
for (int i = 0; i < this.memberTypes.length; i++) {
// SH}
this.memberTypes[i].resolve(this.scope);
}
}
//{ObjectTeams: should we work at all?
Config config = Config.getConfig();
boolean fieldsAndMethods = config != null && config.verifyMethods;
boolean hasFinalFieldInit = false;
if (fieldsAndMethods) {
// SH}
if (this.recordComponents != null) {
for (RecordComponent rc : this.recordComponents) {
rc.resolve(this.initializerScope);
}
}
if (this.fields != null) {
for (int i = 0, count = this.fields.length; i < count; i++) {
FieldDeclaration field = this.fields[i];
switch(field.getKind()) {
case AbstractVariableDeclaration.ENUM_CONSTANT:
hasEnumConstants = true;
if (!(field.initialization instanceof QualifiedAllocationExpression)) {
if (enumConstantsWithoutBody == null)
enumConstantsWithoutBody = new FieldDeclaration[count];
enumConstantsWithoutBody[i] = field;
}
//$FALL-THROUGH$
case AbstractVariableDeclaration.FIELD:
FieldBinding fieldBinding = field.binding;
if (fieldBinding == null) {
// still discover secondary errors
if (field.initialization != null) field.initialization.resolve(field.isStatic() ? this.staticInitializerScope : this.initializerScope);
this.ignoreFurtherInvestigation = true;
continue;
}
if (needSerialVersion
&& ((fieldBinding.modifiers & (ClassFileConstants.AccStatic | ClassFileConstants.AccFinal)) == (ClassFileConstants.AccStatic | ClassFileConstants.AccFinal))
&& CharOperation.equals(TypeConstants.SERIALVERSIONUID, fieldBinding.name)
&& TypeBinding.equalsEquals(TypeBinding.LONG, fieldBinding.type)) {
needSerialVersion = false;
}
localMaxFieldCount++;
lastVisibleFieldID = field.binding.id;
//{ObjectTeams: has init?
if (field.initialization != null && field.isFinal())
hasFinalFieldInit = true;
// SH}
break;
case AbstractVariableDeclaration.INITIALIZER:
((Initializer) field).lastVisibleFieldID = lastVisibleFieldID + 1;
break;
}
field.resolve(field.isStatic() ? this.staticInitializerScope : this.initializerScope);
}
}
//{ObjectTeams: also count type value parameters into maxFieldCount
if (this.typeParameters != null)
TypeValueParameter.updateMaxFieldCount(this);
if (hasFinalFieldInit && isRole())
WordValueAttribute.addClassFlags(getRoleModel(), IOTConstants.OT_CLASS_HAS_FINAL_FIELD_INITS);
}
// SH}
if (this.maxFieldCount < localMaxFieldCount) {
this.maxFieldCount = localMaxFieldCount;
}
if (needSerialVersion) {
//check that the current type doesn't extend javax.rmi.CORBA.Stub
TypeBinding javaxRmiCorbaStub = this.scope.getType(TypeConstants.JAVAX_RMI_CORBA_STUB, 4);
if (javaxRmiCorbaStub.isValidBinding()) {
ReferenceBinding superclassBinding = this.binding.superclass;
loop: while (superclassBinding != null) {
if (TypeBinding.equalsEquals(superclassBinding, javaxRmiCorbaStub)) {
needSerialVersion = false;
break loop;
}
superclassBinding = superclassBinding.superclass();
}
}
if (needSerialVersion) {
this.scope.problemReporter().missingSerialVersion(this);
}
}
// check extends/implements for annotation type
switch(kind(this.modifiers)) {
case TypeDeclaration.ANNOTATION_TYPE_DECL :
if (this.superclass != null) {
this.scope.problemReporter().annotationTypeDeclarationCannotHaveSuperclass(this);
}
if (this.superInterfaces != null) {
this.scope.problemReporter().annotationTypeDeclarationCannotHaveSuperinterfaces(this);
}
break;
case TypeDeclaration.ENUM_DECL :
// check enum abstract methods
if (this.binding.isAbstract()) {
if (!hasEnumConstants) {
for (int i = 0, count = this.methods.length; i < count; i++) {
final AbstractMethodDeclaration methodDeclaration = this.methods[i];
if (methodDeclaration.isAbstract() && methodDeclaration.binding != null)
this.scope.problemReporter().enumAbstractMethodMustBeImplemented(methodDeclaration);
}
} else if (enumConstantsWithoutBody != null) {
for (int i = 0, count = this.methods.length; i < count; i++) {
final AbstractMethodDeclaration methodDeclaration = this.methods[i];
if (methodDeclaration.isAbstract() && methodDeclaration.binding != null) {
for (int f = 0, l = enumConstantsWithoutBody.length; f < l; f++)
if (enumConstantsWithoutBody[f] != null)
this.scope.problemReporter().enumConstantMustImplementAbstractMethod(methodDeclaration, enumConstantsWithoutBody[f]);
}
}
}
}
break;
}
int missingAbstractMethodslength = this.missingAbstractMethods == null ? 0 : this.missingAbstractMethods.length;
int methodsLength = this.methods == null ? 0 : this.methods.length;
if ((methodsLength + missingAbstractMethodslength) > 0xFFFF) {
this.scope.problemReporter().tooManyMethods(this);
}
//{ObjectTeams: flag that from now on all added methods need to be resolved:
// TODO(SH): this should cooperate with the incremental image builder,
// such as to cause one more loop of compilation, hopefully
// containing all required sources that time...
if (StateMemento.hasMethodResolveStarted(this.binding)) {
// FIXME(SH): introduced in r10641 this abort does not cooperate well with lateRolesCatchUp()
// haven't seen it in ages, can it be removed??
this.scope.problemReporter().abortDueToInternalError("circular compilation dependency"); //$NON-NLS-1$
return;
}
StateMemento.methodResolveStart(this.binding);
// any role types found during field resolve? If so, catch up now:
if (this.memberTypes != null && this.memberTypes.length > resolvedMemberCount
&& this.teamModel != null)
{
Dependencies.lateRolesCatchup(this.teamModel);
}
// SH}
//{ObjectTeams: should we work?
if (fieldsAndMethods) {
// SH}
if (this.methods != null) {
for (int i = 0, count = this.methods.length; i < count; i++) {
this.methods[i].resolve(this.scope);
}
}
//{ObjectTeams:
}
// SH}
// Resolve javadoc
if (this.javadoc != null) {
if (this.scope != null && (this.name != TypeConstants.PACKAGE_INFO_NAME)) {
// if the type is package-info, the javadoc was resolved as part of the compilation unit javadoc
this.javadoc.resolve(this.scope);
}
} else if (!sourceType.isLocalType()) {
// Set javadoc visibility
int visibility = sourceType.modifiers & ExtraCompilerModifiers.AccVisibilityMASK;
ProblemReporter reporter = this.scope.problemReporter();
int severity = reporter.computeSeverity(IProblem.JavadocMissing);
//{ObjectTeams: not for generated or copied types
if (this.isGenerated || this.isPurelyCopied)
severity = ProblemSeverities.Ignore;
// SH}
if (severity != ProblemSeverities.Ignore) {
if (this.enclosingType != null) {
visibility = Util.computeOuterMostVisibility(this.enclosingType, visibility);
}
int javadocModifiers = (this.binding.modifiers & ~ExtraCompilerModifiers.AccVisibilityMASK) | visibility;
reporter.javadocMissing(this.sourceStart, this.sourceEnd, severity, javadocModifiers);
}
}
updateNestInfo();
FieldDeclaration[] fieldsDecls = this.fields;
if (fieldsDecls != null) {
for (FieldDeclaration fieldDeclaration : fieldsDecls)
fieldDeclaration.resolveJavadoc(this.initializerScope);
}
AbstractMethodDeclaration[] methodDecls = this.methods;
if (methodDecls != null) {
for (AbstractMethodDeclaration methodDeclaration : methodDecls)
methodDeclaration.resolveJavadoc();
}
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
return;
}
}
/**
* Resolve a local type declaration
*/
@Override
public void resolve(BlockScope blockScope) {
// need to build its scope first and proceed with binding's creation
if ((this.bits & ASTNode.IsAnonymousType) == 0) {
// check collision scenarii
Binding existing = blockScope.getType(this.name);
if (existing instanceof ReferenceBinding
&& existing != this.binding
&& existing.isValidBinding()) {
ReferenceBinding existingType = (ReferenceBinding) existing;
if (existingType instanceof TypeVariableBinding) {
blockScope.problemReporter().typeHiding(this, (TypeVariableBinding) existingType);
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=312989, check for collision with enclosing type.
Scope outerScope = blockScope.parent;
checkOuterScope:while (outerScope != null) {
Binding existing2 = outerScope.getType(this.name);
if (existing2 instanceof TypeVariableBinding && existing2.isValidBinding()) {
TypeVariableBinding tvb = (TypeVariableBinding) existingType;
Binding declaringElement = tvb.declaringElement;
if (declaringElement instanceof ReferenceBinding
&& CharOperation.equals(((ReferenceBinding) declaringElement).sourceName(), this.name)) {
blockScope.problemReporter().typeCollidesWithEnclosingType(this);
break checkOuterScope;
}
} else if (existing2 instanceof ReferenceBinding
&& existing2.isValidBinding()
&& outerScope.isDefinedInType((ReferenceBinding) existing2)) {
blockScope.problemReporter().typeCollidesWithEnclosingType(this);
break checkOuterScope;
} else if (existing2 == null) {
break checkOuterScope;
}
outerScope = outerScope.parent;
}
} else if (existingType instanceof LocalTypeBinding
&& ((LocalTypeBinding) existingType).scope.methodScope() == blockScope.methodScope()) {
// dup in same method
blockScope.problemReporter().duplicateNestedType(this);
} else if (existingType instanceof LocalTypeBinding && blockScope.isLambdaSubscope()
&& blockScope.enclosingLambdaScope().enclosingMethodScope() == ((LocalTypeBinding) existingType).scope.methodScope()) {
blockScope.problemReporter().duplicateNestedType(this);
} else if (blockScope.isDefinedInType(existingType)) {
// collision with enclosing type
blockScope.problemReporter().typeCollidesWithEnclosingType(this);
} else if (blockScope.isDefinedInSameUnit(existingType)){ // only consider hiding inside same unit
// hiding sibling
blockScope.problemReporter().typeHiding(this, existingType);
}
}
blockScope.addLocalType(this);
}
if (this.binding != null) {
// remember local types binding for innerclass emulation propagation
blockScope.referenceCompilationUnit().record((LocalTypeBinding)this.binding);
//{ObjectTeams: trigger intermediate steps:
Dependencies.ensureBindingState(this.binding, ITranslationStates.STATE_RESOLVED-1);
// SH}
// binding is not set if the receiver could not be created
resolve();
updateMaxFieldCount();
}
}
/**
* Resolve a member type declaration (can be a local member)
*/
public void resolve(ClassScope upperScope) {
//{ObjectTeams: role files may be resolved already
if (isRoleFile())
if (this.roleModel.getState() >= ITranslationStates.STATE_RESOLVED)
return;
// SH}
// member scopes are already created
// request the construction of a binding if local member type
if (this.binding != null && this.binding instanceof LocalTypeBinding) {
// remember local types binding for innerclass emulation propagation
upperScope.referenceCompilationUnit().record((LocalTypeBinding)this.binding);
}
resolve();
updateMaxFieldCount();
}
/**
* Resolve a top level type declaration
*/
public void resolve(CompilationUnitScope upperScope) {
// top level : scope are already created
resolve();
updateMaxFieldCount();
}
@Override
public void tagAsHavingErrors() {
//{ObjectTeams: tag class and interface part:
if (isRole() && this.roleModel != null)
this.roleModel.setErrorFlag(true); // both parts
else
// SH}
this.ignoreFurtherInvestigation = true;
}
@Override
public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
// Nothing to do for this context;
}
//{ObjectTeams: untag class and interface part:
@Override
public void resetErrorFlag() {
if (isRole() && this.roleModel != null)
this.roleModel.setErrorFlag(false); // both parts
else
this.ignoreFurtherInvestigation = false;
}
// SH}
//{ObjectTeams: push precedence declaration out to the team:
public void addResolvedPrecedence(char[] roleName, PrecedenceBinding precBinding)
{
if (this.enclosingType != null) {
this.enclosingType.addResolvedPrecedence(this.name, precBinding);
return;
}
if (this.binding.precedences == PrecedenceBinding.NoPrecedences) {
this.binding.precedences = new PrecedenceBinding[] { precBinding };
} else {
// insert at front to account for ordering according to 4.8(d):
// - inner before outer
// - textual order (same nesting level)
int len = this.binding.precedences.length;
System.arraycopy(
this.binding.precedences, 0,
this.binding.precedences = new PrecedenceBinding[len+1], 1,
len);
this.binding.precedences[0] = precBinding;
}
}
// SH}
/**
* Iteration for a package member type
*
*/
public void traverse(ASTVisitor visitor, CompilationUnitScope unitScope) {
try {
if (visitor.visit(this, unitScope)) {
if (this.javadoc != null) {
this.javadoc.traverse(visitor, this.scope);
}
if (this.annotations != null) {
int annotationsLength = this.annotations.length;
for (int i = 0; i < annotationsLength; i++)
this.annotations[i].traverse(visitor, this.staticInitializerScope);
}
if (this.superclass != null)
this.superclass.traverse(visitor, this.scope);
//{ObjectTeams
if (this.baseclass != null)
this.baseclass.traverse(visitor, this.scope);
// Markus Witte}
if (this.superInterfaces != null) {
int length = this.superInterfaces.length;
for (int i = 0; i < length; i++)
this.superInterfaces[i].traverse(visitor, this.scope);
}
if (this.permittedTypes != null) {
int length = this.permittedTypes.length;
for (int i = 0; i < length; i++)
this.permittedTypes[i].traverse(visitor, this.scope);
}
if (this.typeParameters != null) {
int length = this.typeParameters.length;
for (int i = 0; i < length; i++) {
this.typeParameters[i].traverse(visitor, this.scope);
}
}
if (this.recordComponents != null) {
int length = this.recordComponents.length;
for (int i = 0; i < length; i++)
this.recordComponents[i].traverse(visitor, this.initializerScope);
}
if (this.memberTypes != null) {
int length = this.memberTypes.length;
for (int i = 0; i < length; i++)
this.memberTypes[i].traverse(visitor, this.scope);
}
if (this.fields != null) {
int length = this.fields.length;
for (int i = 0; i < length; i++) {
FieldDeclaration field;
if ((field = this.fields[i]).isStatic()) {
field.traverse(visitor, this.staticInitializerScope);
} else {
field.traverse(visitor, this.initializerScope);
}
}
}
if (this.methods != null) {
int length = this.methods.length;
for (int i = 0; i < length; i++)
this.methods[i].traverse(visitor, this.scope);
}
//{ObjectTeams
if(this.callinCallouts != null) {
int methodBindingsLength = this.callinCallouts.length;
for(int idx = 0; idx < methodBindingsLength; idx++)
this.callinCallouts[idx].traverse(visitor, this.scope);
}
// Joachim Haensel}
}
visitor.endVisit(this, unitScope);
} catch (AbortType e) {
// silent abort
}
}
/**
* Iteration for a local inner type
*/
@Override
public void traverse(ASTVisitor visitor, BlockScope blockScope) {
try {
if (visitor.visit(this, blockScope)) {
if (this.javadoc != null) {
this.javadoc.traverse(visitor, this.scope);
}
if (this.annotations != null) {
int annotationsLength = this.annotations.length;
for (int i = 0; i < annotationsLength; i++)
this.annotations[i].traverse(visitor, this.staticInitializerScope);
}
if (this.superclass != null)
this.superclass.traverse(visitor, this.scope);
if (this.superInterfaces != null) {
int length = this.superInterfaces.length;
for (int i = 0; i < length; i++)
this.superInterfaces[i].traverse(visitor, this.scope);
}
if (this.permittedTypes != null) {
int length = this.permittedTypes.length;
for (int i = 0; i < length; i++)
this.permittedTypes[i].traverse(visitor, this.scope);
}
if (this.typeParameters != null) {
int length = this.typeParameters.length;
for (int i = 0; i < length; i++) {
this.typeParameters[i].traverse(visitor, this.scope);
}
}
if (this.recordComponents != null) {
int length = this.recordComponents.length;
for (int i = 0; i < length; i++)
this.recordComponents[i].traverse(visitor, this.initializerScope);
}
if (this.memberTypes != null) {
int length = this.memberTypes.length;
for (int i = 0; i < length; i++)
this.memberTypes[i].traverse(visitor, this.scope);
}
if (this.fields != null) {
int length = this.fields.length;
for (int i = 0; i < length; i++) {
FieldDeclaration field = this.fields[i];
if (field.isStatic() && !field.isFinal()) {
// local type cannot have static fields that are not final, https://bugs.eclipse.org/bugs/show_bug.cgi?id=244544
} else {
field.traverse(visitor, this.initializerScope);
}
}
}
if (this.methods != null) {
int length = this.methods.length;
for (int i = 0; i < length; i++)
this.methods[i].traverse(visitor, this.scope);
}
// Note(SH): local innertypes can't have callinCallouts
}
visitor.endVisit(this, blockScope);
} catch (AbortType e) {
// silent abort
}
}
/**
* Iteration for a member innertype
*
*/
public void traverse(ASTVisitor visitor, ClassScope classScope) {
try {
if (visitor.visit(this, classScope)) {
if (this.javadoc != null) {
this.javadoc.traverse(visitor, this.scope);
}
if (this.annotations != null) {
int annotationsLength = this.annotations.length;
for (int i = 0; i < annotationsLength; i++)
this.annotations[i].traverse(visitor, this.staticInitializerScope);
}
if (this.superclass != null)
this.superclass.traverse(visitor, this.scope);
if (this.superInterfaces != null) {
int length = this.superInterfaces.length;
for (int i = 0; i < length; i++)
this.superInterfaces[i].traverse(visitor, this.scope);
}
if (this.permittedTypes != null) {
int length = this.permittedTypes.length;
for (int i = 0; i < length; i++)
this.permittedTypes[i].traverse(visitor, this.scope);
}
if (this.typeParameters != null) {
int length = this.typeParameters.length;
for (int i = 0; i < length; i++) {
this.typeParameters[i].traverse(visitor, this.scope);
}
}
if (this.recordComponents != null) {
int length = this.recordComponents.length;
for (int i = 0; i < length; i++)
this.recordComponents[i].traverse(visitor, this.initializerScope);
}
if (this.memberTypes != null) {
int length = this.memberTypes.length;
for (int i = 0; i < length; i++)
this.memberTypes[i].traverse(visitor, this.scope);
}
if (this.fields != null) {
int length = this.fields.length;
for (int i = 0; i < length; i++) {
FieldDeclaration field;
if ((field = this.fields[i]).isStatic()) {
field.traverse(visitor, this.staticInitializerScope);
} else {
field.traverse(visitor, this.initializerScope);
}
}
}
if (this.methods != null) {
int length = this.methods.length;
for (int i = 0; i < length; i++)
this.methods[i].traverse(visitor, this.scope);
}
//{ObjectTeams: method mappings:
if (this.callinCallouts != null) {
int callinCalloutsLength = this.callinCallouts.length;
for (int i = 0; i < callinCalloutsLength; i++)
this.callinCallouts[i].traverse(visitor, this.scope);
}
// SH}
}
visitor.endVisit(this, classScope);
} catch (AbortType e) {
// silent abort
}
}
/**
* MaxFieldCount's computation is necessary so as to reserve space for
* the flow info field portions. It corresponds to the maximum amount of
* fields this class or one of its innertypes have.
*
* During name resolution, types are traversed, and the max field count is recorded
* on the outermost type. It is then propagated down during the flow analysis.
*
* This method is doing either up/down propagation.
*/
void updateMaxFieldCount() {
if (this.binding == null)
return; // error scenario
TypeDeclaration outerMostType = this.scope.outerMostClassScope().referenceType();
if (this.maxFieldCount > outerMostType.maxFieldCount) {
outerMostType.maxFieldCount = this.maxFieldCount; // up
} else {
this.maxFieldCount = outerMostType.maxFieldCount; // down
}
}
private SourceTypeBinding findNestHost() {
ClassScope classScope = this.scope.enclosingTopMostClassScope();
return classScope != null ? classScope.referenceContext.binding : null;
}
void updateNestInfo() {
if (this.binding == null)
return;
SourceTypeBinding nestHost = findNestHost();
if (nestHost != null && !this.binding.equals(nestHost)) {// member
this.binding.setNestHost(nestHost);
nestHost.addNestMember(this.binding);
}
}
public boolean isPackageInfo() {
return CharOperation.equals(this.name, TypeConstants.PACKAGE_INFO_NAME);
}
/**
* Returns whether the type is a secondary one or not.
*/
public boolean isSecondary() {
return (this.bits & ASTNode.IsSecondaryType) != 0;
}
//{ObjectTeams
/**
* If a role has an invalid playedBy binding, do not stop to process
* the role class, just skip all its method bindings.
*/
public void pushDownBindingProblem() {
this.ignoreFurtherInvestigation = false;
if (this.callinCallouts != null)
for (int i=0;i<this.callinCallouts.length;i++)
this.callinCallouts[i].ignoreFurtherInvestigation = true;
// these bindings are not useful, simply delete them:
this.binding.callinCallouts = Binding.NO_CALLIN_CALLOUT_BINDINGS;
}
/**
* Remove unwanted elements created during dietParse: fields, methods, methodMappings
*/
public void removeDetails() {
this.fields = null;
this.methods = null;
this.callinCallouts = null;
if (this.memberTypes != null)
for (int i = 0; i < this.memberTypes.length; i++)
this.memberTypes[i].removeDetails();
}
public void cleanupModels() {
if (this.roleModel != null) {
this.roleModel.cleanup();
this.roleModel.setState(ITranslationStates.STATE_FINAL);
}
if (this.teamModel != null) {
this.teamModel.cleanup();
this.teamModel.setState(ITranslationStates.STATE_FINAL);
}
if (this.model != null) {
this.model.cleanup();
this.model.setState(ITranslationStates.STATE_FINAL);
}
// TODO (SH): when do we know, we can free the byte code?
// need to find out, when all tsub-roles are done (which is impossible)!
// don't: model.forgetByteCode();
}
// SH}
}