blob: cead8be0ea3589052f3ace76e2e9832d88cbbfcf [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2004, 2006 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* $Id: BaseAllocationExpression.java 23401 2010-02-02 23:56:05Z stephan $
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Fraunhofer FIRST - Initial API and implementation
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.core.compiler.ast;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.StateHelper;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.Lifting;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.CREATOR_PREFIX_NAME;
import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants._OT_BASE;
/**
* A base constructor invocation "base(args)";
* Translated to "_OT$base = new BaseClass(args);"
*
* @author stephan
* @version $Id: BaseAllocationExpression.java 23401 2010-02-02 23:56:05Z stephan $
*/
public class BaseAllocationExpression extends Assignment {
// intermediate store, before transfering this to the AllocationExpression.
public Expression[] arguments;
public Expression enclosingInstance;
private boolean isAstCreated = false;
private Boolean checkResult = null; // three-valued logic (incl null)
/**
*
*/
public BaseAllocationExpression(int start, int end) {
// only set the lhs yet, expression is constructed later.
super(new SingleNameReference(_OT_BASE, 0), null, end);
this.sourceStart = start;
}
public FlowInfo analyseCode(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo)
{
// in case of error assume it might relate to duplicate base() calls, don't report again.
if (((AbstractMethodDeclaration)currentScope.methodScope().referenceContext).ignoreFurtherInvestigation)
return flowInfo;
return super.analyseCode(currentScope, flowContext, flowInfo);
}
/**
* Initialize the type to create from the "playedBy" clause.
* @param scope non-null
*/
private void createAst(BlockScope scope) {
if (this.isAstCreated) return; // already done.
this.isAstCreated = true; // even if creation fails, don't try again.
ReferenceBinding enclType;
AbstractMethodDeclaration enclMethodDecl;
ReferenceBinding baseclass = null;
enclType = scope.enclosingSourceType();
enclMethodDecl = (AbstractMethodDeclaration)scope.methodScope().referenceContext;
if (enclType.isDirectRole())
baseclass = ((MemberTypeBinding)enclType).baseclass();
if ( baseclass == null
|| !enclMethodDecl.isConstructor())
{
scope.problemReporter().baseConstructorCallInWrongMethod(
this, scope.methodScope().referenceContext);
return;
}
ConstructorDeclaration enclCtor = (ConstructorDeclaration)enclMethodDecl;
if (enclCtor.statements[0] != this)
scope.problemReporter().baseConstructorCallIsNotFirst(this);
AstGenerator gen = new AstGenerator(this.sourceStart, this.sourceEnd);
Expression allocation;
if (this.enclosingInstance != null) {
this.enclosingInstance= new PotentialLowerExpression(this.enclosingInstance, baseclass.enclosingType());
// FIXME(SH): check baseclass.enclosingType();
}
if (baseclass.isDirectRole()) {
// instead of new B() create:
// receiver._OT$createB():
Expression receiver;
if (RoleTypeBinding.isRoleWithExplicitAnchor(baseclass)) {
RoleTypeBinding baseRole = (RoleTypeBinding)baseclass;
ITeamAnchor anchor = baseRole._teamAnchor;
ReferenceBinding startClass = anchor.getFirstDeclaringClass();
char[][] tokens = anchor.tokens();
if (startClass != null) {
// relevant start class, create as receiver:
// EnclType.this.field1.
TypeReference startReference = gen.typeReference(startClass);
startReference.setBaseclassDecapsulation(DecapsulationState.ALLOWED);
receiver = gen.qualifiedThisReference(startReference);
for (int i = 0; i < tokens.length; i++) {
receiver = gen.fieldReference(receiver, tokens[i]);
}
} else {
// the best name path defines the receiver:
receiver = gen.qualifiedNameReference(tokens);
}
} else {
if (this.enclosingInstance != null)
receiver= this.enclosingInstance;
else
receiver = gen.thisReference(); // creating a role of the same team as base instance??
}
char[] selector = CharOperation.concat(CREATOR_PREFIX_NAME, baseclass.sourceName());
MessageSend allocSend = new MessageSend() {
@Override
protected boolean isDecapsulationAllowed(Scope scope2) {
// this message send can decapsulate independent of scope
return true;
}
@Override
public DecapsulationState getBaseclassDecapsulation() {
return DecapsulationState.ALLOWED;
}
};
gen.setPositions(allocSend);
allocSend.receiver = receiver;
allocSend.selector = selector;
allocSend.arguments = this.arguments;
allocation = allocSend;
} else {
AllocationExpression alloc = newAllocation(baseclass, gen);
alloc.type.setBaseclassDecapsulation(DecapsulationState.ALLOWED); // report individually
alloc.arguments = this.arguments;
alloc.sourceStart = this.sourceStart;
alloc.sourceEnd = this.sourceEnd;
alloc.statementEnd = this.statementEnd;
allocation = alloc;
}
this.arguments = null; // don't use any more.
ExplicitConstructorCall selfcall = enclCtor.constructorCall;
if ( selfcall.isImplicitSuper()
&& enclType.superclass().isDirectRole()
&& enclType.superclass().baseclass() != null)
{
// implement 2.4.2(c):
// transform "super(); base(args);" => "super(new MyBase(args)); nop;"
enclCtor.constructorCall = genLiftCtorCall(allocation);
enclCtor.statements[0] = new AstGenerator(this.sourceStart, this.sourceEnd).emptyStatement();
// pretend we are not calling base() because we already call the lifting-ctor.
} else {
if ( !enclType.roleModel.hasBaseclassProblem()
&& !scope.referenceType().ignoreFurtherInvestigation)
{
// needed by ASTConverter:
this.expression = allocation;
MethodModel.setCallsBaseCtor(enclCtor);
// really creating base here, need to register this base object
RoleModel boundRootRoleModel = enclType.roleModel.getBoundRootRole();
if (boundRootRoleModel == null)
throw new InternalCompilerError("Unexpected: role has neither baseclassProblem nor boundRootRole"); //$NON-NLS-1$
Statement[] regStats = Lifting.genRoleRegistrationStatements(scope,
boundRootRoleModel,
baseclass,
enclCtor,
gen);
int len = enclCtor.statements.length;
Statement[] newStats = new Statement[len+regStats.length];
newStats[0] = this;
System.arraycopy(regStats, 0, newStats, 1, regStats.length);
System.arraycopy(enclCtor.statements, 1, newStats, regStats.length+1, len-1);
enclCtor.setStatements(newStats);
}
}
}
private AllocationExpression newAllocation(ReferenceBinding baseclass, AstGenerator gen)
{
if (this.enclosingInstance == null) {
AllocationExpression alloc= new AllocationExpression();
alloc.type= gen.typeReference(baseclass);
return alloc;
} else {
QualifiedAllocationExpression alloc= new QualifiedAllocationExpression();
alloc.enclosingInstance= this.enclosingInstance;
alloc.type= gen.singleTypeReference(baseclass.sourceName);
return alloc;
}
}
/**
* @param baseExpr
*/
private ExplicitConstructorCall genLiftCtorCall(Expression baseExpr) {
ExplicitConstructorCall constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super);
constructorCall.arguments = new Expression[] { baseExpr };
constructorCall.sourceStart = this.sourceStart;
constructorCall.sourceEnd = this.sourceEnd;
return constructorCall;
}
public boolean checkGenerate(BlockScope scope) {
if (this.checkResult != null)
return this.checkResult;
return this.checkResult = Boolean.valueOf(internalCheckGenerate(scope));
}
private boolean internalCheckGenerate(BlockScope scope) {
if (scope == null)
return false;
ReferenceContext referenceContext = scope.methodScope().referenceContext;
if (!(referenceContext instanceof AbstractMethodDeclaration)) {
scope.problemReporter().baseConstructorCallInWrongMethod(this, referenceContext);
return false;
}
AbstractMethodDeclaration enclosingMethodDeclaration = (AbstractMethodDeclaration)referenceContext;
if (!enclosingMethodDeclaration.ignoreFurtherInvestigation)
{
createAst(scope);
return this.expression != null;
}
return false;
}
public void traverse(ASTVisitor visitor, BlockScope scope) {
// we might be the first to analyse this expression:
// (Actually triggered by TransformStatementsVisitor
// - but don't do it before STATE_LENV_DONE_FIELDS_AND_METHODS,
// which is needed to lookup baseclass()!)
TypeDeclaration enclType = (scope != null) ? scope.referenceType() : null;
if ( enclType != null
&& enclType.isDirectRole()
&& StateHelper.hasState(enclType.binding, ITranslationStates.STATE_LENV_DONE_FIELDS_AND_METHODS))
{
if (checkGenerate(scope)) // only if successful:
super.traverse(visitor, scope);
} else {
if (this.expression != null)
super.traverse(visitor, scope);
}
}
public TypeBinding resolveType(BlockScope scope) {
if (!checkGenerate(scope)) { // createAst failed.
return null;
}
if (!scope.methodScope().referenceContext.hasErrors())
return super.resolveType(scope);
return null;
}
public String toString() {
if (this.expression == null)
return "unresolved base() call"; //$NON-NLS-1$
return this.expression.toString();
}
public StringBuffer printExpression (int indent, StringBuffer output) {
if (this.expression != null)
return super.printExpression(indent, output);
return output.append("<no expression yet>"); //$NON-NLS-1$
}
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
if (this.expression != null)
return super.printExpressionNoParenthesis(indent, output);
return output.append("<no expression yet>"); //$NON-NLS-1$
}
}