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