/******************************************************************************* * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.compiler.ast; import net.sourceforge.phpdt.internal.compiler.ASTVisitor; import net.sourceforge.phpdt.internal.compiler.flow.FlowContext; import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo; import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope; import net.sourceforge.phpdt.internal.compiler.lookup.InvocationSite; import net.sourceforge.phpdt.internal.compiler.lookup.LocalTypeBinding; import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding; import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope; import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding; import net.sourceforge.phpdt.internal.compiler.lookup.SourceTypeBinding; import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding; import net.sourceforge.phpdt.internal.compiler.lookup.VariableBinding; 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); manageSyntheticAccessIfNecessary(currentScope); return flowInfo; } finally { ((MethodScope) currentScope).isConstructorCall = false; } } /** * Constructor call code generation * * @param currentScope * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope * @param codeStream * net.sourceforge.phpdt.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) { ReferenceBinding superType; // 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) { // 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 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 try { ((MethodScope) scope).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[] argTypes = NoParameters; if (arguments != null) { boolean argHasError = false; // typeChecks all arguments int length = arguments.length; argTypes = new TypeBinding[length]; for (int i = 0; i < length; i++) if ((argTypes[i] = arguments[i].resolveType(scope)) == null) argHasError = true; if (argHasError) return; } if ((binding = scope.getConstructor(receiverType, argTypes, 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], argTypes[i]); } if (binding.isPrivate()) { binding.modifiers |= AccPrivateUsed; } } else { if (binding.declaringClass == null) binding.declaringClass = receiverType; scope.problemReporter().invalidConstructor(this, binding); } } finally { ((MethodScope) scope).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 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 String toString(int tab) { String s = tabString(tab); if (qualification != null) s = s + qualification.toStringExpression() + "."; //$NON-NLS-1$ if (accessMode == This) { s = s + "this("; //$NON-NLS-1$ } else { s = s + "super("; //$NON-NLS-1$ } if (arguments != null) for (int i = 0; i < arguments.length; i++) { s = s + arguments[i].toStringExpression(); if (i != arguments.length - 1) s = s + ", "; //$NON-NLS-1$ } s = s + ")"; //$NON-NLS-1$ return s; } public void traverse(ASTVisitor visitor, BlockScope scope) { if (visitor.visit(this, scope)) { if (qualification != null) { qualification.traverse(visitor, scope); } if (arguments != null) { int argumentLength = arguments.length; for (int i = 0; i < argumentLength; i++) arguments[i].traverse(visitor, scope); } } visitor.endVisit(this, scope); } }