added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / ExplicitConstructorCall.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 org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.codegen.*;
15 import org.eclipse.jdt.internal.compiler.flow.*;
16 import org.eclipse.jdt.internal.compiler.lookup.*;
17
18 public class ExplicitConstructorCall extends Statement implements InvocationSite {
19                 
20         public Expression[] arguments;
21         public Expression qualification;
22         public MethodBinding binding;                                                   // exact binding resulting from lookup
23         protected MethodBinding codegenBinding;         // actual binding used for code generation (if no synthetic accessor)
24         MethodBinding syntheticAccessor;                                                // synthetic accessor for inner-emulation
25         public int accessMode;
26         public TypeReference[] typeArguments;
27         public TypeBinding[] genericTypeArguments;
28         
29         public final static int ImplicitSuper = 1;
30         public final static int Super = 2;
31         public final static int This = 3;
32
33         public VariableBinding[][] implicitArguments;
34         boolean discardEnclosingInstance;
35         
36         // TODO Remove once DOMParser is activated
37         public int typeArgumentsSourceStart;
38
39         public ExplicitConstructorCall(int accessMode) {
40                 this.accessMode = accessMode;
41         }
42
43         public FlowInfo analyseCode(
44                 BlockScope currentScope,
45                 FlowContext flowContext,
46                 FlowInfo flowInfo) {
47
48                 // must verify that exceptions potentially thrown by this expression are caught in the method.
49
50                 try {
51                         ((MethodScope) currentScope).isConstructorCall = true;
52
53                         // process enclosing instance
54                         if (qualification != null) {
55                                 flowInfo =
56                                         qualification
57                                                 .analyseCode(currentScope, flowContext, flowInfo)
58                                                 .unconditionalInits();
59                         }
60                         // process arguments
61                         if (arguments != null) {
62                                 for (int i = 0, max = arguments.length; i < max; i++) {
63                                         flowInfo =
64                                                 arguments[i]
65                                                         .analyseCode(currentScope, flowContext, flowInfo)
66                                                         .unconditionalInits();
67                                 }
68                         }
69
70                         ReferenceBinding[] thrownExceptions;
71                         if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) {
72                                 // check exceptions
73                                 flowContext.checkExceptionHandlers(
74                                         thrownExceptions,
75                                         (accessMode == ImplicitSuper)
76                                                 ? (ASTNode) currentScope.methodScope().referenceContext
77                                                 : (ASTNode) this,
78                                         flowInfo,
79                                         currentScope);
80                         }
81                         manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
82                         manageSyntheticAccessIfNecessary(currentScope, flowInfo);
83                         return flowInfo;
84                 } finally {
85                         ((MethodScope) currentScope).isConstructorCall = false;
86                 }
87         }
88
89         /**
90          * Constructor call code generation
91          *
92          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
93          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
94          */
95         public void generateCode(BlockScope currentScope, CodeStream codeStream) {
96
97                 if ((bits & IsReachableMASK) == 0) {
98                         return;
99                 }
100                 try {
101                         ((MethodScope) currentScope).isConstructorCall = true;
102
103                         int pc = codeStream.position;
104                         codeStream.aload_0();
105
106                         ReferenceBinding targetType = this.codegenBinding.declaringClass;
107                         
108                         // special name&ordinal argument generation for enum constructors
109                         if (targetType.erasure().id == T_JavaLangEnum || targetType.isEnum()) {
110                                 codeStream.aload_1(); // pass along name param as name arg
111                                 codeStream.iload_2(); // pass along ordinal param as ordinal arg
112                         }
113                         // handling innerclass constructor invocation
114                         // handling innerclass instance allocation - enclosing instance arguments
115                         if (targetType.isNestedType()) {
116                                 codeStream.generateSyntheticEnclosingInstanceValues(
117                                         currentScope,
118                                         targetType,
119                                         discardEnclosingInstance ? null : qualification,
120                                         this);
121                         }
122                         // generate arguments
123                         generateArguments(binding, arguments, currentScope, codeStream);                        
124                         
125                         // handling innerclass instance allocation - outer local arguments
126                         if (targetType.isNestedType()) {
127                                 codeStream.generateSyntheticOuterArgumentValues(
128                                         currentScope,
129                                         targetType,
130                                         this);
131                         }
132                         if (syntheticAccessor != null) {
133                                 // synthetic accessor got some extra arguments appended to its signature, which need values
134                                 for (int i = 0,
135                                         max = syntheticAccessor.parameters.length - this.codegenBinding.parameters.length;
136                                         i < max;
137                                         i++) {
138                                         codeStream.aconst_null();
139                                 }
140                                 codeStream.invokespecial(syntheticAccessor);
141                         } else {
142                                 codeStream.invokespecial(this.codegenBinding);
143                         }
144                         codeStream.recordPositionsFrom(pc, this.sourceStart);
145                 } finally {
146                         ((MethodScope) currentScope).isConstructorCall = false;
147                 }
148         }
149         /**
150          * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments()
151          */
152         public TypeBinding[] genericTypeArguments() {
153                 return this.genericTypeArguments;
154         }
155         public boolean isImplicitSuper() {
156                 //return true if I'm of these compiler added statement super();
157
158                 return (accessMode == ImplicitSuper);
159         }
160
161         public boolean isSuperAccess() {
162
163                 return accessMode != This;
164         }
165
166         public boolean isTypeAccess() {
167
168                 return true;
169         }
170
171         /* Inner emulation consists in either recording a dependency 
172          * link only, or performing one level of propagation.
173          *
174          * Dependency mechanism is used whenever dealing with source target
175          * types, since by the time we reach them, we might not yet know their
176          * exact need.
177          */
178         void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
179                 ReferenceBinding superTypeErasure = (ReferenceBinding) binding.declaringClass.erasure();
180
181                 if (!flowInfo.isReachable()) return;
182                 // perform some emulation work in case there is some and we are inside a local type only
183                 if (superTypeErasure.isNestedType()
184                         && currentScope.enclosingSourceType().isLocalType()) {
185
186                         if (superTypeErasure.isLocalType()) {
187                                 ((LocalTypeBinding) superTypeErasure).addInnerEmulationDependent(currentScope, qualification != null);
188                         } else {
189                                 // locally propagate, since we already now the desired shape for sure
190                                 currentScope.propagateInnerEmulation(superTypeErasure, qualification != null);
191                         }
192                 }
193         }
194
195         public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
196
197                 if (!flowInfo.isReachable()) return;
198                 // if constructor from parameterized type got found, use the original constructor at codegen time
199                 this.codegenBinding = this.binding.original();
200                 
201                 // perform some emulation work in case there is some and we are inside a local type only
202                 if (binding.isPrivate() && accessMode != This) {
203
204                         if (currentScope.environment().options.isPrivateConstructorAccessChangingVisibility) {
205                                 this.codegenBinding.tagForClearingPrivateModifier();
206                                 // constructor will not be dumped as private, no emulation required thus
207                         } else {
208                                 syntheticAccessor =
209                                         ((SourceTypeBinding) this.codegenBinding.declaringClass).addSyntheticMethod(this.codegenBinding, isSuperAccess());
210                                 currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
211                         }
212                 }
213         }
214
215         public StringBuffer printStatement(int indent, StringBuffer output) {
216
217                 printIndent(indent, output);
218                 if (qualification != null) qualification.printExpression(0, output).append('.');
219                 if (typeArguments != null) {
220                         output.append('<');//$NON-NLS-1$
221                         int max = typeArguments.length - 1;
222                         for (int j = 0; j < max; j++) {
223                                 typeArguments[j].print(0, output);
224                                 output.append(", ");//$NON-NLS-1$
225                         }
226                         typeArguments[max].print(0, output);
227                         output.append('>');
228                 }               
229                 if (accessMode == This) {
230                         output.append("this("); //$NON-NLS-1$
231                 } else {
232                         output.append("super("); //$NON-NLS-1$
233                 }
234                 if (arguments != null) {
235                         for (int i = 0; i < arguments.length; i++) {
236                                 if (i > 0) output.append(", "); //$NON-NLS-1$
237                                 arguments[i].printExpression(0, output);
238                         }
239                 }
240                 return output.append(");"); //$NON-NLS-1$
241         }
242         
243         public void resolve(BlockScope scope) {
244                 // the return type should be void for a constructor.
245                 // the test is made into getConstructor
246
247                 // mark the fact that we are in a constructor call.....
248                 // unmark at all returns
249                 MethodScope methodScope = scope.methodScope();
250                 try {
251                         AbstractMethodDeclaration methodDeclaration = methodScope.referenceMethod();
252                         if (methodDeclaration == null 
253                                         || !methodDeclaration.isConstructor()
254                                         || ((ConstructorDeclaration) methodDeclaration).constructorCall != this) {
255                                 scope.problemReporter().invalidExplicitConstructorCall(this);
256                                 return;
257                         }
258                         methodScope.isConstructorCall = true;
259                         ReferenceBinding receiverType = scope.enclosingSourceType();
260                         if (accessMode != This)
261                                 receiverType = receiverType.superclass();
262
263                         if (receiverType == null) {
264                                 return;
265                         }
266                         // prevent (explicit) super constructor invocation from within enum
267                         if (this.accessMode == Super && receiverType.erasure().id == T_JavaLangEnum) {
268                                 scope.problemReporter().cannotInvokeSuperConstructorInEnum(this, methodScope.referenceMethod().binding);
269                         }
270                         // qualification should be from the type of the enclosingType
271                         if (qualification != null) {
272                                 if (accessMode != Super) {
273                                         scope.problemReporter().unnecessaryEnclosingInstanceSpecification(
274                                                 qualification,
275                                                 receiverType);
276                                 }
277                                 ReferenceBinding enclosingType = receiverType.enclosingType();
278                                 if (enclosingType == null) {
279                                         scope.problemReporter().unnecessaryEnclosingInstanceSpecification(
280                                                 qualification,
281                                                 receiverType);
282                                         discardEnclosingInstance = true;
283                                 } else {
284                                         TypeBinding qTb = qualification.resolveTypeExpecting(scope, enclosingType);
285                                         qualification.computeConversion(scope, qTb, qTb);
286                                 }
287                         }
288                         // resolve type arguments (for generic constructor call)
289                         if (this.typeArguments != null) {
290                                 int length = this.typeArguments.length;
291                                 boolean argHasError = false; // typeChecks all arguments
292                                 this.genericTypeArguments = new TypeBinding[length];
293                                 for (int i = 0; i < length; i++) {
294                                         if ((this.genericTypeArguments[i] = this.typeArguments[i].resolveType(scope, true /* check bounds*/)) == null) {
295                                                 argHasError = true;
296                                         }
297                                 }
298                                 if (argHasError) {
299                                         return;
300                                 }
301                         }                       
302         
303                         // arguments buffering for the method lookup
304                         TypeBinding[] argumentTypes = NoParameters;
305                         boolean argsContainCast = false;
306                         if (arguments != null) {
307                                 boolean argHasError = false; // typeChecks all arguments
308                                 int length = arguments.length;
309                                 argumentTypes = new TypeBinding[length];
310                                 for (int i = 0; i < length; i++) {
311                                         Expression argument = this.arguments[i];
312                                         if (argument instanceof CastExpression) {
313                                                 argument.bits |= IgnoreNeedForCastCheckMASK; // will check later on
314                                                 argsContainCast = true;
315                                         }
316                                         if ((argumentTypes[i] = argument.resolveType(scope)) == null) {
317                                                 argHasError = true;
318                                         }
319                                 }
320                                 if (argHasError) {
321                                         return;
322                                 }
323                         } else if (receiverType.erasure().id == T_JavaLangEnum) {
324                                 // TODO (philippe) get rid of once well-known binding is available
325                                 argumentTypes = new TypeBinding[] { scope.getJavaLangString(), BaseTypes.IntBinding };
326                         }
327                         if ((binding = scope.getConstructor(receiverType, argumentTypes, this)).isValidBinding()) {
328                                 if (isMethodUseDeprecated(binding, scope))
329                                         scope.problemReporter().deprecatedMethod(binding, this);
330                                 if (this.arguments != null)
331                                         checkInvocationArguments(scope, null, receiverType, binding, this.arguments, argumentTypes, argsContainCast, this);
332                                 if (binding.isPrivate()) {
333                                         binding.original().modifiers |= AccPrivateUsed;
334                                 }                               
335                         } else {
336                                 if (binding.declaringClass == null)
337                                         binding.declaringClass = receiverType;
338                                 scope.problemReporter().invalidConstructor(this, binding);
339                         }
340                 } finally {
341                         methodScope.isConstructorCall = false;
342                 }
343         }
344
345         public void setActualReceiverType(ReferenceBinding receiverType) {
346                 // ignored
347         }
348
349         public void setDepth(int depth) {
350                 // ignore for here
351         }
352
353         public void setFieldIndex(int depth) {
354                 // ignore for here
355         }
356
357         public void traverse(ASTVisitor visitor, BlockScope scope) {
358
359                 if (visitor.visit(this, scope)) {
360                         if (this.qualification != null) {
361                                 this.qualification.traverse(visitor, scope);
362                         }
363                         if (this.typeArguments != null) {
364                                 for (int i = 0, typeArgumentsLength = this.typeArguments.length; i < typeArgumentsLength; i++) {
365                                         this.typeArguments[i].traverse(visitor, scope);
366                                 }                       
367                         }
368                         if (this.arguments != null) {
369                                 for (int i = 0, argumentLength = this.arguments.length; i < argumentLength; i++)
370                                         this.arguments[i].traverse(visitor, scope);
371                         }
372                 }
373                 visitor.endVisit(this, scope);
374         }
375 }