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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
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.*;
18 public class ExplicitConstructorCall extends Statement implements InvocationSite {
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;
29 public final static int ImplicitSuper = 1;
30 public final static int Super = 2;
31 public final static int This = 3;
33 public VariableBinding[][] implicitArguments;
34 boolean discardEnclosingInstance;
36 // TODO Remove once DOMParser is activated
37 public int typeArgumentsSourceStart;
39 public ExplicitConstructorCall(int accessMode) {
40 this.accessMode = accessMode;
43 public FlowInfo analyseCode(
44 BlockScope currentScope,
45 FlowContext flowContext,
48 // must verify that exceptions potentially thrown by this expression are caught in the method.
51 ((MethodScope) currentScope).isConstructorCall = true;
53 // process enclosing instance
54 if (qualification != null) {
57 .analyseCode(currentScope, flowContext, flowInfo)
58 .unconditionalInits();
61 if (arguments != null) {
62 for (int i = 0, max = arguments.length; i < max; i++) {
65 .analyseCode(currentScope, flowContext, flowInfo)
66 .unconditionalInits();
70 ReferenceBinding[] thrownExceptions;
71 if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) {
73 flowContext.checkExceptionHandlers(
75 (accessMode == ImplicitSuper)
76 ? (ASTNode) currentScope.methodScope().referenceContext
81 manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
82 manageSyntheticAccessIfNecessary(currentScope, flowInfo);
85 ((MethodScope) currentScope).isConstructorCall = false;
90 * Constructor call code generation
92 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
93 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
95 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
97 if ((bits & IsReachableMASK) == 0) {
101 ((MethodScope) currentScope).isConstructorCall = true;
103 int pc = codeStream.position;
104 codeStream.aload_0();
106 ReferenceBinding targetType = this.codegenBinding.declaringClass;
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
113 // handling innerclass constructor invocation
114 // handling innerclass instance allocation - enclosing instance arguments
115 if (targetType.isNestedType()) {
116 codeStream.generateSyntheticEnclosingInstanceValues(
119 discardEnclosingInstance ? null : qualification,
122 // generate arguments
123 generateArguments(binding, arguments, currentScope, codeStream);
125 // handling innerclass instance allocation - outer local arguments
126 if (targetType.isNestedType()) {
127 codeStream.generateSyntheticOuterArgumentValues(
132 if (syntheticAccessor != null) {
133 // synthetic accessor got some extra arguments appended to its signature, which need values
135 max = syntheticAccessor.parameters.length - this.codegenBinding.parameters.length;
138 codeStream.aconst_null();
140 codeStream.invokespecial(syntheticAccessor);
142 codeStream.invokespecial(this.codegenBinding);
144 codeStream.recordPositionsFrom(pc, this.sourceStart);
146 ((MethodScope) currentScope).isConstructorCall = false;
150 * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments()
152 public TypeBinding[] genericTypeArguments() {
153 return this.genericTypeArguments;
155 public boolean isImplicitSuper() {
156 //return true if I'm of these compiler added statement super();
158 return (accessMode == ImplicitSuper);
161 public boolean isSuperAccess() {
163 return accessMode != This;
166 public boolean isTypeAccess() {
171 /* Inner emulation consists in either recording a dependency
172 * link only, or performing one level of propagation.
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
178 void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
179 ReferenceBinding superTypeErasure = (ReferenceBinding) binding.declaringClass.erasure();
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()) {
186 if (superTypeErasure.isLocalType()) {
187 ((LocalTypeBinding) superTypeErasure).addInnerEmulationDependent(currentScope, qualification != null);
189 // locally propagate, since we already now the desired shape for sure
190 currentScope.propagateInnerEmulation(superTypeErasure, qualification != null);
195 public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
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();
201 // perform some emulation work in case there is some and we are inside a local type only
202 if (binding.isPrivate() && accessMode != This) {
204 if (currentScope.environment().options.isPrivateConstructorAccessChangingVisibility) {
205 this.codegenBinding.tagForClearingPrivateModifier();
206 // constructor will not be dumped as private, no emulation required thus
209 ((SourceTypeBinding) this.codegenBinding.declaringClass).addSyntheticMethod(this.codegenBinding, isSuperAccess());
210 currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
215 public StringBuffer printStatement(int indent, StringBuffer output) {
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$
226 typeArguments[max].print(0, output);
229 if (accessMode == This) {
230 output.append("this("); //$NON-NLS-1$
232 output.append("super("); //$NON-NLS-1$
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);
240 return output.append(");"); //$NON-NLS-1$
243 public void resolve(BlockScope scope) {
244 // the return type should be void for a constructor.
245 // the test is made into getConstructor
247 // mark the fact that we are in a constructor call.....
248 // unmark at all returns
249 MethodScope methodScope = scope.methodScope();
251 AbstractMethodDeclaration methodDeclaration = methodScope.referenceMethod();
252 if (methodDeclaration == null
253 || !methodDeclaration.isConstructor()
254 || ((ConstructorDeclaration) methodDeclaration).constructorCall != this) {
255 scope.problemReporter().invalidExplicitConstructorCall(this);
258 methodScope.isConstructorCall = true;
259 ReferenceBinding receiverType = scope.enclosingSourceType();
260 if (accessMode != This)
261 receiverType = receiverType.superclass();
263 if (receiverType == null) {
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);
270 // qualification should be from the type of the enclosingType
271 if (qualification != null) {
272 if (accessMode != Super) {
273 scope.problemReporter().unnecessaryEnclosingInstanceSpecification(
277 ReferenceBinding enclosingType = receiverType.enclosingType();
278 if (enclosingType == null) {
279 scope.problemReporter().unnecessaryEnclosingInstanceSpecification(
282 discardEnclosingInstance = true;
284 TypeBinding qTb = qualification.resolveTypeExpecting(scope, enclosingType);
285 qualification.computeConversion(scope, qTb, qTb);
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) {
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;
316 if ((argumentTypes[i] = argument.resolveType(scope)) == null) {
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 };
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;
336 if (binding.declaringClass == null)
337 binding.declaringClass = receiverType;
338 scope.problemReporter().invalidConstructor(this, binding);
341 methodScope.isConstructorCall = false;
345 public void setActualReceiverType(ReferenceBinding receiverType) {
349 public void setDepth(int depth) {
353 public void setFieldIndex(int depth) {
357 public void traverse(ASTVisitor visitor, BlockScope scope) {
359 if (visitor.visit(this, scope)) {
360 if (this.qualification != null) {
361 this.qualification.traverse(visitor, scope);
363 if (this.typeArguments != null) {
364 for (int i = 0, typeArgumentsLength = this.typeArguments.length; i < typeArgumentsLength; i++) {
365 this.typeArguments[i].traverse(visitor, scope);
368 if (this.arguments != null) {
369 for (int i = 0, argumentLength = this.arguments.length; i < argumentLength; i++)
370 this.arguments[i].traverse(visitor, scope);
373 visitor.endVisit(this, scope);