| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2003, 2006 Fraunhofer Gesellschaft, Munich, Germany, |
| * for its Fraunhofer Institute for Computer Architecture and Software |
| * Technology (FIRST), Berlin, Germany and Technical University Berlin, |
| * Germany. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * $Id: Lifting.java 23416 2010-02-03 19:59:31Z stephan $ |
| * |
| * Please visit http://www.eclipse.org/objectteams for updates and contact. |
| * |
| * Contributors: |
| * Fraunhofer FIRST - Initial API and implementation |
| * Technical University Berlin - Initial API and implementation |
| **********************************************************************/ |
| package org.eclipse.objectteams.otdt.internal.core.compiler.lifting; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.compiler.IProblem; |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.codegen.BranchLabel; |
| import org.eclipse.jdt.internal.compiler.codegen.CodeStream; |
| import org.eclipse.jdt.internal.compiler.codegen.Opcodes; |
| import org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.Scope; |
| import org.eclipse.jdt.internal.compiler.lookup.TagBits; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeIds; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.ast.RoleInitializationMethod; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.WeakenedTypeBinding; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CallinImplementorDyn; |
| 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.SwitchOnBaseTypeGenerator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstConverter; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstEdit; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer; |
| |
| |
| /** |
| * This class generates the AST for liftTo methods and constructors. |
| * Also declared lifting arguments are translated here. |
| * |
| * Special cases: |
| * Declared lifting in team constructors requires several hacks: |
| * A Lifting can only happen _after_ the self-call (it needs access to the team instance!)! |
| * This is bad, because a self-call cannot access the lifted roles. |
| * B If the self call should pass the lifted role, an additional ctor has to be created: |
| * - a copy of the original super ctor which does the lifting in its body |
| * C If a constructor has role types, it must expect to be called via self-call |
| * with base objects instead. For this case two more tricks are needed: |
| * 1. the super call in this constructor is extracted to a new constructor of this class. |
| * 2. local variables for role arguments are created just like for declared lifting, |
| * however, instead of lifting these variables are initialized without translation. |
| * A specific sequence of "6 nops, load, 3 nops, store" leaves room for inserting the |
| * lifting translation during byte-code copy. |
| * Before patching (produced by LocalDeclaration.generateCode()) |
| * nop, nop, nop, nop, nop, nop, <push base value>, nop, nop, nop, store_n |
| * After patching (done by BytecodeTransformer.adjustCode() w/ helper funcs): |
| * aload_0 |
| * invokevirtual _OT$initCaches()Z |
| * pop // the boolean return from initcaches |
| * aload_0 |
| * <push base value> // an aload_x |
| * invokevirtual _OT$liftTo$<MyRole>(<MyBase>)<MyRole> |
| * store_n |
| * With this preparation, a sub team may create a copy of this constructor which |
| * indeed performs the lifting in its body. |
| * As self-call it calls chaining ctor from step 1. |
| * D All generated ctors are distinguished by a regular marker arg. |
| * |
| * This is, how different classes contribute to this behavior: |
| * -> Dependencies.establishTypesAdjusted |
| * -> Lifting.prepareArgLifting |
| * - change the self-call to use the chaining version, to be created later |
| * - create local variables: |
| * dummy (reserve a slot for marker arg to be added later, set LocalDeclaration.isPlaceHolder) |
| * one variable for each role type argument (set LocalDeclaration.isPreparingForLifting) |
| * -> ExplicitConstructorCall.resolve |
| * -> Lifting.createChainingCtor (containing nothing but a super call) |
| * -> MethodModel.createLiftingCopy (need to store original signature with roles) |
| * |
| * -> LocalDeclaration.generateCode |
| * pretend dummy var is used (isPlaceholder) |
| * generate nops to reserve space for lifting call (isPreparingForLifting) |
| * |
| * -> BytecodeTransformer |
| * replace nops with lifting |
| * |
| * |
| * @author brcan, haebor, mac, stephan |
| * @version $Id: Lifting.java 23416 2010-02-03 19:59:31Z stephan $ |
| */ |
| public class Lifting extends SwitchOnBaseTypeGenerator |
| implements TypeIds, ClassFileConstants, ExtraCompilerModifiers |
| { |
| |
| public static enum InstantiationPolicy { NEVER, ONDEMAND, SINGLETON, ALWAYS, ERROR; |
| public boolean isAlways() { return this == ALWAYS; } |
| public boolean isOndemand() { return this == ONDEMAND; } |
| } |
| |
| private RoleModel _boundRootRoleModel = null; |
| private AstGenerator _gen = null; |
| private long _sourceLevel; |
| public char[] variableName = MY_ROLE; |
| |
| // ==== GENERAL API: names of liftTo methods: ==== |
| |
| public static boolean isLiftToMethod (MethodBinding method) { |
| return CharOperation.prefixEquals(IOTConstants._OT_LIFT_TO, method.selector) |
| || CharOperation.prefixEquals(DeclaredLifting.OT_LIFT_DYNAMIC, method.selector); |
| } |
| public static boolean isLiftToMethodCall (Expression expr) { |
| if (!(expr instanceof MessageSend)) |
| return false; |
| char[] selector = ((MessageSend)expr).selector; |
| return CharOperation.prefixEquals(IOTConstants._OT_LIFT_TO, selector) |
| || CharOperation.prefixEquals(DeclaredLifting.OT_LIFT_DYNAMIC, selector); |
| } |
| |
| public static char[] getLiftMethodName(TypeBinding roleType) { |
| // TODO (SH) if roleType is not bound search for bound child role. |
| assert( !roleType.isBaseType()); |
| if (roleType.isArrayType()) |
| roleType = roleType.leafComponentType(); |
| return getLiftMethodName(roleType.sourceName()); |
| } |
| |
| public static char[] getLiftMethodName(char[] roleName) { |
| return CharOperation.concat(IOTConstants._OT_LIFT_TO, roleName); |
| } |
| |
| // ==== MORE API: ==== |
| /** |
| * API: |
| * Generate the call that lifts a base object to its role. |
| * |
| * @param scope used for lookup during AST generation. |
| * @param teamExpr |
| * @param unliftedExpr |
| * @param providedType |
| * @param expectedRole |
| * @param needLowering when lifting a callin param we also need to generate the lowering operation |
| * for the base call |
| * @return teamExpr.liftTo<expectedRole>?(unliftedExpr) |
| */ |
| public static MessageSend liftCall( |
| BlockScope scope, |
| Expression teamExpr, |
| Expression unliftedExpr, |
| TypeBinding providedType, |
| TypeBinding expectedRole, |
| boolean needLowering) |
| { |
| AstGenerator gen= new AstGenerator(unliftedExpr.sourceStart, unliftedExpr.sourceEnd); |
| return liftCall(scope, teamExpr, unliftedExpr, providedType, expectedRole, needLowering, gen); |
| } |
| public static MessageSend liftCall( |
| BlockScope scope, |
| Expression teamExpr, |
| Expression unliftedExpr, |
| TypeBinding providedType, |
| TypeBinding expectedRole, |
| boolean needLowering, |
| AstGenerator gen) |
| { |
| if (providedType.isArrayType()) |
| { |
| // trigger creation of array lifter |
| if (needLowering) { |
| Expression dumExpr= new SingleNameReference("dummy".toCharArray(), 0L); //$NON-NLS-1$ |
| new ArrayLowering(teamExpr).ensureTransformMethod(scope, dumExpr, expectedRole, providedType, false); |
| } |
| |
| ArrayLifting arrayLifting = new ArrayLifting(); |
| return arrayLifting.liftArray(scope, teamExpr, unliftedExpr, providedType, expectedRole); |
| } |
| // TODO (SH): record necessity of array lifting if binding.isArrayType() |
| MessageSend send = gen.messageSend( |
| teamExpr, |
| getLiftMethodName(expectedRole), |
| new Expression[] { unliftedExpr }); |
| send.constant = Constant.NotAConstant; |
| return send; |
| } |
| |
| /* |
| public MyRole(MyBase base) |
| { |
| _OT$base = base; |
| _OT$cache_OT$RootRole.put(_OT$base, this); |
| } |
| or (with bound parent): |
| public MyRole(MyBase base) |
| { |
| super(base); |
| } |
| */ |
| public ConstructorDeclaration createLiftToConstructorDeclaration( |
| TreeNode roleNode, |
| boolean needMethodBodies) |
| { |
| TreeNode instantiableBoundRootRoleNode = roleNode.getTopmostBoundParent(false); |
| if (instantiableBoundRootRoleNode == null) return null; |
| |
| TypeDeclaration roleDecl = roleNode.getTreeObject().getAst(); |
| if (instantiableBoundRootRoleNode == TreeNode.ProblemNode) { |
| roleDecl.scope.problemReporter(). |
| overlappingRoleHierarchies(roleDecl, TreeNode.ProblemNode.toString()); |
| return null; |
| } |
| TypeDeclaration roleType = roleNode.getTreeObject().getAst(); |
| ConstructorDeclaration existingConstructor = findLiftToConstructor(roleType); |
| if ( existingConstructor == null |
| && !roleNode.hasBoundParent(false) |
| && !roleNode.getTreeObject().isIgnoreFurtherInvestigation()) |
| { |
| ReferenceBinding parent = roleNode.getTreeObject().getBinding().superclass(); |
| MethodBinding defCtor = parent.getExactConstructor(Binding.NO_PARAMETERS); |
| boolean hasEmptyCtor = (defCtor != null) |
| ? defCtor.isValidBinding() |
| : (parent.getMethods(TypeConstants.INIT) == Binding.NO_METHODS); |
| if (!hasEmptyCtor) { |
| roleDecl.scope.problemReporter(). |
| missingEmptyCtorForLiftingCtor(roleDecl, parent); |
| return null; |
| } |
| } |
| |
| // for determining the cache interfaces are allowed: |
| this._boundRootRoleModel = roleNode.getTopmostBoundParent(true).getTreeObject(); |
| if (this._boundRootRoleModel != null) |
| roleNode.getTreeObject()._boundRootRole = this._boundRootRoleModel; |
| |
| TypeDeclaration teamTypeDeclaration = roleType.enclosingType; |
| ReferenceBinding baseClassBinding = roleType.binding.baseclass(); |
| |
| if (baseClassBinding == null && |
| ((roleType.binding.tagBits & TagBits.HierarchyHasProblems) != 0)) |
| return null; // assume base class could not be resolved. |
| |
| AstGenerator gen = existingConstructor == null |
| ? new AstGenerator(roleType) |
| : new AstGenerator(existingConstructor); |
| // public MyRole(MyBase base) |
| ConstructorDeclaration generatedConstructor = |
| gen.constructor(teamTypeDeclaration.compilationResult, |
| ClassFileConstants.AccPublic, |
| roleType.name, |
| new Argument[] { // (MyBase base) |
| gen.argument( |
| BASE, // name |
| gen.baseclassReference(baseClassBinding)) // type |
| } |
| ); |
| |
| |
| // default arg name is base. |
| char[] baseArgName = BASE; |
| if (existingConstructor != null) { |
| if (existingConstructor.isCopied) { |
| if (roleNode.getTreeObject().getImplicitSuperRole().isBound()) // parent must be class. |
| return null; // copied constructor has everything we need. |
| } |
| // use the argument name of the existing constructor |
| baseArgName = existingConstructor.arguments[0].name; |
| } |
| |
| if (needMethodBodies) { |
| if (instantiableBoundRootRoleNode == roleNode) |
| genLiftToConstructorStatements( |
| baseClassBinding, |
| roleType, |
| generatedConstructor, |
| baseArgName, |
| gen); |
| else |
| genLiftToConstructorSuperCall( |
| baseClassBinding, |
| roleType, |
| generatedConstructor, |
| baseArgName, |
| gen); |
| } |
| |
| this._boundRootRoleModel = null; |
| |
| if (existingConstructor != null) { |
| //if constructor exists with same signature, merge statements. |
| if (needMethodBodies) { |
| // Also see Parser.parse(ConstructorDeclaration) for merging and |
| // for checking illegal explicit super call. |
| if ( existingConstructor.statements != null) { |
| int len1 = generatedConstructor.statements.length; |
| int len2 = existingConstructor.statements.length; |
| Statement[] newStatements = new Statement[len1+len2]; |
| System.arraycopy( |
| generatedConstructor.statements, 0, |
| newStatements, 0, |
| len1); |
| System.arraycopy( |
| existingConstructor.statements, 0, |
| newStatements, len1, |
| len2); |
| existingConstructor.setStatements(newStatements); |
| } else { |
| existingConstructor.setStatements(generatedConstructor.statements); |
| } |
| // Keep arguments. |
| // If constructorCall is explicit keep it. |
| if ( existingConstructor.constructorCall == null |
| || existingConstructor.constructorCall.isImplicitSuper()) |
| existingConstructor.constructorCall = generatedConstructor.constructorCall; |
| } |
| return existingConstructor; |
| } else { |
| //otherhwise insert new constructor |
| AstEdit.addMethod(roleType, generatedConstructor); |
| |
| return generatedConstructor; |
| } |
| } |
| |
| private static ConstructorDeclaration findLiftToConstructor( |
| TypeDeclaration roleType) |
| { |
| if (roleType.methods == null) |
| return null; |
| // force bindings for constructors: |
| roleType.binding.getMethods(TypeConstants.INIT); |
| for (int i = 0; i < roleType.methods.length; i++) { |
| AbstractMethodDeclaration method = roleType.methods[i]; |
| |
| if (isLiftToConstructor(method, roleType.binding)) |
| return (ConstructorDeclaration) method; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @param baseClassBinding |
| * @param liftToConstructorDeclaration generated constructor |
| * @param baseArgName name of the base argument, either generated or from source |
| * @param gen for generating AST nodes |
| */ |
| private static void genLiftToConstructorSuperCall( |
| ReferenceBinding baseClassBinding, |
| TypeDeclaration roleType, |
| ConstructorDeclaration liftToConstructorDeclaration, |
| char[] baseArgName, |
| AstGenerator gen) |
| { |
| liftToConstructorDeclaration.constructorCall = |
| gen.explicitConstructorCall(ExplicitConstructorCall.Super); |
| // mode maybe refined in ExplicitConstructorCall.resolve()->updateFromTSuper() |
| liftToConstructorDeclaration.constructorCall.arguments = |
| new Expression[] { |
| gen.singleNameReference(baseArgName) |
| }; |
| // start with an empty statements list, except for this._OT$InitFields(); |
| liftToConstructorDeclaration.setStatements(new Statement[] { |
| RoleInitializationMethod.genInvokeInitMethod( |
| gen.thisReference(), |
| roleType.binding, |
| gen) |
| }); |
| } |
| |
| private void genLiftToConstructorStatements( |
| ReferenceBinding baseClassBinding, |
| TypeDeclaration roleType, |
| ConstructorDeclaration liftToConstructorDeclaration, |
| char[] baseArgName, |
| AstGenerator gen) |
| { |
| liftToConstructorDeclaration.constructorCall = |
| gen.explicitConstructorCall(ExplicitConstructorCall.Super); |
| boolean useRoleCache = RoleModel.getInstantiationPolicy(roleType.binding).isOndemand(); |
| Statement[] statements = new Statement[useRoleCache ? 4 : 2]; |
| // _OT$base = <baseArgName>; |
| SingleNameReference lhs = gen.singleNameReference(_OT_BASE); |
| statements[0] = gen.assignment(lhs, gen.singleNameReference(baseArgName)); |
| |
| int idx = 1; |
| if (useRoleCache) { |
| Statement[] regStats = genRoleRegistrationStatements( |
| this._boundRootRoleModel.getAst().scope, |
| this._boundRootRoleModel, |
| baseClassBinding, |
| liftToConstructorDeclaration, |
| gen); |
| System.arraycopy(regStats, 0, statements, idx, regStats.length); |
| idx += 2; |
| } |
| |
| // after initializing _OT$base and storing in the cache we are ready to execute field initializers: |
| |
| // this._OT$InitFields(); |
| statements[idx] = RoleInitializationMethod.genInvokeInitMethod( |
| gen.thisReference(), |
| roleType.binding, |
| gen); |
| liftToConstructorDeclaration.setStatements(statements); |
| } |
| |
| /** |
| * @param baseClassBinding |
| * @param liftToConstructorDeclaration |
| * @param gen |
| */ |
| public static Statement[] genRoleRegistrationStatements( |
| Scope scope, |
| RoleModel boundRootRoleModel, |
| ReferenceBinding baseClassBinding, |
| ConstructorDeclaration liftToConstructorDeclaration, |
| AstGenerator gen) |
| { |
| Statement[] statements = new Statement[2]; |
| |
| |
| // _OT$cache_OT$RootRole.put(<baseArgName>, this); |
| Expression _this = gen.thisReference(); |
| if (scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) |
| { |
| _this = gen.castExpression( |
| _this, gen.typeReference(boundRootRoleModel.getBinding()), CastExpression.DO_WRAP); |
| } |
| statements[0] = |
| gen.messageSend( |
| gen.singleNameReference( |
| LiftingEnvironment.getCacheName(boundRootRoleModel)), |
| PUT, |
| new Expression[] { |
| gen.baseNameReference(_OT_BASE), |
| _this |
| }); |
| |
| // ((IBoundBase)_OT$base).addRole(this); // prevent premature garbage collection |
| Expression roleExpression = gen.thisReference(); |
| if (TypeAnalyzer.isConfined(boundRootRoleModel.getBinding())) { |
| // pretend this were compatible to Object: |
| roleExpression.resolvedType = scope.getJavaLangObject(); |
| roleExpression.constant = Constant.NotAConstant; |
| } else { |
| // here an (unnecessary) cast to j.l.Object prevents a warning re OTJLD 2.2(f): |
| roleExpression = gen.castExpression( |
| roleExpression, |
| gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT), |
| CastExpression.RAW); |
| } |
| statements[1] = |
| // OTDYN: Slightly different methods depending on the weaving strategy: |
| CallinImplementorDyn.DYNAMIC_WEAVING |
| ? gen.messageSend( |
| gen.castExpression( |
| gen.singleNameReference(_OT_BASE), |
| gen.qualifiedTypeReference(ORG_OBJECTTEAMS_IBOUNDBASE), |
| CastExpression.RAW), |
| ADD_REMOVE_ROLE, |
| new Expression[] { |
| roleExpression, |
| gen.booleanLiteral(true)}) // isAdding |
| : gen.messageSend( |
| gen.castExpression( |
| gen.singleNameReference(_OT_BASE), |
| gen.qualifiedTypeReference(ORG_OBJECTTEAMS_IBOUNDBASE), |
| CastExpression.RAW), |
| ADD_ROLE, |
| new Expression[] {roleExpression}); |
| return statements; |
| } |
| |
| public static boolean isLiftingCtor(MethodBinding binding) { |
| if (!binding.isConstructor()) |
| return false; |
| if (binding.parameters.length != 1) |
| return false; |
| return binding.parameters[0] == binding.declaringClass.baseclass(); |
| } |
| |
| public static boolean isLiftToConstructor( |
| AbstractMethodDeclaration method, |
| ReferenceBinding role) |
| { |
| if (!method.isConstructor()) |
| return false; |
| if (method.ignoreFurtherInvestigation) |
| return false; |
| if ( method.arguments == null |
| || (method.arguments.length != 1)) |
| return false; |
| if (method.binding == null) { |
| assert method.scope.referenceType().ignoreFurtherInvestigation : "binding should only be missing in a problem type"; //$NON-NLS-1$ |
| return false; |
| } |
| TypeBinding param = method.binding.parameters[0]; |
| if (param.isBaseType()) |
| return false; |
| if (param.isArrayType()) |
| return false; |
| if (((ReferenceBinding)param).isRole()) |
| param = TeamModel.strengthenRoleType(role, param); |
| ReferenceBinding paramClass = ((ReferenceBinding)param).getRealClass(); |
| // implicitly refined base may be weakened, apply the weak type: |
| ReferenceBinding baseclass = WeakenedTypeBinding.getBytecodeType(role.baseclass()); |
| return RoleTypeBinding.type_eq(paramClass, baseclass); |
| } |
| |
| public static boolean isLiftToConstructor( |
| MethodBinding method, |
| ReferenceBinding role) |
| { |
| if (method == null) |
| return false; |
| if (!method.isConstructor()) |
| return false; |
| if (!method.isValidBinding()) |
| return false; |
| if ( method.parameters == null |
| || (method.parameters.length != 1)) |
| return false; |
| TypeBinding param = method.parameters[0]; |
| if (param.isBaseType()) |
| return false; |
| if (param.isArrayType()) |
| return false; |
| ReferenceBinding paramClass = ((ReferenceBinding)param).getRealClass(); |
| ReferenceBinding baseclass = role.baseclass(); |
| if (baseclass == null) |
| return false; |
| return RoleTypeBinding.type_eq(paramClass, baseclass.getRealClass()); |
| } |
| |
| /* |
| MyRole _OT$liftToMyRole(MyBase base) |
| { |
| synchronized (OT_$cache_OT$RootRole) { |
| MyRole myRole = null; |
| |
| if(base == null) |
| { |
| return null; |
| } |
| // for base-anchored bases (playedBy BaseRole<@base>): |
| if ((base._OT$getTeam() != _OT$base)) |
| { |
| throw new org.objectteams.LiftingVetoException(this, (java.lang.Object) base); |
| } |
| // |
| if(!_OT$cache_OT$RootRole.containsKey(base)) |
| { |
| if (base instanceof MySubBaseA) |
| myRole = new __OT__MySubRoleA((MySubBaseA)base); |
| else if (base instanceof MySubBaseB) |
| myRole = new __OT__MySubRoleB((MySubBaseB)base); |
| else |
| throw new LiftingFailedException((Object)base, "MyRole"); |
| } |
| else |
| { |
| RootRole role = _OT$cache_OT$RootRole.get(base); |
| |
| try |
| { |
| myRole = (MyRole)role; |
| } |
| catch(ClassCastException classcastexception) |
| { |
| throw new WrongRoleException(MyTeam$MyRole.class, base, role); |
| } |
| } |
| return myRole; |
| } |
| } |
| */ |
| |
| public static boolean isUnsafeLiftCall(TypeBinding exceptionType, ASTNode location) { |
| if (!(exceptionType instanceof ReferenceBinding)) // oops? |
| return false; |
| if (!CharOperation.equals(((ReferenceBinding)exceptionType).compoundName, IOTConstants.O_O_LIFTING_FAILED_EXCEPTION)) |
| return false; |
| if (!(location instanceof MessageSend)) |
| return false; |
| return isLiftToMethod(((MessageSend)location).binding); |
| } |
| |
| /** |
| * Create a liftTo method an add it to the team, including creation of bindings. |
| * |
| * @param teamTypeDeclaration type to add the lift method to. |
| * @param roleNode the Role for which the LiftTo Method is to be created |
| * @param caseObjects all informations for creating case statements,. |
| * i.e., the role classes to which lifting could occur. |
| */ |
| public void createLiftToMethod ( |
| TypeDeclaration teamTypeDeclaration, TreeNode roleNode, final RoleModel[] caseObjects) |
| { |
| |
| TreeNode boundRootRoleNode = roleNode.getTopmostBoundParent(true); |
| if (boundRootRoleNode == null) return; |
| |
| final RoleModel roleModel = roleNode.getTreeObject(); |
| |
| TypeDeclaration typeDecl = roleModel.getAst(); // only for positions |
| if (typeDecl == null) |
| typeDecl = teamTypeDeclaration; |
| |
| if (boundRootRoleNode == TreeNode.ProblemNode) { |
| typeDecl.scope.problemReporter(). |
| overlappingRoleHierarchies(typeDecl, TreeNode.ProblemNode.toString()); |
| return; |
| } |
| |
| this._boundRootRoleModel = boundRootRoleNode.getTreeObject(); |
| this._gen = new AstGenerator(typeDecl.sourceStart, typeDecl.sourceEnd); |
| this._sourceLevel= typeDecl.scope.compilerOptions().sourceLevel; |
| |
| try { |
| final ReferenceBinding teamBinding = teamTypeDeclaration.binding; |
| final ReferenceBinding roleClassBinding = roleModel.getBinding(); |
| final ReferenceBinding baseClassBinding = roleClassBinding.baseclass(); |
| |
| if (RoleModel.hasTagBit(roleClassBinding, RoleModel.HasLiftingProblem)) { |
| this._boundRootRoleModel = null; |
| this._gen = null; |
| return; // lift method won't work |
| } |
| |
| char[] methodName = getLiftMethodName(roleClassBinding.sourceName()); |
| Argument[] arguments = new Argument[] { |
| this._gen.argument(BASE, this._gen.baseclassReference(baseClassBinding)) |
| }; |
| |
| MethodDeclaration liftToMethodDeclaration = |
| AstConverter.findAndAdjustCopiedMethod(teamTypeDeclaration, methodName, arguments); |
| |
| boolean needToAdd = false; |
| if (liftToMethodDeclaration == null) { |
| liftToMethodDeclaration = createLiftToMethodDeclaration( |
| teamTypeDeclaration, |
| roleClassBinding, |
| methodName, |
| arguments, |
| baseClassBinding); |
| this._gen.maybeAddTypeParametersToMethod(baseClassBinding, liftToMethodDeclaration); |
| needToAdd = true; |
| } |
| |
| final int problemId = teamBinding.getTeamModel().canLiftingFail(roleClassBinding); |
| if ( caseObjects.length == 0 |
| && teamBinding.isAbstract()) |
| { |
| liftToMethodDeclaration.modifiers |= AccAbstract|AccSemicolonBody; |
| if (liftToMethodDeclaration.binding != null) |
| liftToMethodDeclaration.binding.modifiers |= AccAbstract; |
| } else { |
| final MethodDeclaration newMethod = liftToMethodDeclaration; |
| final AstGenerator gen = this._gen; |
| final RoleModel boundRootRole = this._boundRootRoleModel; |
| MethodModel.getModel(newMethod).setStatementsGenerator(new AbstractStatementsGenerator() { |
| @SuppressWarnings("synthetic-access") |
| public boolean generateStatements(AbstractMethodDeclaration methodDecl) { |
| try { |
| Lifting.this._gen = gen; |
| Lifting.this._boundRootRoleModel = boundRootRole; |
| return createLiftToMethodStatements( |
| newMethod, |
| teamBinding, |
| roleModel, |
| baseClassBinding, |
| caseObjects, |
| problemId); |
| } finally { |
| Lifting.this._gen = null; |
| Lifting.this._boundRootRoleModel = null; |
| } |
| } |
| }); |
| } |
| |
| if (needToAdd) { |
| if (problemId != 0) { |
| liftToMethodDeclaration.thrownExceptions = new TypeReference[] { |
| this._gen.qualifiedTypeReference(O_O_LIFTING_FAILED_EXCEPTION) |
| }; |
| } |
| if (teamTypeDeclaration.isRole()) { |
| TypeDeclaration interfaceAst = teamTypeDeclaration.getRoleModel().getInterfaceAst(); |
| if (interfaceAst != null) { |
| MethodDeclaration ifcMethod = AstConverter.genRoleIfcMethod(interfaceAst, liftToMethodDeclaration); |
| AstEdit.addMethod(interfaceAst, ifcMethod); |
| liftToMethodDeclaration.modifiers = liftToMethodDeclaration.modifiers |
| & ~AccProtected |
| | AccPublic; |
| } |
| } |
| AstEdit.addMethod(teamTypeDeclaration, liftToMethodDeclaration); |
| } |
| |
| } finally { |
| this._boundRootRoleModel = null; |
| this._gen = null; |
| } |
| } |
| |
| private MethodDeclaration createLiftToMethodDeclaration( |
| TypeDeclaration teamDecl, |
| ReferenceBinding returnType, |
| char[] methodName, |
| Argument[] arguments, |
| ReferenceBinding baseType) |
| { |
| return this._gen.method(teamDecl.compilationResult, |
| /*modifiers*/ returnType.modifiers & (AccPublic|AccProtected|AccSynchronized), |
| /*return type*/ createRoleTypeReference(returnType, false/*classPart*/), |
| /*selector*/ methodName, |
| /*arguments*/ arguments |
| ); |
| } |
| |
| // type reference may need to be parameterized if role type has type variables |
| private TypeReference createRoleTypeReference(ReferenceBinding roleType, boolean useClassPart) { |
| TypeVariableBinding[] typeVariables = roleType.typeVariables(); |
| if (typeVariables == Binding.NO_TYPE_VARIABLES) |
| return this._gen.singleTypeReference(useClassPart ? roleType.sourceName : roleType.sourceName()); |
| TypeReference[] typeParameters = new TypeReference[typeVariables.length]; |
| for (int i=0; i<typeVariables.length; i++) |
| typeParameters[i] = this._gen.typeReference(typeVariables[i]); |
| return this._gen.parameterizedSingleTypeReference(roleType.internalName(), typeParameters, 0); |
| } |
| |
| private boolean createLiftToMethodStatements( |
| MethodDeclaration liftToMethodDeclaration, |
| ReferenceBinding teamBinding, |
| RoleModel roleModel, |
| ReferenceBinding baseClassBinding, |
| RoleModel[] caseObjects, |
| int problemId) |
| { |
| ReferenceBinding roleClassBinding = roleModel.getBinding(); |
| |
| if (this._boundRootRoleModel.isRoleFile()) { |
| this._gen = MethodModel.setupSourcePositionMapping(liftToMethodDeclaration, |
| teamBinding._teamModel.getAst(), |
| roleModel, |
| this._gen); |
| } |
| |
| liftToMethodDeclaration.setStatements( |
| new Statement[] { |
| this._gen.synchronizedStatement(createCacheFieldRef(), new Statement[] { |
| |
| // MyRole myRole = null; |
| this._gen.localVariable( |
| MY_ROLE, |
| createRoleTypeReference(roleClassBinding, false/*classPart*/), |
| this._gen.nullLiteral()), |
| |
| |
| // if(base == null) |
| createSanityCheck(), |
| |
| // conditional generation (see below) |
| maybeCreateTeamMemberCheck(baseClassBinding), |
| |
| (RoleModel.getInstantiationPolicy(roleClassBinding).isOndemand()) |
| // if(!_OT$team_param._OT$cache_OT$RootRole.containsKey(base)) |
| ? createRoleExistentCheck( |
| roleClassBinding, |
| baseClassBinding, |
| teamBinding, |
| caseObjects, |
| problemId) |
| : createCreationCascade(roleClassBinding, teamBinding, caseObjects, problemId), |
| |
| // return ... |
| createReturnStatement(roleClassBinding) |
| }) |
| }); |
| return true; |
| } |
| |
| private IfStatement createSanityCheck() { |
| // if |
| return this._gen.ifStatement( |
| // (base == null) |
| this._gen.nullCheck(this._gen.singleNameReference(BASE)), |
| // (then): return null; |
| this._gen.block(new Statement[] { |
| this._gen.returnStatement(this._gen.nullLiteral()) |
| }) |
| ); |
| } |
| |
| /** |
| * If a base class is an externalized role, lifting must check, whether the base object |
| * actually belongs to the team given in the externalized role's team anchor. |
| * (assuming: class R playedBy teamAnchor.BaseRole) |
| */ |
| private Statement maybeCreateTeamMemberCheck(ReferenceBinding baseClass) |
| { |
| // if (((<__OT__BaseRole>)base).getTeam() != <teamAnchor>) |
| // throw new LiftingVetoException(this, base); |
| if (! (baseClass instanceof RoleTypeBinding)) |
| return this._gen.emptyStatement(); |
| RoleTypeBinding baseRole = (RoleTypeBinding)baseClass; |
| if (!baseRole.hasExplicitAnchor()) |
| return this._gen.emptyStatement(); |
| return this._gen.ifStatement( |
| new EqualExpression( |
| this._gen.messageSend( |
| this._gen.baseNameReference(BASE), |
| _OT_GETTEAM, |
| null), |
| this._gen.singleNameReference(baseRole._teamAnchor.internalName()), |
| OperatorIds.NOT_EQUAL), |
| this._gen.block(new Statement[] { |
| this._gen.throwStatement( |
| this._gen.allocation( |
| this._gen.qualifiedTypeReference(ORG_OBJECTTEAMS_LIFTING_VETO), |
| new Expression[] { |
| this._gen.thisReference(), |
| this._gen.singleNameReference(BASE) |
| } |
| ) |
| ) |
| })); |
| } |
| |
| private IfStatement createRoleExistentCheck( |
| ReferenceBinding returnType, |
| ReferenceBinding baseType, |
| ReferenceBinding teamType, |
| RoleModel[] caseObjects, |
| int problemId) |
| { |
| // if |
| return this._gen.ifStatement( |
| // (!_OT$team_param._OT$cache_OT$RootRole.containsKey(base)) |
| createRoleExistentCheck(baseType), |
| // (then:) |
| createCreationCascade(returnType, teamType, caseObjects, problemId), |
| // (else: return existing role) |
| createElseBlock(returnType, teamType)); |
| } |
| |
| private UnaryExpression createRoleExistentCheck( |
| ReferenceBinding baseType) |
| { |
| // !_OT$team_param._OT$cache_OT$RootRole.containsKey(base) |
| return this._gen.setPos(new UnaryExpression( |
| this._gen.messageSend( |
| createCacheFieldRef(), |
| CONTAINS_KEY, |
| new Expression[] { |
| this._gen.singleNameReference(BASE), |
| }), |
| OperatorIds.NOT)); |
| } |
| |
| private Block createCreationCascade(ReferenceBinding roleType, ReferenceBinding teamType, RoleModel[] caseObjects, int problemId) { |
| return this._gen.block( |
| // (create a role of the best matching type:) |
| new Statement[] { |
| caseObjects.length > 0 |
| ? createSwitchStatement(teamType, roleType, caseObjects, problemId, this._gen) |
| : genLiftingFailedException(BASE, roleType, problemId, this._gen)}); |
| } |
| |
| /* |
| * see SwitchOnBaseTypeGenerator.createCaseStatement(RoleModel,AstGenerator). |
| */ |
| protected Statement createCaseStatement(RoleModel role, AstGenerator gen) |
| { |
| /* case 4: |
| * myRole = _OT$team_param. new MySubRoleA((MySubBaseA)base); |
| * break; |
| * case 5: |
| * case 6: |
| * myRole = _OT$team_param. new MySubRoleB((MySubBaseB)base); |
| * break; |
| */ |
| if (role.hasBaseclassProblem()) |
| return null; |
| |
| return |
| gen.assignment( |
| // myRole = ... |
| gen.singleNameReference(this.variableName), |
| |
| // ... new MySubRoleB((MySubBaseB)base) |
| gen.allocation( |
| createRoleTypeReference(role.getBinding(), true/*classPart*/), |
| new Expression[] { |
| // use cast to establish actual type of base object: |
| gen.castExpression( |
| gen.singleNameReference(BASE), |
| gen.baseclassReference( // implicitly refined base may be weakened, apply the weak type: |
| WeakenedTypeBinding.getBytecodeType(role.getBaseTypeBinding())), |
| CastExpression.DO_WRAP) |
| })); |
| } |
| |
| @Override |
| protected Statement createStatementForAmbiguousBase(AstGenerator gen) { |
| return genLiftingFailedException(BASE, this._boundRootRoleModel.getBinding(), IProblem.CallinDespiteBindingAmbiguity, gen); |
| } |
| |
| /* |
| * see SwitchOnBaseTypeGenerator.createDefaultStatement(ReferenceBinding,AstGenerator). |
| */ |
| protected Statement createDefaultStatement(ReferenceBinding roleType, int problemId, AstGenerator gen) |
| { |
| /* |
| * default: |
| * throw new LiftingFailedException(base, "MyRole"); |
| */ |
| return genLiftingFailedException(BASE, roleType, problemId, gen); |
| } |
| |
| public static ThrowStatement genLiftingFailedException(char[] baseVarName, ReferenceBinding roleType, int problemId, AstGenerator gen) { |
| // throw new LiftingFailedException(base, "MyRole"); |
| return |
| gen.throwStatement( |
| gen.allocation( |
| ((problemId == 0) |
| ? gen.qualifiedTypeReference(SOFT_LIFTING_FAILED_EXCEPTION) // runtime should never reach this branch |
| : gen.qualifiedTypeReference(O_O_LIFTING_FAILED_EXCEPTION)), |
| new Expression[] { |
| gen.singleNameReference(baseVarName), |
| gen.stringLiteral(roleType.sourceName()) |
| })); |
| } |
| |
| // try to return casted local role |
| private Block createElseBlock( |
| ReferenceBinding returnType, |
| ReferenceBinding teamType) |
| { |
| // else{ ... } |
| |
| // try { myRole = (MyRole)role } |
| // catch (ClassCastException ex) { throw new WrongRoleException ( ... ); } |
| TryStatement tryStatement = new TryStatement(); |
| tryStatement.sourceStart = this._gen.sourceStart; |
| tryStatement.sourceEnd = this._gen.sourceEnd; |
| tryStatement.tryBlock = createTryCastBlock(returnType); |
| createCatchClassCastExceptionBlock(returnType, teamType, tryStatement); |
| |
| return this._gen.block2( |
| createCacheLookupLocalDeclaration(), |
| tryStatement |
| ); |
| } |
| |
| private LocalDeclaration createCacheLookupLocalDeclaration() { |
| // RootRole role = _OT$team_param._OT$cache_OT$RootRole.get(base); |
| char[] roleType = this._boundRootRoleModel.getName(); |
| Expression getCall = this._gen.messageSend( // init |
| createCacheFieldRef(), |
| GET, |
| new Expression [] { |
| this._gen.singleNameReference(BASE) |
| }); |
| if (this._sourceLevel < ClassFileConstants.JDK1_5) |
| getCall= this._gen.castExpression(getCall, this._gen.singleTypeReference(roleType), CastExpression.RAW); |
| return this._gen.localVariable( |
| ROLE, // name |
| this._gen.singleTypeReference(roleType), // type |
| getCall); |
| } |
| |
| |
| private FieldReference createCacheFieldRef() |
| { |
| // _OT$team_param._OT$cache_OT$RootRole |
| return this._gen.fieldReference( |
| this._gen.setPos(ThisReference.implicitThis()), |
| LiftingEnvironment.getCacheName(this._boundRootRoleModel)); |
| } |
| |
| private Block createTryCastBlock( |
| ReferenceBinding returnType) |
| { |
| /* |
| * try |
| * { |
| * myRole = (MyRole)role; |
| * } |
| */ |
| return this._gen.block(new Statement[]{ |
| this._gen.assignment( |
| this._gen.singleNameReference(MY_ROLE), |
| this._gen.castExpression( |
| this._gen.singleNameReference(ROLE), |
| createRoleTypeReference(returnType, false/*classPart*/), |
| CastExpression.DO_WRAP)) |
| }); |
| } |
| |
| private void createCatchClassCastExceptionBlock( |
| ReferenceBinding returnType, |
| ReferenceBinding teamType, |
| TryStatement tryStatement) |
| { |
| // catch(ClassCastException classcastexception) |
| tryStatement.catchArguments = new Argument[] { |
| this._gen.argument( |
| CLASS_CAST_EXCEPTION, // name |
| this._gen.singleTypeReference(_CLASS_CAST_EXCEPTION_)) // type |
| }; |
| |
| // throw new WrongRoleException(MyTeam$MyRole.class, base, role); |
| tryStatement.catchBlocks = new Block[]{ |
| this._gen.block(new Statement[] { |
| this._gen.throwStatement( |
| this._gen.allocation( |
| this._gen.qualifiedTypeReference(new char[][] { |
| ORG, OBJECTTEAMS, |
| WRONG_ROLE_EXCEPTION |
| }), |
| new Expression[] { |
| this._gen.classLiteralAccess(this._gen.typeReference(returnType)), |
| this._gen.singleNameReference(BASE), |
| this._gen.singleNameReference(ROLE) |
| })) |
| }) |
| }; |
| } |
| |
| private ReturnStatement createReturnStatement(ReferenceBinding returnType) |
| { |
| // return myRole; |
| return this._gen.returnStatement(this._gen.singleNameReference(MY_ROLE)); |
| } |
| |
| /** |
| * API for AbstractMethodDeclaration: |
| * |
| * Create a byte code sequence for a runtime check in a creation method: |
| * R _OT$create_OT$R(B b) { |
| * if (this._OT$cache_OT$R.contains(b)) |
| * throw new DuplicateRoleException("R"); |
| * // continue regular code. |
| * } |
| * |
| * Note, the need for this runtime check is detected quite late. |
| * At this point it is easier to create the byte code sequence directly, |
| * rather the creating AST first. |
| */ |
| public static void createDuplicateRoleCheck(CodeStream codeStream, AbstractMethodDeclaration method) |
| { |
| MethodBinding binding = method.binding; |
| Scope scope = method.scope; |
| |
| ReferenceBinding roleType = (ReferenceBinding)binding.returnType; |
| String roleName = new String(roleType.readableName()); |
| // TODO(SH): check why roleType.getRoleModel().getClassPartBinding().roleModel may yield a different result. |
| // I think it occured in haeder/stopwatch from smile-CVS. |
| //RoleModel role = roleType.getRealClass().roleModel; |
| char[] cacheName = LiftingEnvironment.getCacheName(roleType.roleModel.getBoundRootRole()); |
| |
| ReferenceBinding teamBinding = roleType.enclosingType();//role.getTeamModel().getBinding(); |
| FieldBinding cache = TypeAnalyzer.findField( |
| teamBinding, cacheName, /*static*/false, /*outer*/false, |
| ITranslationStates.STATE_FULL_LIFTING); // generated by this state |
| if (cache == null) |
| throw new InternalCompilerError("generated cache field not found: "+new String(cacheName)); //$NON-NLS-1$ |
| ReferenceBinding map = (ReferenceBinding)scope.getType(IOTConstants.WEAK_HASH_MAP, 3); |
| MethodBinding contains = map.getMethod(scope, IOTConstants.CONTAINS_KEY); |
| |
| ReferenceBinding exc = (ReferenceBinding)scope.getType(IOTConstants.ORG_OBJECTTEAMS_DUPLICATE_ROLE, 3); |
| TypeBinding[] types = new TypeBinding[] { scope.getJavaLangString() }; |
| MethodBinding excInit = exc.getExactConstructor(types); |
| |
| BranchLabel normalCase = new BranchLabel(codeStream); |
| |
| codeStream.aload_0(); // this |
| codeStream.fieldAccess(Opcodes.OPC_getfield, cache, teamBinding); // getfield MyTeam._OT$cache_OT$R Ljava/util/WeakHashMap; |
| codeStream.aload_1(); // arg0 |
| codeStream.invoke(Opcodes.OPC_invokevirtual, contains, map); // invokevirtual java.util.WeakHashMap.containsKey (Ljava/lang/Object;)Z |
| codeStream.ifeq(normalCase); // false -> #endif |
| codeStream.new_(exc); // new <org.objectteams.DuplicateRoleException> |
| codeStream.dup(); // dup |
| codeStream.ldc(roleName); // ldc "R" |
| codeStream.invoke(Opcodes.OPC_invokespecial, excInit, exc); // invokespecial org.objectteams.DuplicateRoleException.<init> (Ljava/lang/String;)V |
| codeStream.athrow(); // athrow |
| normalCase.place(); // #endif |
| } |
| } |