/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Igor Konev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jephyr.easyflow.instrument;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AnalyzerAdapter;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FrameNode;
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.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DCONST_0;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DOUBLE;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DSTORE;
import static org.objectweb.asm.Opcodes.DUP_X2;
import static org.objectweb.asm.Opcodes.FCONST_0;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FLOAT;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.FSTORE;
import static org.objectweb.asm.Opcodes.F_NEW;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFNULL;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INTEGER;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.objectweb.asm.Opcodes.LCONST_0;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LONG;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.LSTORE;
import static org.objectweb.asm.Opcodes.MONITORENTER;
import static org.objectweb.asm.Opcodes.MONITOREXIT;
import static org.objectweb.asm.Opcodes.NULL;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.POP2;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SIPUSH;
import static org.objectweb.asm.Opcodes.SWAP;
import static org.objectweb.asm.Opcodes.TOP;
final class ContinuationMethodAdapter extends AnalyzingMethodNode {
private static final Object[] EMPTY_OBJECTS = new Object[0];
private final String owner;
private final MethodVisitor mv;
private ContinuationMethodAdapter(String owner, int access, String name, String desc, String signature,
String[] exceptions, MethodVisitor mv) {
super(access, name, desc, signature, exceptions);
this.owner = owner;
this.mv = mv;
}
static MethodVisitor create(String owner, int access, String name, String desc, String signature,
String[] exceptions, MethodVisitor mv) {
ContinuationMethodAdapter adapter =
new ContinuationMethodAdapter(owner, access, name, desc, signature, exceptions, mv);
AnalyzerAdapter analyzerAdapter = new AnalyzerAdapter(owner, access, name, desc, adapter);
adapter.adapter = analyzerAdapter;
return analyzerAdapter;
}
@Override
public void visitEnd() {
List<MethodInsnNode> nodes = findNodes();
if (nodes.isEmpty()) {
accept(mv);
return;
}
int implVarIndex = maxLocals;
maxLocals += 1;
Object[] initialLocals = appendValue(ensureSize(frames.get(instructions.getFirst()).locals, implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl");
updateFrames(implVarIndex);
addMonitorHooks(implVarIndex);
LabelNode labelNode = newLabelNode();
instructions.insert(labelNode);
if (!isNextFrameNode(labelNode)) {
instructions.insert(labelNode, newFrameNode(initialLocals, EMPTY_OBJECTS));
}
addInvocationEndedHook(implVarIndex, labelNode);
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKESTATIC, "org/jephyr/continuation/easyflow/ContinuationImpl", "currentImpl",
"()Lorg/jephyr/continuation/easyflow/ContinuationImpl;", false));
instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, implVarIndex));
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode, new JumpInsnNode(IFNULL, labelNode));
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl", "isSuspended",
"()Z", false));
LabelNode labelNode1 = newLabelNode();
instructions.insertBefore(labelNode, new JumpInsnNode(IFEQ, labelNode1));
LabelNode defaultLabelNode = newLabelNode();
int size = nodes.size();
int length = size - 1;
LabelNode[] labelNodes = new LabelNode[length];
for (int i = 0; i < length; i++) {
labelNodes[i] = newLabelNode();
}
if (length > 0) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl", "popInt",
"()I", false));
instructions.insertBefore(labelNode, new TableSwitchInsnNode(0, length - 1, defaultLabelNode, labelNodes));
} else {
instructions.insertBefore(labelNode, new JumpInsnNode(GOTO, defaultLabelNode));
}
updateMaxStack(1);
Type returnType = Type.getReturnType(desc);
for (int i = 0; i < size; i++) {
MethodInsnNode node = nodes.get(i);
Frame frame = frames.get(node);
Object[] locals = frame.locals;
Object[] stack = frame.stack;
// resume
LabelNode labelNode2 = i < length ? labelNodes[i] : defaultLabelNode;
instructions.insertBefore(labelNode, labelNode2);
instructions.insert(labelNode2, newFrameNode(initialLocals, EMPTY_OBJECTS));
int intCount = length > 0 ? 1 : 0;
int floatCount = 0;
int longCount = 0;
int doubleCount = 0;
int objectCount = 0;
for (int j = 0, n = locals.length; j < n; j++) {
Object value = locals[j];
if (value == INTEGER) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popInt", "()I", false));
instructions.insertBefore(labelNode, new VarInsnNode(ISTORE, j));
updateMaxStack(1);
intCount++;
} else if (value == FLOAT) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popFloat", "()F", false));
instructions.insertBefore(labelNode, new VarInsnNode(FSTORE, j));
updateMaxStack(1);
floatCount++;
} else if (value == LONG) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popLong", "()J", false));
instructions.insertBefore(labelNode, new VarInsnNode(LSTORE, j));
updateMaxStack(2);
longCount++;
} else if (value == DOUBLE) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popDouble", "()D", false));
instructions.insertBefore(labelNode, new VarInsnNode(DSTORE, j));
updateMaxStack(2);
doubleCount++;
} else if (value == NULL) {
instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, j));
updateMaxStack(1);
} else if (value instanceof String) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popObject", "()Ljava/lang/Object;", false));
instructions.insertBefore(labelNode, new TypeInsnNode(CHECKCAST, (String) value));
instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, j));
updateMaxStack(1);
objectCount++;
} else if (value != TOP) {
throw new IllegalStateException();
}
}
int sizes = Type.getArgumentsAndReturnSizes(node.desc);
int argSize = sizes >> 2;
boolean invokeStatic = node.getOpcode() == INVOKESTATIC;
if (invokeStatic) {
argSize -= 1;
}
int stackSize = 0;
for (int j = 0, n = stack.length - argSize; j < n; j++) {
Object value = stack[j];
if (value == INTEGER) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popInt", "()I", false));
stackSize += 1;
intCount++;
} else if (value == FLOAT) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popFloat", "()F", false));
stackSize += 1;
floatCount++;
} else if (value == LONG) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popLong", "()J", false));
stackSize += 2;
longCount++;
} else if (value == DOUBLE) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popDouble", "()D", false));
stackSize += 2;
doubleCount++;
} else if (value == NULL) {
instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
stackSize += 1;
} else if (value instanceof String) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popObject", "()Ljava/lang/Object;", false));
instructions.insertBefore(labelNode, new TypeInsnNode(CHECKCAST, (String) value));
stackSize += 1;
objectCount++;
} else if (value != TOP) {
throw new IllegalStateException();
}
}
updateMaxStack(stackSize);
int targetVarIndex;
int objVarIndex;
int argsVarIndex;
if (invokeStatic) {
targetVarIndex = -1;
objVarIndex = -1;
argsVarIndex = -1;
for (Type type : Type.getArgumentTypes(node.desc)) {
instructions.insertBefore(labelNode, newPushDefaultNode(type));
stackSize += type.getSize();
}
} else if (node.owner.equals("java/lang/reflect/Method") && node.name.equals("invoke") &&
node.desc.equals("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")) {
int varIndex = implVarIndex + 1;
if (stack[stack.length - 3] == NULL) {
targetVarIndex = -1;
} else {
targetVarIndex = varIndex;
varIndex++;
}
if (stack[stack.length - 2] == NULL) {
objVarIndex = -1;
} else {
objVarIndex = varIndex;
varIndex++;
}
if (stack[stack.length - 1] == NULL) {
argsVarIndex = -1;
} else {
argsVarIndex = varIndex;
varIndex++;
}
if (maxLocals < varIndex) {
maxLocals = varIndex;
}
if (targetVarIndex == -1) {
instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
} else {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popObject", "()Ljava/lang/Object;", false));
instructions.insertBefore(labelNode, new TypeInsnNode(CHECKCAST, "java/lang/reflect/Method"));
instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, targetVarIndex));
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, targetVarIndex));
objectCount++;
}
if (objVarIndex == -1) {
instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
} else {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popObject", "()Ljava/lang/Object;", false));
instructions.insertBefore(labelNode, new TypeInsnNode(CHECKCAST, (String) stack[stack.length - 2]));
instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, objVarIndex));
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, objVarIndex));
objectCount++;
}
if (targetVarIndex == -1) {
instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
} else {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, targetVarIndex));
}
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "java/lang/reflect/Method", "getParameterTypes",
"()[Ljava/lang/Class;", false));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKESTATIC, "org/jephyr/continuation/easyflow/ContinuationImpl",
"getDefaultArguments", "([Ljava/lang/Class;)[Ljava/lang/Object;", false));
stackSize += 3;
} else {
targetVarIndex = -1;
objVarIndex = -1;
argsVarIndex = -1;
Object value = stack[stack.length - argSize];
if (value == NULL) {
instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, implVarIndex + 1));
instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
} else {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"popObject", "()Ljava/lang/Object;", false));
instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, implVarIndex + 1));
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex + 1));
instructions.insertBefore(labelNode, new TypeInsnNode(CHECKCAST, (String) value));
}
stackSize += 1;
for (Type type : Type.getArgumentTypes(node.desc)) {
instructions.insertBefore(labelNode, newPushDefaultNode(type));
stackSize += type.getSize();
}
objectCount++;
}
updateMaxStack(stackSize);
LabelNode labelNode3 = newLabelNode();
instructions.insertBefore(labelNode, new JumpInsnNode(GOTO, labelNode3));
// invocation starting
if (invokeStatic) {
instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(node, new JumpInsnNode(IFNULL, labelNode3));
instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(node, new LdcInsnNode(Type.getType('L' + node.owner + ';')));
instructions.insertBefore(node, new LdcInsnNode(node.name));
instructions.insertBefore(node, new LdcInsnNode(node.desc));
instructions.insertBefore(node,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"staticInvocationStarting", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V",
false));
instructions.insertBefore(node, labelNode3);
instructions.insert(labelNode3, newFrameNode(appendValue(ensureSize(locals, implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl"), stack));
} else if (node.owner.equals("java/lang/reflect/Method") && node.name.equals("invoke") &&
node.desc.equals("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")) {
instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
LabelNode labelNode4 = newLabelNode();
instructions.insertBefore(node, new JumpInsnNode(IFNULL, labelNode4));
instructions.insertBefore(node,
argsVarIndex == -1 ? new InsnNode(POP) : new VarInsnNode(ASTORE, argsVarIndex));
instructions.insertBefore(node,
objVarIndex == -1 ? new InsnNode(POP) : new VarInsnNode(ASTORE, objVarIndex));
instructions.insertBefore(node,
targetVarIndex == -1 ? new InsnNode(POP) : new VarInsnNode(ASTORE, targetVarIndex));
instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(node,
targetVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, targetVarIndex));
instructions.insertBefore(node,
objVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, objVarIndex));
instructions.insertBefore(node,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"reflectiveInvocationStarting", "(Ljava/lang/reflect/Method;Ljava/lang/Object;)V",
false));
instructions.insertBefore(node,
targetVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, targetVarIndex));
instructions.insertBefore(node,
objVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, objVarIndex));
instructions.insertBefore(node,
argsVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, argsVarIndex));
instructions.insertBefore(node, new JumpInsnNode(GOTO, labelNode3));
instructions.insertBefore(node, labelNode4);
instructions.insert(labelNode4, newFrameNode(appendValue(ensureSize(locals, implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl"), stack));
if (targetVarIndex != -1) {
instructions.insertBefore(node, new InsnNode(ACONST_NULL));
instructions.insertBefore(node, new VarInsnNode(ASTORE, targetVarIndex));
}
if (objVarIndex != -1) {
instructions.insertBefore(node, new InsnNode(ACONST_NULL));
instructions.insertBefore(node, new VarInsnNode(ASTORE, objVarIndex));
}
instructions.insertBefore(node, labelNode3);
Object[] locals1 = appendValue(ensureSize(locals, implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl");
if (targetVarIndex != -1) {
locals1 = appendValue(locals1, "java/lang/reflect/Method");
}
if (objVarIndex != -1) {
locals1 = appendValue(locals1, "java/lang/Object");
}
instructions.insert(labelNode3, newFrameNode(locals1, stack));
} else {
instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
LabelNode labelNode4 = newLabelNode();
instructions.insertBefore(node, new JumpInsnNode(IFNULL, labelNode4));
targetVarIndex = implVarIndex + 1;
int varIndex = targetVarIndex + 1;
for (int j = stack.length - 1, k = stack.length - argSize + 1; j >= k; j--) {
Object value = stack[j];
if (value == INTEGER) {
instructions.insertBefore(node, new VarInsnNode(ISTORE, varIndex));
varIndex += 1;
} else if (value == FLOAT) {
instructions.insertBefore(node, new VarInsnNode(FSTORE, varIndex));
varIndex += 1;
} else if (value == DOUBLE) {
instructions.insertBefore(node, new VarInsnNode(DSTORE, varIndex));
varIndex += 2;
} else if (value == LONG) {
instructions.insertBefore(node, new VarInsnNode(LSTORE, varIndex));
varIndex += 2;
} else if (value == NULL) {
instructions.insertBefore(node, new InsnNode(POP));
} else if (value instanceof String) {
instructions.insertBefore(node, new VarInsnNode(ASTORE, varIndex));
varIndex += 1;
}
}
if (maxLocals < varIndex) {
maxLocals = varIndex;
}
instructions.insertBefore(node, new VarInsnNode(ASTORE, targetVarIndex));
instructions.insertBefore(node, new VarInsnNode(ALOAD, targetVarIndex));
for (int j = stack.length - argSize + 1, n = stack.length; j < n; j++) {
Object value = stack[j];
if (value == INTEGER) {
varIndex -= 1;
instructions.insertBefore(node, new VarInsnNode(ILOAD, varIndex));
} else if (value == FLOAT) {
varIndex -= 1;
instructions.insertBefore(node, new VarInsnNode(FLOAD, varIndex));
} else if (value == DOUBLE) {
varIndex -= 2;
instructions.insertBefore(node, new VarInsnNode(DLOAD, varIndex));
} else if (value == LONG) {
varIndex -= 2;
instructions.insertBefore(node, new VarInsnNode(LLOAD, varIndex));
} else if (value == NULL) {
instructions.insertBefore(node, new InsnNode(ACONST_NULL));
} else if (value instanceof String) {
varIndex -= 1;
instructions.insertBefore(node, new VarInsnNode(ALOAD, varIndex));
}
}
instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(node, new VarInsnNode(ALOAD, targetVarIndex));
instructions.insertBefore(node, new LdcInsnNode(node.name));
instructions.insertBefore(node, new LdcInsnNode(node.desc));
instructions.insertBefore(node,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"invocationStarting", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
false));
instructions.insertBefore(node, new JumpInsnNode(GOTO, labelNode3));
instructions.insertBefore(node, labelNode4);
instructions.insert(labelNode4, newFrameNode(appendValue(ensureSize(locals, implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl"), stack));
instructions.insertBefore(node, new InsnNode(ACONST_NULL));
instructions.insertBefore(node, new VarInsnNode(ASTORE, targetVarIndex));
instructions.insertBefore(node, labelNode3);
instructions.insert(labelNode3, newFrameNode(appendValues(ensureSize(locals, implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl", "java/lang/Object"), stack));
}
updateMaxStack(stack.length + 4);
// suspend
Frame frame1 = findNextFrame(frames, node);
Object[] stack1 = frame1.stack;
int stackSize1 = stack1.length;
LabelNode labelNode4 = newLabelNode();
instructions.insert(node, labelNode4);
if (!isNextFrameNode(labelNode4)) {
instructions.insert(labelNode4, newFrameNode(appendValue(ensureSize(frame1.locals, implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl"), stack1));
}
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new JumpInsnNode(IFNULL, labelNode4));
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"isSuspending", "()Z", false));
instructions.insertBefore(labelNode4, new JumpInsnNode(IFEQ, labelNode4));
int returnSize = sizes & 0x03;
if (returnSize == 1) {
instructions.insertBefore(labelNode4, new InsnNode(POP));
stackSize1 -= 1;
} else if (returnSize == 2) {
instructions.insertBefore(labelNode4, new InsnNode(POP2));
stackSize1 -= 2;
}
if (intCount > 0) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, newPushNode(intCount));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"ensureIntStackSize", "(I)V", false));
updateMaxStack(stackSize1 + 1);
}
if (floatCount > 0) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, newPushNode(floatCount));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"ensureFloatStackSize", "(I)V", false));
updateMaxStack(stackSize1 + 1);
}
if (longCount > 0) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, newPushNode(longCount));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"ensureLongStackSize", "(I)V", false));
updateMaxStack(stackSize1 + 1);
}
if (doubleCount > 0) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, newPushNode(doubleCount));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"ensureDoubleStackSize", "(I)V", false));
updateMaxStack(stackSize1 + 1);
}
if (objectCount > 0) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, newPushNode(objectCount));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"ensureObjectStackSize", "(I)V", false));
updateMaxStack(stackSize1 + 1);
}
if (!invokeStatic) {
if (node.owner.equals("java/lang/reflect/Method") && node.name.equals("invoke") &&
node.desc.equals("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")) {
if (objVarIndex != -1) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, objVarIndex));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushObject", "(Ljava/lang/Object;)V", false));
updateMaxStack(stackSize1 + 2);
}
if (targetVarIndex != -1) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, targetVarIndex));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushObject", "(Ljava/lang/Object;)V", false));
updateMaxStack(stackSize1 + 2);
}
} else if (stack[stack.length - argSize] != NULL) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, targetVarIndex));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushObject", "(Ljava/lang/Object;)V", false));
updateMaxStack(stackSize1 + 2);
}
}
for (int j = stack.length - argSize - 1; j >= 0; j--) {
Object value = stack[j];
if (value == INTEGER) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new InsnNode(SWAP));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushInt", "(I)V", false));
updateMaxStack(stackSize1 + 1);
stackSize1 -= 1;
} else if (value == FLOAT) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new InsnNode(SWAP));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushFloat", "(F)V", false));
updateMaxStack(stackSize1 + 1);
stackSize1 -= 1;
} else if (value == LONG) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new InsnNode(DUP_X2));
instructions.insertBefore(labelNode4, new InsnNode(POP));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushLong", "(J)V", false));
updateMaxStack(stackSize1 + 2);
stackSize1 -= 2;
} else if (value == DOUBLE) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new InsnNode(DUP_X2));
instructions.insertBefore(labelNode4, new InsnNode(POP));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushDouble", "(D)V", false));
updateMaxStack(stackSize1 + 2);
stackSize1 -= 2;
} else if (value == NULL) {
instructions.insertBefore(labelNode4, new InsnNode(POP));
stackSize1 -= 1;
} else if (value instanceof String) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new InsnNode(SWAP));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushObject", "(Ljava/lang/Object;)V", false));
updateMaxStack(stackSize1 + 1);
stackSize1 -= 1;
}
}
for (int j = locals.length - 1; j >= 0; j--) {
Object value = locals[j];
if (value == INTEGER) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new VarInsnNode(ILOAD, j));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushInt", "(I)V", false));
updateMaxStack(stackSize1 + 2);
} else if (value == FLOAT) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new VarInsnNode(FLOAD, j));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushFloat", "(F)V", false));
updateMaxStack(stackSize1 + 2);
} else if (value == LONG) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new VarInsnNode(LLOAD, j));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushLong", "(J)V", false));
updateMaxStack(stackSize1 + 3);
} else if (value == DOUBLE) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new VarInsnNode(DLOAD, j));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushDouble", "(D)V", false));
updateMaxStack(stackSize1 + 3);
} else if (value instanceof String) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, j));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushObject", "(Ljava/lang/Object;)V", false));
updateMaxStack(stackSize1 + 2);
}
}
if (length > 0) {
instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode4, newPushNode(i));
instructions.insertBefore(labelNode4,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"pushInt", "(I)V", false));
updateMaxStack(stackSize1 + 2);
}
int returnSize1 = returnType.getSize();
if (returnSize1 > 0) {
instructions.insertBefore(labelNode4, newPushDefaultNode(returnType));
updateMaxStack(stackSize1 + returnSize1);
}
instructions.insertBefore(labelNode4, new InsnNode(returnType.getOpcode(IRETURN)));
}
instructions.insertBefore(labelNode, labelNode1);
instructions.insert(labelNode1, newFrameNode(initialLocals, EMPTY_OBJECTS));
addInvocationStartedHook(implVarIndex, labelNode);
accept(mv);
}
private List<MethodInsnNode> findNodes() {
List<MethodInsnNode> nodes = new ArrayList<>();
for (AbstractInsnNode next = instructions.getFirst(); next != null; next = next.getNext()) {
if (next instanceof MethodInsnNode) {
MethodInsnNode node = (MethodInsnNode) next;
if (node.getOpcode() != INVOKESPECIAL || node.name.charAt(0) != '<') {
nodes.add(node);
}
}
}
return nodes;
}
private void updateFrames(int implVarIndex) {
AbstractInsnNode next = instructions.getFirst();
while (next != null) {
AbstractInsnNode node = next;
next = next.getNext();
if (node instanceof FrameNode) {
FrameNode frameNode = (FrameNode) node;
Collection<Object> locals = new ArrayList<>();
for (Object value : frameNode.local) {
locals.add(value);
if (isLong(value)) {
locals.add(TOP);
}
}
Object[] locals1 = convertValues(appendValue(ensureSize(locals.toArray(), implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl"));
List<Object> stack = frameNode.stack;
instructions.set(node, new FrameNode(F_NEW, locals1.length, locals1, stack.size(), stack.toArray()));
}
}
}
private void addMonitorHooks(int implVarIndex) {
AbstractInsnNode next = instructions.getFirst();
while (next != null) {
AbstractInsnNode node = next;
next = next.getNext();
int opcode = node.getOpcode();
if (opcode == MONITORENTER || opcode == MONITOREXIT) {
LabelNode labelNode = newLabelNode();
instructions.insert(node, labelNode);
Frame frame = findNextFrame(frames, node);
Object[] stack = frame.stack;
if (!isNextFrameNode(labelNode)) {
instructions.insert(labelNode, newFrameNode(appendValue(ensureSize(frame.locals, implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl"), stack));
}
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode, new JumpInsnNode(IFNULL, labelNode));
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
opcode == MONITORENTER ? "monitorEntered" : "monitorExited", "()V", false));
updateMaxStack(stack.length + 1);
}
}
}
private void addInvocationStartedHook(int implVarIndex, LabelNode labelNode) {
if ((access & ACC_STATIC) == 0) {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, 0));
instructions.insertBefore(labelNode, new LdcInsnNode(name));
instructions.insertBefore(labelNode, new LdcInsnNode(desc));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"invocationStarted", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V", false));
} else {
instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(labelNode, new LdcInsnNode(Type.getType('L' + owner + ';')));
instructions.insertBefore(labelNode, new LdcInsnNode(name));
instructions.insertBefore(labelNode, new LdcInsnNode(desc));
instructions.insertBefore(labelNode,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"staticInvocationStarted", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V",
false));
}
updateMaxStack(4);
}
private void addInvocationEndedHook(int implVarIndex, LabelNode labelNode) {
LabelNode startLabelNode = labelNode;
LabelNode handlerLabelNode = newLabelNode();
for (AbstractInsnNode next = labelNode.getNext(); next != null; next = next.getNext()) {
int opcode = next.getOpcode();
if (opcode == IRETURN || opcode == LRETURN || opcode == FRETURN || opcode == DRETURN ||
opcode == ARETURN || opcode == RETURN) {
LabelNode endLabelNode = newLabelNode();
instructions.insertBefore(next, endLabelNode);
instructions.insertBefore(next, new VarInsnNode(ALOAD, implVarIndex));
LabelNode labelNode1 = newLabelNode();
instructions.insertBefore(next, new JumpInsnNode(IFNULL, labelNode1));
instructions.insertBefore(next, new VarInsnNode(ALOAD, implVarIndex));
instructions.insertBefore(next,
new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"invocationEnded", "()V", false));
Frame frame = frames.get(next);
Object[] stack = frame.stack;
instructions.insertBefore(next, labelNode1);
instructions.insertBefore(next, newFrameNode(appendValue(ensureSize(frame.locals, implVarIndex),
"org/jephyr/continuation/easyflow/ContinuationImpl"), stack));
addTryCatchBlockNode(startLabelNode, endLabelNode, handlerLabelNode);
startLabelNode = newLabelNode();
instructions.insert(next, startLabelNode);
updateMaxStack(stack.length + 1);
}
}
instructions.add(handlerLabelNode);
addTryCatchBlockNode(startLabelNode, handlerLabelNode, handlerLabelNode);
Object[] locals = new Object[implVarIndex + 1];
for (int i = 0; i < implVarIndex; i++) {
locals[i] = TOP;
}
locals[implVarIndex] = "org/jephyr/continuation/easyflow/ContinuationImpl";
Object[] stack = { "java/lang/Throwable" };
instructions.add(newFrameNode(locals, stack));
instructions.add(new VarInsnNode(ALOAD, implVarIndex));
LabelNode labelNode1 = newLabelNode();
instructions.add(new JumpInsnNode(IFNULL, labelNode1));
instructions.add(new VarInsnNode(ALOAD, implVarIndex));
instructions.add(new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
"invocationEnded", "()V", false));
instructions.add(labelNode1);
instructions.add(newFrameNode(locals, stack));
instructions.add(new InsnNode(ATHROW));
updateMaxStack(2);
}
private void addTryCatchBlockNode(LabelNode startLabelNode, LabelNode endLabelNode, LabelNode handlerLabelNode) {
for (AbstractInsnNode next = startLabelNode.getNext(); next != endLabelNode; next = next.getNext()) {
if (next.getOpcode() != -1) {
tryCatchBlocks.add(new TryCatchBlockNode(startLabelNode, endLabelNode, handlerLabelNode, null));
return;
}
}
}
private static boolean isLong(Object value) {
return value == LONG || value == DOUBLE;
}
private static Object[] ensureSize(Object[] values, int size) {
if (values.length >= size) {
return values;
}
Object[] values1 = new Object[size];
System.arraycopy(values, 0, values1, 0, values.length);
for (int i = values.length; i < size; i++) {
values1[i] = TOP;
}
return values1;
}
private static Object[] appendValue(Object[] values, Object value) {
Object[] values1 = new Object[values.length + 1];
System.arraycopy(values, 0, values1, 0, values.length);
values1[values.length] = value;
return values1;
}
private static Object[] appendValues(Object[] values, Object... valuesToSet) {
Object[] values1 = new Object[values.length + valuesToSet.length];
System.arraycopy(values, 0, values1, 0, values.length);
System.arraycopy(valuesToSet, 0, values1, values.length, valuesToSet.length);
return values1;
}
private static LabelNode newLabelNode() {
Label label = new Label();
LabelNode labelNode = new LabelNode(label);
label.info = labelNode;
return labelNode;
}
private static FrameNode newFrameNode(Object[] locals, Object[] stack) {
Object[] locals1 = convertValues(locals);
Object[] stack1 = convertValues(stack);
return new FrameNode(F_NEW, locals1.length, locals1, stack1.length, stack1);
}
private static Object[] convertValues(Object[] values) {
int n = values.length;
Collection<Object> values1 = new ArrayList<>(n);
int i = 0;
while (i < n) {
Object value = values[i];
values1.add(value);
i += isLong(value) ? 2 : 1;
}
return values1.toArray();
}
private static boolean isNextFrameNode(AbstractInsnNode node) {
for (AbstractInsnNode next = node.getNext(); next != null && next.getOpcode() == -1; next = next.getNext()) {
if (next instanceof FrameNode) {
return true;
}
}
return false;
}
private static AbstractInsnNode newPushDefaultNode(Type type) {
switch (type.getSort()) {
case Type.FLOAT:
return new InsnNode(FCONST_0);
case Type.LONG:
return new InsnNode(LCONST_0);
case Type.DOUBLE:
return new InsnNode(DCONST_0);
case Type.ARRAY:
case Type.OBJECT:
return new InsnNode(ACONST_NULL);
default:
return new InsnNode(ICONST_0);
}
}
private static Frame findNextFrame(Map<AbstractInsnNode, Frame> frames, AbstractInsnNode node) {
AbstractInsnNode next = node.getNext();
while (true) {
Frame frame = frames.get(next);
if (frame != null) {
return frame;
}
next = next.getNext();
}
}
private static AbstractInsnNode newPushNode(int operand) {
if (operand <= 5) {
return new InsnNode(ICONST_0 + operand);
} else if (operand <= Byte.MAX_VALUE) {
return new IntInsnNode(BIPUSH, operand);
} else if (operand <= Short.MAX_VALUE) {
return new IntInsnNode(SIPUSH, operand);
} else {
return new LdcInsnNode(operand);
}
}
private void updateMaxStack(int stackSize) {
if (maxStack < stackSize) {
maxStack = stackSize;
}
}
}