--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-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.codegen.*;
+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 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;
+ private MethodDeclaration[] missingAbstractMethods;
+ public Javadoc javadoc;
+
+ public QualifiedAllocationExpression allocation; // for anonymous only
+ public TypeDeclaration enclosingType; // for member types only
+
+ 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.isInterface()) {
+ // report the problem and continue the parsing
+ parser.problemReporter().interfaceCannotHaveConstructors(
+ (ConstructorDeclaration) am);
+ }
+ hasConstructor = true;
+ }
+ }
+ }
+ }
+ return hasConstructor;
+ }
+
+ public CompilationResult compilationResult() {
+
+ return this.compilationResult;
+ }
+
+ public ConstructorDeclaration createsInternalConstructor(
+ 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 = 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 createsInternalConstructorWithBinding(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;
+ cd.modifiers = modifiers & AccVisibilityMASK;
+ 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) {
+ 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) {
+ 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) {
+ 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);
+ }
+ }
+
+ classFile.generateMissingAbstractMethods(this.missingAbstractMethods, scope.referenceCompilationUnit().compilationResult);
+
+ // generate all 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 (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 (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());
+ } 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));
+ } else { // constructor
+ method.analyseCode(scope, initializerContext, constructorInfo.copy());
+ }
+ } else { // regular method
+ method.analyseCode(scope, null, flowInfo.copy());
+ }
+ }
+ }
+ }
+
+ public boolean isInterface() {
+
+ return (modifiers & AccInterface) != 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.isReachable()) return;
+ NestedTypeBinding nestedType = (NestedTypeBinding) binding;
+
+ MethodScope methodScope = currentScope.methodScope();
+ if (!methodScope.isStatic && !methodScope.isConstructorCall){
+
+ nestedType.addSyntheticArgumentAndField(binding.enclosingType());
+ }
+ // add superclass enclosing instance arg for anonymous types (if necessary)
+ if (binding.isAnonymousType()) {
+ ReferenceBinding superclassBinding = binding.superclass;
+ if (superclassBinding.enclosingType() != null && !superclassBinding.isStatic()) {
+ if (!superclassBinding.isLocalType()
+ || ((NestedTypeBinding)superclassBinding).getSyntheticField(superclassBinding.enclosingType(), true) != null){
+
+ nestedType.addSyntheticArgument(superclassBinding.enclosingType());
+ }
+ }
+ }
+ }
+
+ /*
+ * 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 (isInterface())
+ 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;
+ }
+ 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++) {
+ if (fields[i] instanceof Initializer) {
+ ((Initializer) fields[i]).parseStatements(parser, this, unit);
+ }
+ }
+ }
+ }
+
+ public StringBuffer print(int indent, StringBuffer 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) {
+ 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);
+ output.append(isInterface() ? "interface " : "class "); //$NON-NLS-1$ //$NON-NLS-2$
+ output.append(name);
+ if (superclass != null) {
+ output.append(" extends "); //$NON-NLS-1$
+ superclass.print(0, output);
+ }
+ if (superInterfaces != null && superInterfaces.length > 0) {
+ output.append(isInterface() ? " extends " : " implements ");//$NON-NLS-2$ //$NON-NLS-1$
+ 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() {
+
+ if (this.binding == null) {
+ this.ignoreFurtherInvestigation = true;
+ return;
+ }
+ try {
+ if ((this.bits & UndocumentedEmptyBlockMASK) != 0) {
+ this.scope.problemReporter().undocumentedEmptyBlock(this.bodyStart-1, this.bodyEnd);
+ }
+ // check superclass & interfaces
+ if (this.binding.superclass != null) // watch out for Object ! (and other roots)
+ if (isTypeUseDeprecated(this.binding.superclass, this.scope))
+ this.scope.problemReporter().deprecatedType(this.binding.superclass, this.superclass);
+ if (this.superInterfaces != null)
+ for (int i = this.superInterfaces.length; --i >= 0;)
+ if (this.superInterfaces[i].resolvedType != null)
+ if (isTypeUseDeprecated(this.superInterfaces[i].resolvedType, this.scope))
+ this.scope.problemReporter().deprecatedType(
+ this.superInterfaces[i].resolvedType,
+ this.superInterfaces[i]);
+ this.maxFieldCount = 0;
+ int lastVisibleFieldID = -1;
+ if (this.fields != null) {
+ for (int i = 0, count = this.fields.length; i < count; i++) {
+ FieldDeclaration field = this.fields[i];
+ if (field.isField()) {
+ if (field.binding == null) {
+ // still discover secondary errors
+ if (field.initialization != null) field.initialization.resolve(field.isStatic() ? this.staticInitializerScope : this.initializerScope);
+ this.ignoreFurtherInvestigation = true;
+ continue;
+ }
+ this.maxFieldCount++;
+ lastVisibleFieldID = field.binding.id;
+ } else { // initializer
+ ((Initializer) field).lastVisibleFieldID = lastVisibleFieldID + 1;
+ }
+ field.resolve(field.isStatic() ? this.staticInitializerScope : this.initializerScope);
+ }
+ }
+ if (this.memberTypes != null) {
+ for (int i = 0, count = this.memberTypes.length; i < count; i++) {
+ this.memberTypes[i].resolve(this.scope);
+ }
+ }
+ 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 (this.binding != null && !this.binding.isLocalType()) {
+ this.scope.problemReporter().javadocMissing(this.sourceStart, this.sourceEnd, this.binding.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 (superclass != null)
+ superclass.traverse(visitor, scope);
+ if (superInterfaces != null) {
+ int superInterfaceLength = superInterfaces.length;
+ for (int i = 0; i < superInterfaceLength; i++)
+ superInterfaces[i].traverse(visitor, scope);
+ }
+ if (memberTypes != null) {
+ int memberTypesLength = memberTypes.length;
+ for (int i = 0; i < memberTypesLength; i++)
+ memberTypes[i].traverse(visitor, scope);
+ }
+ if (fields != null) {
+ int fieldsLength = fields.length;
+ for (int i = 0; i < fieldsLength; i++) {
+ FieldDeclaration field;
+ if ((field = fields[i]).isStatic()) {
+ field.traverse(visitor, staticInitializerScope);
+ } else {
+ field.traverse(visitor, initializerScope);
+ }
+ }
+ }
+ if (methods != null) {
+ int methodsLength = methods.length;
+ for (int i = 0; i < methodsLength; i++)
+ 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 (superclass != null)
+ superclass.traverse(visitor, scope);
+ if (superInterfaces != null) {
+ int superInterfaceLength = superInterfaces.length;
+ for (int i = 0; i < superInterfaceLength; i++)
+ superInterfaces[i].traverse(visitor, scope);
+ }
+ if (memberTypes != null) {
+ int memberTypesLength = memberTypes.length;
+ for (int i = 0; i < memberTypesLength; i++)
+ memberTypes[i].traverse(visitor, scope);
+ }
+ if (fields != null) {
+ int fieldsLength = fields.length;
+ for (int i = 0; i < fieldsLength; i++) {
+ FieldDeclaration field;
+ if ((field = fields[i]).isStatic()) {
+ // local type cannot have static fields
+ } else {
+ field.traverse(visitor, initializerScope);
+ }
+ }
+ }
+ if (methods != null) {
+ int methodsLength = methods.length;
+ for (int i = 0; i < methodsLength; i++)
+ 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 (superclass != null)
+ superclass.traverse(visitor, scope);
+ if (superInterfaces != null) {
+ int superInterfaceLength = superInterfaces.length;
+ for (int i = 0; i < superInterfaceLength; i++)
+ superInterfaces[i].traverse(visitor, scope);
+ }
+ if (memberTypes != null) {
+ int memberTypesLength = memberTypes.length;
+ for (int i = 0; i < memberTypesLength; i++)
+ memberTypes[i].traverse(visitor, scope);
+ }
+ if (fields != null) {
+ int fieldsLength = fields.length;
+ for (int i = 0; i < fieldsLength; i++) {
+ FieldDeclaration field;
+ if ((field = fields[i]).isStatic()) {
+ field.traverse(visitor, staticInitializerScope);
+ } else {
+ field.traverse(visitor, initializerScope);
+ }
+ }
+ }
+ if (methods != null) {
+ int methodsLength = methods.length;
+ for (int i = 0; i < methodsLength; i++)
+ 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
+ }
+ }
+}