--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.ast;
+
+import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.codegen.*;
+import org.eclipse.jdt.internal.compiler.flow.*;
+import org.eclipse.jdt.internal.compiler.lookup.*;
+
+public class ExplicitConstructorCall
+ extends Statement
+ implements InvocationSite {
+
+ public Expression[] arguments;
+ public Expression qualification;
+ public MethodBinding binding;
+
+ public int accessMode;
+
+ public final static int ImplicitSuper = 1;
+ public final static int Super = 2;
+ public final static int This = 3;
+
+ public VariableBinding[][] implicitArguments;
+ boolean discardEnclosingInstance;
+
+ MethodBinding syntheticAccessor;
+
+ public ExplicitConstructorCall(int accessMode) {
+ this.accessMode = accessMode;
+ }
+
+ public FlowInfo analyseCode(
+ BlockScope currentScope,
+ FlowContext flowContext,
+ FlowInfo flowInfo) {
+
+ // must verify that exceptions potentially thrown by this expression are caught in the method.
+
+ try {
+ ((MethodScope) currentScope).isConstructorCall = true;
+
+ // process enclosing instance
+ if (qualification != null) {
+ flowInfo =
+ qualification
+ .analyseCode(currentScope, flowContext, flowInfo)
+ .unconditionalInits();
+ }
+ // process arguments
+ if (arguments != null) {
+ for (int i = 0, max = arguments.length; i < max; i++) {
+ flowInfo =
+ arguments[i]
+ .analyseCode(currentScope, flowContext, flowInfo)
+ .unconditionalInits();
+ }
+ }
+
+ ReferenceBinding[] thrownExceptions;
+ if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) {
+ // check exceptions
+ flowContext.checkExceptionHandlers(
+ thrownExceptions,
+ (accessMode == ImplicitSuper)
+ ? (ASTNode) currentScope.methodScope().referenceContext
+ : (ASTNode) this,
+ flowInfo,
+ currentScope);
+ }
+ manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
+ manageSyntheticAccessIfNecessary(currentScope, flowInfo);
+ return flowInfo;
+ } finally {
+ ((MethodScope) currentScope).isConstructorCall = false;
+ }
+ }
+
+ /**
+ * Constructor call code generation
+ *
+ * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
+ * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
+ */
+ public void generateCode(BlockScope currentScope, CodeStream codeStream) {
+
+ if ((bits & IsReachableMASK) == 0) {
+ return;
+ }
+ try {
+ ((MethodScope) currentScope).isConstructorCall = true;
+
+ int pc = codeStream.position;
+ codeStream.aload_0();
+
+ // handling innerclass constructor invocation
+ ReferenceBinding targetType = binding.declaringClass;
+ // handling innerclass instance allocation - enclosing instance arguments
+ if (targetType.isNestedType()) {
+ codeStream.generateSyntheticEnclosingInstanceValues(
+ currentScope,
+ targetType,
+ discardEnclosingInstance ? null : qualification,
+ this);
+ }
+ // regular code gen
+ if (arguments != null) {
+ for (int i = 0, max = arguments.length; i < max; i++) {
+ arguments[i].generateCode(currentScope, codeStream, true);
+ }
+ }
+ // handling innerclass instance allocation - outer local arguments
+ if (targetType.isNestedType()) {
+ codeStream.generateSyntheticOuterArgumentValues(
+ currentScope,
+ targetType,
+ this);
+ }
+ if (syntheticAccessor != null) {
+ // synthetic accessor got some extra arguments appended to its signature, which need values
+ for (int i = 0,
+ max = syntheticAccessor.parameters.length - binding.parameters.length;
+ i < max;
+ i++) {
+ codeStream.aconst_null();
+ }
+ codeStream.invokespecial(syntheticAccessor);
+ } else {
+ codeStream.invokespecial(binding);
+ }
+ codeStream.recordPositionsFrom(pc, this.sourceStart);
+ } finally {
+ ((MethodScope) currentScope).isConstructorCall = false;
+ }
+ }
+
+ public boolean isImplicitSuper() {
+ //return true if I'm of these compiler added statement super();
+
+ return (accessMode == ImplicitSuper);
+ }
+
+ public boolean isSuperAccess() {
+
+ return accessMode != This;
+ }
+
+ public boolean isTypeAccess() {
+
+ return true;
+ }
+
+ /* Inner emulation consists in either recording a dependency
+ * link only, or performing one level of propagation.
+ *
+ * Dependency mechanism is used whenever dealing with source target
+ * types, since by the time we reach them, we might not yet know their
+ * exact need.
+ */
+ void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
+ ReferenceBinding superType;
+
+ if (!flowInfo.isReachable()) return;
+ // perform some emulation work in case there is some and we are inside a local type only
+ if ((superType = binding.declaringClass).isNestedType()
+ && currentScope.enclosingSourceType().isLocalType()) {
+
+ if (superType.isLocalType()) {
+ ((LocalTypeBinding) superType).addInnerEmulationDependent(currentScope, qualification != null);
+ } else {
+ // locally propagate, since we already now the desired shape for sure
+ currentScope.propagateInnerEmulation(superType, qualification != null);
+ }
+ }
+ }
+
+ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
+
+ if (!flowInfo.isReachable()) return;
+ // perform some emulation work in case there is some and we are inside a local type only
+ if (binding.isPrivate() && (accessMode != This)) {
+
+ if (currentScope
+ .environment()
+ .options
+ .isPrivateConstructorAccessChangingVisibility) {
+ binding.tagForClearingPrivateModifier();
+ // constructor will not be dumped as private, no emulation required thus
+ } else {
+ syntheticAccessor =
+ ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, isSuperAccess());
+ currentScope.problemReporter().needToEmulateMethodAccess(binding, this);
+ }
+ }
+ }
+
+ public StringBuffer printStatement(int indent, StringBuffer output) {
+
+ printIndent(indent, output);
+ if (qualification != null) qualification.printExpression(0, output).append('.');
+ if (accessMode == This) {
+ output.append("this("); //$NON-NLS-1$
+ } else {
+ output.append("super("); //$NON-NLS-1$
+ }
+ if (arguments != null) {
+ for (int i = 0; i < arguments.length; i++) {
+ if (i > 0) output.append(", "); //$NON-NLS-1$
+ arguments[i].printExpression(0, output);
+ }
+ }
+ return output.append(");"); //$NON-NLS-1$
+ }
+
+ public void resolve(BlockScope scope) {
+ // the return type should be void for a constructor.
+ // the test is made into getConstructor
+
+ // mark the fact that we are in a constructor call.....
+ // unmark at all returns
+ MethodScope methodScope = scope.methodScope();
+ try {
+ AbstractMethodDeclaration methodDeclaration = methodScope.referenceMethod();
+ if (methodDeclaration == null
+ || !methodDeclaration.isConstructor()
+ || ((ConstructorDeclaration) methodDeclaration).constructorCall != this) {
+ scope.problemReporter().invalidExplicitConstructorCall(this);
+ return;
+ }
+ methodScope.isConstructorCall = true;
+ ReferenceBinding receiverType = scope.enclosingSourceType();
+ if (accessMode != This)
+ receiverType = receiverType.superclass();
+
+ if (receiverType == null) {
+ return;
+ }
+
+ // qualification should be from the type of the enclosingType
+ if (qualification != null) {
+ if (accessMode != Super) {
+ scope.problemReporter().unnecessaryEnclosingInstanceSpecification(
+ qualification,
+ receiverType);
+ }
+ ReferenceBinding enclosingType = receiverType.enclosingType();
+ if (enclosingType == null) {
+ scope.problemReporter().unnecessaryEnclosingInstanceSpecification(
+ qualification,
+ receiverType);
+ discardEnclosingInstance = true;
+ } else {
+ TypeBinding qTb = qualification.resolveTypeExpecting(scope, enclosingType);
+ qualification.implicitWidening(qTb, qTb);
+ }
+ }
+
+ // arguments buffering for the method lookup
+ TypeBinding[] argumentTypes = NoParameters;
+ boolean argsContainCast = false;
+ if (arguments != null) {
+ boolean argHasError = false; // typeChecks all arguments
+ int length = arguments.length;
+ argumentTypes = new TypeBinding[length];
+ for (int i = 0; i < length; i++) {
+ Expression argument = this.arguments[i];
+ if (argument instanceof CastExpression) {
+ argument.bits |= IgnoreNeedForCastCheckMASK; // will check later on
+ argsContainCast = true;
+ }
+ if ((argumentTypes[i] = argument.resolveType(scope)) == null) {
+ argHasError = true;
+ }
+ }
+ if (argHasError) {
+ return;
+ }
+ }
+ if ((binding = scope.getConstructor(receiverType, argumentTypes, this)).isValidBinding()) {
+ if (isMethodUseDeprecated(binding, scope))
+ scope.problemReporter().deprecatedMethod(binding, this);
+
+ // see for user-implicit widening conversion
+ if (arguments != null) {
+ int length = arguments.length;
+ TypeBinding[] paramTypes = binding.parameters;
+ for (int i = 0; i < length; i++) {
+ arguments[i].implicitWidening(paramTypes[i], argumentTypes[i]);
+ }
+ if (argsContainCast) {
+ CastExpression.checkNeedForArgumentCasts(scope, null, receiverType, binding, this.arguments, argumentTypes, this);
+ }
+ }
+ if (binding.isPrivate()) {
+ binding.modifiers |= AccPrivateUsed;
+ }
+ } else {
+ if (binding.declaringClass == null)
+ binding.declaringClass = receiverType;
+ scope.problemReporter().invalidConstructor(this, binding);
+ }
+ } finally {
+ methodScope.isConstructorCall = false;
+ }
+ }
+
+ public void setActualReceiverType(ReferenceBinding receiverType) {
+ // ignored
+ }
+
+ public void setDepth(int depth) {
+ // ignore for here
+ }
+
+ public void setFieldIndex(int depth) {
+ // ignore for here
+ }
+
+ public void traverse(ASTVisitor visitor, BlockScope scope) {
+
+ if (visitor.visit(this, scope)) {
+ if (this.qualification != null) {
+ this.qualification.traverse(visitor, scope);
+ }
+ if (this.arguments != null) {
+ for (int i = 0, argumentLength = this.arguments.length; i < argumentLength; i++)
+ this.arguments[i].traverse(visitor, scope);
+ }
+ }
+ visitor.endVisit(this, scope);
+ }
+}