1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2003 IBM Corporation and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v1.0
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v10.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  *******************************************************************************/
 
  11 package net.sourceforge.phpeclipse.internal.compiler.ast;
 
  13 import java.util.ArrayList;
 
  15 import net.sourceforge.phpdt.core.compiler.CharOperation;
 
  16 import net.sourceforge.phpdt.internal.compiler.CompilationResult;
 
  17 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
 
  18 import net.sourceforge.phpdt.internal.compiler.flow.ExceptionHandlingFlowContext;
 
  19 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
 
  20 import net.sourceforge.phpdt.internal.compiler.flow.InitializationFlowContext;
 
  21 import net.sourceforge.phpdt.internal.compiler.lookup.ClassScope;
 
  22 import net.sourceforge.phpdt.internal.compiler.lookup.FieldBinding;
 
  23 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
 
  24 import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
 
  25 import net.sourceforge.phpdt.internal.compiler.problem.AbortMethod;
 
  28 public class ConstructorDeclaration extends AbstractMethodDeclaration {
 
  30         public ExplicitConstructorCall constructorCall;
 
  31         public final static char[] ConstantPoolName = "<init>".toCharArray(); //$NON-NLS-1$
 
  32         public boolean isDefaultConstructor = false;
 
  34         public ConstructorDeclaration(CompilationResult compilationResult){
 
  35                 super(compilationResult);
 
  38         public void analyseCode(
 
  39                 ClassScope classScope,
 
  40                 InitializationFlowContext initializerFlowContext,
 
  43                 if (ignoreFurtherInvestigation)
 
  46                 if (this.binding != null && this.binding.isPrivate() && !this.binding.isPrivateUsed()) {
 
  47                         if (!classScope.referenceCompilationUnit().compilationResult.hasSyntaxError()) {
 
  48                                 scope.problemReporter().unusedPrivateConstructor(this);
 
  52                 // check constructor recursion, once all constructor got resolved
 
  53                 if (isRecursive(null /*lazy initialized visited list*/)) {                              
 
  54                         this.scope.problemReporter().recursiveConstructorInvocation(this.constructorCall);
 
  58                         ExceptionHandlingFlowContext constructorContext =
 
  59                                 new ExceptionHandlingFlowContext(
 
  60                                         initializerFlowContext.parent,
 
  62                                         binding.thrownExceptions,
 
  65                         initializerFlowContext.checkInitializerExceptions(
 
  70                         // anonymous constructor can gain extra thrown exceptions from unhandled ones
 
  71                         if (binding.declaringClass.isAnonymousType()) {
 
  72                                 ArrayList computedExceptions = constructorContext.extendedExceptions;
 
  73                                 if (computedExceptions != null){
 
  75                                         if ((size = computedExceptions.size()) > 0){
 
  76                                                 ReferenceBinding[] actuallyThrownExceptions;
 
  77                                                 computedExceptions.toArray(actuallyThrownExceptions = new ReferenceBinding[size]);
 
  78                                                 binding.thrownExceptions = actuallyThrownExceptions;
 
  83                         // propagate to constructor call
 
  84                         if (constructorCall != null) {
 
  85                                 // if calling 'this(...)', then flag all non-static fields as definitely
 
  86                                 // set since they are supposed to be set inside other local constructor
 
  87                                 if (constructorCall.accessMode == ExplicitConstructorCall.This) {
 
  88                                         FieldBinding[] fields = binding.declaringClass.fields();
 
  89                                         for (int i = 0, count = fields.length; i < count; i++) {
 
  91                                                 if (!(field = fields[i]).isStatic()) {
 
  92                                                         flowInfo.markAsDefinitelyAssigned(field);
 
  96                                 flowInfo = constructorCall.analyseCode(scope, constructorContext, flowInfo);
 
  98                         // propagate to statements
 
  99                         if (statements != null) {
 
 100                                 boolean didAlreadyComplain = false;
 
 101                                 for (int i = 0, count = statements.length; i < count; i++) {
 
 103                                         if (!flowInfo.complainIfUnreachable(stat = statements[i], scope, didAlreadyComplain)) {
 
 104                                                 flowInfo = stat.analyseCode(scope, constructorContext, flowInfo);
 
 106                                                 didAlreadyComplain = true;
 
 110                         // check for missing returning path
 
 111                         this.needFreeReturn = flowInfo.isReachable();
 
 113                         // check missing blank final field initializations
 
 114                         if ((constructorCall != null)
 
 115                                 && (constructorCall.accessMode != ExplicitConstructorCall.This)) {
 
 116                                 flowInfo = flowInfo.mergedWith(constructorContext.initsOnReturn);
 
 117                                 FieldBinding[] fields = binding.declaringClass.fields();
 
 118                                 for (int i = 0, count = fields.length; i < count; i++) {
 
 120                                         if ((!(field = fields[i]).isStatic())
 
 122                                                 && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
 
 123                                                 scope.problemReporter().uninitializedBlankFinalField(
 
 125                                                         isDefaultConstructor ? (AstNode) scope.referenceType() : this);
 
 129                 } catch (AbortMethod e) {
 
 130                         this.ignoreFurtherInvestigation = true;
 
 135          * Bytecode generation for a constructor
 
 137          * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
 
 138          * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
 
 140 //      public void generateCode(ClassScope classScope, ClassFile classFile) {
 
 142 //              int problemResetPC = 0;
 
 143 //              if (ignoreFurtherInvestigation) {
 
 144 //                      if (this.binding == null)
 
 145 //                              return; // Handle methods with invalid signature or duplicates
 
 146 //                      int problemsLength;
 
 147 //                      IProblem[] problems =
 
 148 //                              scope.referenceCompilationUnit().compilationResult.getProblems();
 
 149 //                      IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
 
 150 //                      System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
 
 151 //                      classFile.addProblemConstructor(this, binding, problemsCopy);
 
 155 //                      problemResetPC = classFile.contentsOffset;
 
 156 //                      this.internalGenerateCode(classScope, classFile);
 
 157 //              } catch (AbortMethod e) {
 
 158 //                      if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
 
 159 //                              // a branch target required a goto_w, restart code gen in wide mode.
 
 161 //                                      if (statements != null) {
 
 162 //                                              for (int i = 0, max = statements.length; i < max; i++)
 
 163 //                                                      statements[i].resetStateForCodeGeneration();
 
 165 //                                      classFile.contentsOffset = problemResetPC;
 
 166 //                                      classFile.methodCount--;
 
 167 //                                      classFile.codeStream.wideMode = true; // request wide mode 
 
 168 //                                      this.internalGenerateCode(classScope, classFile); // restart method generation
 
 169 //                              } catch (AbortMethod e2) {
 
 170 //                                      int problemsLength;
 
 171 //                                      IProblem[] problems =
 
 172 //                                              scope.referenceCompilationUnit().compilationResult.getAllProblems();
 
 173 //                                      IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
 
 174 //                                      System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
 
 175 //                                      classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC);
 
 178 //                              int problemsLength;
 
 179 //                              IProblem[] problems =
 
 180 //                                      scope.referenceCompilationUnit().compilationResult.getAllProblems();
 
 181 //                              IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
 
 182 //                              System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
 
 183 //                              classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC);
 
 188 //      public void generateSyntheticFieldInitializationsIfNecessary(
 
 189 //              MethodScope scope,
 
 190 //              CodeStream codeStream,
 
 191 //              ReferenceBinding declaringClass) {
 
 193 //              if (!declaringClass.isNestedType()) return;
 
 195 //              NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
 
 197 //              SyntheticArgumentBinding[] syntheticArgs = nestedType.syntheticEnclosingInstances();
 
 198 //              for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i < max; i++) {
 
 199 //                      SyntheticArgumentBinding syntheticArg;
 
 200 //                      if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
 
 201 //                              codeStream.aload_0();
 
 202 //                              codeStream.load(syntheticArg);
 
 203 //                              codeStream.putfield(syntheticArg.matchingField);
 
 206 //              syntheticArgs = nestedType.syntheticOuterLocalVariables();
 
 207 //              for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i < max; i++) {
 
 208 //                      SyntheticArgumentBinding syntheticArg;
 
 209 //                      if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
 
 210 //                              codeStream.aload_0();
 
 211 //                              codeStream.load(syntheticArg);
 
 212 //                              codeStream.putfield(syntheticArg.matchingField);
 
 217 //      private void internalGenerateCode(ClassScope classScope, ClassFile classFile) {
 
 219 //              classFile.generateMethodInfoHeader(binding);
 
 220 //              int methodAttributeOffset = classFile.contentsOffset;
 
 221 //              int attributeNumber = classFile.generateMethodInfoAttribute(binding);
 
 222 //              if ((!binding.isNative()) && (!binding.isAbstract())) {
 
 224 //                      TypeDeclaration declaringType = classScope.referenceContext;
 
 225 //                      int codeAttributeOffset = classFile.contentsOffset;
 
 226 //                      classFile.generateCodeAttributeHeader();
 
 227 //                      CodeStream codeStream = classFile.codeStream;
 
 228 //                      codeStream.reset(this, classFile);
 
 230 //                      // initialize local positions - including initializer scope.
 
 231 //                      ReferenceBinding declaringClass = binding.declaringClass;
 
 233 //                      int argSlotSize = 1; // this==aload0
 
 235 //                      if (declaringClass.isNestedType()){
 
 236 //                              NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
 
 237 //                              this.scope.extraSyntheticArguments = nestedType.syntheticOuterLocalVariables();
 
 238 //                              scope.computeLocalVariablePositions(// consider synthetic arguments if any
 
 239 //                                      nestedType.enclosingInstancesSlotSize + 1,
 
 241 //                              argSlotSize += nestedType.enclosingInstancesSlotSize;
 
 242 //                              argSlotSize += nestedType.outerLocalVariablesSlotSize;
 
 244 //                              scope.computeLocalVariablePositions(1,  codeStream);
 
 247 //                      if (arguments != null) {
 
 248 //                              for (int i = 0, max = arguments.length; i < max; i++) {
 
 249 //                                      // arguments initialization for local variable debug attributes
 
 250 //                                      LocalVariableBinding argBinding;
 
 251 //                                      codeStream.addVisibleLocalVariable(argBinding = arguments[i].binding);
 
 252 //                                      argBinding.recordInitializationStartPC(0);
 
 253 //                                      TypeBinding argType;
 
 254 //                                      if ((argType = argBinding.type) == LongBinding || (argType == DoubleBinding)) {
 
 262 //                      MethodScope initializerScope = declaringType.initializerScope;
 
 263 //                      initializerScope.computeLocalVariablePositions(argSlotSize, codeStream); // offset by the argument size (since not linked to method scope)
 
 265 //                      boolean needFieldInitializations = constructorCall == null || constructorCall.accessMode != ExplicitConstructorCall.This;
 
 267 //                      // post 1.4 source level, synthetic initializations occur prior to explicit constructor call
 
 268 //                      boolean preInitSyntheticFields = scope.environment().options.targetJDK >= CompilerOptions.JDK1_4;
 
 270 //                      if (needFieldInitializations && preInitSyntheticFields){
 
 271 //                              generateSyntheticFieldInitializationsIfNecessary(scope, codeStream, declaringClass);
 
 273 //                      // generate constructor call
 
 274 //                      if (constructorCall != null) {
 
 275 //                              constructorCall.generateCode(scope, codeStream);
 
 277 //                      // generate field initialization - only if not invoking another constructor call of the same class
 
 278 //                      if (needFieldInitializations) {
 
 279 //                              if (!preInitSyntheticFields){
 
 280 //                                      generateSyntheticFieldInitializationsIfNecessary(scope, codeStream, declaringClass);
 
 282 //                              // generate user field initialization
 
 283 //                              if (declaringType.fields != null) {
 
 284 //                                      for (int i = 0, max = declaringType.fields.length; i < max; i++) {
 
 285 //                                              FieldDeclaration fieldDecl;
 
 286 //                                              if (!(fieldDecl = declaringType.fields[i]).isStatic()) {
 
 287 //                                                      fieldDecl.generateCode(initializerScope, codeStream);
 
 292 //                      // generate statements
 
 293 //                      if (statements != null) {
 
 294 //                              for (int i = 0, max = statements.length; i < max; i++) {
 
 295 //                                      statements[i].generateCode(scope, codeStream);
 
 298 //                      if (this.needFreeReturn) {
 
 299 //                              codeStream.return_();
 
 301 //                      // local variable attributes
 
 302 //                      codeStream.exitUserScope(scope);
 
 303 //                      codeStream.recordPositionsFrom(0, this.bodyEnd);
 
 304 //                      classFile.completeCodeAttribute(codeAttributeOffset);
 
 305 //                      attributeNumber++;
 
 307 //              classFile.completeMethodInfo(methodAttributeOffset, attributeNumber);
 
 309 //              // if a problem got reported during code gen, then trigger problem method creation
 
 310 //              if (ignoreFurtherInvestigation) {
 
 311 //                      throw new AbortMethod(scope.referenceCompilationUnit().compilationResult);
 
 315         public boolean isConstructor() {
 
 320         public boolean isDefaultConstructor() {
 
 322                 return isDefaultConstructor;
 
 325         public boolean isInitializationMethod() {
 
 331          * Returns true if the constructor is directly involved in a cycle.
 
 332          * Given most constructors aren't, we only allocate the visited list
 
 335         public boolean isRecursive(ArrayList visited) {
 
 337                 if (this.binding == null
 
 338                                 || this.constructorCall == null
 
 339                                 || this.constructorCall.binding == null
 
 340                                 || this.constructorCall.isSuperAccess()
 
 341                                 || !this.constructorCall.binding.isValidBinding()) {
 
 345                 ConstructorDeclaration targetConstructor = 
 
 346                         ((ConstructorDeclaration)this.scope.referenceType().declarationOf(constructorCall.binding));
 
 347                 if (this == targetConstructor) return true; // direct case
 
 349                 if (visited == null) { // lazy allocation
 
 350                         visited = new ArrayList(1);
 
 352                         int index = visited.indexOf(this);
 
 353                         if (index >= 0) return index == 0; // only blame if directly part of the cycle
 
 357                 return targetConstructor.isRecursive(visited);
 
 360         public void parseStatements(UnitParser parser, CompilationUnitDeclaration unit) {
 
 362                 //fill up the constructor body with its statements
 
 363                 if (ignoreFurtherInvestigation)
 
 365                 if (isDefaultConstructor){
 
 366                         constructorCall = SuperReference.implicitSuperConstructorCall();
 
 367                         constructorCall.sourceStart = sourceStart;
 
 368                         constructorCall.sourceEnd = sourceEnd; 
 
 371                 parser.parse(this, unit);
 
 376          * Type checking for constructor, just another method, except for special check
 
 377          * for recursive constructor invocations.
 
 379         public void resolveStatements() {
 
 381                 if (!CharOperation.equals(scope.enclosingSourceType().sourceName, selector)){
 
 382                         scope.problemReporter().missingReturnType(this);
 
 385                 // if null ==> an error has occurs at parsing time ....
 
 386                 if (this.constructorCall != null) {
 
 387                         // e.g. using super() in java.lang.Object
 
 388                         if (this.binding != null
 
 389                                 && this.binding.declaringClass.id == T_Object
 
 390                                 && this.constructorCall.accessMode != ExplicitConstructorCall.This) {
 
 391                                         if (this.constructorCall.accessMode == ExplicitConstructorCall.Super) {
 
 392                                                 scope.problemReporter().cannotUseSuperInJavaLangObject(this.constructorCall);
 
 394                                         this.constructorCall = null;
 
 396                                 this.constructorCall.resolve(this.scope);
 
 400                 super.resolveStatements();
 
 403         public String toStringStatements(int tab) {
 
 405                 String s = " {"; //$NON-NLS-1$
 
 406                 if (constructorCall != null) {
 
 407                         s = s + "\n" + constructorCall.toString(tab) + ";"; //$NON-NLS-1$ //$NON-NLS-2$
 
 409                 if (statements != null) {
 
 410                         for (int i = 0; i < statements.length; i++) {
 
 411                                 s = s + "\n" + statements[i].toString(tab); //$NON-NLS-1$
 
 412                                 if (!(statements[i] instanceof Block)) {
 
 413                                         s += ";"; //$NON-NLS-1$
 
 417                 s += "\n" + tabString(tab == 0 ? 0 : tab - 1) + "}"; //$NON-NLS-1$ //$NON-NLS-2$
 
 418                 //$NON-NLS-2$ //$NON-NLS-1$
 
 422         public void traverse(
 
 423                 IAbstractSyntaxTreeVisitor visitor,
 
 424                 ClassScope classScope) {
 
 426                 if (visitor.visit(this, classScope)) {
 
 427                         if (arguments != null) {
 
 428                                 int argumentLength = arguments.length;
 
 429                                 for (int i = 0; i < argumentLength; i++)
 
 430                                         arguments[i].traverse(visitor, scope);
 
 432                         if (thrownExceptions != null) {
 
 433                                 int thrownExceptionsLength = thrownExceptions.length;
 
 434                                 for (int i = 0; i < thrownExceptionsLength; i++)
 
 435                                         thrownExceptions[i].traverse(visitor, scope);
 
 437                         if (constructorCall != null)
 
 438                                 constructorCall.traverse(visitor, scope);
 
 439                         if (statements != null) {
 
 440                                 int statementsLength = statements.length;
 
 441                                 for (int i = 0; i < statementsLength; i++)
 
 442                                         statements[i].traverse(visitor, scope);
 
 445                 visitor.endVisit(this, classScope);