d6a9244753b8eefd3e410dbc751b98f34758c710
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / ConstructorDeclaration.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.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.*;
16 import net.sourceforge.phpdt.internal.compiler.*;
17 import net.sourceforge.phpdt.internal.compiler.codegen.*;
18 import net.sourceforge.phpdt.internal.compiler.flow.*;
19 import net.sourceforge.phpdt.internal.compiler.lookup.*;
20 import net.sourceforge.phpdt.internal.compiler.parser.*;
21 import net.sourceforge.phpdt.internal.compiler.problem.*;
22 import net.sourceforge.phpdt.internal.compiler.util.*;
23
24 public class ConstructorDeclaration extends AbstractMethodDeclaration {
25
26         public ExplicitConstructorCall constructorCall;
27         public final static char[] ConstantPoolName = "<init>".toCharArray(); //$NON-NLS-1$
28         public boolean isDefaultConstructor = false;
29
30         public int referenceCount = 0;
31         // count how many times this constructor is referenced from other local constructors
32
33         public ConstructorDeclaration(CompilationResult compilationResult){
34                 super(compilationResult);
35         }
36         
37         public void analyseCode(
38                 ClassScope classScope,
39                 InitializationFlowContext initializerFlowContext,
40                 FlowInfo flowInfo) {
41
42                 if (ignoreFurtherInvestigation)
43                         return;
44                 try {
45                         ExceptionHandlingFlowContext constructorContext =
46                                 new ExceptionHandlingFlowContext(
47                                         initializerFlowContext.parent,
48                                         this,
49                                         binding.thrownExceptions,
50                                         scope,
51                                         FlowInfo.DeadEnd);
52                         initializerFlowContext.checkInitializerExceptions(
53                                 scope,
54                                 constructorContext,
55                                 flowInfo);
56
57                         // anonymous constructor can gain extra thrown exceptions from unhandled ones
58                         if (binding.declaringClass.isAnonymousType()) {
59                                 ArrayList computedExceptions = constructorContext.extendedExceptions;
60                                 if (computedExceptions != null){
61                                         int size;
62                                         if ((size = computedExceptions.size()) > 0){
63                                                 ReferenceBinding[] actuallyThrownExceptions;
64                                                 computedExceptions.toArray(actuallyThrownExceptions = new ReferenceBinding[size]);
65                                                 binding.thrownExceptions = actuallyThrownExceptions;
66                                         }
67                                 }
68                         }
69                         
70                         // propagate to constructor call
71                         if (constructorCall != null) {
72                                 // if calling 'this(...)', then flag all non-static fields as definitely
73                                 // set since they are supposed to be set inside other local constructor
74                                 if (constructorCall.accessMode == ExplicitConstructorCall.This) {
75                                         FieldBinding[] fields = binding.declaringClass.fields();
76                                         for (int i = 0, count = fields.length; i < count; i++) {
77                                                 FieldBinding field;
78                                                 if (!(field = fields[i]).isStatic()) {
79                                                         flowInfo.markAsDefinitelyAssigned(field);
80                                                 }
81                                         }
82                                 }
83                                 flowInfo = constructorCall.analyseCode(scope, constructorContext, flowInfo);
84                         }
85                         // propagate to statements
86                         if (statements != null) {
87                                 for (int i = 0, count = statements.length; i < count; i++) {
88                                         Statement stat;
89                                         if (!flowInfo.complainIfUnreachable((stat = statements[i]), scope)) {
90                                                 flowInfo = stat.analyseCode(scope, constructorContext, flowInfo);
91                                         }
92                                 }
93                         }
94                         // check for missing returning path
95                         needFreeReturn =
96                                 !((flowInfo == FlowInfo.DeadEnd) || flowInfo.isFakeReachable());
97
98                         // check missing blank final field initializations
99                         if ((constructorCall != null)
100                                 && (constructorCall.accessMode != ExplicitConstructorCall.This)) {
101                                 flowInfo = flowInfo.mergedWith(initializerFlowContext.initsOnReturn);
102                                 FieldBinding[] fields = binding.declaringClass.fields();
103                                 for (int i = 0, count = fields.length; i < count; i++) {
104                                         FieldBinding field;
105                                         if ((!(field = fields[i]).isStatic())
106                                                 && field.isFinal()
107                                                 && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
108                                                 scope.problemReporter().uninitializedBlankFinalField(
109                                                         field,
110                                                         isDefaultConstructor ? (AstNode) scope.referenceType() : this);
111                                         }
112                                 }
113                         }
114                 } catch (AbortMethod e) {
115                         this.ignoreFurtherInvestigation = true;
116                 }
117         }
118
119         /**
120          * Bytecode generation for a constructor
121          *
122          * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
123          * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
124          */
125         public void generateCode(ClassScope classScope, ClassFile classFile) {
126                 int problemResetPC = 0;
127                 if (ignoreFurtherInvestigation) {
128                         if (this.binding == null)
129                                 return; // Handle methods with invalid signature or duplicates
130                         int problemsLength;
131                         IProblem[] problems =
132                                 scope.referenceCompilationUnit().compilationResult.getProblems();
133                         IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
134                         System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
135                         classFile.addProblemConstructor(this, binding, problemsCopy);
136                         return;
137                 }
138                 try {
139                         problemResetPC = classFile.contentsOffset;
140                         this.internalGenerateCode(classScope, classFile);
141                 } catch (AbortMethod e) {
142                         if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
143                                 // a branch target required a goto_w, restart code gen in wide mode.
144                                 try {
145                                         if (statements != null) {
146                                                 for (int i = 0, max = statements.length; i < max; i++)
147                                                         statements[i].resetStateForCodeGeneration();
148                                         }
149                                         classFile.contentsOffset = problemResetPC;
150                                         classFile.methodCount--;
151                                         classFile.codeStream.wideMode = true; // request wide mode 
152                                         this.internalGenerateCode(classScope, classFile); // restart method generation
153                                 } catch (AbortMethod e2) {
154                                         int problemsLength;
155                                         IProblem[] problems =
156                                                 scope.referenceCompilationUnit().compilationResult.getProblems();
157                                         IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
158                                         System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
159                                         classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC);
160                                 }
161                         } else {
162                                 int problemsLength;
163                                 IProblem[] problems =
164                                         scope.referenceCompilationUnit().compilationResult.getProblems();
165                                 IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
166                                 System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
167                                 classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC);
168                         }
169                 }
170         }
171
172         private void internalGenerateCode(ClassScope classScope, ClassFile classFile) {
173                 classFile.generateMethodInfoHeader(binding);
174                 int methodAttributeOffset = classFile.contentsOffset;
175                 int attributeNumber = classFile.generateMethodInfoAttribute(binding);
176                 if ((!binding.isNative()) && (!binding.isAbstract())) {
177                         TypeDeclaration declaringType = classScope.referenceContext;
178                         int codeAttributeOffset = classFile.contentsOffset;
179                         classFile.generateCodeAttributeHeader();
180                         CodeStream codeStream = classFile.codeStream;
181                         codeStream.reset(this, classFile);
182                         // initialize local positions - including initializer scope.
183                         ReferenceBinding declaringClass = binding.declaringClass;
184                         int argSize = 0;
185                         scope.computeLocalVariablePositions(// consider synthetic arguments if any
186                         argSize =
187                                 declaringClass.isNestedType()
188                                         ? ((NestedTypeBinding) declaringClass).syntheticArgumentsOffset
189                                         : 1,
190                                 codeStream);
191                         if (arguments != null) {
192                                 for (int i = 0, max = arguments.length; i < max; i++) {
193                                         // arguments initialization for local variable debug attributes
194                                         LocalVariableBinding argBinding;
195                                         codeStream.addVisibleLocalVariable(argBinding = arguments[i].binding);
196                                         argBinding.recordInitializationStartPC(0);
197                                         TypeBinding argType;
198                                         if ((argType = argBinding.type) == LongBinding || (argType == DoubleBinding)) {
199                                                 argSize += 2;
200                                         } else {
201                                                 argSize++;
202                                         }
203                                 }
204                         }
205                         MethodScope initializerScope = declaringType.initializerScope;
206                         initializerScope.computeLocalVariablePositions(argSize, codeStream);
207                         // offset by the argument size (since not linked to method scope)
208
209                         // generate constructor call
210                         if (constructorCall != null) {
211                                 constructorCall.generateCode(scope, codeStream);
212                         }
213                         // generate field initialization - only if not invoking another constructor call of the same class
214                         if ((constructorCall != null)
215                                 && (constructorCall.accessMode != ExplicitConstructorCall.This)) {
216                                 // generate synthetic fields initialization
217                                 if (declaringClass.isNestedType()) {
218                                         NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
219                                         SyntheticArgumentBinding[] syntheticArgs =
220                                                 nestedType.syntheticEnclosingInstances();
221                                         for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length;
222                                                 i < max;
223                                                 i++) {
224                                                 if (syntheticArgs[i].matchingField != null) {
225                                                         codeStream.aload_0();
226                                                         codeStream.load(syntheticArgs[i]);
227                                                         codeStream.putfield(syntheticArgs[i].matchingField);
228                                                 }
229                                         }
230                                         syntheticArgs = nestedType.syntheticOuterLocalVariables();
231                                         for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length;
232                                                 i < max;
233                                                 i++) {
234                                                 if (syntheticArgs[i].matchingField != null) {
235                                                         codeStream.aload_0();
236                                                         codeStream.load(syntheticArgs[i]);
237                                                         codeStream.putfield(syntheticArgs[i].matchingField);
238                                                 }
239                                         }
240                                 }
241                                 // generate user field initialization
242                                 if (declaringType.fields != null) {
243                                         for (int i = 0, max = declaringType.fields.length; i < max; i++) {
244                                                 FieldDeclaration fieldDecl;
245                                                 if (!(fieldDecl = declaringType.fields[i]).isStatic()) {
246                                                         fieldDecl.generateCode(initializerScope, codeStream);
247                                                 }
248                                         }
249                                 }
250                         }
251                         // generate statements
252                         if (statements != null) {
253                                 for (int i = 0, max = statements.length; i < max; i++) {
254                                         statements[i].generateCode(scope, codeStream);
255                                 }
256                         }
257                         if (needFreeReturn) {
258                                 codeStream.return_();
259                         }
260                         // local variable attributes
261                         codeStream.exitUserScope(scope);
262                         codeStream.recordPositionsFrom(0, this.bodyEnd);
263                         classFile.completeCodeAttribute(codeAttributeOffset);
264                         attributeNumber++;
265                 }
266                 classFile.completeMethodInfo(methodAttributeOffset, attributeNumber);
267
268                 // if a problem got reported during code gen, then trigger problem method creation
269                 if (ignoreFurtherInvestigation) {
270                         throw new AbortMethod(scope.referenceCompilationUnit().compilationResult);
271                 }
272         }
273
274         public boolean isConstructor() {
275
276                 return true;
277         }
278
279         public boolean isDefaultConstructor() {
280
281                 return isDefaultConstructor;
282         }
283
284         public boolean isInitializationMethod() {
285
286                 return true;
287         }
288
289         public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
290
291                 //fill up the constructor body with its statements
292                 if (ignoreFurtherInvestigation)
293                         return;
294                 if (isDefaultConstructor){
295                         constructorCall =
296                                 new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper);
297                         constructorCall.sourceStart = sourceStart;
298                         constructorCall.sourceEnd = sourceEnd; 
299                         return;
300                 }
301                 parser.parse(this, unit);
302
303         }
304
305         /*
306          * Type checking for constructor, just another method, except for special check
307          * for recursive constructor invocations.
308          */
309         public void resolveStatements(ClassScope upperScope) {
310 /*
311                 // checking for recursive constructor call (protection)
312                 if (!ignoreFurtherInvestigation && constructorCall == null){
313                         constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper);
314                         constructorCall.sourceStart = sourceStart;
315                         constructorCall.sourceEnd = sourceEnd;
316                 }
317 */
318                 if (!CharOperation.equals(scope.enclosingSourceType().sourceName, selector)){
319                         scope.problemReporter().missingReturnType(this);
320                 }
321
322                 // if null ==> an error has occurs at parsing time ....
323                 if (constructorCall != null) {
324                         // e.g. using super() in java.lang.Object
325                         if (binding != null
326                                 && binding.declaringClass.id == T_Object
327                                 && constructorCall.accessMode != ExplicitConstructorCall.This) {
328                                         if (constructorCall.accessMode == ExplicitConstructorCall.Super) {
329                                                 scope.problemReporter().cannotUseSuperInJavaLangObject(constructorCall);
330                                         }
331                                         constructorCall = null;
332                         } else {
333                                 constructorCall.resolve(scope);
334                         }
335                 }
336                 
337                 super.resolveStatements(upperScope);
338
339                 // indirect reference: increment target constructor reference count
340                 if (constructorCall != null){
341                         if (constructorCall.binding != null
342                                 && !constructorCall.isSuperAccess()
343                                 && constructorCall.binding.isValidBinding()) {
344                                 ((ConstructorDeclaration)
345                                                 (upperScope.referenceContext.declarationOf(constructorCall.binding))).referenceCount++;
346                         }
347                 }
348         }
349
350         public String toStringStatements(int tab) {
351
352                 String s = " {"; //$NON-NLS-1$
353                 if (constructorCall != null) {
354                         s = s + "\n" + constructorCall.toString(tab) + ";"; //$NON-NLS-1$ //$NON-NLS-2$
355                 }
356                 if (statements != null) {
357                         for (int i = 0; i < statements.length; i++) {
358                                 s = s + "\n" + statements[i].toString(tab); //$NON-NLS-1$
359                                 if (!(statements[i] instanceof Block)) {
360                                         s += ";"; //$NON-NLS-1$
361                                 }
362                         }
363                 }
364                 s += "\n" + tabString(tab == 0 ? 0 : tab - 1) + "}"; //$NON-NLS-1$ //$NON-NLS-2$
365                 //$NON-NLS-2$ //$NON-NLS-1$
366                 return s;
367         }
368
369         public void traverse(
370                 IAbstractSyntaxTreeVisitor visitor,
371                 ClassScope classScope) {
372
373                 if (visitor.visit(this, classScope)) {
374                         if (arguments != null) {
375                                 int argumentLength = arguments.length;
376                                 for (int i = 0; i < argumentLength; i++)
377                                         arguments[i].traverse(visitor, scope);
378                         }
379                         if (thrownExceptions != null) {
380                                 int thrownExceptionsLength = thrownExceptions.length;
381                                 for (int i = 0; i < thrownExceptionsLength; i++)
382                                         thrownExceptions[i].traverse(visitor, scope);
383                         }
384                         if (constructorCall != null)
385                                 constructorCall.traverse(visitor, scope);
386                         if (statements != null) {
387                                 int statementsLength = statements.length;
388                                 for (int i = 0; i < statementsLength; i++)
389                                         statements[i].traverse(visitor, scope);
390                         }
391                 }
392                 visitor.endVisit(this, classScope);
393         }
394 }