removed Makefile; lifted repo/org.ibex.tool/src/ to src/
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / ConstructorDeclaration.java
diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java b/src/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
new file mode 100644 (file)
index 0000000..7170bbf
--- /dev/null
@@ -0,0 +1,454 @@
+/*******************************************************************************
+ * 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 java.util.ArrayList;
+
+import org.eclipse.jdt.core.compiler.*;
+import org.eclipse.jdt.internal.compiler.*;
+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.parser.*;
+import org.eclipse.jdt.internal.compiler.problem.*;
+
+public class ConstructorDeclaration extends AbstractMethodDeclaration {
+
+       public ExplicitConstructorCall constructorCall;
+       public final static char[] ConstantPoolName = "<init>".toCharArray(); //$NON-NLS-1$
+       public boolean isDefaultConstructor = false;
+
+       public ConstructorDeclaration(CompilationResult compilationResult){
+               super(compilationResult);
+       }
+       
+       public void analyseCode(
+               ClassScope classScope,
+               InitializationFlowContext initializerFlowContext,
+               FlowInfo flowInfo) {
+
+               if (ignoreFurtherInvestigation)
+                       return;
+
+               if (this.binding != null && this.binding.isPrivate() && !this.binding.isPrivateUsed()) {
+                       if (!classScope.referenceCompilationUnit().compilationResult.hasSyntaxError()) {
+                               scope.problemReporter().unusedPrivateConstructor(this);
+                       }
+               }
+                       
+               // check constructor recursion, once all constructor got resolved
+               if (isRecursive(null /*lazy initialized visited list*/)) {                              
+                       this.scope.problemReporter().recursiveConstructorInvocation(this.constructorCall);
+               }
+                       
+               try {
+                       ExceptionHandlingFlowContext constructorContext =
+                               new ExceptionHandlingFlowContext(
+                                       initializerFlowContext.parent,
+                                       this,
+                                       binding.thrownExceptions,
+                                       scope,
+                                       FlowInfo.DEAD_END);
+                       initializerFlowContext.checkInitializerExceptions(
+                               scope,
+                               constructorContext,
+                               flowInfo);
+
+                       // anonymous constructor can gain extra thrown exceptions from unhandled ones
+                       if (binding.declaringClass.isAnonymousType()) {
+                               ArrayList computedExceptions = constructorContext.extendedExceptions;
+                               if (computedExceptions != null){
+                                       int size;
+                                       if ((size = computedExceptions.size()) > 0){
+                                               ReferenceBinding[] actuallyThrownExceptions;
+                                               computedExceptions.toArray(actuallyThrownExceptions = new ReferenceBinding[size]);
+                                               binding.thrownExceptions = actuallyThrownExceptions;
+                                       }
+                               }
+                       }
+                       
+                       // propagate to constructor call
+                       if (constructorCall != null) {
+                               // if calling 'this(...)', then flag all non-static fields as definitely
+                               // set since they are supposed to be set inside other local constructor
+                               if (constructorCall.accessMode == ExplicitConstructorCall.This) {
+                                       FieldBinding[] fields = binding.declaringClass.fields();
+                                       for (int i = 0, count = fields.length; i < count; i++) {
+                                               FieldBinding field;
+                                               if (!(field = fields[i]).isStatic()) {
+                                                       flowInfo.markAsDefinitelyAssigned(field);
+                                               }
+                                       }
+                               }
+                               flowInfo = constructorCall.analyseCode(scope, constructorContext, flowInfo);
+                       }
+                       // propagate to statements
+                       if (statements != null) {
+                               boolean didAlreadyComplain = false;
+                               for (int i = 0, count = statements.length; i < count; i++) {
+                                       Statement stat = statements[i];
+                                       if (!stat.complainIfUnreachable(flowInfo, scope, didAlreadyComplain)) {
+                                               flowInfo = stat.analyseCode(scope, constructorContext, flowInfo);
+                                       } else {
+                                               didAlreadyComplain = true;
+                                       }
+                               }
+                       }
+                       // check for missing returning path
+                       this.needFreeReturn = flowInfo.isReachable();
+
+                       // check missing blank final field initializations
+                       if ((constructorCall != null)
+                               && (constructorCall.accessMode != ExplicitConstructorCall.This)) {
+                               flowInfo = flowInfo.mergedWith(constructorContext.initsOnReturn);
+                               FieldBinding[] fields = binding.declaringClass.fields();
+                               for (int i = 0, count = fields.length; i < count; i++) {
+                                       FieldBinding field;
+                                       if ((!(field = fields[i]).isStatic())
+                                               && field.isFinal()
+                                               && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
+                                               scope.problemReporter().uninitializedBlankFinalField(
+                                                       field,
+                                                       isDefaultConstructor ? (ASTNode) scope.referenceType() : this);
+                                       }
+                               }
+                       }
+                       // check unreachable catch blocks
+                       constructorContext.complainIfUnusedExceptionHandlers(this);
+               } catch (AbortMethod e) {
+                       this.ignoreFurtherInvestigation = true;
+               }
+       }
+
+       /**
+        * Bytecode generation for a constructor
+        *
+        * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
+        * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
+        */
+       public void generateCode(ClassScope classScope, ClassFile classFile) {
+               
+               int problemResetPC = 0;
+               if (ignoreFurtherInvestigation) {
+                       if (this.binding == null)
+                               return; // Handle methods with invalid signature or duplicates
+                       int problemsLength;
+                       IProblem[] problems =
+                               scope.referenceCompilationUnit().compilationResult.getProblems();
+                       IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
+                       System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
+                       classFile.addProblemConstructor(this, binding, problemsCopy);
+                       return;
+               }
+               try {
+                       problemResetPC = classFile.contentsOffset;
+                       this.internalGenerateCode(classScope, classFile);
+               } catch (AbortMethod e) {
+                       if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
+                               // a branch target required a goto_w, restart code gen in wide mode.
+                               try {
+                                       classFile.contentsOffset = problemResetPC;
+                                       classFile.methodCount--;
+                                       classFile.codeStream.wideMode = true; // request wide mode 
+                                       this.internalGenerateCode(classScope, classFile); // restart method generation
+                               } catch (AbortMethod e2) {
+                                       int problemsLength;
+                                       IProblem[] problems =
+                                               scope.referenceCompilationUnit().compilationResult.getAllProblems();
+                                       IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
+                                       System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
+                                       classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC);
+                               }
+                       } else {
+                               int problemsLength;
+                               IProblem[] problems =
+                                       scope.referenceCompilationUnit().compilationResult.getAllProblems();
+                               IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
+                               System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
+                               classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC);
+                       }
+               }
+       }
+
+       public void generateSyntheticFieldInitializationsIfNecessary(
+               MethodScope methodScope,
+               CodeStream codeStream,
+               ReferenceBinding declaringClass) {
+                       
+               if (!declaringClass.isNestedType()) return;
+               
+               NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
+
+               SyntheticArgumentBinding[] syntheticArgs = nestedType.syntheticEnclosingInstances();
+               for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i < max; i++) {
+                       SyntheticArgumentBinding syntheticArg;
+                       if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
+                               codeStream.aload_0();
+                               codeStream.load(syntheticArg);
+                               codeStream.putfield(syntheticArg.matchingField);
+                       }
+               }
+               syntheticArgs = nestedType.syntheticOuterLocalVariables();
+               for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i < max; i++) {
+                       SyntheticArgumentBinding syntheticArg;
+                       if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
+                               codeStream.aload_0();
+                               codeStream.load(syntheticArg);
+                               codeStream.putfield(syntheticArg.matchingField);
+                       }
+               }
+       }
+
+       private void internalGenerateCode(ClassScope classScope, ClassFile classFile) {
+               
+               classFile.generateMethodInfoHeader(binding);
+               int methodAttributeOffset = classFile.contentsOffset;
+               int attributeNumber = classFile.generateMethodInfoAttribute(binding);
+               if ((!binding.isNative()) && (!binding.isAbstract())) {
+                       
+                       TypeDeclaration declaringType = classScope.referenceContext;
+                       int codeAttributeOffset = classFile.contentsOffset;
+                       classFile.generateCodeAttributeHeader();
+                       CodeStream codeStream = classFile.codeStream;
+                       codeStream.reset(this, classFile);
+
+                       // initialize local positions - including initializer scope.
+                       ReferenceBinding declaringClass = binding.declaringClass;
+
+                       int argSlotSize = 1; // this==aload0
+                       
+                       if (declaringClass.isNestedType()){
+                               NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
+                               this.scope.extraSyntheticArguments = nestedType.syntheticOuterLocalVariables();
+                               scope.computeLocalVariablePositions(// consider synthetic arguments if any
+                                       nestedType.enclosingInstancesSlotSize + 1,
+                                       codeStream);
+                               argSlotSize += nestedType.enclosingInstancesSlotSize;
+                               argSlotSize += nestedType.outerLocalVariablesSlotSize;
+                       } else {
+                               scope.computeLocalVariablePositions(1,  codeStream);
+                       }
+                               
+                       if (arguments != null) {
+                               for (int i = 0, max = arguments.length; i < max; i++) {
+                                       // arguments initialization for local variable debug attributes
+                                       LocalVariableBinding argBinding;
+                                       codeStream.addVisibleLocalVariable(argBinding = arguments[i].binding);
+                                       argBinding.recordInitializationStartPC(0);
+                                       TypeBinding argType;
+                                       if ((argType = argBinding.type) == LongBinding || (argType == DoubleBinding)) {
+                                               argSlotSize += 2;
+                                       } else {
+                                               argSlotSize++;
+                                       }
+                               }
+                       }
+                       
+                       MethodScope initializerScope = declaringType.initializerScope;
+                       initializerScope.computeLocalVariablePositions(argSlotSize, codeStream); // offset by the argument size (since not linked to method scope)
+
+                       boolean needFieldInitializations = constructorCall == null || constructorCall.accessMode != ExplicitConstructorCall.This;
+
+                       // post 1.4 source level, synthetic initializations occur prior to explicit constructor call
+                       boolean preInitSyntheticFields = scope.environment().options.targetJDK >= ClassFileConstants.JDK1_4;
+
+                       if (needFieldInitializations && preInitSyntheticFields){
+                               generateSyntheticFieldInitializationsIfNecessary(scope, codeStream, declaringClass);
+                       }                       
+                       // generate constructor call
+                       if (constructorCall != null) {
+                               constructorCall.generateCode(scope, codeStream);
+                       }
+                       // generate field initialization - only if not invoking another constructor call of the same class
+                       if (needFieldInitializations) {
+                               if (!preInitSyntheticFields){
+                                       generateSyntheticFieldInitializationsIfNecessary(scope, codeStream, declaringClass);
+                               }
+                               // generate user field initialization
+                               if (declaringType.fields != null) {
+                                       for (int i = 0, max = declaringType.fields.length; i < max; i++) {
+                                               FieldDeclaration fieldDecl;
+                                               if (!(fieldDecl = declaringType.fields[i]).isStatic()) {
+                                                       fieldDecl.generateCode(initializerScope, codeStream);
+                                               }
+                                       }
+                               }
+                       }
+                       // generate statements
+                       if (statements != null) {
+                               for (int i = 0, max = statements.length; i < max; i++) {
+                                       statements[i].generateCode(scope, codeStream);
+                               }
+                       }
+                       if (this.needFreeReturn) {
+                               codeStream.return_();
+                       }
+                       // local variable attributes
+                       codeStream.exitUserScope(scope);
+                       codeStream.recordPositionsFrom(0, this.bodyEnd);
+                       classFile.completeCodeAttribute(codeAttributeOffset);
+                       attributeNumber++;
+               }
+               classFile.completeMethodInfo(methodAttributeOffset, attributeNumber);
+
+               // if a problem got reported during code gen, then trigger problem method creation
+               if (ignoreFurtherInvestigation) {
+                       throw new AbortMethod(scope.referenceCompilationUnit().compilationResult, null);
+               }
+       }
+
+       public boolean isConstructor() {
+
+               return true;
+       }
+
+       public boolean isDefaultConstructor() {
+
+               return this.isDefaultConstructor;
+       }
+
+       public boolean isInitializationMethod() {
+
+               return true;
+       }
+
+       /**
+        * Returns true if the constructor is directly involved in a cycle.
+        * Given most constructors aren't, we only allocate the visited list
+        * lazily.
+        * 
+        * @param visited
+        * @return
+        */
+       public boolean isRecursive(ArrayList visited) {
+
+               if (this.binding == null
+                               || this.constructorCall == null
+                               || this.constructorCall.binding == null
+                               || this.constructorCall.isSuperAccess()
+                               || !this.constructorCall.binding.isValidBinding()) {
+                       return false;
+               }
+               
+               ConstructorDeclaration targetConstructor = 
+                       ((ConstructorDeclaration)this.scope.referenceType().declarationOf(constructorCall.binding));
+               if (this == targetConstructor) return true; // direct case
+
+               if (visited == null) { // lazy allocation
+                       visited = new ArrayList(1);
+               } else {
+                       int index = visited.indexOf(this);
+                       if (index >= 0) return index == 0; // only blame if directly part of the cycle
+               }
+               visited.add(this);
+
+               return targetConstructor.isRecursive(visited);
+       }
+       
+       public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
+
+               //fill up the constructor body with its statements
+               if (ignoreFurtherInvestigation)
+                       return;
+               if (isDefaultConstructor){
+                       constructorCall = SuperReference.implicitSuperConstructorCall();
+                       constructorCall.sourceStart = sourceStart;
+                       constructorCall.sourceEnd = sourceEnd; 
+                       return;
+               }
+               parser.parse(this, unit);
+
+       }
+
+       public StringBuffer printBody(int indent, StringBuffer output) {
+
+               output.append(" {"); //$NON-NLS-1$
+               if (constructorCall != null) {
+                       output.append('\n');
+                       constructorCall.printStatement(indent, output); //$NON-NLS-1$ //$NON-NLS-2$
+               }
+               if (statements != null) {
+                       for (int i = 0; i < statements.length; i++) {
+                               output.append('\n');
+                               statements[i].printStatement(indent, output); //$NON-NLS-1$
+                       }
+               }
+               output.append('\n');
+               printIndent(indent == 0 ? 0 : indent - 1, output).append('}');
+               return output;
+       }
+       
+       public void resolveJavadoc() {
+               
+               if (this.binding == null || this.javadoc != null) {
+                       super.resolveJavadoc();
+               } else if (!isDefaultConstructor) {
+                       this.scope.problemReporter().javadocMissing(this.sourceStart, this.sourceEnd, this.binding.modifiers);
+               }
+       }
+
+       /*
+        * Type checking for constructor, just another method, except for special check
+        * for recursive constructor invocations.
+        */
+       public void resolveStatements() {
+
+               if (!CharOperation.equals(scope.enclosingSourceType().sourceName, selector)){
+                       scope.problemReporter().missingReturnType(this);
+               }
+
+               // if null ==> an error has occurs at parsing time ....
+               if (this.constructorCall != null) {
+                       // e.g. using super() in java.lang.Object
+                       if (this.binding != null
+                               && this.binding.declaringClass.id == T_Object
+                               && this.constructorCall.accessMode != ExplicitConstructorCall.This) {
+                                       if (this.constructorCall.accessMode == ExplicitConstructorCall.Super) {
+                                               scope.problemReporter().cannotUseSuperInJavaLangObject(this.constructorCall);
+                                       }
+                                       this.constructorCall = null;
+                       } else {
+                               this.constructorCall.resolve(this.scope);
+                       }
+               }
+               if ((modifiers & AccSemicolonBody) != 0) {
+                       scope.problemReporter().methodNeedBody(this);           
+               }
+               super.resolveStatements();
+       }
+
+       public void traverse(
+               ASTVisitor visitor,
+               ClassScope classScope) {
+
+               if (visitor.visit(this, classScope)) {
+                       if (arguments != null) {
+                               int argumentLength = arguments.length;
+                               for (int i = 0; i < argumentLength; i++)
+                                       arguments[i].traverse(visitor, scope);
+                       }
+                       if (thrownExceptions != null) {
+                               int thrownExceptionsLength = thrownExceptions.length;
+                               for (int i = 0; i < thrownExceptionsLength; i++)
+                                       thrownExceptions[i].traverse(visitor, scope);
+                       }
+                       if (constructorCall != null)
+                               constructorCall.traverse(visitor, scope);
+                       if (statements != null) {
+                               int statementsLength = statements.length;
+                               for (int i = 0; i < statementsLength; i++)
+                                       statements[i].traverse(visitor, scope);
+                       }
+               }
+               visitor.endVisit(this, classScope);
+       }
+}