blob: c7fbe81471b9b00c88425c317ba8d7a4d3dd0123 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2005, 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: DeclaredLifting.java 23417 2010-02-03 20:13:55Z 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.lifting;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
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.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.LiftingTypeReference;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.PotentialLiftExpression;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.BytecodeTransformer;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.StateMemento;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.OTClassScope;
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.TeamModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.ReplaceSingleNameVisitor;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstClone;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstConverter;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstEdit;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TSuperHelper;
/**
* This class handles transformations relating to declared lifting (Base as Role o).
* A considerable part of the complication results from team constructors which
* require lifting: such constructors must be copied and adjusted in quite tricky ways.
*
* @author stephan
* @version $Id: DeclaredLifting.java 23417 2010-02-03 20:13:55Z stephan $
*/
public class DeclaredLifting implements IOTConstants {
public static final char[] OT_LIFT_DYNAMIC = "_OT$lift_dynamic".toCharArray(); //$NON-NLS-1$
/** do full transformation for Declared Lifting */
public static void transformMethodsWithDeclaredLifting(TypeDeclaration teamDecl, boolean needMethodBodies)
{
if (teamDecl.methods != null)
for (AbstractMethodDeclaration method : teamDecl.methods)
transformMethodWithDeclaredLifting(method, teamDecl, needMethodBodies);
redefineLiftDynamicMethods(teamDecl, needMethodBodies);
}
/*
public int myMethod(MyBase as MyRole obj)
{
int i=5;
return obj.getResult(i+1);
}
=>
public int myMethod(MyBase _OT$obj)
{
MyRole obj = _OT$liftToMyRole(_OT$obj);
int i=5;
return obj.getResult(i+1);
}
*/
public static void transformMethodWithDeclaredLifting(
AbstractMethodDeclaration method, TypeDeclaration teamDecl, boolean needMethodBody)
{
if(method.arguments==null)
return; // No Declared Lifting
// gather and count DeclaredLifting Arguments
List<Argument> liftingTypeArguments = new LinkedList<Argument>();
for (int idx = 0; idx < method.arguments.length; idx++) {
Argument argument = method.arguments[idx];
if ( argument.type != null
&& argument.type.isDeclaredLifting())
{
liftingTypeArguments.add(argument);
}
}
if(liftingTypeArguments.isEmpty())
return; //No DeclaredLifting found in Arguments
if (method.isStatic()) {
method.scope.problemReporter().declaredLiftingInStaticMethod(
method,
liftingTypeArguments.get(0));
return;
}
// generate lift_dynamic method if <T base B> type parameter is declared
if (hasBaseBoundedTypeParameters(method))
for (Argument arg : liftingTypeArguments) {
TypeBinding roleType = teamDecl.binding.getMemberType(((LiftingTypeReference)arg.type).roleToken);
if (roleType != null)
genLiftDynamicMethod(teamDecl, arg.type, roleType, needMethodBody);
}
if (!needMethodBody)
return; // no more details needed
//create modified Statements
Statement[] statements = new Statement[liftingTypeArguments.size()];
int position=0;
for (Iterator<Argument> iter = liftingTypeArguments.iterator(); iter.hasNext();)
{
Argument argument = iter.next();
char[] oldArgumentName = argument.name; // save before renaming argument
LocalDeclaration declaration = createLiftingStatement(method.scope, argument);
if (method instanceof ConstructorDeclaration) {
// replace references in self-call:
ConstructorDeclaration ctor = (ConstructorDeclaration)method;
if (ctor.constructorCall != null) {
// replace argn with _OT$argn in ctor call:
ReplaceSingleNameVisitor.performReplacement(
ctor.constructorCall,
ctor.scope,
oldArgumentName,
argument.name);
}
ctor.needsLifting = true;
}
// collect:
statements[position++] = declaration;
}
// add local variable declarations to the front of statements:
Statement[] originalStatements = method.statements;
if (originalStatements == null)
{
method.setStatements(statements);
}
else
{
Statement[] newStatements = new Statement[statements.length+method.statements.length];
System.arraycopy(statements,0,newStatements,0,statements.length);
System.arraycopy(originalStatements,0,newStatements,statements.length,originalStatements.length);
method.setStatements(newStatements);
}
}
static boolean hasBaseBoundedTypeParameters(AbstractMethodDeclaration method) {
TypeParameter[] typeParameters = method.typeParameters();
if (typeParameters == null)
return false;
for (TypeParameter typeParameter : typeParameters)
if (typeParameter.hasBaseBound())
return true;
return false;
}
public static void transformCatch(Scope scope, Block block, Argument argument) {
int len = block.statements.length;
System.arraycopy(block.statements, 0,
block.statements = new Statement[len+1], 1,
len);
block.statements[0] = createLiftingStatement(scope, argument);
}
/*
* Creates one statement for each argument:
* MyRole obj = lift?(_OT$obj);
*/
private static LocalDeclaration createLiftingStatement(Scope scope, Argument argument) {
LiftingTypeReference ltr = (LiftingTypeReference) argument.type;
int start = argument.type.sourceStart;
int end = argument.type.sourceEnd;
AstGenerator gen = new AstGenerator(start, end);
// names:
char[] oldName = argument.name;
char[] newName = CharOperation.concat(OT_DOLLAR_NAME, oldName);
argument.updateName(newName);
// MyRole obj = _OT$liftToMyRole(_OT$obj); (via PotentialLiftExpression)
TypeBinding roleType = scope.getType(ltr.roleToken);
ReferenceBinding roleRef = (ReferenceBinding)roleType;
// using a PotentialLiftExpression allows us to defer type resolution.
Expression liftCall = null;
if ( roleRef.isValidBinding()
&& roleRef.isRole()
&& (roleRef.tagBits & (TagBits.HierarchyHasProblems | TagBits.BaseclassHasProblems)) == 0)
{
Expression receiverTeam = ThisReference.implicitThis();
ReferenceBinding teamBinding = roleRef.enclosingType();
if (teamBinding != scope.enclosingSourceType())
receiverTeam = gen.qualifiedThisReference(teamBinding);
if (roleRef.baseclass() == null) {
// static adjustment (OTJLD 2.3.2(a)):
ReferenceBinding baseType = null;
if (scope.classScope() instanceof OTClassScope) {
// try base scope first:
Scope baseScope = ((OTClassScope) scope.classScope()).getBaseImportScope();
if (baseScope != null)
baseType = (ReferenceBinding) baseScope.getType(ltr.baseTokens, ltr.baseTokens.length);
}
if (baseType == null || !baseType.isValidBinding())
// fall back to normal scope:
baseType = (ReferenceBinding)scope.getType(ltr.baseTokens, ltr.baseTokens.length);
roleRef = (ReferenceBinding)TeamModel.getRoleToLiftTo(scope, baseType, roleRef, true, ltr, null/*callinDecl*/);
if (baseType.isTypeVariable() && roleRef == null)
roleRef = (ReferenceBinding)roleType; // fall back to the declared type
}
if (roleRef != null) {
if (ltr.baseReference.dimensions() > 0)
roleType = scope.createArrayType(roleRef, ltr.baseReference.dimensions());
liftCall = new PotentialLiftExpression(
receiverTeam,
gen.singleNameReference(newName),
ltr.roleReference);
}
// errors are reported by LiftingTypeReference.resolveType().
}
// assemble the variable decl:
LocalDeclaration declaration = gen.localVariable(
oldName,
AstClone.copyTypeReference(ltr.roleReference),
liftCall);
// store local declaration in lifting type,
// which may need to cancel some generated AST after resolve.
// (cf. LiftingTypeReference.invalidate())
ltr.fakedArgument = declaration;
return declaration;
}
/** If a team ctor uses role type args, prepare the ctor for
* self calls involving declared lifting.
*/
public static void prepareArgLifting(TypeDeclaration teamDecl) {
if (teamDecl.methods != null)
for (int i = 0; i < teamDecl.methods.length; i++) {
if (teamDecl.methods[i].isConstructor()) {
ConstructorDeclaration ctor = (ConstructorDeclaration)teamDecl.methods[i];
prepareArgLifting(teamDecl, ctor);
}
}
}
private static void prepareArgLifting(TypeDeclaration teamDecl, ConstructorDeclaration ctor)
{
if(ctor.arguments==null || ctor.ignoreFurtherInvestigation)
return; // nothing to do
List<LocalDeclaration> localVariables = new LinkedList<LocalDeclaration>();
boolean selfCallIsChanged = false;
boolean chainArgAdded = false;
for (int idx = 0; idx < ctor.arguments.length; idx++) {
Argument argument = ctor.arguments[idx];
TypeBinding param = ctor.binding.parameters[idx];
// TODO(SH): array of roles?
// don't use RoleTypeBinding.isRoleWithoutExplicitAnchor, because
// type wrapping in signature has not been done yet.
if ( isRoleOf(param, teamDecl.binding)
&& !ctor.isTSuper)
{
int start = argument.type.sourceStart;
int end = argument.type.sourceEnd;
AstGenerator gen = new AstGenerator(start, end);
if (!chainArgAdded) { // do this only once:
// reserve space for a marker arg that could be added during copying:
LocalDeclaration chainVar = gen.localVariable(
"_OT$chainArg".toCharArray(), //$NON-NLS-1$
teamDecl.scope.getJavaLangObject(),
null);
chainVar.isPlaceHolder = true;
localVariables.add(chainVar);
chainArgAdded = true;
}
// create local variable similar as for declared lifting
char[] oldName = argument.name;
char[] newName = CharOperation.concat(OT_DOLLAR_NAME, oldName);
// cast might be needed if this collides with signature weakening
// (cf. CopyInheritance.weakenSignature)
Expression rhs = gen.castExpression(
gen.singleNameReference(newName),
gen.typeReference(((ReferenceBinding)param).getRealType()), // cast is safe here, but enclosing if is too strict!
CastExpression.RAW);
argument.updateName(newName);
// only potentially need lifting in a constructor (once copied)
LocalDeclaration declaration = gen.localVariable(
oldName,
AstClone.copyTypeReference(argument.type),
rhs); // assign real argument
declaration.isPreparingForLifting = true;
localVariables.add(declaration);
// replace references in super-call:
if (ctor.constructorCall != null) {
// replace argn with _OT$argn in ctor call:
selfCallIsChanged = ReplaceSingleNameVisitor.performReplacement(
ctor.constructorCall,
ctor.scope,
oldName,
newName);
}
}
if (selfCallIsChanged) {
// add a marker argument to this call:
ExplicitConstructorCall call = ctor.constructorCall;
call.accessMode = ExplicitConstructorCall.This;
AstGenerator gen = new AstGenerator(call.sourceStart, call.sourceEnd);
int numArgs = 0;
if (call.arguments == null) {
call.arguments = new Expression[1];
} else {
numArgs = call.arguments.length;
System.arraycopy(
call.arguments, 0,
call.arguments = new Expression[numArgs+1], 0,
numArgs);
}
call.arguments[numArgs] = TSuperHelper.createMarkerArgExpr(
teamDecl.binding.superclass, gen);
MethodModel.getModel(ctor); // side effect.
}
}
if (!localVariables.isEmpty()) {
// add local variable declartions to the front of statements
int statsLen = (ctor.statements == null) ? 0 : ctor.statements.length;
Statement[] statements = new Statement[localVariables.size()+statsLen];
int i=0;
for (Iterator<LocalDeclaration> iter = localVariables.iterator(); iter.hasNext();) {
statements[i++] = iter.next();
}
if (ctor.statements != null)
System.arraycopy(
ctor.statements, 0,
statements, i,
statsLen);
ctor.setStatements(statements);
}
}
private static boolean isRoleOf(TypeBinding type, ReferenceBinding enclosingTeam) {
if (type instanceof MemberTypeBinding)
return ((MemberTypeBinding)type).enclosingType() == enclosingTeam;
if (type instanceof RoleTypeBinding)
return !((RoleTypeBinding)type).hasExplicitAnchor();
return false;
}
/**
* Copy a team constructor such that the copy is suitable for chaining
* previous super calls through a chain of tsuper-calls. This enables
* a team to insert lifting calls at all required locations.
*
* In a subteam where the role is bound, the role parameter has to be replaced
* with the bound base class (signature & selfcall).
*
* @param scope scope of location triggering this copy operation
* @param superTeamCtor constructor to be copied
* @param providedArgs this are the types of arguments passed by the ExplicitConstructorCall
* @param needsLifting has the context (initiating constructor decl) declared lifting?
*/
public static MethodBinding copyTeamConstructorForDeclaredLifting (
Scope scope,
MethodBinding superTeamCtor,
TypeBinding[] providedArgs,
boolean needsLifting)
{
TypeDeclaration teamDecl = scope.referenceType();
AstGenerator gen = new AstGenerator(scope.methodScope().referenceMethod().sourceStart,
scope.methodScope().referenceMethod().sourceEnd);
if (scope.isOrgObjectteamsTeam(superTeamCtor.declaringClass))
return maybeCreateTurningCtor(teamDecl, superTeamCtor, gen);
// Parameter list with marker arg
TypeBinding[] providedWithMarker = providedArgs;
if ( providedArgs.length == 0
|| !TSuperHelper.isMarkerInterface(providedArgs[providedArgs.length-1]))
{
// need to extend the param list
TypeBinding lastParam = null;
if (superTeamCtor.parameters.length > 0)
lastParam = superTeamCtor.parameters[superTeamCtor.parameters.length-1];
TypeBinding marker =
( lastParam != null
&& TSuperHelper.isMarkerInterface(lastParam)) ? // prefer existing marker
lastParam
:
TSuperHelper.getMarkerInterface(scope, superTeamCtor.declaringClass);
providedWithMarker = AstEdit.extendTypeArray(providedArgs, marker);
}
// Inner self call:
MethodBinding selfcall = null;
AbstractMethodDeclaration src = superTeamCtor.sourceMethod();
if (src != null) {
if (src.ignoreFurtherInvestigation)
return null; // can't create ctor
// src means: look in the AST
ConstructorDeclaration srcCtor = (ConstructorDeclaration)src;
if (src.isCopied) {
if (src.model != null)
selfcall = src.model.adjustedSelfcall;
} else if (srcCtor.constructorCall != null) {
Dependencies.ensureTeamState(superTeamCtor.declaringClass.getTeamModel(),
ITranslationStates.STATE_RESOLVED);
selfcall = srcCtor.constructorCall.binding;
} else {
if (!src.scope.compilationUnitScope().referenceContext.parseMethodBodies) {
// that's why we have no constructorCall: CU is on diet (parse); use a fake (see Trac #142):
MethodBinding result = new MethodBinding(ClassFileConstants.AccPublic, providedArgs, null, superTeamCtor.declaringClass);
return result;
}
selfcall = superTeamCtor.declaringClass.superclass().getExactConstructor(Binding.NO_PARAMETERS);
}
} else if (superTeamCtor.bytecodeMissing || superTeamCtor.model == null) {
return null; // can't create ctor.
} else {
// no src means: peek the byte code:
selfcall = (new BytecodeTransformer()).peekConstructorCall(
teamDecl.getTeamModel(),
superTeamCtor.model,
scope.environment());
}
MethodBinding adjustedSelfcall = null;
if (selfcall == null) {
if (!superTeamCtor.bytecodeMissing) // may have been set by peedConstructorCall above
scope.problemReporter().unsupportedRoleDataflow(scope.methodScope().referenceMethod(), superTeamCtor);
// be sure to continue below, because client needs the new ctor.
} else {
TypeBinding[] selfCallProvidedArgs = providedWithMarker;// TODO(SH): fetch argument order from bytecode
adjustedSelfcall = maybeCopyCtorForSelfCall(scope, selfcall, selfCallProvidedArgs, needsLifting, gen);
// Check for presence of desired constructor:
MethodBinding existingConstructor = teamDecl.binding.getExactConstructor(providedWithMarker);
if (existingConstructor != null) {
if (adjustedSelfcall != null)
{
MethodModel model = MethodModel.getModel(existingConstructor);
model.adjustSelfcall(selfcall, adjustedSelfcall);
}
return existingConstructor;
}
}
// Create new constructor:
ConstructorDeclaration newCtor = gen.constructor(
teamDecl.compilationResult,
ClassFileConstants.AccPublic,
teamDecl.name,
AstConverter.createArgumentsFromParameters(providedWithMarker, gen));
// adjust argument-anchored types in this signature:
for (int i=0; i<superTeamCtor.parameters.length; i++)
if (RoleTypeBinding.isRoleWithExplicitAnchor(superTeamCtor.parameters[i])) {
RoleTypeBinding requiredRTB = (RoleTypeBinding) superTeamCtor.parameters[i];
if (requiredRTB._argumentPosition > -1) {
// we have an arg-anchored type, adjust the anchor within this signature:
Argument newArgument = newCtor.arguments[requiredRTB._argumentPosition]; // argument positions from the declared (super) ctor.
newArgument.modifiers |= ClassFileConstants.AccFinal;
newArgument.name = ((RoleTypeBinding) providedArgs[i])._teamAnchor.internalName(); // argument names from the provided types/arguments
}
}
newCtor.isTSuper = true; // ??
newCtor.isCopied = true;
AstEdit.addMethod(teamDecl, newCtor, false /*not synthetic*/, false/*addToFront*/, superTeamCtor); // incl. resolve
MethodModel model = MethodModel.getModel(newCtor); // already connect binding.
if (needsLifting)
model.liftedParams = superTeamCtor.parameters; // instructions for BytecodeTransformer
if (adjustedSelfcall != null)
model.adjustSelfcall(selfcall, adjustedSelfcall); // be it only strengthening
newCtor.binding.copiedInContext = teamDecl.binding;
newCtor.sourceMethodBinding = superTeamCtor;
return newCtor.binding;
}
/**
* A constructor being copied contains a self call which might require
* transitive copying of more team constructors.
* @param scope scope of the location triggering this copy operation
* @param selfcall
* @param providedArgs arguments provided to the selfcall
* @param needsLifting has the context required lifting of any args? pass this info down.
* @param gen
*/
private static MethodBinding maybeCopyCtorForSelfCall(
Scope scope,
MethodBinding selfcall,
TypeBinding[] providedArgs,
boolean needsLifting,
AstGenerator gen)
{
TypeBinding[] parameters = selfcall.parameters;
assert providedArgs.length >= parameters.length;
if (providedArgs.length > parameters.length)
System.arraycopy (
providedArgs, 0,
providedArgs = new TypeBinding[parameters.length], 0,
parameters.length);
boolean hasProblematicArg = false;
for (int i = 0; i < parameters.length; i++) {
if (!providedArgs[i].isCompatibleWith(parameters[i])) {
if (TSuperHelper.isMarkerInterface(parameters[i]))
continue;
hasProblematicArg = true;
TypeBinding roleType = TeamModel.getRoleToLiftTo(
scope, providedArgs[i], parameters[i], true, scope.referenceType().superclass, null/*callinDecl*/);
if (roleType != null)
{
// compatibility only through lifting, need to copy the constructor for this selfcall
return copyTeamConstructorForDeclaredLifting(
scope, selfcall, mergeSelfcallArgs(providedArgs, parameters), needsLifting);
}
} else if (RoleTypeBinding.isRoleWithoutExplicitAnchor(parameters[i])) {
hasProblematicArg = true;
}
}
if (selfcall.declaringClass.isTeam() && !hasProblematicArg) // don't copy from Object!
return maybeCreateTurningCtor(scope.referenceType(), selfcall, gen);
return null;
}
/**
* Prefer marker args from the super method to call,
* everything else should be taken from what is actually provided to the self call.
* @param thisProvidedArgs provided here
* @param superExpectedArgs expected by the super method
* @return merged array of references to existing type bindings.
*/
private static TypeBinding[] mergeSelfcallArgs(
TypeBinding[] thisProvidedArgs,
TypeBinding[] superExpectedArgs)
{
TypeBinding[] result = new TypeBinding[thisProvidedArgs.length];
for (int i = 0; i < result.length; i++) {
if (TSuperHelper.isMarkerInterface(superExpectedArgs[i]))
result[i] = superExpectedArgs[i];
else
result[i] = thisProvidedArgs[i];
}
return result;
}
/**
* At a specific point the constructor chain turns from this calls (w/ marker args)
* to real super calls. Create a constructor which does nothing but this turning.
* @param teamDecl
* @param superTeamCtor
* @param gen
*
* @return a binding if a new method has been generated, else null.
*/
public static MethodBinding maybeCreateTurningCtor(TypeDeclaration teamDecl,
MethodBinding superTeamCtor,
AstGenerator gen)
{
// constructor chains are created during resolve, so super team must be resolved.
Dependencies.ensureTeamState(teamDecl.binding.superclass().getTeamModel(), ITranslationStates.STATE_RESOLVED);
boolean hasMarkerArg =
( superTeamCtor.parameters.length > 0
&& TSuperHelper.isMarkerInterface(
superTeamCtor.parameters[superTeamCtor.parameters.length-1]));
TypeBinding[] newParams = superTeamCtor.parameters;
if (!hasMarkerArg) {
TypeBinding markerType = TSuperHelper.getMarkerInterface(teamDecl.scope, superTeamCtor.declaringClass);
newParams = AstEdit.extendTypeArray(superTeamCtor.parameters, markerType);
}
// do we need to work at all?
MethodBinding existingCtor = teamDecl.binding.getExactConstructor(newParams);
if (existingCtor != null)
return existingCtor;
// start creating:
Argument[] arguments = AstConverter.createArgumentsFromParameters(superTeamCtor.parameters, gen);
ConstructorDeclaration newCtor = gen.constructor(teamDecl.compilationResult,
ClassFileConstants.AccPublic,
teamDecl.name,
arguments);
// arguments
if (!hasMarkerArg) {
TSuperHelper.addMarkerArg(newCtor, superTeamCtor.declaringClass);
arguments = newCtor.arguments;
}
// constructor call
newCtor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super);
int length = superTeamCtor.parameters.length;// as many as the target (super) expects.
Expression[] selfcallArgs = new Expression[length];
for (int i = 0; i < length; i++) {
selfcallArgs[i] = gen.singleNameReference(arguments[i].name);
}
newCtor.constructorCall.arguments = selfcallArgs;
// empty body
newCtor.setStatements(new Statement[0]);
newCtor.isTSuper = true; // no matter if new or old marker arg
AstEdit.addMethod(teamDecl, newCtor);
return newCtor.binding;
}
/** If a super team has lift_dynamic methods, redefine them in the current team to match its roles. */
public static void redefineLiftDynamicMethods(TypeDeclaration teamDecl, boolean needMethodBodies) {
// if super team has a lift_dynamic method, re-generate it to match the roles of this team:
ReferenceBinding superTeam = teamDecl.binding.superclass();
if (superTeam != null && !CharOperation.equals(IOTConstants.ORG_OBJECTTEAMS_TEAM, superTeam.compoundName)) {
// ensure super team has its lift_dynamic methods generated:
Dependencies.ensureBindingState(superTeam, ITranslationStates.STATE_ROLE_HIERARCHY_ANALYZED);
ASTNode location = teamDecl.superclass != null ? teamDecl.superclass : teamDecl;
for (MethodBinding method : superTeam.methods())
if (CharOperation.prefixEquals(OT_LIFT_DYNAMIC, method.selector)) {
ReferenceBinding roleType = (ReferenceBinding) TeamModel.strengthenRoleType(teamDecl.binding, method.returnType);
DeclaredLifting.genLiftDynamicMethod(teamDecl, location, roleType, needMethodBodies);
}
}
}
/**
* This method generates a lift method suitable for lifting an argument typed to a base-bounded type variable (B base R).
* The generated method will be added to the team type and resolved if suitable (team already in state >= resolving).
* @param teamDecl
* @param ref
* @param roleType
* @param needMethodBody
*/
public static void genLiftDynamicMethod(TypeDeclaration teamDecl, ASTNode ref, TypeBinding roleType, boolean needMethodBody)
{
char[] dynamicLiftingSelector = dynamicLiftSelector(roleType);
if (teamDecl.binding.getMethods(dynamicLiftingSelector) != Binding.NO_METHODS)
return; // already present
if (roleType.isArrayType()) {
teamDecl.scope.problemReporter().missingImplementation(ref, "Generic lifting of array not yet implemented.");
} else if (roleType.isBaseType()) {
teamDecl.scope.problemReporter().primitiveTypeNotAllowedForLifting(teamDecl, ref, roleType);
} else {
// reference type
AstGenerator gen = new AstGenerator(ref);
MethodDeclaration dynLiftMeth = gen.method(teamDecl.compilationResult(),
ClassFileConstants.AccProtected,
roleType.erasure(),
dynamicLiftingSelector,
new Argument[] {
gen.argument(IOTConstants.BASE, gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT))
});
dynLiftMeth.statements = new Statement[] { gen.emptyStatement() }; // to be replaced below
int problemId = teamDecl.getTeamModel().canLiftingFail((ReferenceBinding) roleType.erasure());
if (problemId != 0)
AstEdit.addException(dynLiftMeth,
gen.qualifiedTypeReference(IOTConstants.O_O_LIFTING_FAILED_EXCEPTION),
false/*resolve*/);
dynLiftMeth.hasParsedStatements = true;
AstEdit.addMethod(teamDecl, dynLiftMeth);
dynLiftMeth.binding.returnType = ((ReferenceBinding)roleType).getRealType().erasure(); // force erased type after signature resolve
if (needMethodBody) {
dynLiftMeth.statements[0] = DeclaredLifting.generateDynamicSwitch(dynLiftMeth.scope, (ReferenceBinding)roleType, problemId, gen);
if (StateMemento.hasMethodResolveStarted(teamDecl.binding))
dynLiftMeth.statements[0].resolve(dynLiftMeth.scope);
}
}
}
/** Answer the selector for a dynamic lift method returning `roleType`. */
public static char[] dynamicLiftSelector(TypeBinding roleType) {
return CharOperation.concat(OT_LIFT_DYNAMIC, roleType.sourceName());
}
/* Generate the if-cascade for dynamic lifting depending on the argument's dynamic type. */
static Statement generateDynamicSwitch(BlockScope scope, ReferenceBinding roleType, int problemId, AstGenerator gen) {
ReferenceBinding[] boundDescendants = roleType.roleModel.getBoundDescendants();
if (boundDescendants.length == 0)
return gen.throwStatement(
gen.allocation(
gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_ERROR),
new Expression[] {
gen.stringLiteral(
CharOperation.concat(
"Lifting impossible, role has no bound descendants: ".toCharArray(), //$NON-NLS-1$
roleType.readableName()))
}));
IfStatement ifStat = null;
IfStatement current = null;
LookupEnvironment environment = scope.compilationUnitScope().environment;
for (int i=0; i<boundDescendants.length; i++) {
TypeBinding boundBase = boundDescendants[i].baseclass();
if (RoleTypeBinding.isRoleWithExplicitAnchor(boundBase)) {
if (boundBase.isParameterizedType()) {
// tricky case: need to discard type parameters, but retain/recreate the role type wrapping:
RoleTypeBinding baseRole = (RoleTypeBinding)boundBase;
boundBase = environment.createParameterizedType(baseRole._declaredRoleType,
null, // erase type parameters
baseRole._teamAnchor, // but retain anchor
-1, // valueParamPosition
baseRole.enclosingType());
}
// only RTB but not parameterized: leave unchanged.
} else {
boundBase = boundBase.erasure();
}
IfStatement newIf = gen.ifStatement(
gen.instanceOfExpression(
gen.singleNameReference(IOTConstants.BASE),
gen.typeReference(boundBase)),
gen.returnStatement(
Lifting.liftCall(
scope,
gen.thisReference(),
gen.castExpression(
gen.singleNameReference(IOTConstants.BASE),
gen.typeReference(boundBase),
CastExpression.DO_WRAP),
boundBase,
boundDescendants[i],
false/*needLowering*/)),
null);
if (ifStat == null)
ifStat = newIf; // toplevel "if"
else
current.elseStatement = newIf; // nest within existing "if else { /* here */ }"
current = newIf;
}
// final branch:
current.elseStatement = Lifting.genLiftingFailedException(IOTConstants.BASE, roleType, problemId, gen);
return ifStat;
}
}