org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CallinImplementorDyn.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CallinImplementorDyn.java

Source

/** 
 * 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.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$
    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;
        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;
        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;
    }

}