blob: fb9436e8e5868e85486b99589047f4c9c069dc04 [file] [log] [blame]
/**********************************************************************
* This file is part of the "Object Teams Runtime Environment"
*
* Copyright 2002-2009 Berlin Institute of Technology, Germany.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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 static org.eclipse.objectteams.otre.StaticSliceBaseTransformation._OT_ACTIVE_TEAMS;
import static org.eclipse.objectteams.otre.StaticSliceBaseTransformation._OT_ACTIVE_TEAM_IDS;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Map.Entry;
import org.eclipse.objectteams.otre.util.BoundClass;
import org.eclipse.objectteams.otre.util.CallinBindingManager;
import org.eclipse.objectteams.otre.util.DebugUtil;
import org.eclipse.objectteams.otre.util.ListValueHashMap;
import org.eclipse.objectteams.otre.util.MethodBinding;
import org.eclipse.objectteams.otre.util.TeamIdDispenser;
import org.objectteams.OTREInternalError;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.LineNumber;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.AASTORE;
import org.apache.bcel.generic.ACONST_NULL;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ANEWARRAY;
import org.apache.bcel.generic.ARRAYLENGTH;
import org.apache.bcel.generic.ATHROW;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.BranchInstruction;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.DUP;
import org.apache.bcel.generic.DUP_X1;
import org.apache.bcel.generic.FieldGen;
import org.apache.bcel.generic.GOTO;
import org.apache.bcel.generic.IADD;
import org.apache.bcel.generic.ICONST;
import org.apache.bcel.generic.IFNE;
import org.apache.bcel.generic.IFNONNULL;
import org.apache.bcel.generic.IF_ICMPLT;
import org.apache.bcel.generic.IINC;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.LDC;
import org.apache.bcel.generic.LocalVariableGen;
import org.apache.bcel.generic.MONITOREXIT;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.NOP;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.POP;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.ReturnInstruction;
import org.apache.bcel.generic.TABLESWITCH;
import org.apache.bcel.generic.Type;
/**
* Insert dispatch code into base methods affected by callin bindings. <p>
* If a loaded class contains binding declarations (transmitted by attributes)
* these are stored for further determination of necessity for base-method-transforming.
* Classes for which a callin binding exists will be transformed:
* (base-class)methods <i>m()</i> which are changed by a callin will be copied to
* <tt>_OT$<i>m</i>$orig()</tt>.
* Hereafter the original method <i>m()</i> will be transformend depending on the
* callin-modifier:
* <ul>
* <li> replace: only the (role-)callin-method is called
* <li> after: first the original-method <tt>_OT$<i>m</i>$orig()</tt> is called and
* then the callin-method
* <li> before: first the the callin-method is called and then original-method
* <tt>_OT$<i>m</i>$orig()</tt>
* </ul>
*
* @version $Id: BaseMethodTransformation.java 23408 2010-02-03 18:07:35Z stephan $
* @author Christine Hundt
* @author Stephan Herrmann
*/
public class BaseMethodTransformation
extends ObjectTeamsTransformation
{
private static final String AFTER_INIT = "_OT$after_init$";
// configurability for stepping behavior of the chaining wrapper:
private static boolean SHOW_ORIG_CALL = true;
private static boolean SHOW_RECURSIVE_CALL = true;
private static boolean SHOW_ROLE_CALL = true;
static {
String callinStepping = System.getProperty("ot.debug.callin.stepping");
if (callinStepping != null) {
SHOW_ORIG_CALL = SHOW_RECURSIVE_CALL = SHOW_ROLE_CALL = false;
StringTokenizer tokens = new StringTokenizer(callinStepping, ",");
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
if ("orig".equals(token))
SHOW_ORIG_CALL = true;
else if ("recurse".equals(token))
SHOW_RECURSIVE_CALL = true;
else if ("role".equals(token))
SHOW_ROLE_CALL = true;
}
}
}
// method of o.o.Team:
private static final String IS_ACTIVE = "isActive"; //$NON-NLS-1$
private final static int NORESULT = -1;
private boolean classNeedsTransformation = false;
// methods awaiting statements as an initial wrapper:
private HashSet<String/*methodName*/> pendingInitialWrappers;
// methods awaiting a super call to an existing initial wrapper:
private HashSet<String/*methodName*/> pendingSuperDelegationWrappers;
public boolean useReflection = false;
public BaseMethodTransformation(Object loader) {
super(loader);
}
/**
* The code transformer only replaces the original code with
* the initial wrapper.
*/
public void doTransformCode(ClassGen cg) {
factory = new InstructionFactory(cg);
ConstantPoolGen cpg = cg.getConstantPool();
String class_name = cg.getClassName();
Method[] methods = cg.getMethods();
for (int i=0; i<methods.length; i++) {
Method m = methods[i];
//if (m.isNative())
// continue;
if (m.isVolatile())
continue; // don't touch bridge methods
String method_name = m.getName();
String method_signature = m.getSignature();
if (this.classNeedsTransformation) {
if (pendingInitialWrappers.contains(method_name + '.' + method_signature))
cg.replaceMethod(m, m = generateInitialWrapper(m, class_name, cg.getMajor(), cpg));
else if (pendingSuperDelegationWrappers.contains(method_name + '.' + method_signature))
cg.replaceMethod(m, m = generateSuperCall(m, cg, cpg));
Method replacement = checkReplaceWickedSuper(class_name, m, cpg);
if (replacement != null)
cg.replaceMethod(m, replacement);
}
}
}
/*
* If a base method m1 has a super-call super.m2() and if that method m2 is callin-bound
* we currently bypass aspect dispatch to avoid infinite recursions.
*/
private Method checkReplaceWickedSuper(String className, Method m, ConstantPoolGen cpg)
{
if (m.isAbstract() || m.isNative())
return null;
MethodGen mg = newMethodGen(m, className, cpg);
String method_name = m.getName();
InstructionHandle[] ihs = mg.getInstructionList().getInstructionHandles();
boolean found = false;
for (InstructionHandle ih : ihs) {
if (ih.getInstruction() instanceof INVOKESPECIAL) {
Instruction actInstruction = ih.getInstruction();
INVOKESPECIAL is = (INVOKESPECIAL)actInstruction;
String is_name = is.getName(cpg);
if ( !is_name.equals(method_name) // not same method
&& !is_name.equals("<init>")) // not ctor call
{
@SuppressWarnings("deprecation") // type of is (invokespecial) cannot be array type
String superClassName = is.getClassName(cpg);
if ( !superClassName.equals(className) // not private method of same class
&& CallinBindingManager.isBoundBaseMethod( // target method is callin-affected
superClassName,
is_name,
is.getSignature(cpg)))
{
found = true;
if(logging) printLogMessage("wicked super-call to " + is_name //$NON-NLS-1$
+ " has to be redirected to the orig-version!"); //$NON-NLS-1$
ih.setInstruction(factory.createInvoke(superClassName,
"_OT$"+is_name+"$orig",
is.getReturnType(cpg),
is.getArgumentTypes(cpg),
Constants.INVOKESPECIAL));
}
}
}
}
if (found)
return mg.getMethod();
return null;
}
/**
* Main entry for this transformer.
*/
public void doTransformInterface(ClassEnhancer ce, ClassGen cg) {
String class_name = cg.getClassName();
//SourceMapGeneration sourceMapGen = new SourceMapGeneration(cg);
ConstantPoolGen cpg = cg.getConstantPool();
factory = new InstructionFactory(cg);
if (CallinBindingManager.isBoundBaseClass(class_name)) {
// TODO: where to add the role set infrastructure, if only an interface is bound? Implementing classes?
if (cg.containsField(OTConstants.ROLE_SET) == null) { // TODO(SH): this doesn't help for interfaces, do we need to check more?
switch (CallinBindingManager.hasBoundBaseParent(class_name)) {
case NONE:
ce.addImplements(OTConstants.IBOUND_BASE, cg); // regardless of ifc or class
//$FALL-THROUGH$
case INTERFACE:
if (!cg.isInterface()) {
ce.addField(generateRoleSet(cpg, class_name), cg);
ce.addMethod(generateAddRole(cpg, class_name), cg);
ce.addMethod(generateRemoveRole(cpg, class_name), cg);
}
break;
case CLASS:
// do nothing, superclass has all the infra structure already
break;
}
}
}
if (pendingInitialWrappers == null)
pendingInitialWrappers = new HashSet<String>();
if (pendingSuperDelegationWrappers == null)
pendingSuperDelegationWrappers = new HashSet<String>();
checkReadClassAttributes(ce, cg, class_name, cpg);
//JU: change the class modifier to 'public' if decapsulation required
if(CallinBindingManager.checkBaseClassModifierChange(class_name) && !cg.isPublic()) {
cg.setAccessFlags(makePublicFlags(cg.getAccessFlags()));
}
Collection<MethodBinding> inheritedBindings = CallinBindingManager.getInheritedCallinBindings(class_name);
// if (inheritedSigns!=null && !inheritedSigns.isEmpty()) {
// Iterator itx = inheritedSigns.iterator();
// while (itx.hasNext()) {
// System.err.print(class_name+" : ");
// String[] next = (String[])itx.next();
// System.err.println(next[0] +" " +next[1]) ;
// }
// }
/*
String[] interfaceNames = cg.getInterfaceNames();
Collection interfaceInheritedSigns = new LinkedList();
//System.err.println("searching inherited bindings for: "+class_name);
for (int i=0; i<interfaceNames.length;i++) {
if (!interfaceNames[i].equals(class_name))
interfaceInheritedSigns.addAll(CallinBindingManager.getInterfaceInheritedCallinBindings(interfaceNames[i]));
}*/
/*
if (!interfaceInheritedSigns.isEmpty()) {
System.err.println("BMT: searching inherited bindings for: "+class_name);
for (Iterator iterator = interfaceInheritedSigns.iterator(); iterator.hasNext();) {
String[] element = (String[]) iterator.next();
System.err.println(element[0]+element[1]);
}
}*/
//inheritedSigns.addAll(interfaceInheritedSigns);
// if class is already transformed by this transformer
/*
if (interfaceTransformedClasses.contains(class_name))
continue;
*/
if(cg.isInterface()) {
// may need to add an interface version of the chaining wrapper, to be called for a base-call
int lastDollar = class_name.lastIndexOf('$');
if (lastDollar != -1) {
// try inserting __OT__ to last type name segment
String roleClassName = class_name.substring(0, lastDollar+1)+"__OT__"+class_name.substring(lastDollar+1);
if (CallinBindingManager.isBoundBaseClass(roleClassName)) {
Method[] methods = cg.getMethods();
for (int i=0; i<methods.length; i++) {
Method m = methods[i];
if (m.isVolatile()) // bridge method!
continue;
String method_name = m.getName();
String signature = m.getSignature();
Collection<MethodBinding> bindingsForMethod = CallinBindingManager .
getBindingForBaseMethod(roleClassName, method_name, signature);
if (bindingsForMethod!= null) {
MethodGen chainGen = generateChainingWrapper(class_name, cpg,
m.getAccessFlags()|Constants.ACC_ABSTRACT, method_name, signature, null/*argumentNames*/);
if (cg.containsMethod(chainGen.getName(), chainGen.getSignature()) == null)
ce.addMethod(chainGen.getMethod(), cg);
}
}
}
}
//CallinBindingManager.addBoundBaseInterface(class_name); // <- this is to late, implementing class may be loaded before!!
return; // No transfomations neccessary for interfaces.
}
boolean haveDirectCallin =
CallinBindingManager.isBoundBaseClass(class_name);
// IMPLICIT_INHERITANCE
if (inheritedBindings.size() == 0 && !haveDirectCallin /*&& interfaceInheritedSigns.size()==0*/) {
if(logging) printLogMessage("\nCallins: nothing to do for class " + class_name); //$NON-NLS-1$
return; // nothing to do
}
if(logging) printLogMessage("\nCallin bindings may be changing class " //$NON-NLS-1$
+ class_name + ':');
// A field to optimize class-literal in initial wrapper:
if (cg.getMajor() < 49) {// pre 1.5?
if (cg.containsField(OTConstants.SELF_CLASS) == null)
ce.addField(new FieldGen(Constants.ACC_PROTECTED|Constants.ACC_STATIC,
classType,
OTConstants.SELF_CLASS,
cpg)
.getField(),
cg);
BoundClass topBase = CallinBindingManager.getTopmostBoundBaseClass(class_name);
if (topBase != null && !topBase.getName().equals(class_name)) {
// initial wrapper will lock against that super-base, create another field for that class' literal:
ce.addField(new FieldGen(Constants.ACC_PROTECTED|Constants.ACC_STATIC,
classType,
OTConstants.CLASS+topBase.getName().replace('.','$'),
cpg)
.getField(),
cg);
}
}
Method[] methods = cg.getMethods();
for (int i=0; i<methods.length; i++) {
Method m = methods[i];
if (m.isVolatile()) // bridge method!
continue;
String method_name = m.getName();
String method_signature = m.getSignature();
Collection<MethodBinding> bindingsForMethod = null;
// IMPLICIT_INHERITANCE
if (haveDirectCallin)
bindingsForMethod = CallinBindingManager .
getBindingForBaseMethod(class_name, method_name, m.getSignature());
//JU: added the following statement to determine overridden static base methods
//Collection inheritedCallinBindings = CallinBindingManager.getInheritedCallinBindings(class_name);
//CH: removed it again, because it is the same as 'inheritedSigns'!
MethodGen mg = null;
int firstLine = STEP_OVER_LINENUMBER;
/*if (bindingsForMethod != null || containsSign(inheritedSigns, m)*/ /*|| containsSign(interfaceInheritedSigns, m)*/ //) {
MethodBinding inheritedBinding = matchingBinding(inheritedBindings, m, false);
String method_key = method_name+'.'+method_signature;
if (bindingsForMethod != null && INIT.equals(method_name)) {
//add method '_OT$after_init$' :
mg = getConcretMethodGen(m, class_name, cpg);
MethodGen chainGen = generateAfterConstructorWrapper(class_name, cpg,
mg.getAccessFlags(), method_name, method_signature, mg.getArgumentNames());
Method chain = generateAfterConstructorWrapperBody(class_name, cg, cpg,
mg, method_name, method_signature, chainGen, firstLine);
if (cg.containsMethod(chain.getName(), chain.getSignature()) == null)
ce.addMethod(chain, cg);
mg = new MethodGen(m, class_name, cpg);
InstructionList il = mg.getInstructionList();
InstructionHandle end = il.getEnd();
if (end.getInstruction() instanceof ReturnInstruction) {
// CodeExceptionGen[] excGens = mg.getExceptionHandlers(); // needed to update exception handlers?
end.setInstruction(new NOP());
InstructionHandle afterInitDispatch = il.append(new NOP());
createInitialDispatchCode(il, class_name, AFTER_INIT, mg, false, Type.VOID, cg.getMajor(), cpg);
InstructionHandle[] instructionHandles = il.getInstructionHandles();
for (InstructionHandle ih : instructionHandles) {
if (ih == afterInitDispatch) break;
if (ih.getInstruction() instanceof ReturnInstruction) {
ih.setInstruction(new NOP());
il.insert(ih, new GOTO(afterInitDispatch));
}
}
// tidy:
mg.setMaxStack();
mg.setMaxLocals();
Method generatedMethod = mg.getMethod();
il.dispose();
ce.addOrReplaceMethod(generatedMethod, cg);
}
continue;
}
if (bindingsForMethod != null || (inheritedBinding != null && !m.isStatic() && !m.isPrivate())) {
mg = newMethodGen(m, class_name, cpg);
Method orig_method;
String name_orig = genOrigMethName(method_name);
if (cg.containsMethod(name_orig, m.getSignature())!=null) {
continue;// method was already copied to orig-version!
}
if (debugging)
firstLine = findFirstLineNumber(m);
mg.setName(name_orig);
// TODO(SH): store this match, or keep previous?
if (matchingBinding(inheritedBindings, m, true) != null) // this method was adapted in a super class
replaceSuperCalls(mg, method_name, cpg);
orig_method = mg.getMethod();
ce.addMethod(orig_method, cg);
if(logging) printLogMessage("Method " + method_name + " was backuped as " //$NON-NLS-1$ //$NON-NLS-2$
+ name_orig + '.');
if (inheritedBinding != null) {
if (method_signature.equals(inheritedBinding.getBaseMethodSignature()))
pendingSuperDelegationWrappers.add(method_key);
else // override with covariant return: at the VM-level this is a *new* method, need a new initial wrapper
pendingInitialWrappers.add(method_key);
}
}
/*if (bindingsForMethod != null || (containsSign(inheritedSigns,m) && m.isStatic())*/ /*|| containsSign(interfaceInheritedSigns, m)*/ //) {
//CH: changed 'inheritedCallinBindings' to 'inheritedSigns', because it was the same.
if (bindingsForMethod != null) {
//add method '_OT$<method_name>$chain' :
mg = getConcretMethodGen(m, class_name, cpg);
MethodGen chainGen = generateChainingWrapper(class_name, cpg,
mg.getAccessFlags(), method_name, method_signature, mg.getArgumentNames());
Method chain = generateChainingWrapperBody(class_name, cg, cpg,
mg, method_name, method_signature, chainGen, firstLine);
if (cg.containsMethod(chain.getName(), chain.getSignature()) == null)
ce.addMethod(chain, cg);
pendingInitialWrappers.add(method_key);
pendingSuperDelegationWrappers.remove(method_key); // might have prematurely added this above
}
if (mg == null)
if (logging) printLogMessage("No method binding (direct or inherited) found for " //$NON-NLS-1$
+ method_name);
}
this.classNeedsTransformation = true;
}
private int findFirstLineNumber(Method m) {
LineNumberTable lnt = m.getLineNumberTable();
if (lnt != null && lnt.getTableLength() > 0) {
LineNumber[] lineNumberTable = lnt.getLineNumberTable();
for (int i=0; i<lineNumberTable.length; i++) {
int lineNumber = lineNumberTable[i].getLineNumber();
if (lineNumber != OTConstants.STEP_OVER_LINENUMBER)
return lineNumber;
}
return lineNumberTable[0].getLineNumber();
}
return STEP_OVER_LINENUMBER; // make it a valid line number
}
/**
* "super"-calls in callin bound base methods have to be redirected to the _OT$...$orig version, if the
* super-method is bound (and thus renamed to _OT$..$orig) too.
*
* @param orig_method the copied method
* @param method_name the prior name of the copied method
* @param cpg the corresponding constang pool
*/
private void replaceSuperCalls(MethodGen orig_method, String method_name, ConstantPoolGen cpg) {
// search for super calls:
InstructionList il = orig_method.getInstructionList();
InstructionHandle[] ihs = il.getInstructionHandles();
int actInstrIndex = 0;
while (actInstrIndex < ihs.length) {
if (ihs[actInstrIndex].getInstruction() instanceof INVOKESPECIAL) {
Instruction actInstruction = ihs[actInstrIndex].getInstruction();
INVOKESPECIAL is = (INVOKESPECIAL)actInstruction;
String is_name = is.getName(cpg);
if(is_name.equals(method_name)) {
@SuppressWarnings("deprecation") // type if is (invokespecial) cannot be array type
String superClassName = is.getClassName(cpg);
if(logging) printLogMessage("super-call to " + is_name //$NON-NLS-1$
+ " has to be redirected to the orig-version!"); //$NON-NLS-1$
// generate and set an instruction calling the orig-version of the super method:
InvokeInstruction superOrigCall = factory.createInvoke(superClassName,
orig_method.getName(),
orig_method.getReturnType(),
orig_method.getArgumentTypes(),
Constants.INVOKESPECIAL);
ihs[actInstrIndex].setInstruction(superOrigCall);
}
}
actInstrIndex++;
}
// redirect them ( change called method name to _OT$..$orig ) if super-method has been renamed:
// -->
}
/**
* Is method `m' contained in `baseMethodBindings'?
* @param nameSigns list of MethodBinding
* @param m
* @param strict if true covariance must not be considered
* @return the matching binding from baseMethodBindings or null.
*/
static MethodBinding matchingBinding (Collection<MethodBinding> baseMethodBindings, Method m, boolean strict)
{
for (MethodBinding binding: baseMethodBindings)
if (binding.matchesMethod(m.getName(), m.getSignature(), strict))
return binding;
return null;
}
/**
* Get a MethodGen for `m'.
* If `m' is abstract setup a new concrete method.
*/
static MethodGen getConcretMethodGen (Method m, String class_name, ConstantPoolGen cpg) {
MethodGen mg;
String signature = m.getSignature();
Type[] argTypes = Type.getArgumentTypes(signature);
if (m.isAbstract()) {
Type returnType = Type.getReturnType(signature);
InstructionList il = new InstructionList();
il.append(new NOP());
mg = new MethodGen(m.getAccessFlags()&~Constants.ACC_ABSTRACT,
returnType, argTypes,
null, // names are unknown
m.getName(), class_name,
il, cpg);
} else {
mg = wipeMethod(m, class_name, cpg);
}
if (debugging) {
mg.removeLocalVariables();
int slot = 0;
// create local variable table for "this" and arguments:
if (!m.isAbstract())
mg.addLocalVariable("this", new ObjectType(class_name), slot++, null, null);
for (int i=0; i<argTypes.length; i++)
mg.addLocalVariable("arg"+i, argTypes[i], slot++, null, null);
mg.setMaxLocals();
}
return mg;
}
/**
* Generate the initial wrapper for the passed mehtod.
* It binds callin dispatch to Team-unaware client code.
* @param m the original method
* @param class_name the name of the appropriate class
* @param major
* @param cpg the ConstantPoolGen of the class
* @return the generated method
*/
private Method generateInitialWrapper(Method m,
String class_name,
int major,
ConstantPoolGen cpg)
{
MethodGen mg = getConcretMethodGen(m, class_name, cpg);
String method_name = m.getName();
String name_chain = genChainMethName(method_name);
InstructionList il = mg.getInstructionList();
createInitialDispatchCode(il, class_name, name_chain, mg, m.isStatic(), object, major, cpg);
// tidy:
mg.setMaxStack();
mg.setMaxLocals();
Method generatedMethod = mg.getMethod();
il.dispose();
return generatedMethod;
}
private void createInitialDispatchCode(InstructionList il,
String class_name,
String name_chain,
MethodGen mg,
boolean isStatic,
Type chainReturnType,
int major,
ConstantPoolGen cpg)
{
Type[] argTypes = mg.getArgumentTypes();
Type returnType = mg.getReturnType();
InstructionHandle startSynchronized;
InstructionHandle chainCall;
int monitor;
// start generating
{
LocalVariableGen lg; // used for several local variables
// Team[] _OT$teams;
int teams;
lg = mg.addLocalVariable(TEAMS, teamArray, null, null);
teams = lg.getIndex();
// synchronized (TopMostBoundBaseClass.class) {
BoundClass superBase = CallinBindingManager.getTopmostBoundBaseClass(class_name);
String classNameForLiteral = superBase != null ? superBase.getName() : class_name;
Pair<Integer,InstructionHandle> monitorResult = addClassMonitorEnter(mg, il, class_name, classNameForLiteral, major, cpg);
monitor = monitorResult.first;
if (debugging)
// no natural lines in this method: step-over until chain call, which has step-into: debugging => addLineNumber
mg.addLineNumber(monitorResult.second, STEP_OVER_LINENUMBER);
// _OT$teams= new Teams[_OT$activeTeams.length];
startSynchronized= // begin area protected by exception handler
il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC));
il.append(InstructionConstants.ARRAYLENGTH);
il.append((Instruction)factory.createNewArray(teamType, (short) 1));
il.append(InstructionFactory.createStore(Type.OBJECT, teams));
// _OT$teamIDs=new int[_OT$activeTeamIDs.length];
int teamIDs;
lg = mg.addLocalVariable(TEAMIDS, intArray, null, null);
teamIDs = lg.getIndex();
il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.GETSTATIC));
il.append(InstructionConstants.ARRAYLENGTH);
il.append((Instruction)factory.createNewArray(Type.INT, (short) 1));
il.append(InstructionFactory.createStore(Type.OBJECT, teamIDs));
// for (int i=0; i<_OT$activeTeams.length; ...
int for_index;
lg = mg.addLocalVariable("i", Type.INT, null, null); //$NON-NLS-1$
for_index = lg.getIndex();
il.append(new PUSH(cpg, 0));
il.append(InstructionFactory.createStore(Type.INT, for_index));
InstructionHandle for_start =
il.append(InstructionFactory.createLoad(Type.INT, for_index));
il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC));
il.append(InstructionConstants.ARRAYLENGTH);
BranchInstruction if_loop_finished =
InstructionFactory.createBranchInstruction(Constants.IF_ICMPGE, null);
il.append(if_loop_finished);
// if (!_OT$activeTeams[i].isActive()) {
il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC));
il.append(InstructionFactory.createLoad(Type.INT, for_index));
il.append(InstructionConstants.AALOAD);
il.append(factory.createInvoke(OTConstants.teamName, IS_ACTIVE, Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEINTERFACE));
BranchInstruction if_team_isactive =
InstructionFactory.createBranchInstruction(Constants.IFNE, null);
il.append(if_team_isactive);
// invalidate activation of current team:
// _OT$teams[i]= null;
il.append(InstructionFactory.createLoad(Type.OBJECT, teams));
il.append(InstructionFactory.createLoad(Type.INT, for_index));
il.append(InstructionConstants.ACONST_NULL);
il.append(InstructionConstants.AASTORE);
// _OT$teamIDs[i]= -1;
il.append(InstructionFactory.createLoad(Type.OBJECT, teamIDs));
il.append(InstructionFactory.createLoad(Type.INT, for_index));
il.append(new PUSH(cpg, -1));
il.append(InstructionConstants.IASTORE);
BranchInstruction goto_continue =
InstructionFactory.createBranchInstruction(Constants.GOTO, null);
il.append(goto_continue);
// } else { adopt activation of current team:
// _OT$teams[i]= _OT$activeTeams[i];
InstructionHandle adopt_activation =
il.append(InstructionFactory.createLoad(Type.OBJECT, teams));
il.append(InstructionFactory.createLoad(Type.INT, for_index));
il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAMS, teamArray, Constants.GETSTATIC));
il.append(InstructionFactory.createLoad(Type.INT, for_index));
il.append(InstructionConstants.AALOAD);
il.append(InstructionConstants.AASTORE);
// _OT$teamIDs[i]= _OT$activeTeamIDs[i];
il.append(InstructionFactory.createLoad(Type.OBJECT, teamIDs));
il.append(InstructionFactory.createLoad(Type.INT, for_index));
il.append(factory.createFieldAccess(class_name, _OT_ACTIVE_TEAM_IDS, intArray, Constants.GETSTATIC));
il.append(InstructionFactory.createLoad(Type.INT, for_index));
il.append(InstructionConstants.IALOAD);
il.append(InstructionConstants.IASTORE);
// closing the above for: i++ and jump back:
InstructionHandle do_continue =
il.append(new IINC(for_index, 1));
il.append(InstructionFactory.createBranchInstruction(Constants.GOTO, for_start));
// link jump instructions to their targets:
InstructionHandle loop_finished = il.append(new NOP()); // synthetic jump target
if_loop_finished.setTarget(loop_finished);
if_team_isactive.setTarget(adopt_activation);
goto_continue.setTarget(do_continue);
// No more access to array fields, release monitor:
il.append(InstructionFactory.createLoad(Type.OBJECT, monitor));
il.append(new MONITOREXIT());
// load special arguments:
if(!isStatic) { // "this" cannot be accessed by static methods
chainCall= il.append(InstructionFactory.createThis()); // this
} else {
chainCall= il.append(new NOP());
}
if (debugging)
mg.addLineNumber(chainCall, STEP_INTO_LINENUMBER);
// ih now points to end of area protected by exception handler
il.append(InstructionFactory.createLoad(teamArray, teams)); // _OT$teams
il.append(InstructionFactory.createLoad(intArray, teamIDs)); // _OT$teamIDs
il.append(new ICONST(0)); // _OT$idx = 0
il.append(new ICONST(0)); // _OT$bindIdx = 0
il.append(new ICONST(-1)); // _OT$baseMethTag is unused here
il.append(new ACONST_NULL()); // _OT$unusedArgs = null
}
// load regular arguments:
int index = isStatic?0:1;
// chaining wrapper is always public, so never user INVOKESPECIAL:
short invocationKind = isStatic?Constants.INVOKESTATIC:Constants.INVOKEVIRTUAL;
for (int i=0; i<argTypes.length; i++) {
il.append(InstructionFactory.createLoad(argTypes[i],index));
index += argTypes[i].getSize();
}
//
il.append(factory.createInvoke(class_name, name_chain,
chainReturnType,
enhanceArgumentTypes(argTypes),
invocationKind));
// generated invoke: _OT$<method_name>$chain (a1,.. aN)
adjustValue(il, null, chainReturnType, returnType);
il.append(InstructionFactory.createReturn(returnType));
// handler for exception within synchronized:
LocalVariableGen ex= mg.addLocalVariable("exceptionInSynchronized", Type.THROWABLE, il.getEnd(), null); //$NON-NLS-1$
InstructionHandle handler=
il.append(InstructionFactory.createStore(Type.THROWABLE, ex.getIndex()));
il.append(InstructionFactory.createLoad(Type.OBJECT, monitor));
il.append(new MONITOREXIT());
il.append(InstructionFactory.createLoad(Type.THROWABLE, ex.getIndex()));
il.append(new ATHROW());
mg.addExceptionHandler(startSynchronized, chainCall, handler, Type.THROWABLE);
}
/**
* Generate a version of the passed method which only calls its super method
* (which will eventually call an initial-wrapper in a directly bound class).
* @param m the original method
* @param cg the ClassGen of the appropriate class
* @param cpg the ConstantPoolGen of the class
* @return the generated method
*/
Method generateSuperCall (Method m,
ClassGen cg,
ConstantPoolGen cpg)
{
short invocationKind = m.isStatic() ? Constants.INVOKESTATIC : Constants.INVOKESPECIAL;
MethodGen mg = getConcretMethodGen(m, cg.getClassName(), cpg);
String method_name = m.getName();
Type returnType = mg.getReturnType();
Type[] argTypes = mg.getArgumentTypes();
InstructionList il = mg.getInstructionList();
if(logging) printLogMessage("\nReplacing with call to super: " + method_name); //$NON-NLS-1$
// start generating
if(!m.isStatic()){ //JU
il.append(InstructionFactory.createThis());
}
int index = 1;
for (int i=0; i<argTypes.length; i++) {
il.append(InstructionFactory.createLoad(argTypes[i],index));
index += argTypes[i].getSize();
}
il.append(factory.createInvoke(cg.getSuperclassName(), method_name,
returnType, argTypes,
invocationKind));
il.append(InstructionFactory.createReturn(returnType));
// generated invoke: return super.<method_name> (a1,.. aN)
// tidy:
mg.setMaxStack();
mg.setMaxLocals();
Method generatedMethod = mg.getMethod();
il.dispose();
return generatedMethod;
}
/**
* Generate a chaining wrapper for the original method described by the passed arguments.
* @param class_name the name of the appropriate class
* @param cpg the ConstantPoolGen of the class
* @param accessFlags raw flags of the method to add, visibility will however be ignored/set to public inside
* @param method_name the name of the original method
* @param method_signature the signature of the original method
* @param argumentNames source level argument names, may be null
* @return an new method with an empty instruction list
*/
MethodGen generateChainingWrapper(String class_name,
ConstantPoolGen cpg,
int accessFlags,
String method_name,
String method_signature,
String[] argumentNames)
{
Type[] argumentTypes = Type.getArgumentTypes(method_signature);
if (argumentNames == null) {
argumentNames = new String[argumentTypes.length];
for (int i = 0; i < argumentNames.length; i++)
argumentNames[i] = "arg"+i;
}
return new MethodGen(makePublicFlags(accessFlags), // the chaining wrapper has to be 'public' because it will be called by base calls:
object, // ALWAYS!
enhanceArgumentTypes(argumentTypes),
enhanceArgumentNames(argumentNames),
genChainMethName(method_name),
class_name,
new InstructionList(),
cpg);
}
/**
* Create the instructions for the chaining wrapper, which includes dispatch code
* and the termination condition for the recursion.
* @param class_name
* @param cg
* @param cpg
* @param mg
* @param method_name
* @param method_signature
* @param chainMethod
* @param firstLine
* @return
*/
Method generateChainingWrapperBody(String class_name,
ClassGen cg,
ConstantPoolGen cpg,
MethodGen mg,
String method_name,
String method_signature,
MethodGen chainMethod,
int firstLine)
{
Type origReturnType = mg.getReturnType();
Type[] argumentTypes = mg.getArgumentTypes();
Type enhancedReturnType = chainMethod.getReturnType();
InstructionList il = chainMethod.getInstructionList();
// All chaining calls return an Object.
// Need to store this in a local variable to keep the stack
// balanced, because each section (before, replace, after)
// is guarded by its own exception handler, and they don't
// like pending objects on the stack.
InstructionHandle ih;
int result, ot_team;
{
LocalVariableGen lg =
chainMethod.addLocalVariable("_OT$result", enhancedReturnType, //$NON-NLS-1$
null, null);
result = lg.getIndex();
ih = il.append(InstructionFactory.createNull(enhancedReturnType));
if (debugging)
chainMethod.addLineNumber(ih, STEP_OVER_LINENUMBER);
lg.setStart(il.append(InstructionFactory.createStore(enhancedReturnType, result)));
// generated: RType _OT$result = null;
lg = chainMethod.addLocalVariable("_OT$team", teamType, null, null); //$NON-NLS-1$
ot_team = lg.getIndex();
// generated: Team _OT$team;
}
int indexOffset = chainMethod.isStatic() ? -1 : 0; // argument indizes are decremented for static methods,
// because of the missing 'this'
short invocationKind = getInvocationType(mg);
il.append(InstructionFactory.createLoad(Type.INT, IDX_ARG + indexOffset));
il.append(InstructionFactory.createLoad(teamArray, TEAMS_ARG + indexOffset));
il.append(new ARRAYLENGTH());
IF_ICMPLT recursionNotYetTerminated = new IF_ICMPLT(null);
il.append(recursionNotYetTerminated);
// generated: if (_OT$teams.length < _OT$idx) {
// load arguments:
if (!chainMethod.isStatic()) {
ih = il.append(InstructionFactory.createThis());
} else {
ih = il.append(new NOP());
}
if (debugging)
chainMethod.addLineNumber(ih, SHOW_ORIG_CALL ? firstLine : STEP_INTO_LINENUMBER); // show orig call at method header ("dispatching")
int index = EXTRA_ARGS + 1;
for (int i = 0; i < argumentTypes.length; i++) {
il.append(InstructionFactory.createLoad(argumentTypes[i], index + indexOffset));
index += argumentTypes[i].getSize();
}
//
il.append(factory.createInvoke(class_name, genOrigMethName(method_name),
origReturnType, argumentTypes,
invocationKind));
// generated: this._OT$<method_name>$orig (a1,.., aN) for nonstatic case
// _OT$<method_name>$orig (a1,.., aN) for static case
if (debugging)
chainMethod.addLineNumber(il.append(new NOP()), STEP_OVER_LINENUMBER);
adjustValue(il, null, origReturnType, enhancedReturnType);
il.append(InstructionFactory.createReturn(enhancedReturnType));
// generated: return _OT$result;
ih = il.append(new NOP());
recursionNotYetTerminated.setTarget(ih);
// generated: ; (end of the if part)
il.append(InstructionFactory.createLoad(teamArray, TEAMS_ARG + indexOffset));
il.append(InstructionFactory.createLoad(Type.INT, IDX_ARG + indexOffset));
il.append(InstructionFactory.createArrayLoad(teamType));
il.append(InstructionFactory.createStore(teamType, ot_team));
// generated: _OT$team = _OT$teams[_OT$idx];
// ---------------------------------------------
createDispatchCode(chainMethod, il,
class_name, method_name,
method_signature, result, ot_team, cg, firstLine);
// ---------------------------------------------
ih = il.append(InstructionFactory.createLoad(enhancedReturnType, result));
il.append(InstructionFactory.createReturn(enhancedReturnType));
// generated: return _OT$result;
if (debugging)
chainMethod.addLineNumber(ih, STEP_OVER_LINENUMBER);
// tidy:
chainMethod.removeNOPs();
try { // [SH]: overcautious: I once saw this CCE with no clue, why it happened :(
chainMethod.setMaxStack();
} catch (ClassCastException cce) {
System.err.println(chainMethod);
cce.printStackTrace();
}
chainMethod.setMaxLocals();
//chainMethod.removeNOPs();
Method generated = chainMethod.getMethod();
il.dispose();
return generated;
}
/**
* Generate a chaining wrapper for the original method described by the passed arguments.
* @param class_name the name of the appropriate class
* @param cpg the ConstantPoolGen of the class
* @param accessFlags raw flags of the method to add, visibility will however be ignored/set to public inside
* @param method_name the name of the original method
* @param method_signature the signature of the original method
* @param argumentNames source level argument names, may be null
* @return an new method with an empty instruction list
*/
MethodGen generateAfterConstructorWrapper(String class_name,
ConstantPoolGen cpg,
int accessFlags,
String method_name,
String method_signature,
String[] argumentNames)
{
Type[] argumentTypes = Type.getArgumentTypes(method_signature);
if (argumentNames == null) {
argumentNames = new String[argumentTypes.length];
for (int i = 0; i < argumentNames.length; i++)
argumentNames[i] = "arg"+i;
}
return new MethodGen(accessFlags,
Type.VOID, // ALWAYS!
enhanceArgumentTypes(argumentTypes),
enhanceArgumentNames(argumentNames),
AFTER_INIT,
class_name,
new InstructionList(),
cpg);
}
/**
* Create the instructions for the chaining wrapper, which includes dispatch code
* and the termination condition for the recursion.
* @param class_name
* @param cg
* @param cpg
* @param mg
* @param method_name
* @param method_signature
* @param chainMethod
* @param firstLine
* @return
*/
Method generateAfterConstructorWrapperBody(String class_name,
ClassGen cg,
ConstantPoolGen cpg,
MethodGen mg,
String method_name,
String method_signature,
MethodGen chainMethod,
int firstLine)
{
InstructionList il = chainMethod.getInstructionList();
InstructionHandle ih;
int ot_team;
{
LocalVariableGen lg = chainMethod.addLocalVariable("_OT$team", teamType, null, null); //$NON-NLS-1$
ot_team = lg.getIndex();
// generated: Team _OT$team;
}
il.append(InstructionFactory.createLoad(Type.INT, IDX_ARG));
il.append(InstructionFactory.createLoad(teamArray, TEAMS_ARG));
il.append(new ARRAYLENGTH());
IF_ICMPLT recursionNotYetTerminated = new IF_ICMPLT(null);
il.append(recursionNotYetTerminated);
// generated: if (_OT$teams.length < _OT$idx) {
// terminate the recursion:
il.append(InstructionFactory.createReturn(Type.VOID));
// generated: return;
ih = il.append(new NOP());
recursionNotYetTerminated.setTarget(ih);
// generated: ; (end of the if part)
il.append(InstructionFactory.createLoad(teamArray, TEAMS_ARG));
il.append(InstructionFactory.createLoad(Type.INT, IDX_ARG));
il.append(InstructionFactory.createArrayLoad(teamType));
il.append(InstructionFactory.createStore(teamType, ot_team));
// generated: _OT$team = _OT$teams[_OT$idx];
// ---------------------------------------------
createAfterCtorDispatchCode(chainMethod, il,
class_name, method_name,
method_signature, ot_team, cg, firstLine);
// ---------------------------------------------
il.append(InstructionFactory.createReturn(Type.VOID));
if (debugging)
chainMethod.addLineNumber(ih, STEP_OVER_LINENUMBER);
// tidy:
chainMethod.removeNOPs();
try { // [SH]: overcautious: I once saw this CCE with no clue, why it happened :(
chainMethod.setMaxStack();
} catch (ClassCastException cce) {
System.err.println(chainMethod);
cce.printStackTrace();
}
chainMethod.setMaxLocals();
//chainMethod.removeNOPs();
Method generated = chainMethod.getMethod();
il.dispose();
return generated;
}
private short getInvocationType(MethodGen chainMethod) {
if (chainMethod.isStatic())
return Constants.INVOKESTATIC;
if (chainMethod.isPrivate())
return Constants.INVOKESPECIAL;
else
return Constants.INVOKEVIRTUAL;
}
/**
* @param i
* @return
*/
private static int makePublicFlags(int flags) {
if ((flags & Constants.ACC_PUBLIC) != 0) {
return flags;
}
if ((flags & Constants.ACC_PRIVATE) != 0) {
flags &= ~Constants.ACC_PRIVATE;
} else if ((flags & Constants.ACC_PROTECTED) != 0) {
flags &= ~Constants.ACC_PROTECTED;
}
flags |= Constants.ACC_PUBLIC;
return flags;
}
/**
* Generate the dispatch code by which a chaining wrapper invokes the
* callin method(s).
* This consists of three switch blocks: before, replace, after.
* @param chainMethod the chaining wrapper method
* @param il the InstructionList of the chaining wrapper
* @param class_name the name of the appropriate class
* @param method_name the name of the original method
* @param method_signature the signature of the original method
* @param result the index of the '_OT$result' variable
* @param ot_team the index of the variable containing the team currently processed by the chaining wrapper
* @param cg the ClassGen of the appropriate class
* @param firstLine the first real source line of this method
*/
void createDispatchCode(MethodGen chainMethod, InstructionList il,
String class_name, String method_name,
String method_signature, int result, int ot_team, ClassGen cg, int firstLine)
{
// get bindings for actually modified methods and sort them by callin-modifier:
// modifier -> ArrayList<MethodBinding>
HashMap<String, ArrayList<MethodBinding>> sortedMethodBindings = new HashMap<String, ArrayList<MethodBinding>>();
Collection<MethodBinding> callinsForMethod;
callinsForMethod = CallinBindingManager.getBindingForBaseMethod(class_name,
method_name, method_signature);
//System.err.println(class_name +" : "+callinsForMethod);
List<MethodBinding> inheritedMethodBindings = CallinBindingManager.getInheritedBaseMethodBindings(class_name, method_name, method_signature);
//System.err.println(inheritedMethodBindings);
//--------------------------------------------------------------------------------------------
//JU: initialize callinsForMethod for overridden static base methods (begin)
// if(chainMethod.isStatic()){
// if(callinsForMethod == null) {
// callinsForMethod = new LinkedList();
// }
//
// Collection classesDefBindingsToStaticMethods = CallinBindingManager.getInheritedCallinBindingsForStaticMethods(class_name, method_name, method_signature);
// Iterator classesIt = classesDefBindingsToStaticMethods.iterator();
// while(classesIt.hasNext()){
// String superClassName = (String) classesIt.next();
// Collection callinsFromSuperClasses = CallinBindingManager.getBindingForBaseMethod(superClassName, method_name, method_signature);
// callinsForMethod.addAll(callinsFromSuperClasses);
// }
// }
//JU: (end)
//----------------------------------------------------------------------------------------------
if (!chainMethod.isStatic())
callinsForMethod.addAll(inheritedMethodBindings);
/*
String[] interfaceNames = cg.getInterfaceNames();
Collection interfaceInheritedMethodBindings = new LinkedList();
for (int i=0; i<interfaceNames.length;i++) {
if (!interfaceNames[i].equals(class_name))
interfaceInheritedMethodBindings.addAll(
CallinBindingManager.getInterfaceInheritedMethodBindings(method_name,
method_signature,
interfaceNames[i]));
}
if (callinsForMethod == null)
callinsForMethod = new LinkedList();
callinsForMethod.addAll(interfaceInheritedMethodBindings);
*/
ListValueHashMap<MethodBinding> beforeBindings = new ListValueHashMap<MethodBinding>();
ListValueHashMap<MethodBinding> replaceBindings = new ListValueHashMap<MethodBinding>();
ListValueHashMap<MethodBinding> afterBindings = new ListValueHashMap<MethodBinding>();
Iterator<MethodBinding> it = callinsForMethod.iterator();
while (it.hasNext()) {
MethodBinding methodBinding = it.next();
//sourceMapGen.addSourceMapInfo(methodBinding);
String modifier = methodBinding.getModifier();
ArrayList<MethodBinding> bindings = sortedMethodBindings.get(modifier);
if (bindings == null) {
bindings = new ArrayList<MethodBinding>();
sortedMethodBindings.put(modifier, bindings);
}
bindings.add(methodBinding);
// ----> added for predecedence purpose:
String teamName = methodBinding.getTeamClassName();
if (modifier.equals("before")) { //$NON-NLS-1$
beforeBindings.put(teamName, methodBinding);
} else if (modifier.equals("replace")) { //$NON-NLS-1$
replaceBindings.put(teamName, methodBinding);
} else if (modifier.equals("after")) { //$NON-NLS-1$
afterBindings.put(teamName, methodBinding);
}
// <---
}
// if any team has multiple bindings, we need to insert additional checks to avoid duplicate
// invocation of before/after causes during recursion.
boolean useBindingIdx = false;
for (LinkedList<MethodBinding> perTeamMethods : replaceBindings.valueSet())
if (perTeamMethods.size() > 1) {
useBindingIdx = true;
break;
}
/****************************************************************************/
// before callin :
if (sortedMethodBindings.containsKey("before")) { //$NON-NLS-1$
if(logging) printLogMessage("before bindings will be applied..."); //$NON-NLS-1$
il.append(createSwitch(
beforeBindings,
chainMethod, ot_team,
NORESULT, firstLine, cg.getMajor(), useBindingIdx));
if(logging) printLogMessage("before bindings: " //$NON-NLS-1$
+ sortedMethodBindings.get("before")); //$NON-NLS-1$
}
/****************************************************************************/
// replacement callin or direct recursion :
if (sortedMethodBindings.containsKey("replace")) { //$NON-NLS-1$
if(logging) printLogMessage("recursive call and replace bindings will be applied..."); //$NON-NLS-1$
il.append(createSwitch(
replaceBindings,
chainMethod, ot_team,
result, firstLine, cg.getMajor(), useBindingIdx));
if(logging) printLogMessage("replace bindings: " //$NON-NLS-1$
+ sortedMethodBindings.get("replace")); //$NON-NLS-1$
} else {
if(logging) printLogMessage("recursive chain-method call will be done..."); //$NON-NLS-1$
// recursive call:
createRecursiveCall(il, chainMethod, result, 1, 0, method_name, method_signature, firstLine);
}
/****************************************************************************/
// after callin :
if (sortedMethodBindings.containsKey("after")) { //$NON-NLS-1$
if(logging) printLogMessage("after bindings will be applied..."); //$NON-NLS-1$
il.append(createSwitch(
afterBindings,
chainMethod, ot_team,
/*NORESULT*/result, firstLine, cg.getMajor(), useBindingIdx));
if(logging) printLogMessage("after bindings: " //$NON-NLS-1$
+ sortedMethodBindings.get("after")); //$NON-NLS-1$
}
}
/**
* Generate the dispatch code by which a chaining wrapper invokes the
* callin method(s).
* For constructors we only have one block: after.
* @param chainMethod the chaining wrapper method
* @param il the InstructionList of the chaining wrapper
* @param class_name the name of the appropriate class
* @param method_name the name of the original method
* @param method_signature the signature of the original method
* @param ot_team the index of the variable containing the team currently processed by the chaining wrapper
* @param cg the ClassGen of the appropriate class
* @param firstLine the first real source line of this method
*/
void createAfterCtorDispatchCode(MethodGen chainMethod, InstructionList il,
String class_name, String method_name,
String method_signature, int ot_team, ClassGen cg, int firstLine)
{
// no sort by modifier since we only handle "after" here.
Collection<MethodBinding> callinsForMethod;
callinsForMethod = CallinBindingManager.getBindingForBaseMethod(class_name,
method_name, method_signature);
List<MethodBinding> inheritedMethodBindings = CallinBindingManager.getInheritedBaseMethodBindings(class_name, method_name, method_signature);
//----------------------------------------------------------------------------------------------
if (!chainMethod.isStatic())
callinsForMethod.addAll(inheritedMethodBindings);
ListValueHashMap<MethodBinding> afterBindings = new ListValueHashMap<MethodBinding>();
Iterator<MethodBinding> it = callinsForMethod.iterator();
while (it.hasNext()) {
MethodBinding methodBinding = it.next();
//sourceMapGen.addSourceMapInfo(methodBinding);
// ----> added for precedence purpose:
if (methodBinding.getModifier().equals("after")) { //$NON-NLS-1$
afterBindings.put(methodBinding.getTeamClassName(), methodBinding);
}
// <---
}
/****************************************************************************/
// after callin :
if (afterBindings.size() > 0) { //$NON-NLS-1$
if(logging) printLogMessage("after bindings will be applied..."); //$NON-NLS-1$
il.append(createSwitch(
afterBindings,
chainMethod, ot_team,
/*NORESULT*/-1, firstLine, cg.getMajor(), false));
if(logging) printLogMessage("after bindings: " //$NON-NLS-1$
+ afterBindings); //$NON-NLS-1$
}
}
/**
* Create a switch statement which contains one case for each MethodBinding
* in a given list.
* The switch block is furthermore wrapped in a try-catch block.
* Herein all {@link org.objectteams.LiftingVetoException LiftingVetoException}
* and {@link org.objectteams.LiftingFailedException LiftingFailedException}
* are caught, and possibly reported (if Dot.log.lift ist set).
* @param methodBindings hash map of team names to 'MethodBinding' lists
* @param mg method being generated.
* @param ot_team index of local variable <tt>_OT$team</tt>
* @param ot_result index of local variable <tt>_OT$result</tt>
*/
private InstructionList createSwitch(ListValueHashMap<MethodBinding> methodBindings, MethodGen mg,
int ot_team, int ot_result,
int firstLine,
int major,
boolean useBindingIdx)
{
InstructionList il = new InstructionList();
boolean handlesReplacement = false;
int indexOffset = mg.isStatic()?-1:0; // argument indizes are decremented for static methods,
// because of the missing 'this'
// load value to be switched:
il.append(InstructionFactory.createLoad(intArray, TEAMIDS_ARG+indexOffset));
il.append(InstructionFactory.createLoad(Type.INT, IDX_ARG+indexOffset));
InstructionHandle switchStart = il.append(InstructionFactory.createArrayLoad(Type.INT));
// generated: _OT$teamIDs[_OT$idx]
int numberOfCases = methodBindings.size();
// one break for each case clause
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;
List<MethodBinding> methodBindingsForTeam = null;
MethodBinding mb = null;
Iterator <Entry<String, LinkedList<MethodBinding>>> teamIterator = methodBindings.entrySet().iterator();
while (teamIterator.hasNext()) {
Entry<String, LinkedList<MethodBinding>> entry = teamIterator.next();
String teamName = entry.getKey();
methodBindingsForTeam = CallinBindingManager.sortMethodBindings(entry.getValue(), teamName);
//System.out.println(methodBindings.get(teamName));
/*MethodBinding*/ mb = methodBindingsForTeam.get(0);
matches[caseCounter] = TeamIdDispenser.getTeamId(teamName);
InstructionHandle nextBranch = il.append(new NOP());
// generate (_OT$teamID == teamId) branch here:
// ========== create Cases: ==========
if (mb.isReplace()) { // distinct treatment of replacement:
handlesReplacement = true;
createReplaceCase(mg, il,
teamName, methodBindingsForTeam,
ot_result, ot_team, major, firstLine);
} else { // before or after callin:
BranchInstruction ifBindingIdx = null;
if (useBindingIdx) {
// only if bindingIdx == 0
il.append(InstructionFactory.createLoad(Type.INT, BIND_IDX_ARG+indexOffset));
ifBindingIdx= new IFNE(null);
il.append(ifBindingIdx);
}
createBeforeAfterCase(mg, il, teamName,
methodBindingsForTeam, ot_result, ot_team, major, firstLine);
if (useBindingIdx) {
ifBindingIdx.setTarget(il.append(new NOP()));
}
}
// ===================================
targets[caseCounter] = nextBranch;
/*InstructionHandle break_instr =*/ il.append(breaks[caseCounter]);
// generated: break;
caseCounter++;
} // end of while
// generate default branch here:
InstructionHandle defaultBranch = il.append(new NOP());
if (handlesReplacement)
createRecursiveCall(il, mg, ot_result, 1, 0, mb.getBaseMethodName(), mb.getBaseMethodSignature(), firstLine);
InstructionHandle afterSwitch = il.append(new NOP()); // all breaks point here.
// ===== assemble the switch ====
il.append(switchStart,
createLookupSwitch(matches, targets, breaks,
defaultBranch, afterSwitch)
);
// ==============================
// wrap everything in a try {} catch (LiftingVetoException e) {..}
InstructionHandle endTry = il.getEnd();
GOTO skipHdlr = null;
skipHdlr = new GOTO(null);
il.append(skipHdlr);
// generated: goto normal exit
InstructionHandle hdlr = il.append(new NOP());
il.append(DebugUtil.createReportExc(factory));
// catch: proceed with recursion if caught in a replace call
if (handlesReplacement)
createRecursiveCall(il, mg, ot_result, 1, 0, mb.getBaseMethodName(), mb.getBaseMethodSignature(), firstLine);
mg.addExceptionHandler(il.getStart(), endTry, hdlr, liftingVeto);
mg.addExceptionHandler(il.getStart(), endTry, hdlr, liftingFailed);
InstructionHandle nop = il.append(new NOP());
skipHdlr.setTarget(nop);
return il;
}
/** Create the recursice call of this chaining method.
* @param il insert the call into this list.
* @param mg this chaining method
* @param ot_result stack index of the _OT$result variable or NORESULT.
* @param idx_offset TODO
* @param bindIdx_offset TODO
* @param methodName TODO
* @param methodSignature TODO
*/
void createRecursiveCall (InstructionList il,
MethodGen mg,
int ot_result, int idx_offset, int bindIdx_offset, String methodName, String methodSignature, int firstLine)
{
InstructionHandle ih = !mg.isStatic()
? il.append(InstructionFactory.createThis())
: il.append(new NOP());
if (debugging)
mg.addLineNumber(ih, SHOW_RECURSIVE_CALL ? firstLine : STEP_INTO_LINENUMBER); // show recursive call at method header ("dispatching")
Type[] argTypes = mg.getArgumentTypes();
Type returnType = mg.getReturnType();
short invocationKind = getInvocationType(mg);
// arguments: no adjustment except idx++.
int index = 1;
int indexOffset = mg.isStatic()?-1:0; // argument indizes are decremented for static methods,
// because of the missing 'this'
for (int i=0; i<argTypes.length; i++) {
il.append(InstructionFactory.createLoad(argTypes[i], index+indexOffset));
if (index == OTConstants.IDX_ARG && idx_offset != 0) { // _OT$idx has to be incremented (adding idx_offset)
il.append(new ICONST(idx_offset));
il.append(new IADD());
} else if (index == OTConstants.BIND_IDX_ARG) { // _OT$bindIdx has to be incremented (adding bindIdx_offset)
if (bindIdx_offset == 0) { // bindArg argument has to be set to '0'
il.append(new POP()); // remove loaded _OT$bindIdx
il.append(new ICONST(0)); // replace it by '0'
} else {
il.append(new ICONST(bindIdx_offset));
il.append(new IADD());
}
}
index += argTypes[i].getSize();
}
il.append(factory.createInvoke(mg.getClassName(), mg.getName(),
returnType, argTypes,
invocationKind));
il.append(InstructionFactory.createStore(returnType, ot_result));
// _OT$result = _OT$<method_name>$chain(_OT$teams, _OT$teamIDs, _OT$idx+1,
// a1, .., aN);
if (debugging)
mg.addLineNumber(il.append(new NOP()), STEP_OVER_LINENUMBER);
}
/**
* Create a block for a before or after callin as a case with a surrounding switch.
* @param mg TODO
* @param il InstructionList being assembled.
* @param teamClassName name of a Team which has a callin to this method.
* @param sortedMBList MethodBindings describing the callins (already processed by CallinBindingManager.sortMethodBindings)
* @param ot_result index of local variable <tt>_OT$result</tt>.
* @param ot_team index of local variable <tt>_OT$team</tt>.l
* @param major
* @param firstLine the first real source line of this method
*/
void createBeforeAfterCase(MethodGen mg,
InstructionList il, String teamClassName,
List<MethodBinding> sortedMBList, int ot_result, int ot_team,
int major, int firstLine)
{
MethodBinding mb = sortedMBList.get(0);
ConstantPoolGen cpg = mg.getConstantPool();
// after bindings are processed back-to-front:
if (mb.isAfter()) {
List<MethodBinding> reverse = new ArrayList<MethodBinding>(sortedMBList.size());
for(int i=sortedMBList.size()-1; i>=0; i--)
reverse.add(sortedMBList.get(i));
sortedMBList = reverse;
}
Iterator<MethodBinding> it = sortedMBList.iterator();
while (it.hasNext()) {
/*MethodBinding nextMethodBinding*/mb = it.next();
String baseMethodSignature = mb.getBaseMethodSignature();
Type[] baseArgTypes = Type.getArgumentTypes(baseMethodSignature);
Type baseReturnType = Type.getReturnType(baseMethodSignature);
Type[] wrapperArgTypes = Type.getArgumentTypes(mb.getWrapperSignature());
int argsLen = wrapperArgTypes.length - 1; // don't count base arg.
il.append(InstructionFactory.createLoad(teamType, ot_team));
int packedArgPos = 0;
InstructionHandle argArray = null;
if (useReflection) {
il.append(factory.createInvoke("java.lang.Object", "getClass", classType, new Type[0], Constants.INVOKEVIRTUAL));
il.append(new LDC(cpg.addString(mb.getWrapperName())));
pushTypeArray(il, wrapperArgTypes, major, mg.getClassName(), cpg);
il.append(factory.createInvoke("java.lang.Class", "getMethod", methodType, OTConstants.getMethodSignature, Constants.INVOKEVIRTUAL));
il.append(InstructionFactory.createLoad(teamType, ot_team));
// generated:
// _OT$team.getClass().getMethod(<wrapperName>, <wrapper sign>)
// _OT$team
argArray = il.append(new ANEWARRAY(cpg.addClass(object)));
} else {
il.append(factory.createCast(teamType, new ObjectType(teamClassName)));
// generated: (<TeamClass>)_OT$team
}
// first argument: base object:
packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg);
{
if (!mg.isStatic())
il.append(InstructionFactory.createThis());
else
il.append(InstructionFactory.createNull(Type.OBJECT)); // no base object
}
checkPackValue1(il, useReflection, Type.OBJECT);
// second argument: base result (if appropriate)
if ( mb.isAfter() && !baseReturnType.equals(Type.VOID)) { // after callin wrapper get the base method result as second argument
packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg);
{
il.append(InstructionFactory.createLoad(object, ot_result));
adjustValue(il, null, object, baseReturnType);
argsLen --; // the second (result) arg is also not part of the arg list
}
checkPackValue1(il, useReflection, baseReturnType);
}
// ------- load regular args: --------
// stack positions for load instructions (one-based for non-statics, since 0 == this)
int stackIndex = EXTRA_ARGS + (mg.isStatic() ? 0 : 1);
// where within baseArgTypes do source arguments start?
int firstArg = 0;
if (mb.baseMethodIsCallin()) {
// skip enhancement of this callin method (lower role).
firstArg += EXTRA_ARGS;
stackIndex += EXTRA_ARGS;
argsLen += EXTRA_ARGS;
}
for (int i = firstArg; i < argsLen; i++) {
if(logging) printLogMessage("loading " + baseArgTypes[i].toString()); //$NON-NLS-1$
packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg);
{
il.append(InstructionFactory.createLoad(baseArgTypes[i],stackIndex));
}
checkPackValue1(il, useReflection, baseArgTypes[i]);
stackIndex += baseArgTypes[i].getSize();
}
InstructionHandle callinCall;
if (useReflection) {
// this information was missing above:
il.insert(argArray, createIntegerPush(cpg, packedArgPos));
callinCall = il.append(factory.createInvoke("java.lang.reflect.Method", "invoke", object, new Type[]{object, objectArray}, Constants.INVOKEVIRTUAL));
il.append(new POP()); // before/after wrappers return void
} else {
callinCall = il.append(factory.createInvoke(teamClassName,
mb.getWrapperName(),
Type.VOID, wrapperArgTypes,
Constants.INVOKEVIRTUAL));
}
if (debugging) {
mg.addLineNumber(callinCall, SHOW_ROLE_CALL ? firstLine : STEP_INTO_LINENUMBER); // show role call at method header ("dispatching")
mg.addLineNumber(il.append(new NOP()), STEP_OVER_LINENUMBER);
}
}
}
/* this and the next method serve as a bracket for all pushes that may or may not require
* packing in an array, the array must already exist on the stack. */
private int checkPackValue0(InstructionList il, boolean doPack, int argCount, ConstantPoolGen cpg) {
if (doPack) {
il.append(new DUP());
il.append(createIntegerPush(cpg, argCount));
return argCount+1;
} else {
return argCount;
}
}
private void checkPackValue1(InstructionList il, boolean doPack, Type argType) {
if (doPack) {
if (argType instanceof BasicType)
il.append(createBoxing((BasicType)argType));
il.append(new AASTORE());
}
}
/* Push an array of Class representing the signature given by argTypes. */
private void pushTypeArray(InstructionList il, Type[] argTypes, int major, String class_name_this, ConstantPoolGen cpg) {
il.append(createIntegerPush(cpg, argTypes.length));
il.append(new ANEWARRAY(cpg.addClass(classType)));
for (int i=0; i<argTypes.length; i++) {
Type type = argTypes[i];
il.append(new DUP());
il.append(createIntegerPush(cpg, i));
if (type instanceof BasicType) {
il.append(factory.createFieldAccess(toObjectTypeName((BasicType)type), "TYPE", classType, Constants.GETSTATIC));
} else if (type instanceof ObjectType) {
appendClassLiteral(il, class_name_this, ((ObjectType)type).getClassName(), major, cpg);
} else if (type instanceof ArrayType) {
String prefix = "";
while (type instanceof ArrayType) {
prefix += '[';
type = ((ArrayType)type).getElementType();
}
String elemTypeName = null;
if (type instanceof ObjectType)
elemTypeName = "L"+((ObjectType)type).getClassName()+';';
else if (type instanceof BasicType)
elemTypeName = ((BasicType)type).getSignature();
appendClassLiteral(il, class_name_this, prefix+elemTypeName, major, cpg);
} else {
throw new OTREInternalError("unsupported type in signature "+type);
}
il.append(new AASTORE());
}
}
/**
* Create a block for a replace callin as a case within a surrounding switch. If there are multiple bindings from the
* same team to this base method, the bindings are sorted according to the precedence list of this team.
* @param mg base method being generated.
* @param il instruction list being assembled.
* @param teamClassName name of a Team which has a callin to this method.
* @param sortedMBList MethodBindings describing the callins (already processed by CallinBindingManager.sortMethodBindings)
* @param ot_result index of a local variable <tt>_OT$result</tt>.
* @param ot_team index of local variable <tt>_OT$team</tt>.
* @param major class file version
*/
void createReplaceCase(MethodGen mg, InstructionList il,
String teamClassName, List<MethodBinding> sortedMBList,
int ot_result, int ot_team, int major, int firstLine)
{
int indexOffset = mg.isStatic() ? -1 : 0; // argument indizes are decremented for static methods,
// because of the missing 'this'
boolean multipleBindings = sortedMBList.size() > 1;
LocalVariableGen unused_args_lg = mg.addLocalVariable(UNUSED, objectArray, null, null);
int unused_args = unused_args_lg.getIndex();
unused_args_lg.setStart(il.append(new NOP()));
if (multipleBindings) {
InstructionList addition = new InstructionList();
// load value to be switched:
InstructionHandle switchStart = addition.append(InstructionFactory.createLoad(Type.INT, BIND_IDX_ARG+indexOffset));
// loaded _OT$bindIdx
int numberOfCases = sortedMBList.size();
// one break for each case clause
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> mbIterator = sortedMBList.iterator();
MethodBinding mb = null; // safe because loop will definitely be entered (mbList.size() > 1)
while (mbIterator.hasNext()) {
mb = mbIterator.next();
matches[caseCounter] = caseCounter;
InstructionHandle nextBranch = addition.append(new NOP());
// ========== create Cases: ===========
addition.append(createSingleReplaceCallin(mg, teamClassName, mb, ot_result, ot_team, unused_args, multipleBindings, mg.isStatic(), major, firstLine));
// ==============================
targets[caseCounter] = nextBranch;
/*InstructionHandle break_instr =*/ addition.append(breaks[caseCounter]);
// generated: break;
caseCounter++;
}
// ========== create default: ===========
InstructionHandle defaultBranch = addition.append(new NOP());
createRecursiveCall(addition, mg, ot_result, 1, 0, mb.getBaseMethodName(), mb.getBaseMethodSignature(), firstLine);
// ==============================
InstructionHandle afterSwitch = addition.append(new NOP()); // all breaks point here.
for (int i=0; i<numberOfCases; i++)
breaks[i].setTarget(afterSwitch);
addition.append(switchStart, new TABLESWITCH(matches, targets, defaultBranch));
// wrap everything in a try {} catch (LiftingVetoException e) {..}
InstructionHandle endTry = addition.getEnd();
GOTO skipHdlr = null;
skipHdlr = new GOTO(null);
addition.append(skipHdlr);
// generated: goto normal exit
InstructionHandle hdlr = addition.append(new NOP());
addition.append(DebugUtil.createReportExc(factory));
// ========== create catch instructions: ===========
createRecursiveCall(addition, mg, ot_result, 0, 1, mb.getBaseMethodName(), mb.getBaseMethodSignature(), firstLine);
// =====================================
mg.addExceptionHandler(addition.getStart(), endTry, hdlr, liftingVeto);
mg.addExceptionHandler(addition.getStart(), endTry, hdlr, liftingFailed);
InstructionHandle nop = addition.append(new NOP());
skipHdlr.setTarget(nop);
il.append(addition);
} else { // only a single replace callin:
MethodBinding mb = sortedMBList.get(0); // default, if only one binding exists
il.append(createSingleReplaceCallin(mg, teamClassName, mb, ot_result, ot_team, unused_args, multipleBindings, mg.isStatic(), major, firstLine));
}
unused_args_lg.setEnd(il.getEnd());
}
/**
* Creates a single replace callin call.
*
* @param mg base method being generated.
* @param connectorClassName name of a Team which has a callin to this method.
* @param mb the 'MethodBinding' for this callin.
* @param ot_result index of a local variable <tt>_OT$result</tt>.
* @param ot_team index of local variable <tt>_OT$team</tt>.
* @param unused_args index of local variable <tt>_OT$unused_args</tt>.
* @param multipleBindings flag indicating if there are multiple bindings in this case
* @param staticBaseMethod TODO
* @param major class file version
* @param firstLine first real source line number of this method
* @return instruction list for the callin call
*/
private InstructionList createSingleReplaceCallin(MethodGen mg, String connectorClassName, MethodBinding mb, int ot_result, int ot_team, int unused_args, boolean multipleBindings, boolean staticBaseMethod, int major, int firstLine)
{
// Sequence of values to load is (letters refer to document parameter-passing.odg):
// (f) _OT$team: call target for invoking the callin-wrapper
// (g) baseObject: this or null
// (h) enhancement: [Team[IIII[Object;
// idxs are being manipulated here,
// unusedArgs[] is allocated and filled with
// (i) - (int,Team) if current method is static role method.
// (j) - enhancement arguments, if current method is callin method
// (k) regular arguments.
// ------------------------------------------
// prepare Types:
// ------------------------------------------
Type[] chainArgTypes = mg.getArgumentTypes();
Type[] baseArgTypes = Type.getArgumentTypes(mb.getBaseMethodSignature());
Type[] roleArgTypes = Type.getArgumentTypes(mb.getRoleMethodSignature());
roleArgTypes = enhanceArgumentTypes(roleArgTypes);
String wrapperName = mb.getWrapperName();
Type wrapperReturnType = Type.getReturnType(mb.getWrapperSignature());
Type[] wrapperArgTypes = Type.getArgumentTypes(mb.getWrapperSignature());
// calculate return type:
Type chainReturnType = mg.getReturnType();
ConstantPoolGen cpg = mg.getConstantPool();
InstructionList il = new InstructionList();
// (f):
il.append(InstructionFactory.createLoad(teamType, ot_team));
int packedArgPos = 0;
InstructionHandle argArray = null;
if (useReflection) {
il.append(factory.createInvoke("java.lang.Object", "getClass", classType, new Type[0], Constants.INVOKEVIRTUAL));
il.append(new LDC(cpg.addString(mb.getWrapperName())));
pushTypeArray(il, wrapperArgTypes, major, mg.getClassName(), cpg);
il.append(factory.createInvoke("java.lang.Class", "getMethod", methodType, OTConstants.getMethodSignature, Constants.INVOKEVIRTUAL));
il.append(InstructionFactory.createLoad(teamType, ot_team));
// generated:
// _OT$team.getClass().getMethod(<wrapperName>, <wrapper sign>)
// _OT$team
argArray = il.append(new ANEWARRAY(cpg.addClass(object)));
} else {
il.append(factory.createCast(teamType, new ObjectType(connectorClassName)));
// generated: (<TeamClass>)_OT$team
}
// (g):
packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg);
{
if(!staticBaseMethod)
il.append(InstructionFactory.createThis());
else
il.append(InstructionFactory.createNull(Type.OBJECT)); // no base object
}
checkPackValue1(il, useReflection, Type.OBJECT);
// ----------------------------------------
// (h) Load Extra Arguments:
// ----------------------------------------
int staticOffset = staticBaseMethod?-1:0; // argument indizes are decremented for static methods,
// because of the missing 'this'
// first 4 extra arguments: _OT$teams, _OT$teamIDs, _OT$idx, _OT$bindIdx
for (int i=0; i<4; i++) { // If this is called only once _OT$idx has to be incremented, else _OT$bindIdx has to be incremented
packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg);
{
il.append(InstructionFactory.createLoad(chainArgTypes[i], i+1+staticOffset));
if (!multipleBindings && i+1+staticOffset == OTConstants.IDX_ARG+staticOffset) {// _OT$idx++:
il.append(new ICONST(1));
il.append(new IADD());
} else if (multipleBindings && i+1+staticOffset == OTConstants.BIND_IDX_ARG+staticOffset) {// _OT$bindIdx:
il.append(new ICONST(1));
il.append(new IADD());
}
}
checkPackValue1(il, useReflection, chainArgTypes[i]);
}
// _OT$baseMethTag:
int base_meth_tag = CallinBindingManager.getBaseCallTag(mb.getBaseClassName(),
mb.getBaseMethodName(),
mb.getBaseMethodSignature());
// //JU: added this if-statement (begin) ----------------------------------------------
// if(staticBaseMethod && !mg.getClassName().equals(mb.getBaseClassName())){
// //the method binding is a dummy -> the callin wrapper is performed
// //with an invalid base method tag value -> an exception will be thrown
// base_meth_tag = INVALID_BASE_METHOD_TAG;
// }
// //JU (end) --------------------------------------------------------------------------
packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg);
{
il.append(createIntegerPush(cpg, base_meth_tag));
}
checkPackValue1(il, useReflection, Type.INT);
// collect regular args first, insert into il later:
InstructionList regularArgs = new InstructionList();
// put "new Object[]" on stack, later containing unused arguments:
packedArgPos = checkPackValue0(il, useReflection, packedArgPos, cpg);
{
il.append(createIntegerPush(cpg, baseArgTypes.length));
// enough space to hold ALL base arguments
// Note that the callin wrapper may add more elements to this array.
il.append((Instruction)factory.createNewArray(object, (short)1));
il.append(InstructionFactory.createStore(objectArray, unused_args));
// generated: _OT$unusedArgs = new Object[<n_base_args>];
// ----------------------------------------
// handle more arguments: load regular ones, store unused arguments into the array:
// ----------------------------------------
int unusedArgsIdx = 0; // index into Object[].
int stackIdx = EXTRA_ARGS + 1 + staticOffset; // one-based, since 0 == this (unless static)
int regularArgsStart = EXTRA_ARGS;
boolean baseIsStaticCallin = mb.baseMethodIsCallin() && staticBaseMethod;
if (baseIsStaticCallin) {
// (i) need these synthetic arguments up-front: "int dummy, Team enclosingTeam"
storeUnusedArg(il, unused_args, 0, // constant
new ICONST(0),
Type.INT,
cpg);
storeUnusedArg(il, unused_args, 1, // constant
new ALOAD(regularArgsStart+1),
OTConstants.teamType,
cpg);
// consumed first two parameters into unusedArgs:
regularArgsStart+=2;
stackIdx += 2;
unusedArgsIdx += 2;
}
for (int i=regularArgsStart; i<chainArgTypes.length; i++) {
Type argType = chainArgTypes[i];
Instruction loadingInstruction = InstructionFactory.createLoad(argType, stackIdx);
if (isRegularArg(i, mb.baseMethodIsCallin(), staticBaseMethod)) {
// (k) collect loading instruction, for appending after _OT$unusedArgs.
packedArgPos = checkPackValue0(regularArgs, useReflection, packedArgPos, cpg);
{
regularArgs.append(loadingInstruction);
}
checkPackValue0(regularArgs, useReflection, packedArgPos, cpg);
} else {
// (j) store unused arg
storeUnusedArg(il, unused_args, unusedArgsIdx++, loadingInstruction, argType, cpg);
// generated: _OT$unusedArgs[<unusedArgsIdx>] = maybeBox(a<index>);
}
stackIdx += argType.getSize();
}
il.append(InstructionFactory.createLoad(objectArray, unused_args));
}
checkPackValue1(il, useReflection, objectArray);
// (k) insert previously assembled load-sequence:
il.append(regularArgs);
// ============= INVOKEVIRTUAL (wrapper) =============
InstructionHandle callinCall;
if (useReflection) {
// this information was missing above:
il.insert(argArray, createIntegerPush(cpg, packedArgPos));
callinCall = il.append(factory.createInvoke("java.lang.reflect.Method", "invoke", object, new Type[]{object, objectArray}, Constants.INVOKEVIRTUAL));
wrapperReturnType = Type.OBJECT;
} else {
callinCall = il.append(factory.createInvoke(connectorClassName, wrapperName,
wrapperReturnType,
wrapperArgTypes,
Constants.INVOKEVIRTUAL));
}
if (debugging) {
mg.addLineNumber(callinCall, SHOW_ROLE_CALL ? firstLine : STEP_INTO_LINENUMBER); // show role call at method header ("dispatching")
mg.addLineNumber(il.append(new NOP()), STEP_OVER_LINENUMBER);
}
adjustValue(il, null, wrapperReturnType, chainReturnType);
il.append(InstructionFactory.createStore(chainReturnType, ot_result));
return il;
}
/**
* Store an unused value (loaded by pushInstruction) into _OT$unusedArgs
*/
private void storeUnusedArg(InstructionList il,
int unused_args,
int arrayIndex,
Instruction pushInstruction,
Type argType,
ConstantPoolGen cpg)
{
il.append(InstructionFactory.createLoad(objectArray, unused_args));
il.append(createIntegerPush(cpg, arrayIndex));
il.append(pushInstruction);
if (argType instanceof BasicType)
il.append(createBoxing((BasicType)argType));
il.append(InstructionFactory.createArrayStore(objectArray));
}
/**
* Is the parameter at position idx mapped (by paramPositions or implicitly)?
* Cut off head:
* - (int,Team) if present (static role method)
* - enhancement (possible twice)
* @param idx parameter index of enhanced signature
*/
static boolean isRegularArg (int idx, boolean baseIsCallin, boolean baseIsStatic) {
if (baseIsCallin && baseIsStatic) // FIXME(SH): should be baseIsRole instead of baseIsCallin!
idx -= 2;
int firstVisible = EXTRA_ARGS + (baseIsCallin?EXTRA_ARGS:0); // skip one or two enhancements
return idx >= firstVisible;
}
/**
* Given an argument of type <tt>actual</tt>, must
* we use type <tt>formal</tt> in signatures,
* because it is a supertype of <tt>actual</tt>?
*/
static Type checkWiden (Type actual, Type formal) {
if (!actual.equals(formal)
&& actual instanceof ObjectType
&& formal instanceof ObjectType)
{
ObjectType actualObj = (ObjectType)actual;
ObjectType formalObj = (ObjectType)formal;
if (RepositoryAccess.safeSubclassOf(actualObj, formalObj))
return formalObj;
}
return actual;
}
/**
* Create an instruction list for initializing the role set field on-demand.
* @param valueRequired should the role set be on the stack after this sequence?
* @param cg the ClassGen of the appropriate class
*/
private InstructionList getInitializedRoleSet(String class_name, boolean valueRequired) {;
InstructionList il = new InstructionList();
// try to retrieve existing set:
il.append(new ALOAD(0));
il.append(factory.createGetField(class_name, OTConstants.ROLE_SET, OTConstants.roleSetType));
if (valueRequired)
il.append(new DUP()); // a spare value to keep on the stack if successful
// if (roleSet == null) ..
IFNONNULL branch = new IFNONNULL(null);
il.append(branch);
// conditionally create the set:
if (valueRequired)
il.append(new POP()); // remove useless "null", replace with DUP_X1 below
il.append(new ALOAD(0));
il.append(factory.createNew(OTConstants.roleSetType));
il.append(new DUP());
il.append(factory.createInvoke(OTConstants.roleSetType.getClassName(),
Constants.CONSTRUCTOR_NAME,
Type.VOID,
Type.NO_ARGS,
Constants.INVOKESPECIAL));
if (valueRequired)
il.append(new DUP_X1()); // push below pending "this"
// store in the field:
il.append(factory.createPutField(class_name, OTConstants.ROLE_SET, OTConstants.roleSetType));
// endif
branch.setTarget(il.append(new NOP()));
return il;
}
/**
* Generates the field '_OT$roleSet' which is used to store the added roles.
* @param cpg the ClassGen of the appropriate class
* @param class_name the name of the class
* @return the generated field
*/
private Field generateRoleSet(ConstantPoolGen cpg, String class_name) {
FieldGen fg = new FieldGen(Constants.ACC_PROTECTED|Constants.ACC_TRANSIENT,
OTConstants.roleSetType,
OTConstants.ROLE_SET,
cpg);
return fg.getField();
}
/**
* Generates the method 'public void _OT$addRole(Object role)' which adds the passed object
* to the role set of this base class.
* @param cpg the ClassGen of the appropriate class
* @param class_name the name of the class
* @return the generated method
*/
private Method generateAddRole(ConstantPoolGen cpg, String class_name) {
InstructionList il = new InstructionList();
MethodGen mg = new MethodGen(Constants.ACC_PUBLIC,
Type.VOID,
new Type[] { Type.OBJECT },
new String[] {"role"},
OTConstants.ADD_ROLE, class_name,
il, cpg);
il.append(getInitializedRoleSet(class_name, /*valueRequired*/true));
il.append(InstructionFactory.createLoad(Type.OBJECT, 1));
il.append(factory.createInvoke(OTConstants.roleSetType.getClassName(),
"add",
Type.BOOLEAN,
new Type[] {Type.OBJECT},
Constants.INVOKEVIRTUAL));
il.append(new POP());
il.append(InstructionFactory.createReturn(Type.VOID));
mg.removeNOPs();
mg.setMaxStack();
mg.setMaxLocals(2);
return mg.getMethod();
}
/**
* Generates the method 'public void _OT$removeRole(Object role)' which removes the passed object
* from the role set of this base class.
* @param cpg the ClassGen of the appropriate class
* @param class_name the name of the class
* @return the generated method
*/
private Method generateRemoveRole(ConstantPoolGen cpg, String class_name) {
InstructionList il = new InstructionList();
MethodGen mg = new MethodGen(Constants.ACC_PUBLIC,
Type.VOID,
new Type[] { Type.OBJECT },
new String[] {"role"},
OTConstants.REMOVE_ROLE, class_name,
il, cpg);
il.append(new ALOAD(0));
il.append(factory.createGetField(class_name, OTConstants.ROLE_SET, OTConstants.roleSetType));
il.append(InstructionFactory.createLoad(Type.OBJECT, 1));
il.append(factory.createInvoke(OTConstants.roleSetType.getClassName(),
"remove",
Type.BOOLEAN,
new Type[] {Type.OBJECT},
Constants.INVOKEVIRTUAL));
il.append(new POP());
il.append(InstructionFactory.createReturn(Type.VOID));
mg.removeNOPs();
mg.setMaxStack(2);
mg.setMaxLocals(2);
return mg.getMethod();
}
}