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