All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kilim.analysis.CallWeaver Maven / Gradle / Ivy

Go to download

Coroutines, continuations, fibers, actors and message passing for the JVM

There is a newer version: 2.0.2-jdk7
Show newest version
/* Copyright (c) 2006, Sriram Srinivasan
 *
 * You may distribute this software under the terms of the license 
 * specified in the file "License"
 */
package kilim.analysis;
import static kilim.Constants.ACC_ABSTRACT;
import static kilim.Constants.D_FIBER_LAST_ARG;
import static kilim.Constants.ALOAD_0;
import static kilim.Constants.ASTORE_0;
import static kilim.Constants.DLOAD_0;
import static kilim.Constants.DSTORE_0;
import static kilim.Constants.D_BOOLEAN;
import static kilim.Constants.D_BYTE;
import static kilim.Constants.D_CHAR;
import static kilim.Constants.D_DOUBLE;
import static kilim.Constants.D_FIBER;
import static kilim.Constants.D_FLOAT;
import static kilim.Constants.D_INT;
import static kilim.Constants.D_LONG;
import static kilim.Constants.D_NULL;
import static kilim.Constants.D_OBJECT;
import static kilim.Constants.D_SHORT;
import static kilim.Constants.D_STATE;
import static kilim.Constants.D_VOID;
import static kilim.Constants.D_UNDEFINED;
import static kilim.Constants.FIBER_CLASS;
import static kilim.Constants.FLOAD_0;
import static kilim.Constants.FSTORE_0;
import static kilim.Constants.ILOAD_0;
import static kilim.Constants.ISTORE_0;
import static kilim.Constants.LLOAD_0;
import static kilim.Constants.LSTORE_0;
import static kilim.Constants.STATE_CLASS;
import static kilim.analysis.VMType.TOBJECT;
import static kilim.analysis.VMType.loadVar;
import static kilim.analysis.VMType.storeVar;
import static kilim.analysis.VMType.toVmType;
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.BIPUSH;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DCONST_0;
import static org.objectweb.asm.Opcodes.DCONST_1;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DSTORE;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.FCONST_0;
import static org.objectweb.asm.Opcodes.FCONST_1;
import static org.objectweb.asm.Opcodes.FCONST_2;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.FSTORE;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.I2B;
import static org.objectweb.asm.Opcodes.I2C;
import static org.objectweb.asm.Opcodes.I2S;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_M1;
import static org.objectweb.asm.Opcodes.ILOAD;
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.INVOKEINTERFACE;
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.LCONST_1;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.LSTORE;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.POP2;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SIPUSH;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;

import kilim.mirrors.CachedClassMirrors.ClassMirror;
import kilim.mirrors.CachedClassMirrors.MethodMirror;
import kilim.mirrors.ClassMirrorNotFoundException;
import kilim.mirrors.Detector;

import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.MethodInsnNode;

