| /********************************************************************** |
| * This file is part of the "Object Teams Runtime Environment" |
| * |
| * Copyright 2002-2009 Berlin Institute of Technology, 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: BaseCallRedirection.java 23408 2010-02-03 18:07:35Z stephan $ |
| * |
| * Please visit http://www.objectteams.org for updates and contact. |
| * |
| * Contributors: |
| * Berlin Institute of Technology - Initial API and implementation |
| **********************************************************************/ |
| package org.eclipse.objectteams.otre; |
| |
| import org.apache.bcel.classfile.*; |
| import org.apache.bcel.generic.*; |
| import org.apache.bcel.*; |
| |
| import java.util.*; |
| |
| import org.eclipse.objectteams.otre.jplis.JPLISEnhancer; |
| import org.eclipse.objectteams.otre.util.*; |
| import org.objectteams.OTREInternalError; |
| |
| /** |
| * for callin-role-methods with recursive method-calls (base calls) we add a |
| * method with extendend signature. |
| * Within the extendend method recursive calls are replaced by the corresponding |
| * (chaining)base-call.<p> |
| * For example: |
| * <pre> |
| * callin m1() { m1(); } --> |
| * callin m1(Team _OT$teams[], int _OT$teamIDs[], |
| * int _OT$idx, int _OT$baseMethTag){ |
| * liftToRole(b1._OT$m1$chain(Team _OT$teams[], |
| * int _OT$teamIDs[], |
| * int _OT$idx, |
| * int _OT$baseMethTag)); |
| * } |
| * </pre> |
| * |
| * @version $Id: BaseCallRedirection.java 23408 2010-02-03 18:07:35Z stephan $ |
| * @author Christine Hundt |
| * @author Stephan Herrmann |
| */ |
| public class BaseCallRedirection extends ObjectTeamsTransformation { |
| |
| static class IHPair { |
| private InstructionHandle _ih1, _ih2; |
| public IHPair (InstructionHandle ih1, InstructionHandle ih2) { |
| _ih1 = ih1; |
| _ih2 = ih2; |
| } |
| public InstructionHandle fst() {return _ih1; } |
| public InstructionHandle snd() {return _ih2; } |
| } |
| |
| public BaseCallRedirection(Object loader) { |
| super(loader); |
| } |
| |
| /** |
| * @param ce |
| * @param cg |
| */ |
| public void doTransformInterface(ClassEnhancer ce, ClassGen cg) { |
| factory = new InstructionFactory(cg); |
| String class_name = cg.getClassName(); |
| |
| ConstantPoolGen cpg = cg.getConstantPool(); |
| checkReadClassAttributes(ce, cg, class_name, cpg); |
| |
| if (!CallinBindingManager.isRole(class_name)) { |
| return; |
| } |
| |
| if (!cg.isInterface()) { |
| Set<String> boundBaseMethods = CallinBindingManager.getBoundRoleMethods(class_name); |
| addBaseCallSurrogatesForReplaceBindings(ce, boundBaseMethods, cg); |
| } |
| |
| Method[] methods = cg.getMethods(); |
| for (int i=0; i<methods.length; i++) { |
| Method m = methods[i]; |
| String method_name = m.getName(); |
| String method_signature = m.getSignature(); |
| |
| if (candidateForImplicitActivation(m, cg, cpg)) { // TODO: check the other preconditions, like not abstact etc. |
| cg.replaceMethod(m, genImplicitActivation(m, class_name, cpg, true)); |
| JPLISEnhancer.requireClassFileVersionLessThan51(cg); |
| } |
| |
| if (!isCallin(m, cg)) |
| continue; |
| if (logging) printLogMessage(method_name + " in " + class_name |
| + " IS A CALLIN-METHOD!"); |
| if (method_name.startsWith(OT_PREFIX)) { |
| method_name = revertToOriginalName(method_name); |
| if (logging) printLogMessage("Reverted tsuper name to " + method_name); |
| } |
| // code SHOULD contain at least one base call. |
| if(logging) printLogMessage("----->will add another method " + method_name |
| + " with enhanced signature"); |
| |
| //{SH: retrench signature because otherwise the binding will not be found, |
| // as a result an empty basecall surrogate will be generated leading to repetetive methods... |
| // TODO(SH) is this the proper way to retrench?? |
| String enhancedPrefix = "([Lorg/objectteams/Team;[IIII[Ljava/lang/Object;"; |
| if (method_signature.startsWith(enhancedPrefix)) |
| method_signature = "("+method_signature.substring(enhancedPrefix.length()); |
| // SH} |
| |
| boolean roleMethodIsBound = CallinBindingManager.roleMethodHasBinding(class_name, |
| method_name, |
| method_signature); |
| |
| //if (mbs.isEmpty()) { |
| if (!roleMethodIsBound) { |
| if (logging) printLogMessage("callin method " + method_name |
| + " was not bound in this class!!!"); |
| } |
| MethodGen baseCallSurrogate = null; |
| if (!IS_COMPILER_13X_PLUS) { // since 1.3.0 this part is legacy: |
| if (!roleMethodIsBound && !methodHasCallinFlags(m, cg, OVERRIDING) && !m.isStatic()) { |
| // method not bound in current class and doesn't inherit a base call surrogate |
| baseCallSurrogate = generateEmptyBaseCallSurrogate(cg, m); |
| } |
| if (baseCallSurrogate != null) |
| ce.addMethod(baseCallSurrogate.getMethod(), cg); |
| } |
| |
| } |
| } |
| |
| /** |
| * Generates an "empty" base call surrogate method, which just throws an 'Error'. |
| * This method should normaly never be called, but overwritten in a subclass. |
| * It has to be generated just for the wellformedness of the class file. |
| * @param cg |
| * @param m |
| * @param mbs |
| * @param baseClassName |
| * @return |
| */ |
| private MethodGen generateEmptyBaseCallSurrogate(ClassGen cg, Method m/*, List mbs*/) { |
| if (m.getName().startsWith(OT_PREFIX)) |
| return null; // ot-internal methods don't need this? |
| |
| ConstantPoolGen cpg = cg.getConstantPool(); |
| String class_name = cg.getClassName(); |
| MethodGen mg = newMethodGen(m, class_name, cpg); |
| |
| if (isTSuperWrapper(mg)) { |
| // tsuper wrapper do not need base call surrogates |
| return null; |
| } |
| |
| // role method already was enhanced by compiler |
| Type[] enhancedArgumentTypes = mg.getArgumentTypes(); |
| if (IS_COMPILER_GREATER_123) { |
| // add super flag between enhancement and real arguments: |
| int len = enhancedArgumentTypes.length; |
| Type[] newArgumentTypes = new Type[len+1]; |
| System.arraycopy(enhancedArgumentTypes, 0, newArgumentTypes, 0, EXTRA_ARGS); |
| newArgumentTypes[EXTRA_ARGS] = Type.BOOLEAN; |
| System.arraycopy(enhancedArgumentTypes, EXTRA_ARGS, newArgumentTypes, EXTRA_ARGS+1, len-EXTRA_ARGS); |
| enhancedArgumentTypes = newArgumentTypes; |
| } |
| Type enhancedReturnType = mg.getReturnType(); |
| |
| // {SH: interface method has no argument names? generate dummy names: |
| String[] argumentNames = new String[enhancedArgumentTypes.length]; |
| for (int i = 0; i < argumentNames.length; i++) { |
| argumentNames[i] = "arg"+i; |
| } |
| |
| // role method already was enhanced by compiler: |
| String[] enhancedArgumentNames = argumentNames; |
| // } |
| InstructionList il = new InstructionList(); |
| //int accessFlags = m.getAccessFlags(); |
| int accessFlags = Constants.ACC_PROTECTED; // no unanticipated calls possible |
| |
| // {SH: interface methods must be public abstract: |
| if (cg.isInterface()) |
| accessFlags = Constants.ACC_ABSTRACT|Constants.ACC_PUBLIC; |
| // } |
| |
| MethodGen baseCallSurrogate = new MethodGen(accessFlags, |
| enhancedReturnType, |
| enhancedArgumentTypes, |
| enhancedArgumentNames, |
| getBaseCallSurrogateName(m.getName(), m.isStatic(), class_name /*genRoleInterfaceName(class_name)*/), |
| class_name, |
| il, cpg); |
| // {SH: no code for interface method: |
| if (!cg.isInterface()) { |
| // orig: |
| if (logging) |
| printLogMessage("Exception has to be thrown!"); |
| |
| createThrowInternalError(cpg, il, new InstructionList(new PUSH(cpg, "Binding-Error: base-call impossible!"))); |
| |
| il.append(InstructionFactory.createNull(enhancedReturnType)); |
| il.append(InstructionFactory.createReturn(enhancedReturnType)); |
| |
| if (debugging) |
| baseCallSurrogate.addLineNumber(il.getStart(), STEP_OVER_LINENUMBER); |
| } |
| // } |
| |
| il.setPositions(); |
| baseCallSurrogate.removeNOPs(); |
| baseCallSurrogate.setMaxStack(); |
| baseCallSurrogate.setMaxLocals(); |
| return baseCallSurrogate; |
| } |
| |
| /** |
| * @param mg |
| * @return |
| */ |
| private static boolean isTSuperWrapper(MethodGen mg) { |
| Type[] argTypes = mg.getArgumentTypes(); |
| if (argTypes.length == 0) { |
| return false; // no tsuper marker interface argument existing |
| } |
| String lastArgument = (argTypes[argTypes.length - 1]).toString(); |
| return lastArgument.contains(OTDT_PREFIX); |
| } |
| |
| /** |
| * Adds base call surrogate method for all role method bindings in the current role class. |
| * Thereby method bindings which are defined in super roles are accumulated and |
| * considered as well. |
| * @param ce the ClassEnhancer to which the new method has to be added |
| * @param boundRoleMethods the bound methods of the role class |
| * @param cg the ClassGen for the role class |
| */ |
| private void addBaseCallSurrogatesForReplaceBindings(ClassEnhancer ce, Set<String> boundRoleMethods, ClassGen cg) |
| { |
| Iterator<String> it = boundRoleMethods.iterator(); |
| while (it.hasNext()) { |
| String nameAndSignature = it.next(); |
| int dotIndex = nameAndSignature.indexOf('.'); |
| String methodName = nameAndSignature.substring(0, dotIndex); |
| String methodSignature = nameAndSignature.substring(dotIndex + 1); |
| List<MethodBinding> mbs = CallinBindingManager.getBindingsForRoleMethod(cg.getClassName(), |
| methodName, |
| methodSignature); |
| MethodBinding anyMethodBinding = mbs.get(0); |
| if (!anyMethodBinding.isReplace()) { |
| continue; |
| } |
| mbs.addAll(CallinBindingManager.getInheritedRoleMethodBindings(cg.getClassName(), |
| methodName, |
| methodSignature)); |
| |
| if (anyMethodBinding.hasStaticRoleMethod()) |
| continue; // base call surrogates for static methods are generated within the enclosing team class |
| // TODO: remove this check as soon as static replace method bindings are no longer in 'CallinMethodMappings' |
| MethodGen baseCallSurrogate = genBaseCallSurrogate(cg, mbs); |
| ce.addOrReplaceMethod(baseCallSurrogate.getMethod(), cg); |
| } |
| } |
| |
| /** |
| * Generates base call surrogate method for the role method for which the method bindings 'mbs' are. |
| * Thereby a switch-case for each bound base method is generated. |
| * This method is only for nonstatic role methods. Base call surrogates for static methods |
| * are generated within the enclosing team class |
| * @param cg the ClassGen for the role class |
| * @param mbs the method bindings for one role method |
| * @param baseClassName the name of the base class |
| */ |
| MethodGen genBaseCallSurrogate(ClassGen cg, List<MethodBinding> mbs) { |
| |
| //baseClassName would not be needed here, if I could find out the root-base-class-type... |
| ConstantPoolGen cpg = cg.getConstantPool(); |
| String class_name = cg.getClassName(); |
| |
| MethodBinding anyBindingForRoleMethod = mbs.get(0); |
| String baseClassName = anyBindingForRoleMethod.getRootBoundBase(); |
| String roleMethodSignature = anyBindingForRoleMethod.getRoleMethodSignature(); |
| |
| Type[] enhancedArgumentTypes; |
| { |
| Type[] argTypesTail = Type.getArgumentTypes(roleMethodSignature); |
| if (IS_COMPILER_GREATER_123) { |
| // add super flag between enhancement and real arguments: |
| int len = argTypesTail.length; |
| System.arraycopy(argTypesTail, 0, argTypesTail=new Type[len+1], 1, len); |
| argTypesTail[0] = Type.BOOLEAN; |
| } |
| enhancedArgumentTypes = enhanceArgumentTypes(argTypesTail); |
| } |
| Type enhancedReturnType = generalizeReturnType(Type.getReturnType(roleMethodSignature)); |
| String methodName = anyBindingForRoleMethod.getRoleMethodName(); |
| InstructionList il = new InstructionList(); |
| int accessFlags = Constants.ACC_PROTECTED; |
| |
| MethodGen baseCallSurrogate = new MethodGen(accessFlags, |
| enhancedReturnType, |
| enhancedArgumentTypes, |
| null, // no explicit names |
| getBaseCallSurrogateName(methodName, false, |
| genRoleInterfaceName(class_name)), |
| class_name, |
| il, cpg); |
| |
| ObjectType baseClass = new ObjectType(baseClassName); |
| ObjectType outerClass; |
| { |
| String outerClassName = getOuterClassName(class_name); |
| outerClass = new ObjectType(outerClassName); |
| } |
| |
| LocalVariableGen otResult = null; |
| |
| otResult = baseCallSurrogate.addLocalVariable("_OT$result", |
| enhancedReturnType, null, null); |
| |
| il.insert(InstructionFactory.createStore(enhancedReturnType, |
| otResult.getIndex())); |
| il.insert(new ACONST_NULL()); |
| il.setPositions(); // about to retrieve instruction handles. |
| |
| if (logging) printLogMessage("base-call switch has to be inserted!"); |
| InstructionList loading = new InstructionList(); |
| loading.append(InstructionFactory.createThis()); |
| int index = 1; |
| for (int i = 0; i < enhancedArgumentTypes.length; i++) { |
| loading.append(InstructionFactory.createLoad(enhancedArgumentTypes[i],index)); |
| index += enhancedArgumentTypes[i].getSize(); |
| } |
| |
| Type[] argumentTypes = Type.getArgumentTypes(roleMethodSignature); |
| Type returnType = Type.getReturnType(roleMethodSignature); |
| |
| if (debugging) { |
| baseCallSurrogate.addLineNumber(il.getStart(), STEP_OVER_LINENUMBER); |
| } |
| |
| boolean generateSuperAccess = false; |
| List<SuperMethodDescriptor> superAccesses = IS_COMPILER_GREATER_123 ? CallinBindingManager.getSuperAccesses(baseClassName) : null; |
| if (superAccesses != null) { |
| outer: for (SuperMethodDescriptor superMethod : superAccesses) { |
| for (MethodBinding methodBinding : mbs) { |
| if ( superMethod.methodName.equals(methodBinding.getBaseMethodName()) |
| && superMethod.signature.equals(methodBinding.getBaseMethodSignature())) |
| { |
| generateSuperAccess = true; |
| break outer; |
| } |
| } |
| } |
| } |
| |
| BranchInstruction ifSuper = new IFEQ(null); |
| GotoInstruction skipElse = new GOTO(null); |
| if (generateSuperAccess) { |
| // gen: if (isSuperAccess) { _OT$base._OT$m$super(args); } else ... |
| il.append(InstructionFactory.createLoad(Type.BOOLEAN, EXTRA_ARGS+1)); // last synthetic arg is super-flag |
| il.append(ifSuper); |
| il.append(genBaseCallSwitch(cpg, mbs, baseCallSurrogate, |
| argumentTypes, |
| outerClass, baseClass, |
| returnType, |
| otResult, loading, true)); |
| il.append(skipElse); |
| } |
| InstructionList |
| basecall = genBaseCallSwitch(cpg, mbs, baseCallSurrogate, |
| argumentTypes, |
| outerClass, baseClass, |
| returnType, |
| otResult, loading, false); |
| InstructionHandle callStart = basecall.getStart(); // store handle before append eats the list |
| il.append(basecall); |
| if (generateSuperAccess) { |
| ifSuper.setTarget(callStart); |
| skipElse.setTarget(il.append(new NOP())); |
| } |
| |
| il.append(InstructionFactory.createLoad(enhancedReturnType, otResult.getIndex())); |
| il.append(InstructionFactory.createReturn(enhancedReturnType)); |
| |
| il.setPositions(); |
| baseCallSurrogate.removeNOPs(); |
| baseCallSurrogate.setMaxStack(); |
| baseCallSurrogate.setMaxLocals(); |
| return baseCallSurrogate; |
| } |
| |
| /* |
| Type findBaseFieldType(JavaClass c, ConstantPoolGen cpg) { |
| Field[] fields = c.getFields(); |
| for (int l=0; l<fields.length; l++) { |
| Field f = fields[l]; |
| if (f.getName().equals(BASE)) { |
| FieldGen fg = new FieldGen(f, cpg); |
| return fg.getType(); |
| } |
| } |
| JavaClass superClass = Repository.lookupClass(c.getSuperclassName()); |
| // BCEL bug: super class of "Object" is "Object": |
| //System.err.println("Superclass of "+ c.getClassName()+" is " +c.getSuperclassName()); |
| if (!superClass.getClassName().equals("java.lang.Object")) { |
| return findBaseFieldType(superClass, cpg); |
| } |
| return null; |
| }*/ |
| |
| /** |
| * Iterate through the instructions of a callin method. |
| * <ul> |
| * <li>Adjust local variable instructions due to inserted extra arguments. |
| * <li>Replace base-calls and calls to activate. |
| * </ul> |
| * @param cg |
| * @param m |
| * @param mbs List<MethodBinding> |
| * @return MethodGen |
| */ |
| MethodGen replaceBaseCalls(ClassGen cg, Method m, List<MethodBinding> mbs) { |
| |
| int indexOffset = m.isStatic() ? -1 : 0; // argument indices are decremented for static methods, |
| // because of the missing 'this' |
| |
| ConstantPoolGen cpg = cg.getConstantPool(); |
| String class_name = cg.getClassName(); |
| String method_name = m.getName(); |
| |
| MethodGen mg = newMethodGen(m, class_name, cpg); |
| |
| Type[] argumentTypes = mg.getArgumentTypes(); |
| Type returnType = mg.getReturnType(); |
| |
| Type[] enhancedArgumentTypes = enhanceArgumentTypes(mg.getArgumentTypes()); |
| // {SH abstract methods may not have argument names?? |
| String[] argumentNames; |
| if (m.isAbstract()) { |
| argumentNames = new String[argumentTypes.length]; |
| int index = 0; |
| for (int i = 0; i < argumentNames.length; i++) { |
| argumentNames[i] = "arg" + index/* i */; |
| index += argumentTypes[i].getSize(); |
| } |
| |
| /* |
| // load regular arguments: |
| int index = 1; |
| for (int i=0; i<argTypes.length; i++) { |
| il.append(InstructionFactory.createLoad(argTypes[i],index)); |
| index += argTypes[i].getSize(); |
| } |
| //*/ |
| |
| } else { |
| argumentNames = mg.getArgumentNames(); |
| } |
| String[] enhancedArgumentNames = enhanceArgumentNames(argumentNames); |
| // orig: String[] enhancedArgumentNames = enhanceArgumentNames(mg.getArgumentNames()); |
| // SH} |
| |
| Type enhancedMethodReturnType = generalizeReturnType(m.getSignature()); |
| |
| // {SH instruction list may be null for abstract method |
| // orig: InstructionList il = mg.getInstructionList().copy(); |
| InstructionList il = mg.getInstructionList(); |
| if (il != null) |
| il = il.copy(); |
| else |
| il = new InstructionList(); |
| // SH} |
| |
| MethodGen enhancedMethod = new MethodGen(m.getAccessFlags(), |
| enhancedMethodReturnType, |
| enhancedArgumentTypes, |
| enhancedArgumentNames, |
| method_name, class_name, |
| il, cpg); |
| //not needed??: |
| // or not?????? |
| copyLocalVariables(mg, enhancedMethod); |
| copyLineNumbers(mg, enhancedMethod); |
| |
| boolean returnValueAdded = (returnType == Type.VOID) |
| && (enhancedMethodReturnType != Type.VOID); |
| |
| // all exception handlers of this method, which have to be updated later. |
| CodeExceptionGen [] handlers = copyExceptionHandlers(mg, enhancedMethod, il); |
| // list of instruction handles (old and new) that are replaced in the sequel |
| ArrayList<IHPair> replacedInstructions = new ArrayList<IHPair>(); // of IHPair; |
| // set of instruction handles which signal TargetLostException during delete(). |
| HashSet<InstructionHandle> targetLost = new HashSet<InstructionHandle>(); // of InstructionHandle |
| |
| // create LocalVariable Object _OT$result: |
| LocalVariableGen otResult = null; |
| |
| int slot = mg.getMaxLocals() + EXTRA_ARGS-indexOffset; |
| otResult = enhancedMethod.addLocalVariable("_OT$result", enhancedMethodReturnType, |
| slot, null, null); |
| |
| // subtract EXTRA_ARGS since this offset will be added again below. |
| il.insert(InstructionFactory.createStore(enhancedMethodReturnType, |
| otResult.getIndex() - EXTRA_ARGS)); |
| |
| il.insert(new ACONST_NULL()); |
| il.setPositions(); // about to retrieve instruction handles. |
| |
| InstructionHandle[] ihs = il.getInstructionHandles(); |
| //printLogMessage("every call of base." + method_name + "(...) will be replaced by " |
| // + liftMethodName + BASE + "."+baseChainMethodName+"(...))"); |
| |
| int actInstruction = 0; |
| int offset = EXTRA_ARGS; |
| while (actInstruction < ihs.length) { |
| Instruction act_instruction = ihs[actInstruction].getInstruction(); |
| /****************************** variable index adaption: **********************************/ |
| if(act_instruction instanceof LocalVariableInstruction) { |
| // add offset to the index of every variable load or store instruction, |
| // because of the inserted EXTRA_ARGS arguments: |
| LocalVariableInstruction localVariableInstruction = (LocalVariableInstruction) act_instruction; |
| if (localVariableInstruction.getIndex() != 0 || (enhancedMethod.isStatic())) { // 'this' stays at index 0 |
| if (localVariableInstruction instanceof StoreInstruction) { |
| localVariableInstruction = |
| InstructionFactory.createStore(localVariableInstruction.getType(cpg), |
| offset+localVariableInstruction.getIndex()); |
| } else if (localVariableInstruction instanceof LoadInstruction) { |
| localVariableInstruction = |
| InstructionFactory.createLoad(localVariableInstruction.getType(cpg), |
| offset+localVariableInstruction.getIndex()); |
| } else if (localVariableInstruction instanceof IINC) { |
| localVariableInstruction.setIndex(offset+localVariableInstruction.getIndex()); |
| // TODO: check, if this is enough for all kinds of LocalVariableInstructions |
| // and if there are more instructions which use variable indizes!! |
| } |
| ihs[actInstruction].setInstruction(localVariableInstruction); |
| } |
| } else if (act_instruction instanceof RET) { |
| RET ret = (RET)act_instruction; |
| if (ret.getIndex() != 0) |
| ihs[actInstruction].setInstruction(new RET(offset + ret.getIndex())); |
| |
| /*************************** "super"- & "tsuper"-call enhancement: **********************************/ |
| } else if (super_or_tsuper_instruction(act_instruction, method_name, cpg) ) { |
| |
| InvokeInstruction ii = (InvokeInstruction)act_instruction; |
| InstructionHandle next = ihs[actInstruction+1]; |
| InstructionList changedArea; |
| InstructionHandle[] delim = new InstructionHandle[2]; |
| int stackDepth = computeArgumentStackDepth(cpg, ii); |
| InstructionList loading = pruneLoading(il, ihs, actInstruction, |
| stackDepth, cpg, |
| targetLost, delim, true); |
| if (loading == null) { |
| actInstruction++; |
| continue; |
| } |
| |
| changedArea = genEnhancedSuperCall(cpg, ii, enhancedMethod, loading); |
| if (returnValueAdded) { |
| changedArea.append(InstructionFactory.createStore( |
| enhancedMethodReturnType, otResult.getIndex())); |
| } else { |
| InstructionHandle ih = adjustValue(changedArea, changedArea.getEnd(), enhancedMethodReturnType, returnType); |
| if (debugging && ih != null) |
| mg.addLineNumber(ih, STEP_OVER_LINENUMBER); |
| } |
| replacedInstructions.add(new IHPair(delim[0], changedArea.getStart())); |
| replacedInstructions.add(new IHPair(delim[1], changedArea.getEnd())); |
| |
| il.insert(next, changedArea); |
| actInstruction++; |
| continue; |
| |
| /*************************** "activate" substitution and base-call generation: ************/ |
| //} else if (act_instruction instanceof INVOKEVIRTUAL) { |
| // INVOKEVIRTUAL iv = (INVOKEVIRTUAL)act_instruction; |
| } else if (act_instruction instanceof InvokeInstruction) { |
| InvokeInstruction iv = (InvokeInstruction)act_instruction; |
| String iv_name = iv.getName(cpg); |
| |
| // FIXME(SH): is this still needed? |
| // - activate is commented-out, |
| // - base call is now generated by the compiler. |
| if(!(iv_name.equals(method_name) || |
| iv_name.equals("activate"))) |
| { |
| actInstruction++; |
| continue; |
| } |
| InstructionHandle next = ihs[actInstruction+1]; |
| InstructionList changedArea; |
| InstructionHandle[] delim = new InstructionHandle[2]; |
| /* |
| if (iv_name.equals("activate")) { |
| // blank original invokevirtual: |
| ihs[actInstruction].setInstruction(new NOP()); |
| Type [] ivArgTypes = iv.getArgumentTypes(cpg); |
| int activateArgCount = ivArgTypes.length; |
| InstructionList loading = null; |
| if (activateArgCount == 1) |
| loading = pruneLoading (il, ihs, actInstruction, |
| ivArgTypes[0].getSize(), cpg, |
| targetLost, delim, false); |
| changedArea = enhanceActivateCall(factory, cpg, loading, iv); |
| if (activateArgCount == 1) { |
| replacedInstructions.add(new IHPair(delim[0], |
| changedArea.getStart())); |
| replacedInstructions.add(new IHPair(delim[1], |
| changedArea.getEnd())); |
| } |
| } else*/ { // base call: |
| int stackDepth = computeArgumentStackDepth(cpg, iv); |
| boolean deleteThis = true; |
| |
| if(enhancedMethod.isStatic()) |
| deleteThis = false; |
| |
| /* |
| if (iv.getOpcode()==Constants.INVOKESTATIC) |
| deleteThis = false; |
| */ |
| InstructionList loading = pruneLoading(il, ihs, actInstruction, |
| stackDepth, cpg, |
| targetLost, delim, /*true*/deleteThis); |
| //System.err.println(loading); |
| if (loading == null) { |
| actInstruction++; |
| continue; |
| } |
| |
| // insert call of base-call surrogate method: |
| String roleInterfaceName = genRoleInterfaceName(cg.getClassName()); |
| |
| String calleeClassName = null; |
| if(m.isStatic()) { |
| calleeClassName = extractTeamName(roleInterfaceName); |
| } |
| changedArea = genBaseCallSurrogateCall(cpg, iv, enhancedMethod, loading, extractRoleName(roleInterfaceName), calleeClassName); |
| |
| if (returnValueAdded) { |
| changedArea.append(InstructionFactory.createStore(enhancedMethodReturnType, |
| otResult.getIndex())); |
| } else { |
| InstructionHandle ih = adjustValue(changedArea, changedArea.getEnd(), enhancedMethodReturnType, returnType); |
| if (debugging && ih != null) |
| mg.addLineNumber(ih, STEP_OVER_LINENUMBER); |
| } |
| replacedInstructions.add(new IHPair(delim[0], changedArea.getStart())); |
| replacedInstructions.add(new IHPair(delim[1], changedArea.getEnd())); |
| |
| } // if (activate or base call) |
| |
| il.insert(next, changedArea); |
| |
| |
| /*************************** "return" enhancements: ************/ |
| } else if (act_instruction instanceof ReturnInstruction) { |
| // replace return statement by result preparation and a new return statement |
| // construct back to front, to keep the insertion position! |
| InstructionHandle oldReturn = ihs[actInstruction]; |
| InstructionHandle replacedPos = oldReturn; |
| il.append(oldReturn, InstructionFactory.createReturn(enhancedMethodReturnType)); |
| if (returnValueAdded) { |
| // load ot_result: |
| oldReturn.setInstruction(InstructionFactory.createLoad(enhancedMethodReturnType, |
| otResult.getIndex())); |
| } else { |
| oldReturn.setInstruction(new NOP()); |
| replacedPos = |
| adjustValue(il, oldReturn, returnType, enhancedMethodReturnType); |
| if (debugging && replacedPos != null) |
| mg.addLineNumber(replacedPos, STEP_OVER_LINENUMBER); |
| } |
| if (replacedPos != null) |
| replacedInstructions.add(new IHPair(oldReturn, replacedPos)); |
| } // conditional over instruction types |
| actInstruction++; |
| } //end while |
| |
| // tidy: |
| checkUpdate(handlers, replacedInstructions, targetLost); |
| enhancedMethod.removeNOPs(); |
| il.setPositions(); |
| enhancedMethod.setMaxStack(); |
| enhancedMethod.setMaxLocals(); |
| |
| return enhancedMethod; |
| } |
| |
| /** |
| * Given an invokevirtual compute the space its arguments use on the stack. |
| * @param cpg |
| * @param iv |
| * @return int stack size. |
| */ |
| static int computeArgumentStackDepth(ConstantPoolGen cpg, InvokeInstruction ii) { |
| Type [] iiargs = ii.getArgumentTypes(cpg); |
| int depth=0; |
| for (int i=0; i<iiargs.length; i++) |
| depth += iiargs[i].getSize(); |
| return depth; |
| } |
| |
| /** |
| * Copy all local variables from <tt>src</tt> to <tt>dest</tt>. |
| * While doing so, increment their index by EXTRA_ARGS. |
| */ |
| static void copyLocalVariables(MethodGen src, MethodGen dest) { |
| Type[] argumentTypes = src.getArgumentTypes(); |
| LocalVariableGen[] lvgs = src.getLocalVariables(); |
| for (int l=argumentTypes.length; l<lvgs.length; l++) { |
| LocalVariableGen lvg = lvgs[l]; |
| if (lvg.getIndex() > 0) { |
| dest.addLocalVariable(lvg.getName(), |
| lvg.getType(), |
| lvg.getIndex()+(EXTRA_ARGS+1), // +1????? |
| null, null); |
| //System.err.println("adding:" +src.getClassName() +" "+src.getName()+" "+lvg.getName() +" " + lvg.getType() +" "+ (lvg.getIndex()+(EXTRA_ARGS+1))); |
| } |
| } |
| } |
| /** Copy all line numbers from <tt>src</tt> to <tt>dest</tt>. */ |
| static void copyLineNumbers(MethodGen src, MethodGen dest) { |
| InstructionList il_dest = dest.getInstructionList(); |
| il_dest.setPositions(); |
| LineNumberGen[] src_lng = src.getLineNumbers(); |
| for (int i=0; i<src_lng.length; i++) { |
| int position = src_lng[i].getInstruction().getPosition(); |
| InstructionHandle ih = il_dest.findHandle(position); |
| dest.addLineNumber(ih, src_lng[i].getSourceLine()); |
| } |
| } |
| /** |
| * Prune a invokevirtual portion from a given instruction list. |
| * Note, that arguments don't include 'this', which is not pruned but blanked |
| * (need to keep as possible jump target). |
| * @param il the source list |
| * @param ihs array of handles of this list |
| * @param idx points to a invokevirtual that shall be removed |
| * @param stackDepth size of the called method's arguments on the stack. |
| * This is how deep we need to cut into the stack. |
| * @param cpg |
| * @param targetLost set of lost InstructionHandles to be filled |
| * @param delim array of two handles, which should be filled with start and end of |
| * the pruned region. |
| * @param blankThis should the 'this' call target be overwritten? |
| * @return InstructionList a copy of the original value loading. |
| */ |
| static InstructionList pruneLoading (InstructionList il, InstructionHandle[] ihs, int idx, |
| int stackDepth, ConstantPoolGen cpg, |
| HashSet<InstructionHandle> targetLost, InstructionHandle[] delim, |
| boolean blankThis) |
| { |
| InstructionList nlist = new InstructionList(); |
| InstructionHandle start = ihs[idx]; |
| InstructionHandle end = ihs[idx--]; |
| while (stackDepth > 0) { |
| start = ihs[idx--]; |
| Instruction instr = start.getInstruction(); |
| stackDepth -= stackDiff(instr, cpg); |
| nlist.insert(instr); |
| } |
| if (blankThis) { |
| if (!isALoad0(ihs[idx].getInstruction())) |
| return null; |
| ihs[idx].setInstruction(new NOP()); // keep as jump target but delete 'this' |
| } |
| delim[0] = start; |
| delim[1] = end; |
| safeDelete(il, start, end, targetLost); |
| return nlist; |
| } |
| |
| static boolean isALoad0(Instruction i) { |
| if (!(i instanceof ALOAD)) return false; |
| return ((ALOAD)i).getIndex() == 0; |
| } |
| |
| /** Get the lenght of the longest base method signature in mbs. |
| * @param mbs List of {@link MethodBinding MethodBinding} |
| */ |
| // static int getMaxBaseArgLen (List mbs) { |
| // int max=0; |
| // Iterator it = mbs.iterator(); |
| // while (it.hasNext()) { |
| // MethodBinding mb = (MethodBinding)it.next(); |
| // String sign = mb.getBaseMethodSignature(); |
| // int len = Type.getArgumentTypes(sign).length; |
| // if (len>max) max = len; |
| // } |
| // return max; |
| // } |
| |
| /** |
| * Generate a dispatching switch statement which calls the proper base method. |
| * @param cpg |
| * @param mbs list of MethodBinding that applies to this callin method |
| * @param enhancedMethod the enhanced callin method |
| * @param roleArgumentTypes arg types of the callin method |
| * @param outerClass the Team |
| * @param baseClass the base bound to this role |
| * @param returnType the return type of the original callin method |
| * @param otResult the local variable storing the base call result |
| * @param loading an instruction list holding the original instructions for |
| * loading parameters |
| * @return InstructionList the complete replacement implementing the base call. |
| */ |
| InstructionList genBaseCallSwitch (ConstantPoolGen cpg, |
| List<MethodBinding> mbs, MethodGen enhancedMethod, |
| Type[] roleArgumentTypes, |
| ObjectType outerClass, ObjectType baseClass, |
| Type returnType, LocalVariableGen otResult, |
| InstructionList loading, |
| boolean isSuperAccess) |
| { |
| |
| String className = enhancedMethod.getClassName(); |
| Type enhancedMethodReturnType = enhancedMethod.getReturnType(); |
| boolean callinHasReturnValue = returnType != Type.VOID; |
| |
| InstructionList il = new InstructionList(); |
| |
| // Setup a variable which holds the result of this base call. |
| // This variabel is local to this segment of code and used only |
| // to transport this result out off the switch statement. |
| int localResult = -1; |
| LocalVariableGen lg = null; |
| if (callinHasReturnValue) { |
| lg = enhancedMethod.addLocalVariable("_OT$tmpResult", returnType, |
| null, null); |
| localResult = lg.getIndex(); |
| il.append(InstructionFactory.createNull (returnType)); |
| il.append(InstructionFactory.createStore(returnType, localResult)); |
| } |
| |
| // ---- Prepare the switch: ---- |
| InstructionHandle switchStart = il.append |
| (InstructionFactory.createLoad(Type.INT, BASE_METH_ARG)); |
| // generated: _OT$baseMethTag |
| |
| removeDuplicatedBaseMethodTags(mbs); |
| |
| // one break for each case clause |
| int numberOfCases = mbs.size(); |
| GOTO[] breaks = new GOTO[numberOfCases]; |
| for (int i=0; i<numberOfCases; i++) |
| breaks[i] = new GOTO(null); |
| |
| int[] matches = new int[numberOfCases]; |
| InstructionHandle[] targets = new InstructionHandle[numberOfCases]; |
| int caseCounter = 0; |
| |
| Iterator<MethodBinding> it = mbs.iterator(); |
| while (it.hasNext()) { |
| |
| MethodBinding mb = it.next(); |
| |
| String wrapperName = mb.getWrapperName(); |
| int[] paramPositions = CallinBindingManager.getParamPositions(outerClass.getClassName(), |
| wrapperName); |
| if (logging) printLogMessage("param pos(" + wrapperName + ")=" + paramPositions); |
| |
| matches[caseCounter] = CallinBindingManager.getBaseCallTag(mb.getBaseClassName(), |
| mb.getBaseMethodName(), |
| mb.getBaseMethodSignature()); |
| InstructionHandle nextBranch = il.append(new NOP()); |
| |
| short invocationKind = isSuperAccess |
| ? Constants.INVOKESTATIC |
| : Constants.INVOKEVIRTUAL; |
| |
| String boundBaseClassName = mb.getBaseClassName(); |
| if (boundBaseClassName.indexOf(OTDT_PREFIX) != -1) { |
| // if base is a role class, switch now to the interface part to support base-side implicit inheritance: |
| boundBaseClassName = ObjectTeamsTransformation.genRoleInterfaceName(boundBaseClassName); |
| invocationKind = Constants.INVOKEINTERFACE; |
| } |
| String baseMethodName = mb.getBaseMethodName(); |
| String baseMethodSignature = mb.getBaseMethodSignature(); |
| Type[] baseMethodArgumentTypes = Type.getArgumentTypes(baseMethodSignature); |
| Type baseMethodReturnType = Type.getReturnType (baseMethodSignature); |
| |
| String baseChainMethodName; |
| Type baseChainReturnType; |
| Type[] enhancedBaseArgumentTypes; |
| if (isSuperAccess) { |
| baseChainMethodName = OT_PREFIX+baseMethodName+"$super"; |
| baseChainReturnType = returnType; |
| // base arguments are un-enhanced but have a leading base instance: |
| int len = baseMethodArgumentTypes.length; |
| System.arraycopy(baseMethodArgumentTypes, 0, enhancedBaseArgumentTypes=new Type[len+1], 1, len); |
| enhancedBaseArgumentTypes[0] = baseClass; |
| } else { |
| baseChainMethodName = genChainMethName(baseMethodName); |
| baseChainReturnType = object; // ALWAYS |
| enhancedBaseArgumentTypes = enhanceArgumentTypes(baseMethodArgumentTypes); |
| } |
| |
| // --- call target: --- |
| |
| // if base class type is a role type _OT$base field has role interface type: |
| { |
| String baseClassName = baseClass.toString(); |
| if (baseClassName.indexOf(OTDT_PREFIX) != -1) { |
| baseClass = new ObjectType(ObjectTeamsTransformation.genRoleInterfaceName(baseClassName)); |
| if(logging) printLogMessage(baseClassName + " --> " + ObjectTeamsTransformation.genRoleInterfaceName(baseClassName)); |
| } |
| } |
| |
| // load '_OT$base' field: |
| InstructionHandle baseCallLine = il.append(InstructionFactory.createThis()); |
| il.append(factory.createFieldAccess(className, BASE, baseClass, Constants.GETFIELD)); |
| |
| if (!baseClass.getClassName().equals(boundBaseClassName)) { |
| // playedBy has been refined in the sub role; |
| // create a cast to the sub base class: |
| il.append(factory.createCast(baseClass, new ObjectType(boundBaseClassName))); |
| } |
| |
| // --- load arguments of the new method: --- |
| // (letters refer to document parameter-passing.odg) |
| |
| // (u) generate extra arguments (indices are equal at role and base): |
| if (!isSuperAccess) |
| for (int idx = 0; idx < EXTRA_ARGS; idx++) |
| il.append(InstructionFactory.createLoad(enhancedMethod.getArgumentTypes()[idx], |
| idx+1/*translating non-static*/)); |
| |
| // (v)(w)(x) split loading sequence and transfer source-level arguments |
| // (includes reverse-application of parameter mappings): |
| |
| // Start at EXTRA_ARGS, because one set of enhancement has already been loaded, |
| // except when doing super access which only has one extra arg: base instance. |
| int start = isSuperAccess ? 1 : EXTRA_ARGS; |
| il.append(translateLoads(splitLoading(cpg, |
| loading.copy(), |
| roleArgumentTypes), |
| enhancedMethod.getArgumentTypes(), |
| enhancedBaseArgumentTypes, |
| paramPositions, |
| extractTeamName(enhancedMethod.getClassName()), |
| className, |
| new BaseMethodInfo(mb.baseMethodIsCallin(), false/*static*/, mb.getTranslationFlags()), |
| start, |
| cpg)); |
| // --- done loading --- |
| |
| // invoke the chaining method of the base class (base-call!): |
| il.append(factory.createInvoke(boundBaseClassName, |
| baseChainMethodName, |
| baseChainReturnType, |
| enhancedBaseArgumentTypes, |
| invocationKind |
| )); |
| |
| boolean resultLiftingNecessary = ((mb.getTranslationFlags()&1)!=0); |
| |
| if (resultLiftingNecessary) { // call the lift-method: |
| // STATIC_PARTS_OK: in role: lift method call |
| String liftMethodName = mb.getLiftMethodName(); |
| Type liftMethodReturnType = Type.getReturnType(mb.getLiftMethodSignature()); |
| Type[] liftMethodArgs = Type.getArgumentTypes(mb.getLiftMethodSignature()); |
| |
| il.append(factory.createCast(baseChainReturnType, baseMethodReturnType)); |
| |
| // determine receiver for lift-call: |
| ObjectType liftType = (ObjectType)((liftMethodReturnType instanceof ArrayType) |
| ? ((ArrayType) liftMethodReturnType).getBasicType() |
| : liftMethodReturnType); |
| int liftedDepth = countOccurrences(liftType.getClassName(), '$'); |
| int thisNestingDepth = countOccurrences(className, '$'); |
| ObjectType liftReceiverType = outerClass; // default; |
| if (liftedDepth == thisNestingDepth) { |
| // normal case: liftedType is at the same nesting level as "this" |
| il.append(InstructionFactory.createThis()); |
| il.append(factory.createGetField(className, "this$" + (thisNestingDepth-1), outerClass)); // access enclosing team-this |
| } else if (liftedDepth == thisNestingDepth+1){ |
| // current role is already the team containing the role to get |
| liftReceiverType = new ObjectType(className); |
| il.append(new ALOAD(0)); |
| } else { |
| throw new OTREInternalError("Mismatching nesting levels for lift-call in base-call-surrogate: "+thisNestingDepth+" vs. "+liftedDepth); |
| } |
| |
| il.append(new SWAP()); // -> .., liftReceiver, (BaseType)result |
| |
| il.append(factory.createInvoke(liftReceiverType.getClassName(), |
| liftMethodName, |
| liftMethodReturnType, |
| liftMethodArgs, |
| Constants.INVOKEVIRTUAL)); |
| } |
| |
| InstructionHandle afterBaseCallLine = il.append(new NOP()); |
| |
| if (baseChainReturnType != Type.VOID) { |
| // adjust the return value to the type expected by the WRAPPER: |
| il.append(new DUP()); // keep for adjustment below |
| if (!resultLiftingNecessary) |
| adjustValue(il, null, baseChainReturnType, enhancedMethodReturnType); |
| il.append(InstructionFactory.createStore(enhancedMethodReturnType, |
| otResult.getIndex())); // store "globally" |
| // this store is needed to tunnel unused results through the callin. |
| |
| // adjust the return value to the type expected by the ORIGINAL CALLIN: |
| adjustValue(il, null, baseChainReturnType, returnType); |
| if (callinHasReturnValue) |
| il.append(InstructionFactory.createStore(returnType, localResult)); // store "locally" |
| // this store is useful for callins which make use of the result. |
| } |
| |
| targets[caseCounter] = nextBranch; |
| il.append(breaks[caseCounter]); |
| // generated: break; |
| |
| caseCounter++; |
| |
| if (debugging) { |
| enhancedMethod.addLineNumber(baseCallLine, STEP_INTO_LINENUMBER); |
| enhancedMethod.addLineNumber(afterBaseCallLine, STEP_OVER_LINENUMBER); |
| } |
| } |
| // Default case: throw an exception reporting the situation: |
| // create: String msg = ("Unhandled base-call case!"+base_method_tag) |
| InstructionList messagePush = new InstructionList(); |
| messagePush.append(factory.createNew(OTConstants.STRING_BUFFER_NAME)); |
| messagePush.append(new DUP()); |
| messagePush.append(factory.createInvoke(OTConstants.STRING_BUFFER_NAME, Constants.CONSTRUCTOR_NAME, Type.VOID, new Type[0], Constants.INVOKESPECIAL)); |
| messagePush.append(new PUSH(cpg, "Unhandled base-call case: ")); |
| messagePush.append(factory.createInvoke(OTConstants.STRING_BUFFER_NAME, "append", Type.STRINGBUFFER, new Type[]{Type.STRING}, Constants.INVOKEVIRTUAL)); |
| messagePush.append(InstructionFactory.createLoad(Type.INT, BASE_METH_ARG)); |
| messagePush.append(factory.createInvoke(OTConstants.STRING_BUFFER_NAME, "append", Type.STRINGBUFFER, new Type[]{Type.INT}, Constants.INVOKEVIRTUAL)); |
| messagePush.append(factory.createInvoke(OTConstants.STRING_BUFFER_NAME, "toString", Type.STRING, new Type[0], Constants.INVOKEVIRTUAL)); |
| // create: throw new OTREInternalError(msg) |
| InstructionHandle defaultCase = createThrowInternalError(cpg, il, messagePush); |
| |
| InstructionHandle afterSwitch = il.append(new NOP()); // all breaks point here. |
| |
| il.append(switchStart, createLookupSwitch(matches, targets, breaks, |
| defaultCase, afterSwitch)); |
| |
| // retrieve locally stored result: |
| if (callinHasReturnValue) { |
| il.append(InstructionFactory.createLoad(returnType, localResult)); |
| lg.setStart(il.getStart()); // restrict local variable to this segment. |
| lg.setEnd(il.getEnd()); |
| } |
| |
| return il; |
| } |
| |
| /** |
| * Removes duplicated method bindings with the same base call tag from the list, to avoid duplicated |
| * cases in the base call surrogate-switch. |
| * @param mbs |
| */ |
| private static void removeDuplicatedBaseMethodTags(List<MethodBinding> mbs) { |
| if (mbs.size() < 2) // nothing to remove |
| return; |
| |
| MethodBinding[] mbArray = mbs.toArray(new MethodBinding[mbs.size()]); |
| |
| Comparator<MethodBinding> baseCallTagComparator = new Comparator<MethodBinding>() { |
| public int compare(MethodBinding firstMB, MethodBinding secondMB) { |
| int firstBaseTag = CallinBindingManager.getBaseCallTag(firstMB.getBaseClassName(), |
| firstMB.getBaseMethodName(), |
| firstMB.getBaseMethodSignature()); |
| int secondBaseTag = CallinBindingManager.getBaseCallTag(secondMB.getBaseClassName(), |
| secondMB.getBaseMethodName(), |
| secondMB.getBaseMethodSignature()); |
| |
| if (firstBaseTag < secondBaseTag) |
| return -1; |
| if (firstBaseTag > secondBaseTag) |
| return 1; |
| return 0; |
| } |
| }; |
| Arrays.sort(mbArray, baseCallTagComparator); |
| for (int i = 0; i + 1 < mbArray.length; i++) { |
| if (baseCallTagComparator.compare(mbArray[i], mbArray[i + 1]) == 0) { |
| mbs.remove(mbArray[i + 1]); |
| } |
| } |
| } |
| |
| |
| /** |
| * @param className |
| * @return |
| */ |
| private static String extractTeamName(String roleClassName) { |
| int lastDollarIndex = roleClassName.lastIndexOf('$'); |
| return roleClassName.substring(0, lastDollarIndex); |
| } |
| |
| /** |
| * @param className |
| * @return |
| */ |
| private static String extractRoleName(String roleClassName) { |
| int lastDollarIndex = roleClassName.lastIndexOf('$'); |
| return roleClassName.substring(lastDollarIndex+1, roleClassName.length()); |
| } |
| |
| /** |
| * FIXME(SH): obsolete! |
| * @param baseMethodReturnType |
| * @param returnType |
| * @return |
| */ |
| // private static boolean returnTypeCompatible(Type from, Type to) { |
| // System.out.println("test for " + from + "->" + to); |
| // if (from.equals(to)) |
| // return true; |
| // if (from instanceof ObjectType && to instanceof ObjectType) {// how to handle compatible basic types?? |
| // ObjectType otFrom = (ObjectType) from; |
| // ObjectType otTo = (ObjectType) to; |
| // if (otFrom.subclassOf(otTo)) |
| // return true; |
| // } |
| // return false; |
| // } |
| |
| /** |
| * Copy all exception handlers of a method. |
| * @param source the method from where to copy |
| * @param dest the method where to copy to |
| * @param il instructions of `dest' which must still have the same positions |
| * as the instructions in `source'. |
| * @return an array of handler generators, which still has to be maintained, |
| * whenever instructions are replaced in the methods instruction list. |
| */ |
| static CodeExceptionGen[] copyExceptionHandlers(MethodGen source, |
| MethodGen dest, |
| InstructionList il) { |
| il.setPositions(); // needed to retrieve handles by position. |
| CodeExceptionGen[] excGens = source.getExceptionHandlers(); |
| CodeExceptionGen[] newGens = new CodeExceptionGen[excGens.length]; |
| if ((excGens != null) && excGens.length > 0) { |
| for (int hcount=0; hcount<excGens.length; hcount++) { |
| CodeExceptionGen excGen = excGens[hcount]; |
| InstructionHandle excStart = il.findHandle(excGen.getStartPC().getPosition()); |
| InstructionHandle excEnd = il.findHandle(excGen.getEndPC().getPosition()); |
| InstructionHandle excHandler = il.findHandle(excGen.getHandlerPC().getPosition()); |
| ObjectType catchType = excGen.getCatchType(); |
| newGens[hcount] = |
| dest.addExceptionHandler(excStart, excEnd, excHandler, catchType); |
| } |
| } |
| return newGens; |
| } |
| |
| /** |
| * Update the positions of all exception handlers. |
| * @param handlers the handlers of this method. |
| * @param replaced a pair of InstructionHandles, the first is replaced by the second. |
| */ |
| static void updateHandlers (CodeExceptionGen[] handlers, IHPair replaced) { |
| InstructionHandle old = replaced.fst(); |
| InstructionHandle neu = replaced.snd(); |
| for (int i=0; i<handlers.length; i++) { |
| // System.out.println("handler "+handlers[i]); |
| if (handlers[i].containsTarget(old) && (old != neu)) { |
| // System.out.println("update "+old+"->"+neu); |
| handlers[i].updateTarget(old, neu); |
| } |
| } |
| } |
| |
| /** |
| * Delete a range of instructions, whithout throwing TargetLostException. |
| * @param il the list to delete from |
| * @param start handle to first instruction to delete. |
| * @param end handle to last instruction to delete. |
| * @param collect a set of InstructionHandle which are still targeted. |
| */ |
| static void safeDelete(InstructionList il, |
| InstructionHandle start, InstructionHandle end, |
| HashSet<InstructionHandle> collect) |
| { |
| try { |
| il.delete(start, end); |
| } catch(TargetLostException e) { |
| // System.out.print("Loosing:"+e+" "); |
| InstructionHandle [] targets = e.getTargets(); |
| for (int tcount = 0; tcount < targets.length; tcount++) { |
| collect.add(targets[tcount]); |
| // System.out.println(targets[tcount]+"!!"); |
| } |
| } |
| } |
| |
| /** |
| * Update all exceptions handlers with respect to all instructions that were replaced. |
| * Check that this covers all instruction handles in 'lost'. |
| * @param handles the exception handlers of this method. |
| * @param replacedList list of IHPairs describing what has been modified. |
| * @param lost set of instruction handles that were still referred to when |
| * they were deleted. All these handles should be updated. |
| */ |
| static void checkUpdate(CodeExceptionGen[] handlers, ArrayList<IHPair> replacedList, HashSet<InstructionHandle> lost) { |
| // System.out.println("Update "+replacedList+"/"+lost); |
| Iterator<IHPair> iter = replacedList.iterator(); |
| while (iter.hasNext()) { |
| IHPair replaced = iter.next(); |
| updateHandlers(handlers, replaced); |
| lost.remove(replaced.fst()); |
| } |
| if (!lost.isEmpty()) { |
| System.err.println("Warning: "+lost.size()+" target(s) lost: "); |
| Iterator<InstructionHandle> it = lost.iterator(); |
| while (it.hasNext()) |
| System.out.println(it.next()); |
| } |
| } |
| |
| /** |
| * While adding extra args to an activate call: |
| * <ul> |
| * <li>Check whether an activationLevel was passed (single argument) |
| * <li>if so, insert the loading sequence for that expression. |
| * <li>also the return differs (void or int), should however be |
| * consistent between plain and enhanced version. |
| * <li>decrement "idx" so this team is the currently active in the chain. |
| * </ul> |
| * @param factory |
| * @param cpg |
| * @param loading sequence, which loads "level" argument, else null. |
| * @param iv the invokevirtual for "activate", used to inspect the |
| * original signature. |
| * @return the full sequence for loading the arguments, but not the |
| * call target (because that's the enclosing team and is kept |
| * unmodified in the original instruction list). |
| */ |
| // static InstructionList enhanceActivateCall (final InstructionFactory factory, |
| // ConstantPoolGen cpg, |
| // InstructionList loading, |
| // INVOKEVIRTUAL iv) { |
| // InstructionList changedArea = new InstructionList(); |
| // |
| // // load arguments of the new method: |
| // int index = 1; |
| // Type[] enhancedArgumentTypes = enhanceArgumentTypes(iv.getArgumentTypes(cpg), |
| // 0, false, false); |
| // Type returnType = Type.VOID; |
| // int kount = enhancedArgumentTypes.length; |
| // if (iv.getArgumentTypes(cpg).length == 1) { |
| // kount--; // "level" loaded separately via 'loading' |
| // returnType = Type.INT; |
| // } |
| // |
| // for (int k=0; k<kount; k++) { |
| // changedArea.append(InstructionFactory.createLoad(enhancedArgumentTypes[k], index)); |
| // index += enhancedArgumentTypes[k].getSize(); |
| // } |
| // if (loading != null) |
| // changedArea.append(loading); |
| // |
| // // invoke the overloaded activate method: |
| // changedArea.append(factory.createInvoke("org.objectteams.Team", |
| // "activate", |
| // returnType, |
| // enhancedArgumentTypes, |
| // Constants.INVOKEVIRTUAL)); |
| // |
| // // generate: idx--; |
| // changedArea.append(InstructionFactory.createLoad(Type.INT, IDX_ARG)); |
| // changedArea.append(new ICONST(1)); |
| // changedArea.append(new ISUB()); |
| // changedArea.append(InstructionFactory.createStore(Type.INT, IDX_ARG)); |
| // |
| // return changedArea; |
| // } |
| |
| /** |
| * Generates the instructions to call the enhanced version of the 'super' respectively 'tsuper' |
| * call in a role method. |
| * @param cpg the constant pool |
| * @param ii the original invoke instruction |
| * @param enhancedMethod the enhanced role method |
| * @param loading the originally loaded arguments |
| * @return the instruction list containing the method call ingredients |
| */ |
| @SuppressWarnings("deprecation") // ii.getClassName() is deprecated |
| InstructionList genEnhancedSuperCall(ConstantPoolGen cpg, InvokeInstruction ii, |
| MethodGen enhancedMethod, InstructionList loading) |
| { |
| Type returnType = enhancedMethod.getReturnType(); |
| //Type[] argTypes = enhancedMethod.getArgumentTypes(); |
| Type[] argTypes = enhanceArgumentTypes(ii.getArgumentTypes(cpg)); |
| InstructionList il = new InstructionList(); |
| |
| il.append(InstructionFactory.createThis()); |
| // load all additional arguments of the enhanced method (first EXTRA_ARGS): |
| int index = 1; |
| for (int i=0; i<EXTRA_ARGS; i++) { |
| il.append(InstructionFactory.createLoad(argTypes[i],index)); |
| index += argTypes[i].getSize(); |
| } |
| // load arguments of the originally call: |
| il.append(loading); |
| |
| // call super.<enhancedMethod>: |
| short kind=0; |
| if (ii instanceof INVOKESPECIAL) |
| kind = Constants.INVOKESPECIAL; |
| else |
| kind = Constants.INVOKEVIRTUAL; |
| |
| il.append(factory.createInvoke(ii.getClassName(cpg), // deprecated but we're safe because we have not array here (super/tsuper call) |
| ii.getMethodName(cpg), |
| returnType, |
| argTypes, |
| kind)); |
| return il; |
| } |
| |
| /** |
| * Generates the instructions to call the base call surrogate method. |
| * @param cpg the constant pool |
| * @param iv the original invoke instruction |
| * @param enhancedMethod the enhanced role method |
| * @param loading the originally loaded arguments |
| * @return the instruction list containing the method call ingredients |
| */ |
| @SuppressWarnings("deprecation") // iv.getClassName is deprecated |
| InstructionList genBaseCallSurrogateCall(ConstantPoolGen cpg, InvokeInstruction/*INVOKEVIRTUAL*/ iv, |
| MethodGen enhancedMethod, InstructionList loading, |
| String roleClassName, String calleeClassName) //JU: added String roleClassName and teamClassName to the method signature |
| { |
| int indexOffset = enhancedMethod.isStatic()?-1:0; // argument indexes are decremented for static methods, |
| // because of the missing 'this' |
| // for static methods callee is 'null' -> substitute by current class (JU) |
| if(calleeClassName == null) { |
| calleeClassName = iv.getClassName(cpg); // deprecated but we're safe because we have not array here (activate() call) |
| } |
| |
| Type returnType = enhancedMethod.getReturnType(); |
| //Type[] argTypes = enhancedMethod.getArgumentTypes(); |
| Type[] argTypes = enhanceArgumentTypes(iv.getArgumentTypes(cpg)); |
| InstructionList il = new InstructionList(); |
| |
| String methodName = getBaseCallSurrogateName(enhancedMethod.getName(), |
| enhancedMethod.isStatic(), |
| roleClassName); |
| short invokeKind; |
| |
| if (!enhancedMethod.isStatic()) { |
| il.append(InstructionFactory.createThis()); |
| invokeKind = Constants.INVOKEVIRTUAL; |
| |
| } else { // role method is static: |
| invokeKind = Constants.INVOKESTATIC; |
| } |
| |
| // load all additional arguments of the enhanced method (first EXTRA_ARGS): |
| int index = 1; |
| for (int i = 0; i < argTypes.length; i++) { |
| if(i < EXTRA_ARGS){ // skip original arguments |
| il.append(InstructionFactory.createLoad(argTypes[i], index+indexOffset)); |
| } |
| //calculate the next index |
| index += argTypes[i].getSize(); |
| } |
| // load arguments of the originally call: |
| il.append(loading); |
| |
| // call _OT$<enhancedMethod>$base(): |
| il.append(factory.createInvoke(calleeClassName, |
| methodName, |
| returnType, |
| argTypes, |
| invokeKind/*Constants.INVOKEVIRTUAL*/)); |
| return il; |
| } |
| |
| |
| /** |
| * Checks, if a given instuction is a super or a tsuper call |
| * @param instr the instruction to check |
| * @param method_name the name of the method from wich the 'inst' came |
| * @param cpg the constant pool |
| * @return true, if 'inst' is a super or tsuper call |
| */ |
| private static boolean super_or_tsuper_instruction(Instruction instr, String method_name, ConstantPoolGen cpg) { |
| if (isTSuperCall(instr, method_name, cpg)) |
| return true; |
| if (isSuperCall(instr, method_name, cpg)) |
| return true; |
| return false; |
| } |
| |
| private static boolean isTSuperCall(Instruction instr, String method_name, ConstantPoolGen cpg) { |
| if (instr instanceof INVOKEVIRTUAL) { |
| INVOKEVIRTUAL iv = (INVOKEVIRTUAL)instr; |
| String iv_name = iv.getName(cpg); |
| Type[] argTypes = iv.getArgumentTypes(cpg); |
| if (argTypes.length<1) // no tsuper marker interface parameter! |
| return false; |
| String lastArgument = (argTypes[argTypes.length-1]).toString(); |
| if (iv_name.equals(method_name) && (lastArgument.indexOf(TSUPER_PREFIX)!=-1)) { |
| // if iv == <method_name>(..., TSuper__OT__<SuperTeamName>) --> |
| if(logging) printLogMessage("tsuper-call to " + iv_name + " has to be enhanced!"); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static boolean isSuperCall(Instruction instr, String method_name, ConstantPoolGen cpg) { |
| if (instr instanceof INVOKESPECIAL) { |
| INVOKESPECIAL is = (INVOKESPECIAL)instr; |
| String is_name = is.getName(cpg); |
| |
| if(is_name.equals(method_name)) { |
| if(logging) printLogMessage("super-call to " + is_name + " has to be enhanced!"); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Generates the base call surrogate name for a given method name. |
| * @param method_name the name of the role method |
| * @param staticFlag |
| * @param roleClassName the name of the role method |
| * @return the base call surrogate name |
| */ |
| private static String getBaseCallSurrogateName(String method_name, boolean staticFlag, String roleClassName) { |
| //JU: for static methods the role class name should be inserted |
| if(staticFlag) { |
| return OT_PREFIX+roleClassName+"$"+method_name+"$base"; |
| } |
| return OT_PREFIX+method_name+"$base"; |
| } |
| |
| /** |
| * Reverts a method name to its original by returning the substring after the last '$' |
| * until the end. |
| * @param method_name method name to be adjusted |
| * @return the original method name |
| */ |
| private static String revertToOriginalName(String method_name) { |
| int p = method_name.lastIndexOf('$'); |
| return method_name.substring(p + 1); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.objectteams.otre.common.ObjectTeamsTransformation#doTransformCode(org.apache.bcel.generic.ClassGen) |
| */ |
| public void doTransformCode(ClassGen cg) { |
| // nothing to do |
| } |
| } |