blob: 8df58818df656079261741f4dbc475f955b8440e [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.ast;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.IAbstractSyntaxTreeVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
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);
manageSyntheticAccessIfNecessary(currentScope);
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;
if ((targetType = binding.declaringClass).isNestedType()){
codeStream.generateSyntheticArgumentValues(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);
}
}
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);
} 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 invocationType, 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, true); // request direct access
} else {
// locally propagate, since we already now the desired shape for sure
currentScope.propagateInnerEmulation(superType, qualification != null, true); // request direct access
}
}
}
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);
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]);
}
} else {
if (binding.declaringClass == null)
binding.declaringClass = receiverType;
scope.problemReporter().invalidConstructor(this, binding);
}
} finally {
((MethodScope) scope).isConstructorCall = false;
}
}
public void setDepth(int depth){
// ignore for here
}
public void setFieldIndex(int depth){
// ignore for here
}
public String toString(int tab){
/* slow code */
String s = tabString(tab);
if (qualification != null)
s = s + qualification.toStringExpression() + "."/*nonNLS*/ ;
if (accessMode == This){
s = s + "this("/*nonNLS*/;
} else {
s = s + "super("/*nonNLS*/;
}
if (arguments != null)
for (int i=0 ; i < arguments.length ; i++)
{ s = s + arguments[i].toStringExpression();
if (i != arguments.length-1) s = s + ", "/*nonNLS*/;};;
s = s+")"/*nonNLS*/ ;
return s;}
public void traverse(IAbstractSyntaxTreeVisitor 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);
}
}