blob: 9722d1dcf0627b78af335f7059951fcb5ba55634 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.env.IGenericType;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.parser.*;
import org.eclipse.jdt.internal.compiler.problem.*;
public class TypeDeclaration
extends Statement
implements ProblemSeverities, ReferenceContext {
public static final char[] ANONYMOUS_EMPTY_NAME = new char[] {};
public int modifiers = AccDefault;
public int modifiersSourceStart;
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 bodyStart;
public int bodyEnd; // doesn't include the trailing comment if any.
protected boolean hasBeenGenerated = false;
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
// 1.5 support
public TypeParameter[] typeParameters;
public TypeDeclaration(CompilationResult compilationResult){
this.compilationResult = compilationResult;
}
/*
* We cause the compilation task to abort to a given extent.
*/
public void abort(int abortLevel, IProblem problem) {
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 = sourceStart;
clinit.declarationSourceEnd = clinit.sourceEnd = sourceEnd;
clinit.bodyEnd = 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 = sourceStart;
methodDeclaration.sourceEnd = sourceEnd;
methodDeclaration.modifiers = methodBinding.getAccessFlags() & ~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*/, 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, //methodDeclaration
methodBinding.selector,
methodBinding.returnType,
argumentsLength == 0 ? NoParameters : argumentTypes, //arguments bindings
methodBinding.thrownExceptions, //exceptions
binding); //declaringClass
methodDeclaration.scope = new MethodScope(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
*
*/
public FlowInfo analyseCode(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo) {
if (ignoreFurtherInvestigation)
return flowInfo;
try {
if (flowInfo.isReachable()) {
bits |= IsReachableMASK;
LocalTypeBinding localType = (LocalTypeBinding) 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;
}
return flowInfo;
}
/**
* Flow analysis for a member innertype
*
*/
public void analyseCode(ClassScope enclosingClassScope) {
if (ignoreFurtherInvestigation)
return;
try {
// propagate down the max field count
updateMaxFieldCount();
internalAnalyseCode(null, FlowInfo.initial(maxFieldCount));
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
}
}
/**
* Flow analysis for a local member innertype
*
*/
public void analyseCode(
ClassScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo) {
if (ignoreFurtherInvestigation)
return;
try {
if (flowInfo.isReachable()) {
bits |= IsReachableMASK;
LocalTypeBinding localType = (LocalTypeBinding) 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 (ignoreFurtherInvestigation)
return;
try {
internalAnalyseCode(null, FlowInfo.initial(maxFieldCount));
} 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 (methods != null) {
for (int i = methods.length; --i >= 0;) {
AbstractMethodDeclaration am;
if ((am = methods[i]).isConstructor()) {
if (!CharOperation.equals(am.selector, 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);
methods[i] = m;
}
} else {
if (this.kind() == IGenericType.INTERFACE_DECL) {
// report the problem and continue the parsing
parser.problemReporter().interfaceCannotHaveConstructors(
(ConstructorDeclaration) am);
}
hasConstructor = true;
}
}
}
}
return hasConstructor;
}
public CompilationResult compilationResult() {
return this.compilationResult;
}
public ConstructorDeclaration createDefaultConstructor(
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)
//the constructor
ConstructorDeclaration constructor = new ConstructorDeclaration(this.compilationResult);
constructor.isDefaultConstructor = true;
constructor.selector = this.name;
if (modifiers != AccDefault) {
constructor.modifiers =
(((this.bits & ASTNode.IsMemberTypeMASK) != 0) && (modifiers & AccPrivate) != 0)
? AccDefault
: modifiers & AccVisibilityMASK;
}
//if you change this setting, please update the
//SourceIndexer2.buildTypeDeclaration(TypeDeclaration,char[]) method
constructor.declarationSourceStart = constructor.sourceStart = sourceStart;
constructor.declarationSourceEnd =
constructor.sourceEnd = constructor.bodyEnd = sourceEnd;
//the super call inside the constructor
if (needExplicitConstructorCall) {
constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
constructor.constructorCall.sourceStart = sourceStart;
constructor.constructorCall.sourceEnd = sourceEnd;
}
//adding the constructor in the methods list
if (needToInsert) {
if (methods == null) {
methods = new AbstractMethodDeclaration[] { constructor };
} else {
AbstractMethodDeclaration[] newMethods;
System.arraycopy(
methods,
0,
newMethods = new AbstractMethodDeclaration[methods.length + 1],
1,
methods.length);
newMethods[0] = constructor;
methods = newMethods;
}
}
return constructor;
}
// anonymous type constructor creation
public MethodBinding createDefaultConstructorWithBinding(MethodBinding inheritedConstructorBinding) {
//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 cd = new ConstructorDeclaration(this.compilationResult);
cd.selector = new char[] { 'x' }; //no maining
cd.sourceStart = sourceStart;
cd.sourceEnd = sourceEnd;
int newModifiers = modifiers & AccVisibilityMASK;
if (inheritedConstructorBinding.isVarargs()) {
newModifiers |= AccVarargs;
}
cd.modifiers = newModifiers;
cd.isDefaultConstructor = true;
if (argumentsLength > 0) {
Argument[] arguments = (cd.arguments = new Argument[argumentsLength]);
for (int i = argumentsLength; --i >= 0;) {
arguments[i] = new Argument((baseName + i).toCharArray(), 0L, null /*type ref*/, AccDefault);
}
}
//the super call inside the constructor
cd.constructorCall = SuperReference.implicitSuperConstructorCall();
cd.constructorCall.sourceStart = sourceStart;
cd.constructorCall.sourceEnd = sourceEnd;
if (argumentsLength > 0) {
Expression[] args;
args = cd.constructorCall.arguments = new Expression[argumentsLength];
for (int i = argumentsLength; --i >= 0;) {
args[i] = new SingleNameReference((baseName + i).toCharArray(), 0L);
}
}
//adding the constructor in the methods list
if (methods == null) {
methods = new AbstractMethodDeclaration[] { cd };
} else {
AbstractMethodDeclaration[] newMethods;
System.arraycopy(
methods,
0,
newMethods = new AbstractMethodDeclaration[methods.length + 1],
1,
methods.length);
newMethods[0] = cd;
methods = newMethods;
}
//============BINDING UPDATE==========================
cd.binding = new MethodBinding(
cd.modifiers, //methodDeclaration
argumentsLength == 0 ? NoParameters : argumentTypes, //arguments bindings
inheritedConstructorBinding.thrownExceptions, //exceptions
binding); //declaringClass
cd.scope = new MethodScope(scope, cd, true);
cd.bindArguments();
cd.constructorCall.resolve(cd.scope);
if (binding.methods == null) {
binding.methods = new MethodBinding[] { cd.binding };
} else {
MethodBinding[] newMethods;
System.arraycopy(
binding.methods,
0,
newMethods = new MethodBinding[binding.methods.length + 1],
1,
binding.methods.length);
newMethods[0] = cd.binding;
binding.methods = newMethods;
}
//===================================================
return cd.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 ((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;
}
/*
* 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
* eg. 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;
}
/**
* Generic bytecode generation for type
*/
public void generateCode(ClassFile enclosingClassFile) {
if (hasBeenGenerated)
return;
hasBeenGenerated = true;
if (ignoreFurtherInvestigation) {
if (binding == null)
return;
ClassFile.createProblemType(
this,
scope.referenceCompilationUnit().compilationResult);
return;
}
try {
// create the result for a compiled type
ClassFile classFile = new ClassFile(binding, enclosingClassFile, false);
// generate all fiels
classFile.addFieldInfos();
// record the inner type inside its own .class file to be able
// to generate inner classes attributes
if (binding.isMemberType())
classFile.recordEnclosingTypeAttributes(binding);
if (binding.isLocalType()) {
enclosingClassFile.recordNestedLocalAttribute(binding);
classFile.recordNestedLocalAttribute(binding);
}
if (memberTypes != null) {
for (int i = 0, max = memberTypes.length; i < max; i++) {
// record the inner type inside its own .class file to be able
// to generate inner classes attributes
classFile.recordNestedMemberAttribute(memberTypes[i].binding);
memberTypes[i].generateCode(scope, classFile);
}
}
// generate all methods
classFile.setForMethodInfos();
if (methods != null) {
for (int i = 0, max = methods.length; i < max; i++) {
methods[i].generateCode(scope, classFile);
}
}
// generate all synthetic and abstract methods
classFile.addSpecialMethods();
if (ignoreFurtherInvestigation) { // trigger problem type generation for code gen errors
throw new AbortType(scope.referenceCompilationUnit().compilationResult, null);
}
// finalize the compiled type result
classFile.addAttributes();
scope.referenceCompilationUnit().compilationResult.record(
binding.constantPoolName(),
classFile);
} catch (AbortType e) {
if (binding == null)
return;
ClassFile.createProblemType(
this,
scope.referenceCompilationUnit().compilationResult);
}
}
/**
* Bytecode generation for a local inner type (API as a normal statement code gen)
*/
public void generateCode(BlockScope blockScope, CodeStream codeStream) {
if ((this.bits & IsReachableMASK) == 0) {
return;
}
if (hasBeenGenerated) return;
int pc = codeStream.position;
if (binding != null) ((NestedTypeBinding) binding).computeSyntheticArgumentSlotSizes();
generateCode(codeStream.classFile);
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
/**
* Bytecode generation for a member inner type
*/
public void generateCode(ClassScope classScope, ClassFile enclosingClassFile) {
if (hasBeenGenerated) return;
if (binding != null) ((NestedTypeBinding) binding).computeSyntheticArgumentSlotSizes();
generateCode(enclosingClassFile);
}
/**
* Bytecode generation for a package member
*/
public void generateCode(CompilationUnitScope unitScope) {
generateCode((ClassFile) null);
}
public boolean hasErrors() {
return this.ignoreFurtherInvestigation;
}
/**
* Common flow analysis for all types
*
*/
public void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) {
if (this.binding.isPrivate() && !this.binding.isPrivateUsed()) {
if (!scope.referenceCompilationUnit().compilationResult.hasSyntaxError()) {
scope.problemReporter().unusedPrivateType(this);
}
}
InitializationFlowContext initializerContext = new InitializationFlowContext(null, this, initializerScope);
InitializationFlowContext staticInitializerContext = new InitializationFlowContext(null, this, staticInitializerScope);
FlowInfo nonStaticFieldInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations();
FlowInfo staticFieldInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations();
if (fields != null) {
for (int i = 0, count = fields.length; i < count; i++) {
FieldDeclaration field = fields[i];
if (field.isStatic()) {
if (!staticFieldInfo.isReachable())
field.bits &= ~ASTNode.IsReachableMASK;
/*if (field.isField()){
staticInitializerContext.handledExceptions = NoExceptions; // no exception is allowed jls8.3.2
} else {*/
staticInitializerContext.handledExceptions = AnyException; // tolerate them all, and record them
/*}*/
staticFieldInfo =
field.analyseCode(
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) {
staticInitializerScope.problemReporter().initializerMustCompleteNormally(field);
staticFieldInfo = FlowInfo.initial(maxFieldCount).setReachMode(FlowInfo.UNREACHABLE);
}
} else {
if (!nonStaticFieldInfo.isReachable())
field.bits &= ~ASTNode.IsReachableMASK;
/*if (field.isField()){
initializerContext.handledExceptions = NoExceptions; // no exception is allowed jls8.3.2
} else {*/
initializerContext.handledExceptions = AnyException; // tolerate them all, and record them
/*}*/
nonStaticFieldInfo =
field.analyseCode(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) {
initializerScope.problemReporter().initializerMustCompleteNormally(field);
nonStaticFieldInfo = FlowInfo.initial(maxFieldCount).setReachMode(FlowInfo.UNREACHABLE);
}
}
}
}
if (memberTypes != null) {
for (int i = 0, count = memberTypes.length; i < count; i++) {
if (flowContext != null){ // local type
memberTypes[i].analyseCode(scope, flowContext, nonStaticFieldInfo.copy().setReachMode(flowInfo.reachMode())); // reset reach mode in case initializers did abrupt completely
} else {
memberTypes[i].analyseCode(scope);
}
}
}
if (methods != null) {
UnconditionalFlowInfo outerInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations();
FlowInfo constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo);
for (int i = 0, count = methods.length; i < count; i++) {
AbstractMethodDeclaration method = methods[i];
if (method.ignoreFurtherInvestigation)
continue;
if (method.isInitializationMethod()) {
if (method.isStatic()) { // <clinit>
method.analyseCode(
scope,
staticInitializerContext,
staticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo).setReachMode(flowInfo.reachMode())); // reset reach mode in case initializers did abrupt completely
} else { // constructor
method.analyseCode(scope, initializerContext, constructorInfo.copy().setReachMode(flowInfo.reachMode())); // reset reach mode in case initializers did abrupt completely
}
} else { // regular method
method.analyseCode(scope, null, flowInfo.copy());
}
}
}
// enable enum support ?
if (this.binding.isEnum()) {
this.enumValuesSyntheticfield = this.binding.addSyntheticFieldForEnumValues();
}
}
public int kind() {
switch (modifiers & (AccInterface|AccAnnotation|AccEnum)) {
case AccInterface :
return IGenericType.INTERFACE_DECL;
case AccInterface|AccAnnotation :
return IGenericType.ANNOTATION_TYPE_DECL;
case AccEnum :
return IGenericType.ENUM_DECL;
default :
return IGenericType.CLASS_DECL;
}
}
/*
* 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.isReachable()) return;
NestedTypeBinding nestedType = (NestedTypeBinding) 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){
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.environment().options.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);
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.isReachable()) return;
NestedTypeBinding nestedType = (NestedTypeBinding) binding;
nestedType.addSyntheticArgumentAndField(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 & AddAssertionMASK) != 0)
return true;
if (fields == null)
return false;
if (kind() == IGenericType.INTERFACE_DECL)
return true; // fields are implicitly statics
for (int i = fields.length; --i >= 0;) {
FieldDeclaration field = fields[i];
//need to test the modifier directly while there is no binding yet
if ((field.modifiers & AccStatic) != 0)
return true; // TODO (philippe) shouldn't it check whether field is initializer or has some initial value ?
if (field.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT)
return true;
}
return false;
}
public void parseMethod(Parser parser, CompilationUnitDeclaration unit) {
//connect method bodies
if (unit.ignoreMethodBodies)
return;
//members
if (memberTypes != null) {
int length = memberTypes.length;
for (int i = 0; i < length; i++)
memberTypes[i].parseMethod(parser, unit);
}
//methods
if (methods != null) {
int length = methods.length;
for (int i = 0; i < length; i++) {
methods[i].parseStatements(parser, unit);
}
}
//initializers
if (fields != null) {
int length = fields.length;
for (int i = 0; i < length; i++) {
final FieldDeclaration fieldDeclaration = fields[i];
switch(fieldDeclaration.getKind()) {
case AbstractVariableDeclaration.INITIALIZER:
((Initializer) fieldDeclaration).parseStatements(parser, this, unit);
break;
}
}
}
}
public StringBuffer print(int indent, StringBuffer output) {
if (this.javadoc != null) {
this.javadoc.print(indent, output);
}
if ((this.bits & IsAnonymousTypeMASK) == 0) {
printIndent(indent, output);
printHeader(0, output);
}
return printBody(indent, output);
}
public StringBuffer printBody(int indent, StringBuffer output) {
output.append(" {"); //$NON-NLS-1$
if (memberTypes != null) {
for (int i = 0; i < memberTypes.length; i++) {
if (memberTypes[i] != null) {
output.append('\n');
memberTypes[i].print(indent + 1, output);
}
}
}
if (fields != null) {
for (int fieldI = 0; fieldI < fields.length; fieldI++) {
if (fields[fieldI] != null) { // TODO (olivier) should improve to deal with enumconstants using ',' separator
output.append('\n');
fields[fieldI].print(indent + 1, output);
}
}
}
if (methods != null) {
for (int i = 0; i < methods.length; i++) {
if (methods[i] != null) {
output.append('\n');
methods[i].print(indent + 1, output);
}
}
}
output.append('\n');
return printIndent(indent, output).append('}');
}
public StringBuffer printHeader(int indent, StringBuffer output) {
printModifiers(this.modifiers, output);
if (this.annotations != null) printAnnotations(this.annotations, output);
switch (kind()) {
case IGenericType.CLASS_DECL :
output.append("class "); //$NON-NLS-1$
break;
case IGenericType.INTERFACE_DECL :
output.append("interface "); //$NON-NLS-1$
break;
case IGenericType.ENUM_DECL :
output.append("enum "); //$NON-NLS-1$
break;
case IGenericType.ANNOTATION_TYPE_DECL :
output.append("@interface "); //$NON-NLS-1$
break;
}
output.append(name);
if (typeParameters != null) {
output.append("<");//$NON-NLS-1$
for (int i = 0; i < typeParameters.length; i++) {
if (i > 0) output.append( ", "); //$NON-NLS-1$
typeParameters[i].print(0, output);
}
output.append(">");//$NON-NLS-1$
}
if (superclass != null) {
output.append(" extends "); //$NON-NLS-1$
superclass.print(0, output);
}
if (superInterfaces != null && superInterfaces.length > 0) {
switch (kind()) {
case IGenericType.CLASS_DECL :
case IGenericType.ENUM_DECL :
output.append(" implements "); //$NON-NLS-1$
break;
case IGenericType.INTERFACE_DECL :
case IGenericType.ANNOTATION_TYPE_DECL :
output.append(" extends "); //$NON-NLS-1$
break;
}
for (int i = 0; i < superInterfaces.length; i++) {
if (i > 0) output.append( ", "); //$NON-NLS-1$
superInterfaces[i].print(0, output);
}
}
return output;
}
public StringBuffer printStatement(int tab, StringBuffer output) {
return print(tab, output);
}
public void resolve() {
SourceTypeBinding sourceType = this.binding;
if (sourceType == null) {
this.ignoreFurtherInvestigation = true;
return;
}
try {
boolean old = this.staticInitializerScope.insideTypeAnnotation;
try {
this.staticInitializerScope.insideTypeAnnotation = true;
resolveAnnotations(this.staticInitializerScope, this.annotations, sourceType);
} finally {
this.staticInitializerScope.insideTypeAnnotation = old;
}
if ((this.bits & UndocumentedEmptyBlockMASK) != 0) {
this.scope.problemReporter().undocumentedEmptyBlock(this.bodyStart-1, this.bodyEnd);
}
boolean needSerialVersion =
this.scope.environment().options.getSeverity(CompilerOptions.MissingSerialVersion) != ProblemSeverities.Ignore
&& sourceType.isClass()
&& !sourceType.isAbstract()
&& sourceType.findSuperTypeErasingTo(T_JavaIoSerializable, false /*Serializable is not a class*/) != null;
if (this.typeParameters != null && scope.getJavaLangThrowable().isSuperclassOf(sourceType)) {
this.scope.problemReporter().genericTypeCannotExtendThrowable(this);
}
this.maxFieldCount = 0;
int lastVisibleFieldID = -1;
boolean hasEnumConstants = false;
boolean hasEnumConstantsWithoutBody = false;
if (this.typeParameters != null) {
for (int i = 0, count = this.typeParameters.length; i < count; i++) {
this.typeParameters[i].resolve(this.scope);
}
}
if (this.memberTypes != null) {
for (int i = 0, count = this.memberTypes.length; i < count; i++) {
this.memberTypes[i].resolve(this.scope);
}
}
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))
hasEnumConstantsWithoutBody = true;
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 & (AccStatic | AccFinal)) == (AccStatic | AccFinal))
&& CharOperation.equals(TypeConstants.SERIALVERSIONUID, fieldBinding.name)
&& BaseTypes.LongBinding == fieldBinding.type) {
needSerialVersion = false;
}
this.maxFieldCount++;
lastVisibleFieldID = field.binding.id;
break;
case AbstractVariableDeclaration.INITIALIZER:
((Initializer) field).lastVisibleFieldID = lastVisibleFieldID + 1;
break;
}
field.resolve(field.isStatic() ? this.staticInitializerScope : this.initializerScope);
}
}
if (needSerialVersion) {
this.scope.problemReporter().missingSerialVersion(this);
}
// check extends/implements for annotation type
if (kind() == IGenericType.ANNOTATION_TYPE_DECL) {
if (this.superclass != null) {
this.scope.problemReporter().annotationTypeDeclarationCannotHaveSuperclass(this);
}
if (this.superInterfaces != null) {
this.scope.problemReporter().annotationTypeDeclarationCannotHaveSuperinterfaces(this);
}
}
// check enum abstract methods
if (kind() == IGenericType.ENUM_DECL && this.binding.isAbstract()) {
if (!hasEnumConstants || hasEnumConstantsWithoutBody) {
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);
}
}
}
}
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);
}
if (this.methods != null) {
for (int i = 0, count = this.methods.length; i < count; i++) {
this.methods[i].resolve(this.scope);
}
}
// Resolve javadoc
if (this.javadoc != null) {
if (this.scope != null) {
this.javadoc.resolve(this.scope);
}
} else if (sourceType != null && !sourceType.isLocalType()) {
this.scope.problemReporter().javadocMissing(this.sourceStart, this.sourceEnd, sourceType.modifiers);
}
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
return;
}
}
public void resolve(BlockScope blockScope) {
// local type declaration
// need to build its scope first and proceed with binding's creation
if ((this.bits & IsAnonymousTypeMASK) == 0) blockScope.addLocalType(this);
if (binding != null) {
// remember local types binding for innerclass emulation propagation
blockScope.referenceCompilationUnit().record((LocalTypeBinding)binding);
// binding is not set if the receiver could not be created
resolve();
updateMaxFieldCount();
}
}
public void resolve(ClassScope upperScope) {
// member scopes are already created
// request the construction of a binding if local member type
if (binding != null && binding instanceof LocalTypeBinding) {
// remember local types binding for innerclass emulation propagation
upperScope.referenceCompilationUnit().record((LocalTypeBinding)binding);
}
resolve();
updateMaxFieldCount();
}
public void resolve(CompilationUnitScope upperScope) {
// top level : scope are already created
resolve();
updateMaxFieldCount();
}
public void tagAsHavingErrors() {
ignoreFurtherInvestigation = true;
}
/**
* Iteration for a package member type
*
*/
public void traverse(
ASTVisitor visitor,
CompilationUnitScope unitScope) {
if (ignoreFurtherInvestigation)
return;
try {
if (visitor.visit(this, unitScope)) {
if (this.annotations != null) {
int annotationsLength = this.annotations.length;
for (int i = 0; i < annotationsLength; i++)
this.annotations[i].traverse(visitor, scope);
}
if (this.superclass != null)
this.superclass.traverse(visitor, scope);
if (this.superInterfaces != null) {
int length = this.superInterfaces.length;
for (int i = 0; i < length; i++)
this.superInterfaces[i].traverse(visitor, scope);
}
if (this.typeParameters != null) {
int length = this.typeParameters.length;
for (int i = 0; i < length; i++) {
this.typeParameters[i].traverse(visitor, scope);
}
}
if (this.memberTypes != null) {
int length = this.memberTypes.length;
for (int i = 0; i < length; i++)
this.memberTypes[i].traverse(visitor, 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, staticInitializerScope);
} else {
field.traverse(visitor, initializerScope);
}
}
}
if (this.methods != null) {
int length = this.methods.length;
for (int i = 0; i < length; i++)
this.methods[i].traverse(visitor, scope);
}
}
visitor.endVisit(this, unitScope);
} catch (AbortType e) {
// silent abort
}
}
/**
* Iteration for a local innertype
*
*/
public void traverse(ASTVisitor visitor, BlockScope blockScope) {
if (ignoreFurtherInvestigation)
return;
try {
if (visitor.visit(this, blockScope)) {
if (this.annotations != null) {
int annotationsLength = this.annotations.length;
for (int i = 0; i < annotationsLength; i++)
this.annotations[i].traverse(visitor, scope);
}
if (this.superclass != null)
this.superclass.traverse(visitor, scope);
if (this.superInterfaces != null) {
int length = this.superInterfaces.length;
for (int i = 0; i < length; i++)
this.superInterfaces[i].traverse(visitor, scope);
}
if (this.typeParameters != null) {
int length = this.typeParameters.length;
for (int i = 0; i < length; i++) {
this.typeParameters[i].traverse(visitor, scope);
}
}
if (this.memberTypes != null) {
int length = this.memberTypes.length;
for (int i = 0; i < length; i++)
this.memberTypes[i].traverse(visitor, 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()) {
// local type cannot have static fields
} else {
field.traverse(visitor, initializerScope);
}
}
}
if (this.methods != null) {
int length = this.methods.length;
for (int i = 0; i < length; i++)
this.methods[i].traverse(visitor, scope);
}
}
visitor.endVisit(this, blockScope);
} catch (AbortType e) {
// silent abort
}
}
/**
* Iteration for a member innertype
*
*/
public void traverse(ASTVisitor visitor, ClassScope classScope) {
if (ignoreFurtherInvestigation)
return;
try {
if (visitor.visit(this, classScope)) {
if (this.annotations != null) {
int annotationsLength = this.annotations.length;
for (int i = 0; i < annotationsLength; i++)
this.annotations[i].traverse(visitor, scope);
}
if (this.superclass != null)
this.superclass.traverse(visitor, scope);
if (this.superInterfaces != null) {
int length = this.superInterfaces.length;
for (int i = 0; i < length; i++)
this.superInterfaces[i].traverse(visitor, scope);
}
if (this.typeParameters != null) {
int length = this.typeParameters.length;
for (int i = 0; i < length; i++) {
this.typeParameters[i].traverse(visitor, scope);
}
}
if (this.memberTypes != null) {
int length = this.memberTypes.length;
for (int i = 0; i < length; i++)
this.memberTypes[i].traverse(visitor, 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, staticInitializerScope);
} else {
field.traverse(visitor, initializerScope);
}
}
}
if (this.methods != null) {
int length = this.methods.length;
for (int i = 0; i < length; i++)
this.methods[i].traverse(visitor, scope);
}
}
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 (binding == null)
return; // error scenario
TypeDeclaration outerMostType = scope.outerMostClassScope().referenceType();
if (maxFieldCount > outerMostType.maxFieldCount) {
outerMostType.maxFieldCount = maxFieldCount; // up
} else {
maxFieldCount = outerMostType.maxFieldCount; // down
}
}
}