/********************************************************************** | |
* This file is part of "Object Teams Dynamic Runtime Environment" | |
* | |
* Copyright 2009, 2012 Oliver Frank and others. | |
* | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Please visit http://www.eclipse.org/objectteams for updates and contact. | |
* | |
* Contributors: | |
* Oliver Frank - Initial API and implementation | |
* Stephan Herrmann - Initial API and implementation | |
**********************************************************************/ | |
package org.eclipse.objectteams.otredyn.bytecode.asm; | |
import org.eclipse.objectteams.otredyn.transformer.names.ClassNames; | |
import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers; | |
import org.objectweb.asm.Opcodes; | |
import org.objectweb.asm.Type; | |
import org.objectweb.asm.tree.AbstractInsnNode; | |
import org.objectweb.asm.tree.InsnList; | |
import org.objectweb.asm.tree.InsnNode; | |
import org.objectweb.asm.tree.IntInsnNode; | |
import org.objectweb.asm.tree.JumpInsnNode; | |
import org.objectweb.asm.tree.LabelNode; | |
import org.objectweb.asm.tree.MethodInsnNode; | |
import org.objectweb.asm.tree.MethodNode; | |
import org.objectweb.asm.tree.TypeInsnNode; | |
/** | |
* Create the code for the dispatch from a base class to the teams. <br/> <br/> | |
* The code was generated as follows: <br/> | |
* <code> | |
* switch (boundMethodId) { // this was generated in CreateSwitchAdapter <br/> | |
* ... <br/> | |
* case (...): // this was generated in concrete implementations <br/> | |
* // of this abstract class <br/> | |
* Teams[] teams = TeamManager.getTeams(joinpointId) <br/><br/> | |
* if (teams == null) { <br/> | |
* break; <br/> | |
* } <br/> | |
* <br/> | |
* Team t = teams[0]; <br/> | |
* int[] callinIds = TeamManager.getCallinIds(joinpointId); <br/> | |
* Object[] args = {arg1, ... , argn}; | |
* return team._OT$callAllBindings(this, teams, 0, callinIds, boundMethodId, args); | |
* } <br/> | |
* </code> | |
* @author Oliver Frank | |
*/ | |
public abstract class AbstractCreateDispatchCodeAdapter extends | |
AbstractTransformableClassNode { | |
private boolean isStatic; | |
public AbstractCreateDispatchCodeAdapter(boolean isStatic) { | |
this.isStatic = isStatic; | |
} | |
private Type[] args; | |
protected InsnList getDispatchCode(MethodNode method, int joinPointId, | |
int boundMethodId) { | |
InsnList instructions = new InsnList(); | |
// teams = TeamManager.getTeams(joinpointId) | |
instructions.add(createLoadIntConstant(joinPointId)); | |
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, | |
ClassNames.TEAM_MANAGER_SLASH, ConstantMembers.getTeams.getName(), | |
ConstantMembers.getTeams.getSignature())); | |
instructions.add(createInstructionsToCheackTeams(method)); | |
// get the first team | |
instructions.add(new InsnNode(Opcodes.DUP)); | |
instructions.add(new InsnNode(Opcodes.ICONST_0)); | |
instructions.add(new InsnNode(Opcodes.AALOAD)); | |
instructions.add(new InsnNode(Opcodes.SWAP)); | |
if (isStatic) { | |
instructions.add(new InsnNode(Opcodes.ACONST_NULL)); | |
} else { | |
// put "this" on the stack and cast it to IBoundBase | |
instructions.add(new IntInsnNode(Opcodes.ALOAD, 0)); | |
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, | |
ClassNames.I_BOUND_BASE_SLASH)); | |
} | |
instructions.add(new InsnNode(Opcodes.SWAP)); | |
// start index | |
instructions.add(new InsnNode(Opcodes.ICONST_0)); | |
// TeamManager.getCallinIds(joinpointId) | |
instructions.add(createLoadIntConstant(joinPointId)); | |
instructions | |
.add(new MethodInsnNode(Opcodes.INVOKESTATIC, | |
ClassNames.TEAM_MANAGER_SLASH, | |
ConstantMembers.getCallinIds.getName(), | |
ConstantMembers.getCallinIds.getSignature())); | |
instructions.add(createLoadIntConstant(boundMethodId)); | |
args = Type.getArgumentTypes(method.desc); | |
// box the arguments | |
instructions.add(getBoxedArguments(args)); | |
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, | |
ClassNames.ITEAM_SLASH, ConstantMembers.callAllBindingsTeam | |
.getName(), ConstantMembers.callAllBindingsTeam | |
.getSignature())); | |
Type returnType = Type.getReturnType(method.desc); | |
instructions.add(getUnboxingInstructionsForReturnValue(returnType)); | |
return instructions; | |
} | |
protected InsnList createInstructionsToCheackTeams(MethodNode method) { | |
// if (teams == null) { | |
// break; | |
// } | |
InsnList instructions = new InsnList(); | |
instructions.add(new InsnNode(Opcodes.DUP)); | |
LabelNode label = new LabelNode(); | |
instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, label)); | |
instructions.add(new InsnNode(Opcodes.POP)); | |
instructions.add(new JumpInsnNode(Opcodes.GOTO, findBreakLabel(method.instructions))); | |
instructions.add(label); | |
return instructions; | |
} | |
private LabelNode findBreakLabel(InsnList instructions) { | |
for (int i = instructions.size() - 1; i >= 0; i--) { | |
AbstractInsnNode node = instructions.get(i); | |
if (node.getType() == AbstractInsnNode.LABEL) { | |
return (LabelNode) node; | |
} | |
} | |
throw new RuntimeException("Can't find break label to create dispatch code"); | |
} | |
protected abstract InsnList getBoxedArguments(Type[] args); | |
protected int getMaxLocals() { | |
return args.length + 1; | |
} | |
protected int getMaxStack() { | |
return 10; | |
} | |
} |