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