6e62d4797ad223eaec5d0fd6d6fd901bdb7f9866
[phpeclipse.git] /
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpeclipse.internal.compiler.ast;
12
13 import java.util.ArrayList;
14
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;
26
27
28 public class ConstructorDeclaration extends AbstractMethodDeclaration {
29
30         public ExplicitConstructorCall constructorCall;
31         public final static char[] ConstantPoolName = "<init>".toCharArray(); //$NON-NLS-1$
32         public boolean isDefaultConstructor = false;
33
34         public ConstructorDeclaration(CompilationResult compilationResult){
35                 super(compilationResult);
36         }
37         
38         public void analyseCode(
39                 ClassScope classScope,
40                 InitializationFlowContext initializerFlowContext,
41                 FlowInfo flowInfo) {
42
43                 if (ignoreFurtherInvestigation)
44                         return;
45
46                 if (this.binding != null && this.binding.isPrivate() && !this.binding.isPrivateUsed()) {
47                         if (!classScope.referenceCompilationUnit().compilationResult.hasSyntaxError()) {
48                                 scope.problemReporter().unusedPrivateConstructor(this);
49                         }
50                 }
51                         
52                 // check constructor recursion, once all constructor got resolved
53                 if (isRecursive(null /*lazy initialized visited list*/)) {                              
54                         this.scope.problemReporter().recursiveConstructorInvocation(this.constructorCall);
55                 }
56                         
57                 try {
58                         ExceptionHandlingFlowContext constructorContext =
59                                 new ExceptionHandlingFlowContext(
60                                         initializerFlowContext.parent,
61                                         this,
62                                         binding.thrownExceptions,
63                                         scope,
64                                         FlowInfo.DEAD_END);
65                         initializerFlowContext.checkInitializerExceptions(
66                                 scope,
67                                 constructorContext,
68                                 flowInfo);
69
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){
74                                         int size;
75                                         if ((size = computedExceptions.size()) > 0){
76                                                 ReferenceBinding[] actuallyThrownExceptions;
77                                                 computedExceptions.toArray(actuallyThrownExceptions = new ReferenceBinding[size]);
78                                                 binding.thrownExceptions = actuallyThrownExceptions;
79                                         }
80                                 }
81                         }
82                         
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++) {
90                                                 FieldBinding field;
91                                                 if (!(field = fields[i]).isStatic()) {
92                                                         flowInfo.markAsDefinitelyAssigned(field);
93                                                 }
94                                         }
95                                 }
96                                 flowInfo = constructorCall.analyseCode(scope, constructorContext, flowInfo);
97                         }
98                         // propagate to statements
99                         if (statements != null) {
100                                 boolean didAlreadyComplain = false;
101                                 for (int i = 0, count = statements.length; i < count; i++) {
102                                         Statement stat;
103                                         if (!flowInfo.complainIfUnreachable(stat = statements[i], scope, didAlreadyComplain)) {
104                                                 flowInfo = stat.analyseCode(scope, constructorContext, flowInfo);
105                                         } else {
106                                                 didAlreadyComplain = true;
107                                         }
108                                 }
109                         }
110                         // check for missing returning path
111                         this.needFreeReturn = flowInfo.isReachable();
112
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++) {
119                                         FieldBinding field;
120                                         if ((!(field = fields[i]).isStatic())
121                                                 && field.isFinal()
122                                                 && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
123                                                 scope.problemReporter().uninitializedBlankFinalField(
124                                                         field,
125                                                         isDefaultConstructor ? (AstNode) scope.referenceType() : this);
126                                         }
127                                 }
128                         }
129                 } catch (AbortMethod e) {
130                         this.ignoreFurtherInvestigation = true;
131                 }
132         }
133
134         /**
135          * Bytecode generation for a constructor
136          *
137          * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
138          * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
139          */
140 //      public void generateCode(ClassScope classScope, ClassFile classFile) {
141 //              
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);
152 //                      return;
153 //              }
154 //              try {
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.
160 //                              try {
161 //                                      if (statements != null) {
162 //                                              for (int i = 0, max = statements.length; i < max; i++)
163 //                                                      statements[i].resetStateForCodeGeneration();
164 //                                      }
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);
176 //                              }
177 //                      } else {
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);
184 //                      }
185 //              }
186 //      }
187 //
188 //      public void generateSyntheticFieldInitializationsIfNecessary(
189 //              MethodScope scope,
190 //              CodeStream codeStream,
191 //              ReferenceBinding declaringClass) {
192 //                      
193 //              if (!declaringClass.isNestedType()) return;
194 //              
195 //              NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
196 //
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);
204 //                      }
205 //              }
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);
213 //                      }
214 //              }
215 //      }
216 //
217 //      private void internalGenerateCode(ClassScope classScope, ClassFile classFile) {
218 //              
219 //              classFile.generateMethodInfoHeader(binding);
220 //              int methodAttributeOffset = classFile.contentsOffset;
221 //              int attributeNumber = classFile.generateMethodInfoAttribute(binding);
222 //              if ((!binding.isNative()) && (!binding.isAbstract())) {
223 //                      
224 //                      TypeDeclaration declaringType = classScope.referenceContext;
225 //                      int codeAttributeOffset = classFile.contentsOffset;
226 //                      classFile.generateCodeAttributeHeader();
227 //                      CodeStream codeStream = classFile.codeStream;
228 //                      codeStream.reset(this, classFile);
229 //
230 //                      // initialize local positions - including initializer scope.
231 //                      ReferenceBinding declaringClass = binding.declaringClass;
232 //
233 //                      int argSlotSize = 1; // this==aload0
234 //                      
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,
240 //                                      codeStream);
241 //                              argSlotSize += nestedType.enclosingInstancesSlotSize;
242 //                              argSlotSize += nestedType.outerLocalVariablesSlotSize;
243 //                      } else {
244 //                              scope.computeLocalVariablePositions(1,  codeStream);
245 //                      }
246 //                              
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)) {
255 //                                              argSlotSize += 2;
256 //                                      } else {
257 //                                              argSlotSize++;
258 //                                      }
259 //                              }
260 //                      }
261 //                      
262 //                      MethodScope initializerScope = declaringType.initializerScope;
263 //                      initializerScope.computeLocalVariablePositions(argSlotSize, codeStream); // offset by the argument size (since not linked to method scope)
264 //
265 //                      boolean needFieldInitializations = constructorCall == null || constructorCall.accessMode != ExplicitConstructorCall.This;
266 //
267 //                      // post 1.4 source level, synthetic initializations occur prior to explicit constructor call
268 //                      boolean preInitSyntheticFields = scope.environment().options.targetJDK >= CompilerOptions.JDK1_4;
269 //
270 //                      if (needFieldInitializations && preInitSyntheticFields){
271 //                              generateSyntheticFieldInitializationsIfNecessary(scope, codeStream, declaringClass);
272 //                      }                       
273 //                      // generate constructor call
274 //                      if (constructorCall != null) {
275 //                              constructorCall.generateCode(scope, codeStream);
276 //                      }
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);
281 //                              }
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);
288 //                                              }
289 //                                      }
290 //                              }
291 //                      }
292 //                      // generate statements
293 //                      if (statements != null) {
294 //                              for (int i = 0, max = statements.length; i < max; i++) {
295 //                                      statements[i].generateCode(scope, codeStream);
296 //                              }
297 //                      }
298 //                      if (this.needFreeReturn) {
299 //                              codeStream.return_();
300 //                      }
301 //                      // local variable attributes
302 //                      codeStream.exitUserScope(scope);
303 //                      codeStream.recordPositionsFrom(0, this.bodyEnd);
304 //                      classFile.completeCodeAttribute(codeAttributeOffset);
305 //                      attributeNumber++;
306 //              }
307 //              classFile.completeMethodInfo(methodAttributeOffset, attributeNumber);
308 //
309 //              // if a problem got reported during code gen, then trigger problem method creation
310 //              if (ignoreFurtherInvestigation) {
311 //                      throw new AbortMethod(scope.referenceCompilationUnit().compilationResult);
312 //              }
313 //      }
314
315         public boolean isConstructor() {
316
317                 return true;
318         }
319
320         public boolean isDefaultConstructor() {
321
322                 return isDefaultConstructor;
323         }
324
325         public boolean isInitializationMethod() {
326
327                 return true;
328         }
329
330         /**
331          * Returns true if the constructor is directly involved in a cycle.
332          * Given most constructors aren't, we only allocate the visited list
333          * lazily.
334          */
335         public boolean isRecursive(ArrayList visited) {
336
337                 if (this.binding == null
338                                 || this.constructorCall == null
339                                 || this.constructorCall.binding == null
340                                 || this.constructorCall.isSuperAccess()
341                                 || !this.constructorCall.binding.isValidBinding()) {
342                         return false;
343                 }
344                 
345                 ConstructorDeclaration targetConstructor = 
346                         ((ConstructorDeclaration)this.scope.referenceType().declarationOf(constructorCall.binding));
347                 if (this == targetConstructor) return true; // direct case
348
349                 if (visited == null) { // lazy allocation
350                         visited = new ArrayList(1);
351                 } else {
352                         int index = visited.indexOf(this);
353                         if (index >= 0) return index == 0; // only blame if directly part of the cycle
354                 }
355                 visited.add(this);
356
357                 return targetConstructor.isRecursive(visited);
358         }
359         
360         public void parseStatements(UnitParser parser, CompilationUnitDeclaration unit) {
361
362                 //fill up the constructor body with its statements
363                 if (ignoreFurtherInvestigation)
364                         return;
365                 if (isDefaultConstructor){
366                         constructorCall = SuperReference.implicitSuperConstructorCall();
367                         constructorCall.sourceStart = sourceStart;
368                         constructorCall.sourceEnd = sourceEnd; 
369                         return;
370                 }
371                 parser.parse(this, unit);
372
373         }
374
375         /*
376          * Type checking for constructor, just another method, except for special check
377          * for recursive constructor invocations.
378          */
379         public void resolveStatements() {
380
381                 if (!CharOperation.equals(scope.enclosingSourceType().sourceName, selector)){
382                         scope.problemReporter().missingReturnType(this);
383                 }
384
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);
393                                         }
394                                         this.constructorCall = null;
395                         } else {
396                                 this.constructorCall.resolve(this.scope);
397                         }
398                 }
399                 
400                 super.resolveStatements();
401         }
402
403         public String toStringStatements(int tab) {
404
405                 String s = " {"; //$NON-NLS-1$
406                 if (constructorCall != null) {
407                         s = s + "\n" + constructorCall.toString(tab) + ";"; //$NON-NLS-1$ //$NON-NLS-2$
408                 }
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$
414                                 }
415                         }
416                 }
417                 s += "\n" + tabString(tab == 0 ? 0 : tab - 1) + "}"; //$NON-NLS-1$ //$NON-NLS-2$
418                 //$NON-NLS-2$ //$NON-NLS-1$
419                 return s;
420         }
421
422         public void traverse(
423                 IAbstractSyntaxTreeVisitor visitor,
424                 ClassScope classScope) {
425
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);
431                         }
432                         if (thrownExceptions != null) {
433                                 int thrownExceptionsLength = thrownExceptions.length;
434                                 for (int i = 0; i < thrownExceptionsLength; i++)
435                                         thrownExceptions[i].traverse(visitor, scope);
436                         }
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);
443                         }
444                 }
445                 visitor.endVisit(this, classScope);
446         }
447 }