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