blob: 300b68f6cba4af1b76c3f8f583fd0be80d48374f [file] [log] [blame]
/**
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2009, 2015 Stephan Herrmann
*
* 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
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors: Stephan Herrmann - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.core.compiler.mappings;
import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccPublic;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Reference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
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.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.core.compiler.Pair;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.AbstractMethodMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CallinMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.PotentialLiftExpression;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.PotentialLowerExpression;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.PotentialRoleReceiverExpression;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.PrivateRoleMethodCall;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.OTDynCallinBindingsAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.Lifting;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.Lowering;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.CallinCalloutBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding;
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.model.TeamModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.AbstractStatementsGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.MethodSignatureEnhancer;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.PredicateGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.ReplaceResultReferenceVisitor;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.TeamMethodGenerator;
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.RoleTypeCreator;
/**
* This class translates callin binding to the dynamic weaving strategy.
* This strategy is enabled by defining the property <code>ot.weaving=dynamic</code>
*
* @author stephan
* @since 1.3.0M3
*/
public class CallinImplementorDyn extends MethodMappingImplementor {
//_OT$role
static final char[] ROLE_VAR_NAME = CharOperation.concat(IOTConstants.OT_DOLLAR_NAME, IOTConstants.ROLE);
// method names
public static final char[] OT_CALL_BEFORE = "_OT$callBefore".toCharArray(); //$NON-NLS-1$
public static final char[] OT_CALL_AFTER = "_OT$callAfter".toCharArray(); //$NON-NLS-1$
public static final char[] OT_CALL_REPLACE = "_OT$callReplace".toCharArray(); //$NON-NLS-1$
// used for base calls:
public static final char[] OT_CALL_NEXT = "_OT$callNext".toCharArray(); //$NON-NLS-1$
// - both the team version (II[Object;) and the base version (I[Object;)
public static final char[] OT_CALL_ORIG_STATIC = "_OT$callOrigStatic".toCharArray(); //$NON-NLS-1$
private static final char[] OT_TERMINAL_CALL_NEXT = "_OT$terminalCallNext".toCharArray(); //$NON-NLS-1$
// variable names (arguments ...)
static final char[] TEAMS = "teams".toCharArray(); //$NON-NLS-1$
static final char[] INDEX = "index".toCharArray(); //$NON-NLS-1$
static final char[] CALLIN_ID = "callinID".toCharArray(); //$NON-NLS-1$
static final char[] BOUND_METHOD_ID = "boundMethodID".toCharArray(); //$NON-NLS-1$
static final char[] ARGUMENTS = "arguments".toCharArray(); //$NON-NLS-1$
static final char[] _OT_RESULT = "_OT$result".toCharArray(); //$NON-NLS-1$
static final char[] RESULT = "result".toCharArray(); //$NON-NLS-1$
static final String LOCAL_ROLE = "local$role$"; //$NON-NLS-1$
static final char[] _BASE$ = "_base$".toCharArray(); //$NON-NLS-1$
// for call next:
private static final char[] BASE_CALL_ARGS = "baseCallArguments".toCharArray(); //$NON-NLS-1$
// for call{replace,before,after}:
static final char[][] REPLACE_ARG_NAMES = new char[][]{_BASE$, TEAMS, INDEX, CALLIN_ID, BOUND_METHOD_ID, ARGUMENTS};
static final char[][] BEFORE_ARG_NAMES = new char[][]{_BASE$, CALLIN_ID, BOUND_METHOD_ID, ARGUMENTS};
static final char[][] AFTER_ARG_NAMES = new char[][]{_BASE$, CALLIN_ID, BOUND_METHOD_ID, ARGUMENTS, _OT_RESULT};
protected static final String OT_LOCAL = "_OT$local$"; //$NON-NLS-1$
static final char[] CATCH_ARG = "_OT$caughtException".toCharArray(); //$NON-NLS-1$
private ClassScope _roleScope;
/**
* Main entry from Dependencies - roles.
*
* Generates the byte code attributes.
*/
public void transformRole(RoleModel role)
{
this._role = role;
this._roleScope = role.getAst().scope; // we definitely have an AST here
this.bindingDirection = TerminalTokens.TokenNameBINDIN;
AbstractMethodMappingDeclaration[] methodMappings = this._role.getAst().callinCallouts;
if(methodMappings == null || methodMappings.length == 0)
{
// there are no mappings in this role!
return;
}
if (this._role._hasBindingAmbiguity) {
for (int i = 0; i < methodMappings.length; i++) {
this._roleScope.problemReporter().callinDespiteLiftingProblem(
this._role.getBinding(),
IProblem.CallinDespiteBindingAmbiguity,
methodMappings[i]);
}
}
CallinMappingDeclaration[] callinMappings = new CallinMappingDeclaration[methodMappings.length];
int num = 0;
// LinkedList<CallinMappingDeclaration> staticReplaces = new LinkedList<CallinMappingDeclaration>();
for (int idx = 0; idx < methodMappings.length; idx++)
{
AbstractMethodMappingDeclaration methodMapping = methodMappings[idx];
if(!methodMapping.ignoreFurtherInvestigation && methodMapping.isCallin())
{
// CRIPPLE:
// result &= createCallin((CallinMappingDeclaration) methodMapping);
callinMappings[num++] = (CallinMappingDeclaration)methodMapping;
// if (methodMapping.isStaticReplace())
// staticReplaces.add((CallinMappingDeclaration)methodMapping);
}
}
System.arraycopy(
callinMappings, 0,
callinMappings = new CallinMappingDeclaration[num], 0,
num);
OTDynCallinBindingsAttribute.createOrMerge(this._role.getTeamModel(), this._role.getBaseTypeBinding().getRealClass().constantPoolName(), callinMappings);
// CRIPPLE:
// if (staticReplaces.size() > 0) {
// CallinMappingDeclaration[] callins = new CallinMappingDeclaration[staticReplaces.size()];
// staticReplaces.toArray(callins);
// this._role.getTeamModel().addOrMergeAttribute(new StaticReplaceBindingsAttribute(callins));
// }
}
// copied and slightly adjusted from old CallinImplementor:
Expression getArgument(
CallinMappingDeclaration methodMapping,
MethodDeclaration wrapperDeclaration,
TypeBinding[] implParameters,
int idx,
final MethodSpec sourceMethodSpec)
{
final MethodSpec implementationMethodSpec = methodMapping.getImplementationMethodSpec();
Expression mappedArgExpr = null;
int pos = -1;
char[] targetArgName = null;
int generatedArgsLen = methodMapping.isReplaceCallin() ?
MethodSignatureEnhancer.getEnhancingArgLen(WeavingScheme.OTDRE):
0;
final int srcIdx = idx-generatedArgsLen; // index into source-code signatures.
targetArgName = implementationMethodSpec.arguments[srcIdx].name;
// retrieve info collected during analyzeParameterMappings:
Pair<Expression,Integer> mapper = methodMapping.mappingExpressions[srcIdx];
mappedArgExpr = mapper.first;
if (mapper.second != null)
pos = mapper.second.intValue();
if (mappedArgExpr != null) {
if (methodMapping.baseMethodSpecs.length > 1) // multi-mappings need to copy mapped argument expressions:
mappedArgExpr = copyExpression(mappedArgExpr, methodMapping.scope, methodMapping.compilationResult.getCompilationUnit());
SourceTypeBinding roleType = methodMapping.scope.enclosingSourceType();
if (idx >= implParameters.length) // CLOVER: never true in jacks suite
return mappedArgExpr; // arg is invisible to receiver, don't lift
TypeBinding expectedType = implParameters[idx];
// arg might have been weakened:
if ( expectedType.isRole()
&& TypeBinding.notEquals(expectedType.enclosingType(), roleType.enclosingType()))
expectedType = TeamModel.strengthenRoleType(roleType, expectedType);
AstGenerator gen = new AstGenerator(mappedArgExpr.sourceStart, mappedArgExpr.sourceEnd);
Expression receiver = null;
if ( RoleTypeBinding.isRoleWithoutExplicitAnchor(expectedType)
&& TypeBinding.equalsEquals(roleType.getRealClass(), ((ReferenceBinding)expectedType).enclosingType()))
{
// expectedType is a role of the current role(=team),
// use the role as the receiver for the lift call:
receiver = gen.singleNameReference(ROLE_VAR_NAME);
}
if (sourceMethodSpec.hasSignature) {
if (sourceMethodSpec.argNeedsTranslation(srcIdx)) {
mappedArgExpr.tagReportedBaseclassDecapsulation();
return Lifting.liftCall(methodMapping.scope,
receiver != null ? receiver : ThisReference.implicitThis(),
mappedArgExpr,
sourceMethodSpec.resolvedParameters()[srcIdx],
expectedType,
methodMapping.isReplaceCallin()/*needLowering*/);
}
if (methodMapping.mappings == null)
// we have signatures and no parameter mapping.
// if no need for translation has been recorded, it IS not needed.
return mappedArgExpr;
}
// don't know yet whether lifting is actually needed (=>potentially)
Expression liftExpr =
gen.potentialLift(receiver, mappedArgExpr, expectedType, methodMapping.isReplaceCallin()); // reversible?
// if param mappings are present, connect the PLE to the method spec for propagating translation flag
// (CallinMethodMappingsAttribute needs to know whether lifting is actually needed.)
if (methodMapping.mappings != null && pos != -1 && liftExpr instanceof PotentialLiftExpression) {
final int srcPos = pos;
((PotentialLiftExpression)liftExpr).onLiftingRequired(new Runnable() {public void run() {
sourceMethodSpec.argNeedsTranslation[srcPos] = true;
implementationMethodSpec.argNeedsTranslation[srcIdx] = true;
}});
}
return liftExpr;
}
wrapperDeclaration.scope.problemReporter().unmappedParameter(
targetArgName,
implementationMethodSpec,
methodMapping.isCallout());
return null;
}
Expression copyExpression(Expression expression, Scope scope, ICompilationUnit cu) {
if (cu == null) return expression; // FIXME: do we need a fallback when cu is built from model?
final Parser parser = new Parser(scope.problemReporter(), false);
char[] source = cu.getContents();
return parser.parseExpression(source, expression.sourceStart, expression.sourceEnd - expression.sourceStart + 1,
scope.referenceCompilationUnit(), false /* record line separators */);
}
/**
* Main entry from Dependencies - teams.
*
* Creates the dispatch methods.
*/
public void transformTeam(TeamModel aTeam)
{
List<CallinMappingDeclaration> beforeMappings = new ArrayList<CallinMappingDeclaration>();
List<CallinMappingDeclaration> replaceMappings = new ArrayList<CallinMappingDeclaration>();
List<CallinMappingDeclaration> afterMappings = new ArrayList<CallinMappingDeclaration>();
List<CallinMappingDeclaration> mappingsWithStaticBase = new ArrayList<CallinMappingDeclaration>();
for (RoleModel role : aTeam.getRoles(false)) {
TypeDeclaration roleDecl = role.getAst(); // FIXME(SH): this breaks incremental compilation: all roles must be present as AST!!
if (roleDecl == null) continue; // FIXME(SH): check if this is OK
if (roleDecl.callinCallouts != null) {
for (AbstractMethodMappingDeclaration mappingDecl : roleDecl.callinCallouts) {
if (mappingDecl.isCallin()) {
CallinMappingDeclaration callinDecl = (CallinMappingDeclaration) mappingDecl;
switch (callinDecl.callinModifier) {
case TerminalTokens.TokenNamebefore: beforeMappings.add(callinDecl); break;
case TerminalTokens.TokenNameafter: afterMappings.add(callinDecl); break;
case TerminalTokens.TokenNamereplace: replaceMappings.add(callinDecl); break;
}
if (callinDecl.hasStaticBaseMethod())
mappingsWithStaticBase.add(callinDecl);
}
}
}
}
if (beforeMappings.size() > 0)
generateDispatchMethod(OT_CALL_BEFORE, false, false, beforeMappings, aTeam);
if (afterMappings.size() > 0)
generateDispatchMethod(OT_CALL_AFTER, false, true, afterMappings, aTeam);
if (replaceMappings.size() > 0) {
generateDispatchMethod(OT_CALL_REPLACE, true, false, replaceMappings, aTeam);
generateCallNext(replaceMappings, aTeam);
}
if (!mappingsWithStaticBase.isEmpty())
generateCallOrigStatic(mappingsWithStaticBase, aTeam);
}
private void generateDispatchMethod(char[] methodName, final boolean isReplace, final boolean isAfter, final List<CallinMappingDeclaration> callinDecls, final TeamModel aTeam)
{
// FIXME(SH): once we know that Team has empty implementations (and checked cases involving team inheritance)
// we probably want to avoid generating empty methods here.
final TypeDeclaration teamDecl = aTeam.getAst();
if (teamDecl == null) return;
final AstGenerator gen = new AstGenerator(teamDecl);
gen.replaceableEnclosingClass = teamDecl.binding;
// public void _OT$callBefore (IBoundBase2 base, int boundMethodId, int callinId, Object[] args)
// public void _OT$callAfter (IBoundBase2 base, int boundMethodId, int callinId, Object[] args, Object result)
// public void _OT$callReplace (IBoundBase2 base, Team[] teams, int index, int boundMethodId, int[] callinIds, Object[] args)
int length = 4;
if (isReplace)
length = 6;
else if (isAfter)
length = 5;
Argument[] arguments = new Argument[length];
int a = 0;
arguments[a++] = gen.argument(_BASE$, gen.qualifiedTypeReference(IOTConstants.ORG_OBJECTTEAMS_IBOUNDBASE2));
if (isReplace)
arguments[a++] = gen.argument(TEAMS, gen.qualifiedArrayTypeReference(IOTConstants.ORG_OBJECTTEAMS_ITEAM, 1));
if (isReplace)
arguments[a++] = gen.argument(INDEX, gen.typeReference(TypeBinding.INT));
arguments[a++] =
isReplace ? gen.argument(CALLIN_ID, gen.createArrayTypeReference(TypeBinding.INT, 1))
: gen.argument(CALLIN_ID, gen.typeReference(TypeBinding.INT));
arguments[a++] = gen.argument(BOUND_METHOD_ID, gen.typeReference(TypeBinding.INT));
arguments[a++] = gen.argument(ARGUMENTS, gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1));
if (isAfter)
arguments[a++] = gen.argument(_OT_RESULT, gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT));
TypeReference returnTypeRef = isReplace ? gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT) : gen.typeReference(TypeBinding.VOID);
final MethodDeclaration callMethod = gen.method(teamDecl.compilationResult, AccPublic, returnTypeRef, methodName, arguments);
callMethod.isMappingWrapper = AbstractMethodDeclaration.WrapperKind.CALLIN;
AstEdit.addMethod(teamDecl, callMethod);
MethodModel.addCallinFlag(callMethod, IOTConstants.CALLIN_FLAG_WRAPPER);
callMethod.model._declaringMappings = callinDecls;
MethodModel.getModel(callMethod).setStatementsGenerator(new AbstractStatementsGenerator() {
protected boolean generateStatements(AbstractMethodDeclaration methodDecl) {
// into head of tryStats we generate local vars to be shared by case statements:
List<Statement> tryStats = new ArrayList<Statement>();
SwitchStatement switchStat = new SwitchStatement();
switchStat.expression =
isReplace
? gen.arrayReference(gen.singleNameReference(CALLIN_ID), gen.singleNameReference(INDEX)) // switch(callinId[index]) { ...
: gen.singleNameReference(CALLIN_ID); // switch(callinId) { ...
// statements for the body of the switchStatement:
List<Statement> statements = new ArrayList<Statement>();
int callinIdCount = teamDecl.getTeamModel().getCallinIdCount();
// callinIds not handled here will be handled using a super-call.
boolean[] handledCallinIds = new boolean[callinIdCount];
// do we need to catch LiftingFailedException?
boolean canLiftingFail = false;
// one case block per callin mapping:
for (CallinMappingDeclaration callinDecl : callinDecls)
{
if (callinDecl.ignoreFurtherInvestigation || RoleModel.isRoleWithBaseProblem(callinDecl.scope.referenceType()))
continue;
if (!callinDecl.hasParsedParamMappings) // during reconcile we may not be interested in this level of detail (e.g., of a role file)
continue;
gen.retargetFrom(callinDecl);
// one case label per bound base method:
for (MethodSpec baseSpec : callinDecl.baseMethodSpecs) {
statements.add(gen.caseStatement(gen.intLiteral(baseSpec.callinID))); // case <baseMethod.callinId>:
handledCallinIds[baseSpec.callinID] = true;
PredicateGenerator predGen = new PredicateGenerator(
callinDecl.binding._declaringRoleClass,
callinDecl.isReplaceCallin());
TypeBinding baseReturn = baseSpec.resolvedType();
boolean isStaticRoleMethod = callinDecl.getRoleMethod().isStatic();
ReferenceBinding roleType = callinDecl.scope.enclosingSourceType();
if (roleType.isGenericType()) // cannot handle generic role in this generated code
roleType = (ReferenceBinding) callinDecl.scope.environment().convertToRawType(roleType, false);
MethodBinding roleMethodBinding = callinDecl.getRoleMethod();
boolean needLiftedRoleVar = !isStaticRoleMethod
&& roleType.isCompatibleWith(roleMethodBinding.declaringClass);
List<Statement> blockStatements = new ArrayList<Statement>();
// do we need to expose _OT$result as result?
char[] resultName = null;
if (callinDecl.callinModifier == TerminalTokens.TokenNameafter
&& (callinDecl.mappings != null || callinDecl.predicate != null)
&& baseReturn != TypeBinding.VOID) {
resultName = RESULT;
callinDecl.resultVar = gen.localBaseVariable(RESULT, baseReturn, // BaseReturnType result = (BaseReturnType)_OT$result;
gen.createCastOrUnboxing(gen.singleNameReference(_OT_RESULT), baseReturn, true/*baseAccess*/));
blockStatements.add(callinDecl.resultVar);
}
// expose casted _base$ as "base":
blockStatements.add(gen.localVariable(IOTConstants.BASE,
gen.alienScopeTypeReference(gen.baseTypeReference(roleType.baseclass()),callinDecl.scope),
gen.castExpression(gen.baseNameReference(_BASE$),
gen.alienScopeTypeReference(gen.baseTypeReference(roleType.baseclass()),callinDecl.scope),
CastExpression.RAW)));
// -------------- base predicate check -------
boolean hasBasePredicate = false;
for (MethodSpec baseMethodSpec : callinDecl.baseMethodSpecs) { // FIXME: check this inner loop, outer already loops over baseMethods!!
char[] resultName2 = null;
if ( callinDecl.callinModifier == TerminalTokens.TokenNameafter
&& baseMethodSpec.resolvedType() != TypeBinding.VOID)
{
resultName2 = IOTConstants.RESULT;
}
// FIXME(SH): only call predidate for the current base method (from BoundMethodID?)
Statement predicateCheck = predGen.createBasePredicateCheck(
callinDecl, baseMethodSpec, resultName2, gen);
if (predicateCheck != null) {
blockStatements.add(predicateCheck); // if (!base$when(baseArg,...)) throw new LiftingVetoException();
hasBasePredicate = true;
}
}
Expression resetFlag =
CallinImplementor.setExecutingCallin(roleType.roleModel, blockStatements); // boolean _OT$oldIsExecutingCallin = _OT$setExecutingCallin(true);
// ----------- receiver for role method call: -----------
Expression receiver;
char[] roleVar = null;
if (!isStaticRoleMethod) {
if (needLiftedRoleVar) {
canLiftingFail |= checkLiftingProblem(teamDecl, callinDecl, roleType);
roleVar = (LOCAL_ROLE+statements.size()).toCharArray();
TypeReference roleTypeReference = gen.roleTypeReference(teamDecl.getTeamModel().getTThis(), roleType, 0);
blockStatements.add(gen.localVariable(roleVar, // RoleType local$n = this._OT$liftToRoleType((BaseType)base);
gen.alienScopeTypeReference(roleTypeReference, callinDecl.scope),
ClassFileConstants.AccFinal,
Lifting.liftCall(callMethod.scope,
gen.thisReference(),
gen.baseNameReference(IOTConstants.BASE),
callMethod.scope.getType(IOTConstants.ORG_OBJECTTEAMS_IBOUNDBASE2, 3),
roleType,
false,
gen)));
receiver = gen.thislikeNameReference(roleVar);
// private receiver needs to be casted to the class.
} else {
// method is from role's enclosing team
receiver = gen.qualifiedThisReference(TeamModel.strengthenEnclosing(teamDecl.binding, roleMethodBinding.declaringClass));
}
} else {
receiver = gen.singleNameReference(callinDecl.getRoleMethod().declaringClass.sourceName());
}
int baseArgOffset = 0;
if (baseSpec.isCallin()) baseArgOffset+=MethodSignatureEnhancer.getEnhancingArgLen(WeavingScheme.OTDRE);
if (baseSpec.isStatic() && baseSpec.getDeclaringClass().isRole()) baseArgOffset+=2;
// unpack arguments to be used by parameter mappings and base predicate:
// ArgTypeN argn = args[n]
if (callinDecl.mappings != null || (hasBasePredicate && baseSpec.arguments != null)) {
TypeBinding[] baseParams = baseSpec.resolvedParameters();
for (int i=0; i<baseSpec.arguments.length; i++) { // BaseType baseArg = castAndOrUnbox(arguments[n]);
Argument baseArg = baseSpec.arguments[i];
Expression rawArg = gen.arrayReference(gen.singleNameReference(ARGUMENTS), i+baseArgOffset);
Expression init = rawArg;
if (!baseParams[i].isTypeVariable())
init = gen.createCastOrUnboxing(rawArg, baseParams[i], callinDecl.scope);
LocalDeclaration baseArgLocal = gen.localVariable(baseArg.name,
gen.alienScopeTypeReference(baseArg.type, callinDecl.scope),
init);
baseArgLocal.modifiers |= (baseArg.modifiers & ClassFileConstants.AccFinal);
if (hasBasePredicate) {
// add to front so it is already available for the base predicate check:
blockStatements.add(i, baseArgLocal);
} else {
// otherwise give it a chance for expressions/types that depend on the role instance
baseArgLocal.initialization = new PotentialRoleReceiverExpression(
init,
roleVar,
gen.typeReference(roleType));
blockStatements.add(baseArgLocal);
}
}
}
// -- assemble arguments:
TypeBinding[] roleParams = callinDecl.roleMethodSpec.resolvedParameters();
Expression[] callArgs = new Expression [roleParams.length + (isReplace ? MethodSignatureEnhancer.getEnhancingArgLen(WeavingScheme.OTDRE) : 0)];
int idx = 0;
if (isReplace)
for (char[] argName : REPLACE_ARG_NAMES)
callArgs[idx++] = gen.singleNameReference(argName); // prepare: base, teams, boundMethodId, callinIds, index, arguments ...
// prepare parameter mappings:
callinDecl.traverse(new ReplaceResultReferenceVisitor(callinDecl), callinDecl.scope.classScope());
boolean hasArgError = false;
for (int i=0; i<roleParams.length; i++) {
Expression arg;
TypeBinding roleParam = roleParams[i];
if (roleParam.isTypeVariable()) {
TypeVariableBinding tvb = (TypeVariableBinding) roleParam;
if (tvb.declaringElement instanceof MethodBinding) {
if (TypeBinding.equalsEquals(((MethodBinding)tvb.declaringElement).declaringClass, roleType))
// don't use type variable of target method, see test4140_callinReplaceCompatibility10s()
roleParam = roleParam.erasure();
}
}
TypeReference localTypeRef = null;
if (callinDecl.mappings == null) {
// ------------ unmapped arguments --------------
arg = gen.arrayReference(gen.singleNameReference(ARGUMENTS), i+baseArgOffset); // prepare: somePreparation(arguments[i])
TypeBinding baseArgType = baseSpec.resolvedParameters()[i];
if (roleParam.isBaseType()) {
// this includes intermediate cast to boxed type:
arg = gen.createUnboxing(arg, (BaseTypeBinding)roleParam);
} else if (baseArgType.isBaseType()) {
// Object -> BoxingType
arg = gen.castExpression(arg,
gen.qualifiedTypeReference(AstGenerator.boxTypeName((BaseTypeBinding) baseArgType)),
CastExpression.RAW);
} else {
// Object -> MyBaseClass
ReferenceBinding baseclass = roleType.baseclass();
if (baseclass instanceof DependentTypeBinding && baseArgType instanceof ReferenceBinding)
baseArgType = RoleTypeCreator.maybeInstantiateFromPlayedBy(callinDecl.scope, (ReferenceBinding)baseArgType);
arg = gen.castExpression(arg,
gen.alienScopeTypeReference(
gen.typeReference(baseArgType),
callinDecl.scope),
CastExpression.DO_WRAP);
if (!roleParam.leafComponentType().isBaseType()
&& PotentialLiftExpression.isLiftingRequired(callinDecl.scope, roleParam, baseArgType, arg)) {
// lift?(MyBaseClass)
Reference liftReceiver = null; // default: let gen find the team
if (roleType.isTeam() && TypeBinding.equalsEquals(roleParam.enclosingType(), roleType))
liftReceiver = gen.singleNameReference(roleVar); // lift to inner role
arg = gen.potentialLift(liftReceiver, arg, roleParam, isReplace/*reversible*/);
localTypeRef = gen.typeReference(roleParam);
canLiftingFail |= checkLiftingProblem(teamDecl, callinDecl, (ReferenceBinding)roleParam.leafComponentType());
}
}
if (localTypeRef == null)
localTypeRef = gen.baseclassReference(baseArgType); // unless lifting was required above
} else {
// ------------ mapped arguments --------------
if (roleParam.isTypeVariable() && ((TypeVariableBinding)roleParam).declaringElement instanceof CallinCalloutBinding)
localTypeRef = gen.typeReference(roleParam.erasure()); // cannot explicitly mention this TVB
else
localTypeRef = gen.typeReference(roleParam);
arg = getArgument(callinDecl, // prepare: <mappedArg<n>>
(MethodDeclaration) methodDecl,
callinDecl.getRoleMethod().parameters,
i+idx,
baseSpec);
if (arg == null) {
hasArgError = true;
continue; // keep going to find problems with other args, too.
}
if (Lifting.isLiftToMethodCall(arg))
canLiftingFail |= checkLiftingProblem(teamDecl, callinDecl, roleType);
boolean isBaseReference = arg instanceof SingleNameReference
&& CharOperation.equals(((SingleNameReference)arg).token, IOTConstants.BASE);
if (needLiftedRoleVar)
arg = new PotentialRoleReceiverExpression(arg, roleVar, gen.typeReference(roleType.getRealClass()));
// mapped expression may require casting: "base" reference has static type IBoundBase2
if (isBaseReference)
arg = gen.castExpression(arg, gen.typeReference(roleParam), CastExpression.RAW);
}
char[] localName = (OT_LOCAL+i).toCharArray(); // RoleParamType _OT$local$n = preparedArg<n>;
blockStatements.add(gen.localVariable(localName, gen.alienScopeTypeReference(localTypeRef, callinDecl.scope), arg));
callArgs[i+idx] = gen.singleNameReference(localName); // prepare: ... _OT$local$ ...
}
if (hasArgError)
continue;
// -- role side predicate:
Expression[] predicateArgs = isReplace
? MethodSignatureEnhancer.retrenchBasecallArguments(callArgs, true, WeavingScheme.OTDRE)
: callArgs;
predicateArgs = maybeAddResultReference(callinDecl, predicateArgs, resultName, gen);
Statement rolePredicateCheck = predGen.createPredicateCheck( // if (!when(callArgs)) throw new LiftingVetoException();
callinDecl,
callinDecl.scope.referenceType(),
receiver,
predicateArgs,
callArgs,
gen);
if (rolePredicateCheck != null)
// predicateCheck(_OT$role)
blockStatements.add(rolePredicateCheck);
// -- assemble the method call: // local$n.roleMethod((ArgType0)args[0], .. (ArgTypeN)args[n]);
boolean lhsResolvesToTeamMethod = TypeBinding.equalsEquals(callinDecl.getRoleMethod().declaringClass, roleType.enclosingType()); // TODO(SH): more levels
MessageSend roleMethodCall = (callinDecl.getRoleMethod().isPrivate() && !lhsResolvesToTeamMethod)
? new PrivateRoleMethodCall(receiver, callinDecl.roleMethodSpec.selector, callArgs, false/*c-t-f*/,
callinDecl.scope, roleType, callinDecl.getRoleMethod(), gen)
: gen.messageSend(receiver, callinDecl.roleMethodSpec.selector, callArgs);
roleMethodCall.isGenerated = true; // for PrivateRoleMethodCall
roleMethodCall.isPushedOutRoleMethodCall = true;
// -- post processing:
Statement[] messageSendStatements;
if (isReplace) {
Expression result = roleMethodCall;
if (baseSpec.returnNeedsTranslation) {
// lowering:
TypeBinding[]/*role,base*/ returnTypes = getReturnTypes(callinDecl, 0);
// who is responsible for lowering: the team or the current role?
Expression lowerReceiver = (isRoleOfCurrentRole(roleType, returnTypes[0]))
? gen.singleNameReference(roleVar)
: genTeamThis(gen, returnTypes[0]);
result = new Lowering().lowerExpression(methodDecl.scope, result, returnTypes[0], returnTypes[1],
lowerReceiver, true/*needNullCheck*/, true/*delayedResolve*/);
}
// possibly convert using result mapping
callinDecl.checkResultMapping();
boolean isResultBoxed = baseReturn.isBaseType() && baseReturn != TypeBinding.VOID;
if ( callinDecl.mappings != null
&& callinDecl.isResultMapped)
{
if (isResultBoxed)
result = gen.createUnboxing(result, (BaseTypeBinding) baseReturn);
Expression mappedResult = new PotentialRoleReceiverExpression(
callinDecl.getResultExpression(baseSpec, isResultBoxed, gen/*stepOverGen*/),
roleVar,
gen.typeReference(roleType.getRealClass()));
messageSendStatements = new Statement[] {
callinDecl.resultVar =
gen.localVariable(IOTConstants.RESULT, baseReturn, // result = (Type)role.roleMethod(args);
gen.castExpression(result, gen.typeReference(baseReturn), CastExpression.RAW)),
// cast because role return might be generalized
gen.returnStatement(mappedResult) // return mappedResult(result);
};
} else {
if (isResultBoxed) { // $if_need_result_unboxing$
messageSendStatements = new Statement[] {
gen.localVariable(IOTConstants.OT_RESULT, // Object _OT$result = role.roleMethod(args);
gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT),
result),
CallinImplementor.genResultNotProvidedCheck( // if (_OT$result == null)
teamDecl.binding.readableName(), // throw new ResultNotProvidedException(..)
roleType.readableName(),
roleMethodBinding,
roleType.baseclass(),
baseSpec,
gen),
gen.returnStatement(gen.singleNameReference(IOTConstants.OT_RESULT)) // return _OT$result;
};
} else { // $endif$
messageSendStatements = new Statement[] { gen.returnStatement(result) }; // return role.roleMethod(args);
}
}
} else {
messageSendStatements = new Statement[] {
roleMethodCall, // role.roleMethod(args);
gen.breakStatement() // break;
};
}
// assemble:
// try { roleMessageSend(); }
// catch(Exception _OT$caughtException) { throw new SneakyException(_OT$caughtException); }
// finally { _OT$setExecutingCallin(_OT$oldIsExecutingCallin); }
blockStatements.add(protectRoleMethodCall(messageSendStatements, roleMethodBinding, resetFlag, gen));
statements.add(gen.block(blockStatements.toArray(new Statement[blockStatements.size()])));
// collectively report the problem(s)
if (canLiftingFail && callinDecl.rolesWithLiftingProblem != null)
for (Map.Entry<ReferenceBinding, Integer> entry : callinDecl.rolesWithLiftingProblem.entrySet())
callinDecl.scope.problemReporter().callinDespiteLiftingProblem(entry.getKey(), entry.getValue(), callinDecl);
}
} // END for (CallinMappingDeclaration callinDecl : callinDecls)
gen.retargetFrom(teamDecl);
boolean needSuperCall = false;
// do we have a relevant super team, which possibly defines more callins?
ReferenceBinding superTeam = aTeam.getBinding().superclass();
if (superTeam != null && superTeam.isTeam() && superTeam.id != IOTConstants.T_OrgObjectTeamsTeam) {
// callinIds to be handled by super call?
for (int i=0; i < callinIdCount; i++)
if (!handledCallinIds[i]) {
statements.add(gen.caseStatement(gen.intLiteral(i))); // case callinIdOfSuper:
needSuperCall = true;
}
if (!isReplace)
needSuperCall = true;
// a super call might become necessary after the fact when this dispatch method
// is copy-inherited to a tsub-team, because the tsub-team may have a super
// with more callins, see test1111_roleInheritsCallinFromTsupers1.
// TODO: can we safely handle this for the replace-case, too??
// (replace needs to "return _OT$callNext();" in the default branch, see below).
// See https://bugs.eclipse.org/433123
}
if (needSuperCall) {
if (!isReplace)
statements.add(gen.caseStatement(null)); // default label
char[] selector; char[][] argNames;
if (isReplace) {
selector = OT_CALL_REPLACE; argNames = REPLACE_ARG_NAMES;
} else if (isAfter) {
selector = OT_CALL_AFTER; argNames = AFTER_ARG_NAMES;
} else {
selector = OT_CALL_BEFORE; argNames = BEFORE_ARG_NAMES;
}
Expression[] superCallArgs = new Expression[argNames.length];
for (int idx=0; idx < argNames.length; idx++)
superCallArgs[idx] = gen.singleNameReference(argNames[idx]);
// if we have a tsuper team which a corresponding dispatch method that one takes precedence:
MessageSend superCall = aTeam.hasTSuperTeamMethod(selector)
? gen.tsuperMessageSend(gen.thisReference(), selector, superCallArgs)
: gen.messageSend(gen.superReference(), selector, superCallArgs);
if (isReplace)
statements.add(gen.returnStatement(superCall)); // return super._OT$callReplace(..);
else
statements.add(superCall); // super._OT$callBefore/After(..);
}
Statement catchStatement1 = gen.emptyStatement();
Statement catchStatement2 = gen.emptyStatement();
if (isReplace) {
// default: callNext:
Expression[] callArgs = new Expression[REPLACE_ARG_NAMES.length+1];
for (int idx=0; idx < REPLACE_ARG_NAMES.length; idx++)
callArgs[idx] = gen.singleNameReference(REPLACE_ARG_NAMES[idx]);
callArgs[callArgs.length-1] = gen.nullLiteral(); // no explicit baseCallArguments
statements.add(gen.caseStatement(null)); // default:
statements.add(gen.returnStatement( // return _OT$callNext(..);
gen.messageSend(
gen.qualifiedThisReference(aTeam.getBinding()),
OT_CALL_NEXT,
callArgs)));
catchStatement1 = gen.returnStatement(
gen.messageSend(
gen.qualifiedThisReference(aTeam.getBinding()),
OT_CALL_NEXT,
callArgs));
catchStatement2 = gen.returnStatement(
gen.messageSend(
gen.qualifiedThisReference(aTeam.getBinding()),
OT_CALL_NEXT,
callArgs));
}
// ==== overall assembly: ====
switchStat.statements = statements.toArray(new Statement[statements.size()]);
Argument[] exceptionArguments;
Statement[][] exceptionStatementss;
if (canLiftingFail) {
exceptionArguments = new Argument[] {
gen.argument("ex".toCharArray(), //$NON-NLS-1$
gen.qualifiedTypeReference(IOTConstants.ORG_OBJECTTEAMS_LIFTING_VETO)),
gen.argument("ex".toCharArray(), //$NON-NLS-1$
gen.qualifiedTypeReference(IOTConstants.O_O_LIFTING_FAILED_EXCEPTION))
};
exceptionStatementss = new Statement[][]{{catchStatement1},{catchStatement2}};
} else {
exceptionArguments = new Argument[] {
gen.argument("ex".toCharArray(), //$NON-NLS-1$
gen.qualifiedTypeReference(IOTConstants.ORG_OBJECTTEAMS_LIFTING_VETO))
};
exceptionStatementss = new Statement[][]{{catchStatement1}};
}
tryStats.add(switchStat);
methodDecl.statements = new Statement[] {
gen.tryCatch(
tryStats.toArray(new Statement[tryStats.size()]),
// expected exception is ignored, do nothing (before/after) or proceed to callNext (replace)
exceptionArguments,
exceptionStatementss)
};
methodDecl.hasParsedStatements = true;
return true;
}
});
}
/**
* Assemble message send arguments plus perhaps a result reference to
* yield argument expressions for a predicate call.
* (From old CallinImplementor).
*/
Expression[] maybeAddResultReference(CallinMappingDeclaration callinBindingDeclaration,
Expression[] messageSendArguments,
char[] resultName,
AstGenerator gen)
{
Expression[] predicateArgs = null;
if (callinBindingDeclaration.hasSignature) {
predicateArgs = messageSendArguments;
if (resultName != null) // has resultVar (after with non-void base return)
{
int l = messageSendArguments.length;
System.arraycopy(messageSendArguments, 0, predicateArgs = new Expression[l+1], 0, l);
predicateArgs[l] = gen.baseNameReference(resultName);
}
}
return predicateArgs;
}
private void generateCallNext(final List<CallinMappingDeclaration> callinDecls, final TeamModel aTeam) {
// public Object _OT$callNext(IBoundBase2 baze, Team[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args, Object[] baseCallArgs)
final TypeDeclaration teamDecl = aTeam.getAst();
if (teamDecl == null) return;
final AstGenerator gen = new AstGenerator(teamDecl);
Argument[] args = new Argument[] {
gen.argument(IOTConstants.BASE, gen.qualifiedTypeReference(IOTConstants.ORG_OBJECTTEAMS_IBOUNDBASE2)),
gen.argument(TEAMS, gen.qualifiedArrayTypeReference(IOTConstants.ORG_OBJECTTEAMS_ITEAM, 1)),
gen.argument(INDEX, gen.typeReference(TypeBinding.INT)),
gen.argument(CALLIN_ID, gen.createArrayTypeReference(TypeBinding.INT, 1)),
gen.argument(BOUND_METHOD_ID, gen.typeReference(TypeBinding.INT)),
gen.argument(ARGUMENTS, gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1)),
gen.argument(BASE_CALL_ARGS, gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1))
};
// super call directly passes all these args through:
Expression[] superArgs = new Expression[args.length];
for (int i=0; i<args.length;i++)
superArgs[i] = gen.singleNameReference(args[i].name);
MethodDeclaration decl = gen.method(teamDecl.compilationResult,
AccPublic,
gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT),
OT_CALL_NEXT,
args);
// the payload: reverse parameter mappings:
SwitchStatement swStat = new SwitchStatement();
swStat.expression = gen.arrayReference(gen.singleNameReference(CALLIN_ID), gen.singleNameReference(INDEX)); // switch(callinId[index]) { ...
List<Statement> swStatements = new ArrayList<Statement>();
for (CallinMappingDeclaration mapping : callinDecls) {
List<Statement> caseBlockStats = new ArrayList<Statement>();
int nLabels = 0;
for (MethodSpec baseSpec : mapping.baseMethodSpecs)
caseBlockStats.add(gen.caseStatement(gen.intLiteral(baseSpec.getCallinId(aTeam)))); // case bseSpecCallinId:
int nRoleArgs = mapping.getRoleMethod().getSourceParamLength();
TypeBinding[] roleParams = mapping.getRoleMethod().getSourceParameters();
for (int i=0; i<roleParams.length; i++)
if (roleParams[i].isRole() && TeamModel.isTeamContainingRole(teamDecl.binding, (ReferenceBinding) roleParams[i]))
roleParams[i] = TeamModel.strengthenRoleType(teamDecl.binding, roleParams[i]);
List<Statement> repackingStats = new ArrayList<Statement>();
if (mapping.positions != null) {
int[] poss = mapping.positions;
nLabels = caseBlockStats.size();
int argOffset = 0; // some methods have their real arguments at an offset
MethodSpec baseSpec = mapping.baseMethodSpecs[0]; // TODO(SH): check all base methods??
if (baseSpec.isCallin())
argOffset += 6;
if (baseSpec.isStatic() && baseSpec.getDeclaringClass().isRole())
argOffset += 2;
for (int i=0; i<poss.length; i++)
// arguments[basepos] = baseCallArguments[i]
if (poss[i] > 0) {
// FIXME(SH): this is cheating: should obtain translation info from actual
// parameter mapping (see cast in test432_expressionInReplaceParameterMapping11)
TypeBinding roleSideParameter = roleParams[i];
// FIXME(SH): per basemethod:
TypeBinding baseSideParameter = mapping.baseMethodSpecs[0].resolvedParameters()[poss[i]-1];
Expression roleSideArgument = gen.arrayReference(gen.singleNameReference(BASE_CALL_ARGS), i);// ... baseCallArguments[i] ...
if (TypeBinding.notEquals(roleSideParameter, baseSideParameter))
roleSideArgument = gen.resolvedCastExpression(roleSideArgument, roleSideParameter, CastExpression.RAW);
TypeBinding roleSideLeaf = roleSideParameter.leafComponentType();
TypeBinding baseSideLeaf = baseSideParameter.leafComponentType();
if ( roleSideLeaf.isRole()
&& ((ReferenceBinding)roleSideLeaf).baseclass().isCompatibleWith(baseSideLeaf))
roleSideArgument = new PotentialLowerExpression(roleSideArgument, baseSideParameter, gen.thisReference());
repackingStats.add(gen.assignment(gen.arrayReference(gen.singleNameReference(ARGUMENTS), // arguments[p] = baseCallArguments[i];
poss[i]-1+argOffset), // 0 represents result
roleSideArgument));
}
} else if (nRoleArgs > 0) {
for (int i=0; i<nRoleArgs; i++) {
Expression basecallArg = gen.arrayReference(gen.singleNameReference(BASE_CALL_ARGS), i);
if (mapping.baseMethodSpecs[0].argNeedsTranslation(i)) { // FIXME(SH): per basemethod!
basecallArg = new PotentialLowerExpression(gen.castExpression(basecallArg,
gen.typeReference(roleParams[i]),
CastExpression.RAW),
mapping.baseMethodSpecs[0].resolvedParameters()[i], // FIXME(SH): per basemethod!
gen.qualifiedThisReference(teamDecl.binding));
}
repackingStats.add(gen.assignment(gen.arrayReference(gen.singleNameReference(ARGUMENTS), i), // arguments[i] = lower?(baseCallArguments[i])
basecallArg));
}
}
caseBlockStats.add(gen.ifStatement(gen.nullCheck(gen.singleNameReference(BASE_CALL_ARGS)), // if (baseCallArgs == null) {} { arguments[i] = ...; ... }
gen.emptyStatement(),
gen.block(repackingStats.toArray(new Statement[repackingStats.size()]))));
Expression result = genSuperCallNext(gen, teamDecl.binding, superArgs); // return cast+lift?(super._OT$callNext(..));
if (mapping.baseMethodSpecs[0].returnNeedsTranslation) { // FIXME(SH): per basemethod!
// lifting:
TypeBinding[]/*role,base*/ returnTypes = getReturnTypes(mapping, 0);
// who is responsible for lifting: the team or the current role?
ReferenceBinding currentRole = mapping.scope.enclosingReceiverType();
Expression liftReceiver = (isRoleOfCurrentRole(currentRole, returnTypes[0]))
? Lifting.liftCall(mapping.scope,
gen.thisReference(),
gen.castExpression(gen.singleNameReference(IOTConstants.BASE), gen.typeReference(currentRole.baseclass()), CastExpression.RAW),
currentRole.baseclass(), currentRole, false)
// TODO: might want to extend the signature of callNext to pass the current role to avoid this lifting?
: genTeamThis(gen, returnTypes[0]);
result = Lifting.liftCall(mapping.scope,
liftReceiver,
gen.castExpression(result,
gen.baseclassReference(returnTypes[1]),
CastExpression.RAW),
returnTypes[1],
returnTypes[0],
false,
gen);
}
caseBlockStats.add(gen.returnStatement(result));
if (caseBlockStats.size() > nLabels) { // any action added ?
swStatements.addAll(caseBlockStats);
}
} // } // end-switch
if (swStatements.size() == 0)
return; // don't add useless method
swStat.statements = swStatements.toArray(new Statement[swStatements.size()]);
decl.statements = new Statement[] {
swStat,
gen.returnStatement(genSuperCallNext(gen, teamDecl.binding, superArgs)) // delegate with unchanged arguments/return
};
decl.hasParsedStatements = true;
AstEdit.addMethod(teamDecl, decl);
}
private Expression genSuperCallNext(AstGenerator gen, SourceTypeBinding binding, Expression[] superArgs) {
if (binding.superclass.isTeam())
return gen.messageSend(gen.superReference(), OT_CALL_NEXT, superArgs);
// no super-*team* so call the static variant:
return gen.messageSend(gen.qualifiedNameReference(IOTConstants.ORG_OBJECTTEAMS_TEAM), OT_TERMINAL_CALL_NEXT, superArgs);
}
Reference genTeamThis(AstGenerator gen, TypeBinding type) {
TypeBinding leaf = type.leafComponentType();
if (leaf instanceof ReferenceBinding) {
ReferenceBinding teamBinding = ((ReferenceBinding) leaf).enclosingType();
if (teamBinding != null)
return gen.qualifiedThisReference(teamBinding);
}
return gen.thisReference();
}
TypeBinding[] getReturnTypes(CallinMappingDeclaration mapping, int i) {
TypeBinding baseReturn = mapping.baseMethodSpecs[i].resolvedType();
TypeBinding roleReturn = mapping.roleMethodSpec.resolvedType();
if (roleReturn.isTypeVariable())
roleReturn = ((TypeVariableBinding)roleReturn).firstBound;
return new TypeBinding[]{roleReturn, baseReturn};
}
/** Convert custom exceptions into SneakyException as to bypass checking by the compiler. */
TryStatement protectRoleMethodCall(Statement[] statements, MethodBinding roleMethod, Statement finallyStat, AstGenerator gen) {
Argument catchArg = gen.argument(CATCH_ARG, gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_EXCEPTION));
Statement[] catchStat = new Statement[] {
gen.throwStatement(gen.allocation(
gen.qualifiedTypeReference(IOTConstants.SNEAKY_EXCEPTION),
new Expression[] { gen.singleNameReference(CATCH_ARG) }
))
};
return gen.tryStatement(
statements,
new Argument[] {catchArg},
new Statement[][] {catchStat},
new Statement[] {finallyStat});
}
private void generateCallOrigStatic(List<CallinMappingDeclaration> callinDecls, TeamModel aTeam) {
// public Object _OT$callOrigStatic(int callinId, int boundMethodId, Object[] args)
// this team method delegates to the corresponding _OT$callOrigStatic(int,Object[])
// of the appropriate base classes.
final TypeDeclaration teamDecl = aTeam.getAst();
if (teamDecl == null) return;
int idxOfExisting = -1;
AbstractMethodDeclaration[] teamMethods = teamDecl.methods;
if (teamMethods != null) {
for (int i = 0; i < teamMethods.length; i++) {
if (CharOperation.equals(OT_CALL_ORIG_STATIC, teamMethods[i].selector) && teamMethods[i] instanceof TeamMethodGenerator.CopiedTeamMethod) {
idxOfExisting = i;
break;
}
}
}
final AstGenerator gen = new AstGenerator(teamDecl);
Argument[] args = new Argument[] {
gen.argument(CALLIN_ID, gen.typeReference(TypeBinding.INT)),
gen.argument(BOUND_METHOD_ID, gen.typeReference(TypeBinding.INT)),
gen.argument(ARGUMENTS, gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1))
};
Expression[] passThroughArgs = new Expression[] {
gen.singleNameReference(BOUND_METHOD_ID),
gen.singleNameReference(ARGUMENTS)
};
MethodDeclaration decl = gen.method(teamDecl.compilationResult,
AccPublic,
gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT),
OT_CALL_ORIG_STATIC,
args);
SwitchStatement swStat = new SwitchStatement();
swStat.expression = gen.singleNameReference(CALLIN_ID); // switch(callinId) { ...
List<Statement> swStatements = new ArrayList<Statement>();
for (CallinMappingDeclaration mapping : callinDecls) {
for (MethodSpec baseSpec : mapping.baseMethodSpecs) {
MethodBinding baseMethod = baseSpec.resolvedMethod;
if (baseMethod.isStatic()) {
ReferenceBinding baseClass = mapping.scope.enclosingReceiverType().baseclass();
swStatements.add(gen.caseStatement(gen.intLiteral(baseSpec.getCallinId(aTeam)))); // case baseSpecCallinId:
Expression result = gen.fakeMessageSend(gen.baseTypeReference(baseClass), // return BaseClass._OT$callOrigStatic(boundMethodId, args);
OT_CALL_ORIG_STATIC,
passThroughArgs,
baseMethod.declaringClass,
mapping.scope.getJavaLangObject());
swStatements.add(gen.returnStatement(result));
}
}
} // } // end-switch
if (swStatements.size() == 0)
return; // don't add useless method
swStat.statements = swStatements.toArray(new Statement[swStatements.size()]);
decl.statements = new Statement[] {
swStat,
gen.returnStatement(gen.nullLiteral()) // shouldn't happen
};
decl.hasParsedStatements = true;
if (teamMethods != null && idxOfExisting > -1) {
teamMethods[idxOfExisting] = decl; // directly replace
teamDecl.binding.resolveGeneratedMethod(decl, false, null);
} else {
AstEdit.addMethod(teamDecl, decl);
}
}
boolean checkLiftingProblem(TypeDeclaration teamDecl, CallinMappingDeclaration callinDecl, ReferenceBinding roleType) {
int iProblem = teamDecl.getTeamModel().canLiftingFail(roleType);
if (iProblem != 0) {
callinDecl.addRoleLiftingProblem(roleType, iProblem);
return true;
}
return false;
}
boolean isRoleOfCurrentRole(ReferenceBinding currentRole, TypeBinding type) {
TypeBinding leafType = type.leafComponentType();
if (leafType.isRole()) {
return currentRole.erasure().isCompatibleWith(leafType.enclosingType().erasure());
}
return false;
}
}