A massive organize imports and formatting of the sources using default Eclipse code...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / ConstructorDeclaration.java
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.phpdt.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.ASTVisitor;
17 import net.sourceforge.phpdt.internal.compiler.CompilationResult;
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 public class ConstructorDeclaration extends AbstractMethodDeclaration {
28
29         public ExplicitConstructorCall constructorCall;
30
31         public final static char[] ConstantPoolName = "<init>".toCharArray(); //$NON-NLS-1$
32
33         public boolean isDefaultConstructor = false;
34
35         public ConstructorDeclaration(CompilationResult compilationResult) {
36                 super(compilationResult);
37         }
38
39         public void analyseCode(ClassScope classScope,
40                         InitializationFlowContext initializerFlowContext, FlowInfo flowInfo) {
41
42                 if (ignoreFurtherInvestigation)
43                         return;
44
45                 if (this.binding != null && this.binding.isPrivate()
46                                 && !this.binding.isPrivateUsed()) {
47                         if (!classScope.referenceCompilationUnit().compilationResult
48                                         .hasSyntaxError()) {
49                                 scope.problemReporter().unusedPrivateConstructor(this);
50                         }
51                 }
52
53                 // check constructor recursion, once all constructor got resolved
54                 if (isRecursive(null /* lazy initialized visited list */)) {
55                         this.scope.problemReporter().recursiveConstructorInvocation(
56                                         this.constructorCall);
57                 }
58
59                 try {
60                         ExceptionHandlingFlowContext constructorContext = new ExceptionHandlingFlowContext(
61                                         initializerFlowContext.parent, this,
62                                         binding.thrownExceptions, scope, FlowInfo.DEAD_END);
63                         initializerFlowContext.checkInitializerExceptions(scope,
64                                         constructorContext, flowInfo);
65
66                         // anonymous constructor can gain extra thrown exceptions from
67                         // unhandled ones
68                         if (binding.declaringClass.isAnonymousType()) {
69                                 ArrayList computedExceptions = constructorContext.extendedExceptions;
70                                 if (computedExceptions != null) {
71                                         int size;
72                                         if ((size = computedExceptions.size()) > 0) {
73                                                 ReferenceBinding[] actuallyThrownExceptions;
74                                                 computedExceptions
75                                                                 .toArray(actuallyThrownExceptions = new ReferenceBinding[size]);
76                                                 binding.thrownExceptions = actuallyThrownExceptions;
77                                         }
78                                 }
79                         }
80
81                         // propagate to constructor call
82                         if (constructorCall != null) {
83                                 // if calling 'this(...)', then flag all non-static fields as
84                                 // definitely
85                                 // set since they are supposed to be set inside other local
86                                 // 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,
97                                                 constructorContext, flowInfo);
98                         }
99                         // propagate to statements
100                         if (statements != null) {
101                                 boolean didAlreadyComplain = false;
102                                 for (int i = 0, count = statements.length; i < count; i++) {
103                                         Statement stat;
104                                         if (!flowInfo.complainIfUnreachable(stat = statements[i],
105                                                         scope, didAlreadyComplain)) {
106                                                 flowInfo = stat.analyseCode(scope, constructorContext,
107                                                                 flowInfo);
108                                         } else {
109                                                 didAlreadyComplain = true;
110                                         }
111                                 }
112                         }
113                         // check for missing returning path
114                         this.needFreeReturn = flowInfo.isReachable();
115
116                         // check missing blank final field initializations
117                         if ((constructorCall != null)
118                                         && (constructorCall.accessMode != ExplicitConstructorCall.This)) {
119                                 flowInfo = flowInfo
120                                                 .mergedWith(constructorContext.initsOnReturn);
121                                 FieldBinding[] fields = binding.declaringClass.fields();
122                                 for (int i = 0, count = fields.length; i < count; i++) {
123                                         FieldBinding field;
124                                         if ((!(field = fields[i]).isStatic()) && field.isFinal()
125                                                         && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
126                                                 scope.problemReporter().uninitializedBlankFinalField(
127                                                                 field,
128                                                                 isDefaultConstructor ? (ASTNode) scope
129                                                                                 .referenceType() : this);
130                                         }
131                                 }
132                         }
133                 } catch (AbortMethod e) {
134                         this.ignoreFurtherInvestigation = true;
135                 }
136         }
137
138         /**
139          * Bytecode generation for a constructor
140          * 
141          * @param classScope
142          *            net.sourceforge.phpdt.internal.compiler.lookup.ClassScope
143          * @param classFile
144          *            net.sourceforge.phpdt.internal.compiler.codegen.ClassFile
145          */
146         // public void generateCode(ClassScope classScope, ClassFile classFile) {
147         //              
148         // int problemResetPC = 0;
149         // if (ignoreFurtherInvestigation) {
150         // if (this.binding == null)
151         // return; // Handle methods with invalid signature or duplicates
152         // int problemsLength;
153         // IProblem[] problems =
154         // scope.referenceCompilationUnit().compilationResult.getProblems();
155         // IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
156         // System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
157         // classFile.addProblemConstructor(this, binding, problemsCopy);
158         // return;
159         // }
160         // try {
161         // problemResetPC = classFile.contentsOffset;
162         // this.internalGenerateCode(classScope, classFile);
163         // } catch (AbortMethod e) {
164         // if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
165         // // a branch target required a goto_w, restart code gen in wide mode.
166         // try {
167         // if (statements != null) {
168         // for (int i = 0, max = statements.length; i < max; i++)
169         // statements[i].resetStateForCodeGeneration();
170         // }
171         // classFile.contentsOffset = problemResetPC;
172         // classFile.methodCount--;
173         // classFile.codeStream.wideMode = true; // request wide mode
174         // this.internalGenerateCode(classScope, classFile); // restart method
175         // generation
176         // } catch (AbortMethod e2) {
177         // int problemsLength;
178         // IProblem[] problems =
179         // scope.referenceCompilationUnit().compilationResult.getAllProblems();
180         // IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
181         // System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
182         // classFile.addProblemConstructor(this, binding, problemsCopy,
183         // problemResetPC);
184         // }
185         // } else {
186         // int problemsLength;
187         // IProblem[] problems =
188         // scope.referenceCompilationUnit().compilationResult.getAllProblems();
189         // IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
190         // System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
191         // classFile.addProblemConstructor(this, binding, problemsCopy,
192         // problemResetPC);
193         // }
194         // }
195         // }
196         //
197         // public void generateSyntheticFieldInitializationsIfNecessary(
198         // MethodScope scope,
199         // CodeStream codeStream,
200         // ReferenceBinding declaringClass) {
201         //                      
202         // if (!declaringClass.isNestedType()) return;
203         //              
204         // NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
205         //
206         // SyntheticArgumentBinding[] syntheticArgs =
207         // nestedType.syntheticEnclosingInstances();
208         // for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i
209         // < max; i++) {
210         // SyntheticArgumentBinding syntheticArg;
211         // if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
212         // codeStream.aload_0();
213         // codeStream.load(syntheticArg);
214         // codeStream.putfield(syntheticArg.matchingField);
215         // }
216         // }
217         // syntheticArgs = nestedType.syntheticOuterLocalVariables();
218         // for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i
219         // < max; i++) {
220         // SyntheticArgumentBinding syntheticArg;
221         // if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
222         // codeStream.aload_0();
223         // codeStream.load(syntheticArg);
224         // codeStream.putfield(syntheticArg.matchingField);
225         // }
226         // }
227         // }
228         //
229         // private void internalGenerateCode(ClassScope classScope, ClassFile
230         // classFile) {
231         //              
232         // classFile.generateMethodInfoHeader(binding);
233         // int methodAttributeOffset = classFile.contentsOffset;
234         // int attributeNumber = classFile.generateMethodInfoAttribute(binding);
235         // if ((!binding.isNative()) && (!binding.isAbstract())) {
236         //                      
237         // TypeDeclaration declaringType = classScope.referenceContext;
238         // int codeAttributeOffset = classFile.contentsOffset;
239         // classFile.generateCodeAttributeHeader();
240         // CodeStream codeStream = classFile.codeStream;
241         // codeStream.reset(this, classFile);
242         //
243         // // initialize local positions - including initializer scope.
244         // ReferenceBinding declaringClass = binding.declaringClass;
245         //
246         // int argSlotSize = 1; // this==aload0
247         //                      
248         // if (declaringClass.isNestedType()){
249         // NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
250         // this.scope.extraSyntheticArguments =
251         // nestedType.syntheticOuterLocalVariables();
252         // scope.computeLocalVariablePositions(// consider synthetic arguments if
253         // any
254         // nestedType.enclosingInstancesSlotSize + 1,
255         // codeStream);
256         // argSlotSize += nestedType.enclosingInstancesSlotSize;
257         // argSlotSize += nestedType.outerLocalVariablesSlotSize;
258         // } else {
259         // scope.computeLocalVariablePositions(1, codeStream);
260         // }
261         //                              
262         // if (arguments != null) {
263         // for (int i = 0, max = arguments.length; i < max; i++) {
264         // // arguments initialization for local variable debug attributes
265         // LocalVariableBinding argBinding;
266         // codeStream.addVisibleLocalVariable(argBinding = arguments[i].binding);
267         // argBinding.recordInitializationStartPC(0);
268         // TypeBinding argType;
269         // if ((argType = argBinding.type) == LongBinding || (argType ==
270         // DoubleBinding)) {
271         // argSlotSize += 2;
272         // } else {
273         // argSlotSize++;
274         // }
275         // }
276         // }
277         //                      
278         // MethodScope initializerScope = declaringType.initializerScope;
279         // initializerScope.computeLocalVariablePositions(argSlotSize, codeStream);
280         // // offset by the argument size (since not linked to method scope)
281         //
282         // boolean needFieldInitializations = constructorCall == null ||
283         // constructorCall.accessMode != ExplicitConstructorCall.This;
284         //
285         // // post 1.4 source level, synthetic initializations occur prior to
286         // explicit constructor call
287         // boolean preInitSyntheticFields = scope.environment().options.targetJDK >=
288         // CompilerOptions.JDK1_4;
289         //
290         // if (needFieldInitializations && preInitSyntheticFields){
291         // generateSyntheticFieldInitializationsIfNecessary(scope, codeStream,
292         // declaringClass);
293         // }
294         // // generate constructor call
295         // if (constructorCall != null) {
296         // constructorCall.generateCode(scope, codeStream);
297         // }
298         // // generate field initialization - only if not invoking another
299         // constructor call of the same class
300         // if (needFieldInitializations) {
301         // if (!preInitSyntheticFields){
302         // generateSyntheticFieldInitializationsIfNecessary(scope, codeStream,
303         // declaringClass);
304         // }
305         // // generate user field initialization
306         // if (declaringType.fields != null) {
307         // for (int i = 0, max = declaringType.fields.length; i < max; i++) {
308         // FieldDeclaration fieldDecl;
309         // if (!(fieldDecl = declaringType.fields[i]).isStatic()) {
310         // fieldDecl.generateCode(initializerScope, codeStream);
311         // }
312         // }
313         // }
314         // }
315         // // generate statements
316         // if (statements != null) {
317         // for (int i = 0, max = statements.length; i < max; i++) {
318         // statements[i].generateCode(scope, codeStream);
319         // }
320         // }
321         // if (this.needFreeReturn) {
322         // codeStream.return_();
323         // }
324         // // local variable attributes
325         // codeStream.exitUserScope(scope);
326         // codeStream.recordPositionsFrom(0, this.bodyEnd);
327         // classFile.completeCodeAttribute(codeAttributeOffset);
328         // attributeNumber++;
329         // }
330         // classFile.completeMethodInfo(methodAttributeOffset, attributeNumber);
331         //
332         // // if a problem got reported during code gen, then trigger problem method
333         // creation
334         // if (ignoreFurtherInvestigation) {
335         // throw new
336         // AbortMethod(scope.referenceCompilationUnit().compilationResult);
337         // }
338         // }
339         public boolean isConstructor() {
340
341                 return true;
342         }
343
344         public boolean isDefaultConstructor() {
345
346                 return isDefaultConstructor;
347         }
348
349         public boolean isInitializationMethod() {
350
351                 return true;
352         }
353
354         /**
355          * Returns true if the constructor is directly involved in a cycle. Given
356          * most constructors aren't, we only allocate the visited list lazily.
357          */
358         public boolean isRecursive(ArrayList visited) {
359
360                 if (this.binding == null || this.constructorCall == null
361                                 || this.constructorCall.binding == null
362                                 || this.constructorCall.isSuperAccess()
363                                 || !this.constructorCall.binding.isValidBinding()) {
364                         return false;
365                 }
366
367                 ConstructorDeclaration targetConstructor = ((ConstructorDeclaration) this.scope
368                                 .referenceType().declarationOf(constructorCall.binding));
369                 if (this == targetConstructor)
370                         return true; // direct case
371
372                 if (visited == null) { // lazy allocation
373                         visited = new ArrayList(1);
374                 } else {
375                         int index = visited.indexOf(this);
376                         if (index >= 0)
377                                 return index == 0; // only blame if directly part of the cycle
378                 }
379                 visited.add(this);
380
381                 return targetConstructor.isRecursive(visited);
382         }
383
384         public void parseStatements(UnitParser parser,
385                         CompilationUnitDeclaration unit) {
386
387                 // fill up the constructor body with its statements
388                 if (ignoreFurtherInvestigation)
389                         return;
390                 if (isDefaultConstructor) {
391                         constructorCall = SuperReference.implicitSuperConstructorCall();
392                         constructorCall.sourceStart = sourceStart;
393                         constructorCall.sourceEnd = sourceEnd;
394                         return;
395                 }
396                 parser.parse(this, unit);
397
398         }
399
400         /*
401          * Type checking for constructor, just another method, except for special
402          * check for recursive constructor invocations.
403          */
404         public void resolveStatements() {
405
406                 if (!CharOperation.equals(scope.enclosingSourceType().sourceName,
407                                 selector)) {
408                         scope.problemReporter().missingReturnType(this);
409                 }
410
411                 // if null ==> an error has occurs at parsing time ....
412                 if (this.constructorCall != null) {
413                         // e.g. using super() in java.lang.Object
414                         if (this.binding != null
415                                         && this.binding.declaringClass.id == T_Object
416                                         && this.constructorCall.accessMode != ExplicitConstructorCall.This) {
417                                 if (this.constructorCall.accessMode == ExplicitConstructorCall.Super) {
418                                         scope.problemReporter().cannotUseSuperInJavaLangObject(
419                                                         this.constructorCall);
420                                 }
421                                 this.constructorCall = null;
422                         } else {
423                                 this.constructorCall.resolve(this.scope);
424                         }
425                 }
426
427                 super.resolveStatements();
428         }
429
430         public String toStringStatements(int tab) {
431
432                 String s = " {"; //$NON-NLS-1$
433                 if (constructorCall != null) {
434                         s = s + "\n" + constructorCall.toString(tab) + ";"; //$NON-NLS-1$ //$NON-NLS-2$
435                 }
436                 if (statements != null) {
437                         for (int i = 0; i < statements.length; i++) {
438                                 s = s + "\n" + statements[i].toString(tab); //$NON-NLS-1$
439                                 if (!(statements[i] instanceof Block)) {
440                                         s += ";"; //$NON-NLS-1$
441                                 }
442                         }
443                 }
444                 s += "\n" + tabString(tab == 0 ? 0 : tab - 1) + "}"; //$NON-NLS-1$ //$NON-NLS-2$
445                 //$NON-NLS-2$ //$NON-NLS-1$
446                 return s;
447         }
448
449         public void traverse(ASTVisitor visitor, ClassScope classScope) {
450
451                 if (visitor.visit(this, classScope)) {
452                         if (arguments != null) {
453                                 int argumentLength = arguments.length;
454                                 for (int i = 0; i < argumentLength; i++)
455                                         arguments[i].traverse(visitor, scope);
456                         }
457                         if (thrownExceptions != null) {
458                                 int thrownExceptionsLength = thrownExceptions.length;
459                                 for (int i = 0; i < thrownExceptionsLength; i++)
460                                         thrownExceptions[i].traverse(visitor, scope);
461                         }
462                         if (constructorCall != null)
463                                 constructorCall.traverse(visitor, scope);
464                         if (statements != null) {
465                                 int statementsLength = statements.length;
466                                 for (int i = 0; i < statementsLength; i++)
467                                         statements[i].traverse(visitor, scope);
468                         }
469                 }
470                 visitor.endVisit(this, classScope);
471         }
472 }