/**
 * This class produces all the code associated with a specific pausable method
 * invocation. There are three distinct chunks of code.
 * 
*
  • Rewind: At the beginning of the method, right after the opening * switch statement (switch fiber.pc), which is the "rewind" portion. This stage * pushes in (mostly) dummy values on the stack and jumps to the next method * invocation in the cycle.
  • * *
  • Call: The actual call. We push fiber as the last argument to the * pausable method (by calling fiber.down()), before making the call.
  • * *
  • Post-call The bulk of the code produced by this object.
  • * *

    * * An explanation of some terms used in the code may be useful. * * Much of this code concerns itself with storing and retrieving values from/to * the stack and the local variables. The stack consists of three logical parts:
    * *

     *   +--------------+----------------------+---------------+
     *   | Stack bottom | callee obj reference | args for call | (Top of stack)
     *   +--------------+----------------------+---------------+
     * 
    * * The callee's object reference and the arguments at the top of stack are * consumed by the method invocation. If the call is static, there is no object * reference, of course. The bottom of the stack (which may also be empty) * refers to the part of the stack that is left over once the args are consumed. * This is the part that we need to save if we have to yield. *

    * * As for the local variables, we are interested in saving var 0 (the "this" * pointer) and all other variables that the flow analysis has shown to be * live-in, that is, is used downstream of the call. Typically, only about 30% * of all available vars are actually used downstream, so we use the rest for * temporary storage. *

    * * @see #genRewind(MethodVisitor) * @see #genCall(MethodVisitor) * @see #genPostCall(MethodVisitor) * */ public class CallWeaver { /** * The parent method-weaver responsible for writing the whole method */ private MethodWeaver methodWeaver; /** * The basic block that calls the pausable method */ BasicBlock bb; private LabelNode resumeLabel; LabelNode callLabel; private ValInfoList valInfoList; /** * varUsage[i] is true if the i-th var is taken. We don't touch the first * maxLocals vars. It is used for minimizing usage of extra vars. */ BitSet varUsage; /** * number of local registers required. */ int numVars; /** The canoncial name of the state class responsible for storing * the state (@see kilim.State) */ private String stateClassName; /** Memoized version of getNumArgs() */ int numArgs = -1; private Detector detector; public CallWeaver(MethodWeaver mw, Detector d, BasicBlock aBB) { detector = d; methodWeaver = mw; bb = aBB; callLabel = bb.startLabel; varUsage = new BitSet(2 * bb.flow.maxLocals); resumeLabel = bb.flow.getLabelAt(bb.startPos + 1); if (resumeLabel == null) resumeLabel = new LabelNode(); assignRegisters(); stateClassName = createStateClass(); methodWeaver.ensureMaxStack(getNumBottom() + 3); // } /** * The basic block's frame tells us the number of parameters in the stack * and which local variables are needed later on. * * If the method is pausable, we'll need the bottom part of the stack and * the object reference from the top part of the stack to be able to resume * it. We don't need to worry about the arguments, because they will be * saved (if needed) in the _called_ method's state. * * The "this" arg (var0) is given special treatment. It is always saved in * all states, so it doesn't count as "data". */ private void assignRegisters() { Frame f = bb.startFrame; MethodWeaver mw = methodWeaver; varUsage.set(mw.getFiberVar()); numVars = mw.getFiberVar() + 1; // knowing fiberVar is beyond anything // that's used mw.ensureMaxVars(numVars); Usage u = bb.usage; valInfoList = new ValInfoList(); /* * Create ValInfos for each Value that needs to be saved (all live-in * vars (except var 0, if not static) and all stack bottom vars count, * except if they are duplicates of earlier ones or are constants which * can be reproduced in bytecode itself. * * Process local vars before the stack, so that we can figure out which * elements of the stack are duplicates. Saving the stack requires more * processing, and the more we can reduce it, the better. */ // ensure we don't touch any of the locals in the range that the // original // code knows about. varUsage.set(0, f.getMaxLocals()); int i = bb.flow.isStatic() ? 0 : 1; for (; i < f.getMaxLocals(); i++) { Value v = f.getLocal(i); if (u.isLiveIn(i)) { if (!(v.isConstant() || valInfoList.contains(v))) { ValInfo vi = new ValInfo(v); vi.var = i; valInfoList.add(vi); } } } /* * All stack values at the bottom (those not consumed by the called * method) will have to be saved, if they are are not already accounted * for or they are constants. */ int numBottom = getNumBottom(); for (i = 0; i < numBottom; i++) { Value v = f.getStack(i); if (!(v.isConstant() || valInfoList.contains(v))) { ValInfo vi = new ValInfo(v); valInfoList.add(vi); } } Collections.sort(valInfoList); // sorts by type and var int fieldNum = 0; for (ValInfo vi : valInfoList) { vi.fieldName = "f" + fieldNum++; } } int getStackLen() { return bb.startFrame.getStackLen(); } /** * The total number consumed by the call, including its object reference */ int getNumArgs() { if (numArgs == -1) { numArgs = TypeDesc.getNumArgumentTypes(getMethodInsn().desc) + (isStaticCall() ? 0 : 1); } return numArgs; } final boolean isStaticCall() { return getMethodInsn().getOpcode() == INVOKESTATIC; } final MethodInsnNode getMethodInsn() { return (MethodInsnNode) bb.getInstruction(bb.startPos); } int getNumBottom() { return getStackLen() - getNumArgs(); } /** *

         * The following template is produced in the method's prelude for each
         * pausable method. 
         * F_REWIND: 
         *   for each bottom stack operand
         *      introduce a dummy constant of the appropriate type.
         *         (iconst_0, aconst_null, etc.)
         *      if the call is not static, 
         *         we need the called object's object reference
         *         ask the next state in the fiber's list
         *   goto F_CALL: // jump to the invocation site.
         * 
    * * @param mv */ void genRewind(MethodVisitor mv) { Frame f = bb.startFrame; // The last parameter to the method is fiber, but the original // code doesn't know that and will use up that slot as it // pleases. If we are going to jump directly to the basicblock // bb, we'll have to make sure it has some dummy value of that // type going in just to keep the verifier happy. for (int i = methodWeaver.getNumWordsInArg(); i < f.getMaxLocals(); ) { Value v = f.getLocal(i); if (v.getTypeDesc() != D_UNDEFINED) { // if (u.isLiveIn(i)) { int vmt = toVmType(v.getTypeDesc()); mv.visitInsn(VMType.constInsn[vmt]); storeVar(mv, vmt, i); } i += v.isCategory2() ? 2 : 1; } // store dummy values in stack. The constants have to correspond // to the types of each of the bottom elements. int numBottom = getNumBottom(); // spos == stack pos. Note that it is incremented beyond the // 'for' loop below. int spos; for (spos = 0; spos < numBottom; spos++) { Value v = f.getStack(spos); if (v.isConstant()) { mv.visitInsn(VMType.constInsn[VMType.toVmType(v.getTypeDesc())]); } else { ValInfo vi = valInfoList.find(v); mv.visitInsn(VMType.constInsn[vi.vmt]); } } if (!isStaticCall()) { // The next element in the stack after numBottom is the object // reference for the callee. This can't be a dummy because it // is needed by the invokevirtual call. If this reference // is found in the local vars, we'll use it, otherwise we need // to dip into the next activation frame in the fiber to // retrieve it. Value v = f.getStack(numBottom); if (!methodWeaver.isStatic() && f.getLocal(0) == v) { // "this" is already properly initialized. mv.visitVarInsn(ALOAD, 0); } else { loadVar(mv, TOBJECT, methodWeaver.getFiberVar()); mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "getCallee", "()Ljava/lang/Object;", false); mv.visitTypeInsn(CHECKCAST, getReceiverTypename()); } spos++; } int len = f.getStackLen(); // fill rest of stack with dummy args for (; spos < len; spos++) { Value v = f.getStack(spos); int vmt = VMType.toVmType(v.getTypeDesc()); mv.visitInsn(VMType.constInsn[vmt]); } mv.visitJumpInsn(GOTO, callLabel.getLabel()); } /** * Before we make the target call, we need to call fiber.down(), to update * it on the depth of the stack. genPostCall subsequently arranges to call * fiber.up(). We also need to push the fiber as the last argument to the * pausable method. We accomplish both of these objectives by having * fiber.down() do its book-keeping and return fiber, which is left on the * stack before the call is made. * *
         *             
         *            F_CALL: 
         *               push fiber.down()
         *               invoke[virtual|static] classname/method modifiedDesc 
         * 
    * * @param mv */ void genCall(MethodVisitor mv) { mv.visitLabel(callLabel.getLabel()); loadVar(mv, TOBJECT, methodWeaver.getFiberVar()); mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "down", "()" + D_FIBER, false); MethodInsnNode mi = getMethodInsn(); if (isSAM(mi)) { ClassWeaver cw = methodWeaver.getClassWeaver(); SAMweaver sw = cw.getSAMWeaver(mi.owner, mi.name, mi.desc, mi.itf); //System.out.println("SAM call to: " + mi.owner + mi.name + mi.desc); //System.out.println("invokestatic: " + cw.getName() + " " + sw.getShimMethodName()); mi = new MethodInsnNode(INVOKESTATIC, cw.getName(), sw.getShimMethodName(), sw.getShimDesc(), cw.isInterface()); } if (mi.desc.indexOf(D_FIBER_LAST_ARG) == -1) { // Don't add another fiberarg if it already has one. It'll already // have one if we have copied jsr instructions and modified the // same instruction node earlier. mi.desc = mi.desc.replace(")", D_FIBER_LAST_ARG); } mi.accept(mv); } /** * Is the given method the sole abstract method (modulo woven variants). */ boolean isSAM(MethodInsnNode mi) { Detector det = this.detector; int count = 0; boolean match = false; try { ClassMirror cm = det.classForName(mi.owner); // java7 can't call java8, but java8 can call java7 - only check the callee if (cm.version() < 52) return false; String fdesc = mi.desc.replace(")", D_FIBER_LAST_ARG); for (MethodMirror m: cm.getDeclaredMethods()) { if (count < 2 & (m.getModifiers() & ACC_ABSTRACT) > 0) { count++; String desc = m.getMethodDescriptor(); if (m.getName().equals(mi.name) & (desc.equals(mi.desc) | desc.equals(fdesc))) match = true; } } } catch (ClassMirrorNotFoundException ignore) { /* This error would have been caught earlier in MethodFlow, if the class can't be found */ } return match && (count == 1); } /** * After the pausable method call is over, we have four possibilities. The * called method yielded, or it returned normally. Orthogonally, we have * saved state or not. fiber.up() returns the combined status * ? *
         *                     
         * switch (fiber.up()) {
         *    default:
         *    0: goto RESUME; // Not yielding , no State --  resume normal operations
         *    1: goto RESTORE; // Not yielding, has State -- restore state before resuming
         *    2: goto SAVE; // Yielding, no state -- save state before unwinding stack
         *    3: goto UNWIND // Yielding, has state -- nothing to do but unwind.
         * }
         * SAVE:
         *     ...
         *     xRETURN
         * UNWIND:
         *     ...
         *     xRETURN
         * RESTORE:
         *     ...
         *     // fall through to RESUME
         * RESUME:
         *     ... original code
         * 
    * * @param mv * */ void genPostCall(MethodVisitor mv) { loadVar(mv, TOBJECT, methodWeaver.getFiberVar()); mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "up", "()I", false); LabelNode restoreLabel = new LabelNode(); LabelNode saveLabel = new LabelNode(); LabelNode unwindLabel = new LabelNode(); LabelNode[] labels = new LabelNode[] { resumeLabel, restoreLabel, saveLabel, unwindLabel }; new TableSwitchInsnNode(0, 3, resumeLabel, labels).accept(mv); genSave(mv, saveLabel); genUnwind(mv, unwindLabel); genRestore(mv, restoreLabel); resumeLabel.accept(mv); } /** * Code for the case where we are yielding, and we have state built up from * a previous call. There's nothing meaningful to do except keep the * verifier happy. Pop the bottom stack, then return a dummy return value * (if this method -- note: not the called method -- returns a value) * * @param mv */ private void genUnwind(MethodVisitor mv, LabelNode unwindLabel) { unwindLabel.accept(mv); // After the call returns, the stack would be left with numBottom plus // return value // pop callee's dummy return value first String rdesc = getReturnType(); if (rdesc != D_VOID) { mv.visitInsn(TypeDesc.isDoubleWord(rdesc) ? POP2 : POP); } // pop numbottom int i = getNumBottom() - 1; // the numBottom'th element Frame f = bb.startFrame; for (; i >= 0; i--) { mv.visitInsn(f.getStack(i).isCategory1() ? POP : POP2); } // Now for the return value of this (the calling) method rdesc = TypeDesc.getReturnTypeDesc(bb.flow.desc); if (rdesc != D_VOID) { // ICONST_0; IRETURN or ACONST_NULL; ARETURN etc. int vmt = VMType.toVmType(rdesc); mv.visitInsn(VMType.constInsn[vmt]); mv.visitInsn(VMType.retInsn[vmt]); } else { mv.visitInsn(RETURN); } } private String getReturnType() { return TypeDesc.getReturnTypeDesc(getMethodInsn().desc); } /** * Yielding, but state hasn't been captured yet. We create a state object * and save each object in valInfoList in its corresponding field. * * Note that we save each stack item into a scratch register before loading * it into a field. The reason is that we need to get the State ref under * the stack item before we can do a putfield. The alternative is to load * the State item, then do a swap or a dup_x2;pop (depending on the value's * category). We'll go with the earlier approach because stack manipulations * don't seem to perform as well in the current crop of JVMs. * * @param mv */ private void genSave(MethodVisitor mv, LabelNode saveLabel) { saveLabel.accept(mv); Frame f = bb.startFrame; // pop return value if any. String retType = getReturnType(); if (retType != D_VOID) { // Yielding, so the return value from the called // function is a dummy value mv.visitInsn(TypeDesc.isDoubleWord(retType) ? POP2 : POP); } /* * Instantiate state class. Call one of new xxxState(this, pc, fiber), * or new xxxState(pc, fiber) depending whether this method is static or * not. Note that are not referring to the called method. * * pc, "the program counter" is merely the index of the call weaver in * the method weaver's list. This allows us to do a switch in the * method's entry. */ mv.visitTypeInsn(NEW, stateClassName); mv.visitInsn(DUP); // // call constructor mv.visitMethodInsn(INVOKESPECIAL, stateClassName, "", "()V", false); // save state in register int stateVar = allocVar(1); storeVar(mv, TOBJECT, stateVar); // state.self = this if the current executing method isn't static if (!bb.flow.isStatic()) { loadVar(mv, TOBJECT, stateVar); mv.visitVarInsn(ALOAD, 0); // for state.self == this mv.visitFieldInsn(PUTFIELD, STATE_CLASS, "self", D_OBJECT); } int pc = methodWeaver.getPC(this); loadVar(mv, TOBJECT, stateVar); // state.pc if (pc < 6) { mv.visitInsn(ICONST_0 + pc); } else { mv.visitIntInsn(BIPUSH, pc); } mv.visitFieldInsn(PUTFIELD, STATE_CLASS, "pc", D_INT); // First save bottom stack into state int i = getNumBottom() - 1; for (; i >= 0; i--) { Value v = f.getStack(i); ValInfo vi = valInfoList.find(v); if (vi == null) { // it must be a constant or a duplicate, which is why we don't // have any information on it. just pop it. mv.visitInsn(v.category() == 2 ? POP2 : POP); } else { /** * xstore ; send stack elem to some local var * aload ; load state * xload ; get stack elem back * putfield ... ; state.fx = */ int var = allocVar(vi.val.category()); storeVar(mv, vi.vmt, var); loadVar(mv, VMType.TOBJECT, stateVar); loadVar(mv, vi.vmt, var); mv.visitFieldInsn(PUTFIELD, stateClassName, vi.fieldName, vi.fieldDesc()); releaseVar(var, vi.val.category()); } } // Now load up registers into fields for (ValInfo vi : valInfoList) { // Ignore values on stack if (vi.var == -1) continue; // aload state var // xload loadVar(mv, TOBJECT, stateVar); loadVar(mv, vi.vmt, vi.var); mv.visitFieldInsn(PUTFIELD, stateClassName, vi.fieldName, vi.fieldDesc()); } // Fiber.setState(state); loadVar(mv, TOBJECT, methodWeaver.getFiberVar()); loadVar(mv, TOBJECT, stateVar); mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "setState", "(" + D_STATE + ")V", false); releaseVar(stateVar, 1); // Figure out the return type of the calling method and issue the // appropriate xRETURN instruction retType = TypeDesc.getReturnTypeDesc(bb.flow.desc); if (retType == D_VOID) { mv.visitInsn(RETURN); } else { int vmt = VMType.toVmType(retType); // ICONST_0, DCONST_0 etc. mv.visitInsn(VMType.constInsn[vmt]); // IRETURN, DRETURN, etc. mv.visitInsn(VMType.retInsn[vmt]); } } /** * Not yielding (resuming normally), but have stored state. We need to * restore from state before resuming. This is slightly more work than * saving state, because we have to restore constants and duplicates too. * * * Note that the return value (if any) has a real value, which is why it * needs to be saved away before we can get access to the bottom elements to * pop them out. * *
         *           If there is anything at the bottom save return value in scratch register
         *                 pop unnecessary bottom stuff.
         *          
         *           load fiber.curState 
         *           cast to specific state (if necessary) 
         *           astore in scratch <stateVar>
         *          
         *          for each value in frame.var 
         *               if val is constant or is in valInfoList, 
         *                   push constant or load field (appropriately) 
         *          for each value in bottom stack
         *                 restore value similar to above 
         *          restore return value if any from scratch register
         * 
    */ private void genRestore(MethodVisitor mv, LabelNode restoreLabel) { restoreLabel.accept(mv); Frame f = bb.startFrame; int numBottom = getNumBottom(); int retVar = -1; int retctype = -1; if (numBottom > 0) { // We have dummy values sitting in the stack. pop 'em. // But first, check if we have a real return value on top String retType = getReturnType(); if (retType != D_VOID) { // .. yep. save it to scratch register retctype = VMType.toVmType(retType); retVar = allocVar(VMType.category[retctype]); storeVar(mv, retctype, retVar); } // pop dummy values from stack bottom for (int i = numBottom-1; i >= 0; i--) { Value v = f.getStack(i); int insn = v.isCategory1() ? POP : POP2; mv.visitInsn(insn); } } // Restore variables from state int stateVar = -1; if (valInfoList.size() > 0) { stateVar = allocVar(1); } genRestoreVars(mv, stateVar); // Now restore the bottom values in the stack from state for (int i = 0; i < numBottom; i++) { Value v = f.getStack(i); if (v.isConstant()) { loadConstant(mv, v); } else { ValInfo vi = valInfoList.find(v); if (vi.var == -1) { loadVar(mv, TOBJECT, stateVar); mv.visitFieldInsn(GETFIELD, stateClassName, vi.fieldName, vi.fieldDesc()); checkcast(mv, v); } else { // this stack value is a duplicate of a local var, which has // already been loaded and is of the right type loadVar(mv, vi.vmt, vi.var); } } } // restore the saved return value, if any // But we would have popped and saved the return value only // if there were any dummy values in the stack bottom to be // cleared out. If not, we wouldn't have bothered // popping the return value in the first place. if (numBottom > 0) { if (retVar != -1) { loadVar(mv, retctype, retVar); } } releaseVar(stateVar, 1); if (retctype != -1) { releaseVar(retVar, VMType.category[retctype]); } // Fall through to the resumeLabel in genNY_NS, so no goto required. } void genRestoreEx(MethodVisitor mv, LabelNode restoreLabel) { restoreLabel.accept(mv); int stateVar = -1; if (valInfoList.size() > 0) { stateVar = allocVar(1); } genRestoreVars(mv, stateVar); releaseVar(stateVar, 1); } /* */ private void genRestoreVars(MethodVisitor mv, int stateVar) { Frame f = bb.startFrame; if (valInfoList.size() > 0) { // need to have state in a local variable loadVar(mv, TOBJECT, methodWeaver.getFiberVar()); mv.visitFieldInsn(GETFIELD, FIBER_CLASS, "curState", D_STATE); if (!stateClassName.equals(STATE_CLASS)) { mv.visitTypeInsn(CHECKCAST, stateClassName); } storeVar(mv, TOBJECT, stateVar); } // no need to restore "this" if it's already there. Usage u = bb.usage; int len = f.getMaxLocals(); int i = bb.flow.isStatic() ? 0 : 1; for (; i < len; i++) { if (!u.isLiveIn(i)) continue; Value v = f.getLocal(i); int vmt = VMType.toVmType(v.getTypeDesc()); if (v.isConstant()) { loadConstant(mv, v); } else { ValInfo vi = valInfoList.find(v); if (vi.var == i) { // load val from state loadVar(mv, TOBJECT, stateVar); mv.visitFieldInsn(GETFIELD, stateClassName, vi.fieldName, vi.fieldDesc()); checkcast(mv, v); // don't need to do this in the constant case } else { // It is a duplicate of another var. No need to load this var from stack assert vi.var < i; loadVar(mv, vi.vmt, vi.var); } } // Convert types from vm types to value's real type, if necessary // store into local storeVar(mv, vmt, i); } releaseVar(stateVar, 1); } private String getReceiverTypename() { MethodInsnNode min = getMethodInsn(); return min.owner; } /** * We have loaded a value of one of the five VM types into the stack and we * need to cast it to the value's type, if necessary * * @param mv * @param v */ private void checkcast(MethodVisitor mv, Value v) { String valType = v.getTypeDesc(); int vmt = VMType.toVmType(valType); switch (vmt) { case VMType.TOBJECT: if (valType == D_OBJECT || valType == D_NULL) { return; } mv.visitTypeInsn(CHECKCAST, TypeDesc.getInternalName(valType)); break; case VMType.TINT: if (valType == D_INT) return; int insn = 0; if (valType == D_SHORT) insn = I2S; else if (valType == D_BYTE) insn = I2B; else if (valType == D_CHAR) insn = I2C; else assert valType == D_BOOLEAN; mv.visitInsn(insn); break; default: break; } } private void loadConstant(MethodVisitor mv, Value v) { if (v.getTypeDesc() == D_NULL) { mv.visitInsn(ACONST_NULL); return; } Object c = v.getConstVal(); if (c instanceof Integer) { int i = (Integer) c; if (i > -1 && i <= 5) { mv.visitInsn(i + 1 + ICONST_M1); return; } else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) { mv.visitIntInsn(BIPUSH, i); return; } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) { mv.visitIntInsn(SIPUSH, i); return; } } else if (c instanceof Float) { Float f = ((Float) c).floatValue(); int insn = 0; if (f == 0.0) insn = FCONST_0; else if (f == 1.0) insn = FCONST_1; else if (f == 2.0) insn = FCONST_2; if (insn != 0) { mv.visitInsn(insn); return; } } else if (c instanceof Long) { Long l = ((Long) c).longValue(); int insn = 0; if (l == 0L) insn = LCONST_0; else if (l == 1L) insn = LCONST_1; if (insn != 0) { mv.visitInsn(insn); return; } } else if (c instanceof Double) { Double d = ((Double) c).doubleValue(); int insn = 0; if (d == 0.0) insn = DCONST_0; else if (d == 1.0) insn = DCONST_1; if (insn != 0) { mv.visitInsn(insn); return; } } // No special constants found. mv.visitLdcInsn(c); } private String createStateClass() { return valInfoList.size() == 0 ? STATE_CLASS : methodWeaver.createStateClass(valInfoList); } private int allocVar(int size) { int var; for (var = 0;; var++) { // if the var'th local is not set (if double, check the next word // too) if (!varUsage.get(var)) { if (size == 1 || !varUsage.get(var + 1)) { break; } } } varUsage.set(var); if (size == 2) { varUsage.set(var + 1); methodWeaver.ensureMaxVars(var + 2); // var is 0-based } else { methodWeaver.ensureMaxVars(var + 1); } return var; } private void releaseVar(int var, int size) { if (var == -1) return; varUsage.clear(var); if (size == 2) { varUsage.clear(var + 1); } } BasicBlock getBasicBlock() { return bb; } } class ValInfo implements Comparable { /** * The var to which the value belongs. It remains undefined if it is a stack * item. */ int var = -1; /** * The value to hold. This gives us information about the type, whether the * value is duplicated and whether it is a constant value. */ Value val; /** * The type of value boiled down to one of the canonical types. */ int vmt; /** * Names of the fields in the state var: "f0", "f1", etc, according to their * position in the call weaver's valInfoList. */ String fieldName; ValInfo(Value v) { val = v; vmt = VMType.toVmType(v.getTypeDesc()); } String fieldDesc() { return VMType.fieldDesc[vmt]; } public int compareTo(ValInfo that) { if (this == that) return 0; if (this.vmt < that.vmt) return -1; if (this.vmt > that.vmt) return 1; if (this.var < that.var) return -1; if (this.var > that.var) return 1; return 0; } } class ValInfoList extends ArrayList { private static final long serialVersionUID = -2339264992519046024L; public ValInfo find(Value v) { int i = indexOf(v); return (i == -1) ? null : get(i); } public int indexOf(Value v) { int len = size(); for (int i = 0; i < len; i++) { if (get(i).val == v) return i; } return -1; } public boolean contains(Value v) { return indexOf(v) != -1; } } class VMType { static final int TOBJECT = 0; static final int TINT = 1; static final int TLONG = 2; static final int TDOUBLE = 3; static final int TFLOAT = 4; static final int[] constInsn = { ACONST_NULL, ICONST_0, LCONST_0, DCONST_0, FCONST_0 }; static final int[] loadInsn = { ALOAD, ILOAD, LLOAD, DLOAD, FLOAD }; static final int[] retInsn = { ARETURN, IRETURN, LRETURN, DRETURN, FRETURN }; static final int[] ldInsn = { ALOAD_0, ILOAD_0, LLOAD_0, DLOAD_0, FLOAD_0 }; static final int[] stInsn = { ASTORE_0, ISTORE_0, LSTORE_0, DSTORE_0, FSTORE_0 }; static final int[] storeInsn = { ASTORE, ISTORE, LSTORE, DSTORE, FSTORE }; static final String[] fieldDesc = { D_OBJECT, D_INT, D_LONG, D_DOUBLE, D_FLOAT }; static final String[] abbrev = { "O", "I", "L", "D", "F" }; static final int[] category = { 1, 1, 2, 2, 1 }; static int toVmType(String type) { switch (type.charAt(0)) { case 'Z': case 'B': case 'C': case 'S': case 'I': return TINT; case 'D': return TDOUBLE; case 'F': return TFLOAT; case 'J': return TLONG; case 'N': // null case 'A': // catch handler return address case 'L': // normal type case '[': // array return TOBJECT; default: assert false : "Type " + type + " not handled"; } return ' '; } static void loadVar(MethodVisitor mv, int vmt, int var) { assert var >= 0 : "Got var = " + var; // ASM4.1 doesn't like short-form ALOAD_n instructions. Instead, we use ALOAD n. // if (var < 4) { // // short instructions like ALOAD_n exist for n = 0 .. 4 // mv.visitInsn(ldInsn[vmt] + var); // } else { mv.visitVarInsn(loadInsn[vmt], var); // } } static void storeVar(MethodVisitor mv, int vmt, int var) { assert var >= 0; // if (var < 4) { // // short instructions like ALOAD_n exist for n = 0 .. 4 // mv.visitInsn(stInsn[vmt] + var); // } else { mv.visitVarInsn(storeInsn[vmt], var); // } } }




    © 2015 - 2025 Weber Informatics LLC | Privacy Policy