PageRenderTime 112ms CodeModel.GetById 38ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 0ms

/src/kilim/analysis/CallWeaver.java

http://github.com/kilim/kilim
Java | 1083 lines | 652 code | 93 blank | 338 comment | 167 complexity | 3e711f9d80e22ed4c922c442b28dcd1b MD5 | raw file
   1/* Copyright (c) 2006, Sriram Srinivasan
   2 *
   3 * You may distribute this software under the terms of the license 
   4 * specified in the file "License"
   5 */
   6package kilim.analysis;
   7import static kilim.Constants.ACC_ABSTRACT;
   8import static kilim.Constants.D_FIBER_LAST_ARG;
   9import static kilim.Constants.ALOAD_0;
  10import static kilim.Constants.ASTORE_0;
  11import static kilim.Constants.DLOAD_0;
  12import static kilim.Constants.DSTORE_0;
  13import static kilim.Constants.D_BOOLEAN;
  14import static kilim.Constants.D_BYTE;
  15import static kilim.Constants.D_CHAR;
  16import static kilim.Constants.D_DOUBLE;
  17import static kilim.Constants.D_FIBER;
  18import static kilim.Constants.D_FLOAT;
  19import static kilim.Constants.D_INT;
  20import static kilim.Constants.D_LONG;
  21import static kilim.Constants.D_NULL;
  22import static kilim.Constants.D_OBJECT;
  23import static kilim.Constants.D_SHORT;
  24import static kilim.Constants.D_STATE;
  25import static kilim.Constants.D_VOID;
  26import static kilim.Constants.D_UNDEFINED;
  27import static kilim.Constants.FIBER_CLASS;
  28import static kilim.Constants.FLOAD_0;
  29import static kilim.Constants.FSTORE_0;
  30import static kilim.Constants.ILOAD_0;
  31import static kilim.Constants.ISTORE_0;
  32import static kilim.Constants.LLOAD_0;
  33import static kilim.Constants.LSTORE_0;
  34import static kilim.Constants.STATE_CLASS;
  35import static kilim.analysis.VMType.TOBJECT;
  36import static kilim.analysis.VMType.loadVar;
  37import static kilim.analysis.VMType.storeVar;
  38import static kilim.analysis.VMType.toVmType;
  39import static org.objectweb.asm.Opcodes.ACONST_NULL;
  40import static org.objectweb.asm.Opcodes.ALOAD;
  41import static org.objectweb.asm.Opcodes.ARETURN;
  42import static org.objectweb.asm.Opcodes.ASTORE;
  43import static org.objectweb.asm.Opcodes.BIPUSH;
  44import static org.objectweb.asm.Opcodes.CHECKCAST;
  45import static org.objectweb.asm.Opcodes.DCONST_0;
  46import static org.objectweb.asm.Opcodes.DCONST_1;
  47import static org.objectweb.asm.Opcodes.DLOAD;
  48import static org.objectweb.asm.Opcodes.DRETURN;
  49import static org.objectweb.asm.Opcodes.DSTORE;
  50import static org.objectweb.asm.Opcodes.DUP;
  51import static org.objectweb.asm.Opcodes.FCONST_0;
  52import static org.objectweb.asm.Opcodes.FCONST_1;
  53import static org.objectweb.asm.Opcodes.FCONST_2;
  54import static org.objectweb.asm.Opcodes.FLOAD;
  55import static org.objectweb.asm.Opcodes.FRETURN;
  56import static org.objectweb.asm.Opcodes.FSTORE;
  57import static org.objectweb.asm.Opcodes.GETFIELD;
  58import static org.objectweb.asm.Opcodes.GOTO;
  59import static org.objectweb.asm.Opcodes.I2B;
  60import static org.objectweb.asm.Opcodes.I2C;
  61import static org.objectweb.asm.Opcodes.I2S;
  62import static org.objectweb.asm.Opcodes.ICONST_0;
  63import static org.objectweb.asm.Opcodes.ICONST_M1;
  64import static org.objectweb.asm.Opcodes.ILOAD;
  65import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
  66import static org.objectweb.asm.Opcodes.INVOKESTATIC;
  67import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
  68import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
  69import static org.objectweb.asm.Opcodes.IRETURN;
  70import static org.objectweb.asm.Opcodes.ISTORE;
  71import static org.objectweb.asm.Opcodes.LCONST_0;
  72import static org.objectweb.asm.Opcodes.LCONST_1;
  73import static org.objectweb.asm.Opcodes.LLOAD;
  74import static org.objectweb.asm.Opcodes.LRETURN;
  75import static org.objectweb.asm.Opcodes.LSTORE;
  76import static org.objectweb.asm.Opcodes.NEW;
  77import static org.objectweb.asm.Opcodes.POP;
  78import static org.objectweb.asm.Opcodes.POP2;
  79import static org.objectweb.asm.Opcodes.PUTFIELD;
  80import static org.objectweb.asm.Opcodes.RETURN;
  81import static org.objectweb.asm.Opcodes.SIPUSH;
  82
  83import java.util.ArrayList;
  84import java.util.BitSet;
  85import java.util.Collections;
  86
  87import kilim.mirrors.CachedClassMirrors.ClassMirror;
  88import kilim.mirrors.CachedClassMirrors.MethodMirror;
  89import kilim.mirrors.ClassMirrorNotFoundException;
  90import kilim.mirrors.Detector;
  91
  92import org.objectweb.asm.tree.LabelNode;
  93import org.objectweb.asm.tree.TableSwitchInsnNode;
  94import org.objectweb.asm.MethodVisitor;
  95import org.objectweb.asm.tree.MethodInsnNode;
  96
  97/**
  98 * This class produces all the code associated with a specific pausable method
  99 * invocation. There are three distinct chunks of code.
 100 * <dl>
 101 * <li> <b>Rewind</b>: At the beginning of the method, right after the opening
 102 * switch statement (switch fiber.pc), which is the "rewind" portion. This stage
 103 * pushes in (mostly) dummy values on the stack and jumps to the next method
 104 * invocation in the cycle. </li>
 105 * 
 106 * <li> <b>Call</b>: The actual call. We push fiber as the last argument to the
 107 * pausable method (by calling fiber.down()), before making the call. </li>
 108 * 
 109 * <li> <b>Post-call</b> The bulk of the code produced by this object. </li>
 110 * </ol>
 111 * <p>
 112 * 
 113 * An explanation of some terms used in the code may be useful.
 114 * 
 115 * Much of this code concerns itself with storing and retrieving values from/to
 116 * the stack and the local variables. The stack consists of three logical parts:<br>
 117 * 
 118 * <pre>
 119 *   +--------------+----------------------+---------------+
 120 *   | Stack bottom | callee obj reference | args for call | (Top of stack)
 121 *   +--------------+----------------------+---------------+
 122 * </pre>
 123 * 
 124 * The callee's object reference and the arguments at the top of stack are
 125 * consumed by the method invocation. If the call is static, there is no object
 126 * reference, of course. The bottom of the stack (which may also be empty)
 127 * refers to the part of the stack that is left over once the args are consumed.
 128 * This is the part that we need to save if we have to yield.
 129 * <p>
 130 * 
 131 * As for the local variables, we are interested in saving var 0 (the "this"
 132 * pointer) and all other variables that the flow analysis has shown to be
 133 * live-in, that is, is used downstream of the call. Typically, only about 30%
 134 * of all available vars are actually used downstream, so we use the rest for
 135 * temporary storage.
 136 * <p>
 137 * 
 138 * @see #genRewind(MethodVisitor)
 139 * @see #genCall(MethodVisitor)
 140 * @see #genPostCall(MethodVisitor)
 141
 142 * 
 143 */
 144
 145public class CallWeaver {
 146    /**
 147     * The parent method-weaver responsible for writing the whole method
 148     */
 149    private MethodWeaver methodWeaver;
 150
 151    /**
 152     * The basic block that calls the pausable method
 153     */
 154    BasicBlock           bb;
 155
 156    private LabelNode    resumeLabel;
 157
 158    LabelNode            callLabel;
 159
 160    private ValInfoList  valInfoList;
 161
 162    /**
 163     * varUsage[i] is true if the i-th var is taken. We don't touch the first
 164     * maxLocals vars. It is used for minimizing usage of extra vars.
 165     */
 166    BitSet               varUsage;
 167
 168    /**
 169     * number of local registers required. 
 170     */
 171    int                  numVars;
 172
 173    /** The canoncial name of the state class responsible for storing
 174     * the state (@see kilim.State)
 175     */
 176    private String       stateClassName;
 177
 178    /** Memoized version of getNumArgs() */
 179    int                  numArgs = -1;
 180
 181    private Detector detector;
 182
 183    public CallWeaver(MethodWeaver mw, Detector d, BasicBlock aBB) {
 184        detector = d;
 185        methodWeaver = mw;
 186        bb = aBB;
 187        callLabel = bb.startLabel;
 188        varUsage = new BitSet(2 * bb.flow.maxLocals);
 189        resumeLabel = bb.flow.getLabelAt(bb.startPos + 1);
 190        if (resumeLabel == null)
 191            resumeLabel = new LabelNode();
 192        assignRegisters();
 193        stateClassName = createStateClass();
 194        methodWeaver.ensureMaxStack(getNumBottom() + 3); // 
 195    }
 196
 197    /**
 198     * The basic block's frame tells us the number of parameters in the stack
 199     * and which local variables are needed later on.
 200     * 
 201     * If the method is pausable, we'll need the bottom part of the stack and
 202     * the object reference from the top part of the stack to be able to resume
 203     * it. We don't need to worry about the arguments, because they will be
 204     * saved (if needed) in the _called_ method's state.
 205     * 
 206     * The "this" arg (var0) is given special treatment. It is always saved in
 207     * all states, so it doesn't count as "data".
 208     */
 209    private void assignRegisters() {
 210        Frame f = bb.startFrame;
 211        MethodWeaver mw = methodWeaver;
 212        varUsage.set(mw.getFiberVar());
 213        numVars = mw.getFiberVar() + 1; // knowing fiberVar is beyond anything
 214                                        // that's used
 215        mw.ensureMaxVars(numVars);
 216        Usage u = bb.usage;
 217        valInfoList = new ValInfoList();
 218
 219        /*
 220         * Create ValInfos for each Value that needs to be saved (all live-in
 221         * vars (except var 0, if not static) and all stack bottom vars count,
 222         * except if they are duplicates of earlier ones or are constants which
 223         * can be reproduced in bytecode itself.
 224         * 
 225         * Process local vars before the stack, so that we can figure out which
 226         * elements of the stack are duplicates. Saving the stack requires more
 227         * processing, and the more we can reduce it, the better.
 228         */
 229
 230        // ensure we don't touch any of the locals in the range that the
 231        // original
 232        // code knows about.
 233        varUsage.set(0, f.getMaxLocals());
 234
 235        int i = bb.flow.isStatic() ? 0 : 1;
 236        for (; i < f.getMaxLocals(); i++) {
 237            Value v = f.getLocal(i);
 238            if (u.isLiveIn(i)) {
 239                if (!(v.isConstant() || valInfoList.contains(v))) {
 240                    ValInfo vi = new ValInfo(v);
 241                    vi.var = i;
 242                    valInfoList.add(vi);
 243                }
 244            }
 245        }
 246
 247        /*
 248         * All stack values at the bottom (those not consumed by the called
 249         * method) will have to be saved, if they are are not already accounted
 250         * for or they are constants.
 251         */
 252        int numBottom = getNumBottom();
 253        for (i = 0; i < numBottom; i++) {
 254            Value v = f.getStack(i);
 255            if (!(v.isConstant() || valInfoList.contains(v))) {
 256                ValInfo vi = new ValInfo(v);
 257                valInfoList.add(vi);
 258            }
 259        }
 260        Collections.sort(valInfoList); // sorts by type and var
 261        int fieldNum = 0;
 262        for (ValInfo vi : valInfoList) {
 263            vi.fieldName = "f" + fieldNum++;
 264        }
 265    }
 266
 267    int getStackLen() {
 268        return bb.startFrame.getStackLen();
 269    }
 270
 271    /**
 272     * The total number consumed by the call, including its object reference
 273     */
 274    int getNumArgs() {
 275        if (numArgs == -1) {
 276            numArgs = TypeDesc.getNumArgumentTypes(getMethodInsn().desc)
 277                    + (isStaticCall() ? 0 : 1);
 278        }
 279        return numArgs;
 280    }
 281
 282    final boolean isStaticCall() {
 283        return getMethodInsn().getOpcode() == INVOKESTATIC;
 284    }
 285
 286    final MethodInsnNode getMethodInsn() {
 287        return (MethodInsnNode) bb.getInstruction(bb.startPos);
 288    }
 289
 290    int getNumBottom() {
 291        return getStackLen() - getNumArgs();
 292    }
 293
 294    /**
 295     * <pre>
 296     * The following template is produced in the method's prelude for each
 297     * pausable method. 
 298     * F_REWIND: 
 299     *   for each bottom stack operand
 300     *      introduce a dummy constant of the appropriate type.
 301     *         (iconst_0, aconst_null, etc.)
 302     *      if the call is not static, 
 303     *         we need the called object's object reference
 304     *         ask the next state in the fiber's list
 305     *   goto F_CALL: // jump to the invocation site.
 306     * </pre>
 307     * 
 308     * @param mv
 309     */
 310    void genRewind(MethodVisitor mv) {
 311        Frame f = bb.startFrame;
 312        
 313        // The last parameter to the method is fiber, but the original
 314        // code doesn't know that and will use up that slot as it
 315        // pleases. If we are going to jump directly to the basicblock
 316        // bb, we'll have to make sure it has some dummy value of that
 317        // type going in just to keep the verifier happy. 
 318
 319        for (int i = methodWeaver.getFiberArgVar(); i < f.getMaxLocals(); ) {
 320            Value v = f.getLocal(i);
 321            if (v.getTypeDesc() != D_UNDEFINED) {
 322//            if (u.isLiveIn(i)) {
 323                int vmt = toVmType(v.getTypeDesc());
 324                mv.visitInsn(VMType.constInsn[vmt]);
 325                storeVar(mv, vmt, i);
 326            }
 327            i += v.isCategory2() ? 2 : 1;
 328        }
 329
 330        // store dummy values in stack. The constants have to correspond
 331        // to the types of each of the bottom elements.
 332        int numBottom = getNumBottom();
 333
 334        // spos == stack pos. Note that it is incremented beyond the
 335        // 'for' loop below.
 336        int spos;
 337        for (spos = 0; spos < numBottom; spos++) {
 338            Value v = f.getStack(spos);
 339            if (v.isConstant()) {
 340                mv.visitInsn(VMType.constInsn[VMType.toVmType(v.getTypeDesc())]);
 341            } else {
 342                ValInfo vi = valInfoList.find(v);
 343                mv.visitInsn(VMType.constInsn[vi.vmt]);
 344            }
 345        }
 346        if (!isStaticCall()) {
 347            // The next element in the stack after numBottom is the object
 348            // reference for the callee. This can't be a dummy because it 
 349            // is needed by the invokevirtual call. If this reference
 350            // is found in the local vars, we'll use it, otherwise we need
 351            // to dip into the next activation frame in the fiber to
 352            // retrieve it.
 353            Value v = f.getStack(numBottom);
 354            if (!methodWeaver.isStatic() && f.getLocal(0) == v) {
 355                // "this" is already properly initialized.
 356                mv.visitVarInsn(ALOAD, 0);
 357            } else {
 358                loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
 359                mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "getCallee", "()Ljava/lang/Object;", false);
 360                mv.visitTypeInsn(CHECKCAST, getReceiverTypename());
 361            }
 362            spos++;
 363        }
 364
 365        int len = f.getStackLen();
 366        // fill rest of stack with dummy args
 367        for (; spos < len; spos++) {
 368            Value v = f.getStack(spos);
 369            int vmt = VMType.toVmType(v.getTypeDesc());
 370            mv.visitInsn(VMType.constInsn[vmt]);
 371        }
 372
 373        mv.visitJumpInsn(GOTO, callLabel.getLabel());
 374    }
 375
 376    /**
 377     * Before we make the target call, we need to call fiber.down(), to update
 378     * it on the depth of the stack. genPostCall subsequently arranges to call
 379     * fiber.up(). We also need to push the fiber as the last argument to the
 380     * pausable method. We accomplish both of these objectives by having
 381     * fiber.down() do its book-keeping and return fiber, which is left on the
 382     * stack before the call is made.
 383     * 
 384     * <pre>
 385     *             
 386     *            F_CALL: 
 387     *               push fiber.down()
 388     *               invoke[virtual|static] classname/method modifiedDesc 
 389     * </pre>
 390     * 
 391     * @param mv
 392     */
 393    void genCall(MethodVisitor mv) {
 394        mv.visitLabel(callLabel.getLabel());
 395        loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
 396        mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "down", "()" + D_FIBER, false);
 397        MethodInsnNode mi = getMethodInsn();
 398        if (isSAM(mi)) {
 399            ClassWeaver cw = methodWeaver.getClassWeaver();
 400            SAMweaver sw = cw.getSAMWeaver(mi.owner, mi.name, mi.desc, mi.itf);
 401            //System.out.println("SAM call to: " + mi.owner + mi.name + mi.desc);
 402            //System.out.println("invokestatic: " + cw.getName() + " " + sw.getShimMethodName());
 403            mi = new MethodInsnNode(INVOKESTATIC, cw.getName(), sw.getShimMethodName(), 
 404                    sw.getShimDesc(), cw.isInterface());
 405        }
 406        if (mi.desc.indexOf(D_FIBER_LAST_ARG) == -1) {
 407            // Don't add another fiberarg if it already has one. It'll already
 408            // have one if we have copied jsr instructions and modified the 
 409            // same instruction node earlier. 
 410            mi.desc = mi.desc.replace(")", D_FIBER_LAST_ARG);
 411        }
 412        mi.accept(mv);
 413    }
 414    
 415    /**
 416     * Is the given method the sole abstract method (modulo woven variants).
 417     */
 418    boolean isSAM(MethodInsnNode mi) {
 419        Detector det = this.detector;
 420        int count = 0;
 421        boolean match = false; 
 422        try {
 423            ClassMirror cm = det.classForName(mi.owner);
 424            // java7 can't call java8, but java8 can call java7 - only check the callee
 425            if (cm.version() < 52)
 426                return false;
 427            for (MethodMirror m: cm.getDeclaredMethods()) {
 428                if (m.getMethodDescriptor().indexOf(D_FIBER_LAST_ARG) == -1) {
 429                    if ((m.getModifiers() & ACC_ABSTRACT) > 0) {
 430                        count++;
 431                        if (m.getName().equals(mi.name) && m.getMethodDescriptor().equals(mi.desc)) {
 432                            match = true;
 433                        }
 434                    }
 435                }
 436            }
 437        } catch (ClassMirrorNotFoundException ignore) {
 438            /* This error would have been caught earlier in MethodFlow, if the class can't be found */
 439        }
 440        return match && (count == 1);
 441    }
 442
 443    /**
 444     * After the pausable method call is over, we have four possibilities. The
 445     * called method yielded, or it returned normally. Orthogonally, we have
 446     * saved state or not. fiber.up() returns the combined status
 447     * 
 448?     * <pre>
 449     *                     
 450     * switch (fiber.up()) {
 451     *    default:
 452     *    0: goto RESUME; // Not yielding , no State --  resume normal operations
 453     *    1: goto RESTORE; // Not yielding, has State -- restore state before resuming
 454     *    2: goto SAVE; // Yielding, no state -- save state before unwinding stack
 455     *    3: goto UNWIND // Yielding, has state -- nothing to do but unwind.
 456     * }
 457     * SAVE:
 458     *     ...
 459     *     xRETURN
 460     * UNWIND:
 461     *     ...
 462     *     xRETURN
 463     * RESTORE:
 464     *     ...
 465     *     // fall through to RESUME
 466     * RESUME:
 467     *     ... original code
 468     * </pre>
 469     * 
 470     * @param mv
 471     * 
 472     */
 473    void genPostCall(MethodVisitor mv) {
 474        loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
 475        mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "up", "()I", false);
 476        LabelNode restoreLabel = new LabelNode();
 477        LabelNode saveLabel = new LabelNode();
 478        LabelNode unwindLabel = new LabelNode();
 479        LabelNode[] labels = new LabelNode[] { resumeLabel, restoreLabel, saveLabel,
 480                unwindLabel };
 481        new TableSwitchInsnNode(0, 3, resumeLabel, labels).accept(mv);
 482        genSave(mv, saveLabel);
 483        genUnwind(mv, unwindLabel);
 484        genRestore(mv, restoreLabel);
 485        resumeLabel.accept(mv);
 486    }
 487
 488    /**
 489     * Code for the case where we are yielding, and we have state built up from
 490     * a previous call. There's nothing meaningful to do except keep the
 491     * verifier happy. Pop the bottom stack, then return a dummy return value
 492     * (if this method -- note: not the called method -- returns a value)
 493     * 
 494     * @param mv
 495     */
 496    private void genUnwind(MethodVisitor mv, LabelNode unwindLabel) {
 497        unwindLabel.accept(mv);
 498        // After the call returns, the stack would be left with numBottom plus
 499        // return value
 500
 501        // pop callee's dummy return value first
 502        String rdesc = getReturnType();
 503        if (rdesc != D_VOID) {
 504            mv.visitInsn(TypeDesc.isDoubleWord(rdesc) ? POP2 : POP);
 505        }
 506        // pop numbottom
 507        int i = getNumBottom() - 1; // the numBottom'th element
 508        Frame f = bb.startFrame;
 509        for (; i >= 0; i--) {
 510            mv.visitInsn(f.getStack(i).isCategory1() ? POP : POP2);
 511        }
 512
 513        // Now for the return value of this (the calling) method
 514        rdesc = TypeDesc.getReturnTypeDesc(bb.flow.desc);
 515        if (rdesc != D_VOID) {
 516            // ICONST_0; IRETURN or ACONST_NULL; ARETURN etc.
 517            int vmt = VMType.toVmType(rdesc);
 518            mv.visitInsn(VMType.constInsn[vmt]);
 519            mv.visitInsn(VMType.retInsn[vmt]);
 520        } else {
 521            mv.visitInsn(RETURN);
 522        }
 523    }
 524
 525    private String getReturnType() {
 526        return TypeDesc.getReturnTypeDesc(getMethodInsn().desc);
 527    }
 528
 529    /**
 530     * Yielding, but state hasn't been captured yet. We create a state object
 531     * and save each object in valInfoList in its corresponding field.
 532     * 
 533     * Note that we save each stack item into a scratch register before loading
 534     * it into a field. The reason is that we need to get the State ref under
 535     * the stack item before we can do a putfield. The alternative is to load
 536     * the State item, then do a swap or a dup_x2;pop (depending on the value's
 537     * category). We'll go with the earlier approach because stack manipulations
 538     * don't seem to perform as well in the current crop of JVMs.
 539     * 
 540     * @param mv
 541     */
 542    private void genSave(MethodVisitor mv, LabelNode saveLabel) {
 543        saveLabel.accept(mv);
 544
 545        Frame f = bb.startFrame;
 546        // pop return value if any.
 547        String retType = getReturnType(); 
 548        if (retType != D_VOID) {
 549            // Yielding, so the return value from the called
 550            // function is a dummy value
 551            mv.visitInsn(TypeDesc.isDoubleWord(retType) ? POP2 : POP);
 552        }
 553        /*
 554         * Instantiate state class. Call one of new xxxState(this, pc, fiber),
 555         * or new xxxState(pc, fiber) depending whether this method is static or
 556         * not. Note that are not referring to the called method.
 557         * 
 558         * pc, "the program counter" is merely the index of the call weaver in
 559         * the method weaver's list. This allows us to do a switch in the
 560         * method's entry.
 561         */
 562        mv.visitTypeInsn(NEW, stateClassName);
 563        mv.visitInsn(DUP); // 
 564        // call constructor
 565        mv.visitMethodInsn(INVOKESPECIAL, stateClassName, "<init>", "()V", false);
 566        // save state in register
 567        int stateVar = allocVar(1);
 568        storeVar(mv, TOBJECT, stateVar);
 569        // state.self = this if the current executing method isn't static
 570        if (!bb.flow.isStatic()) {
 571            loadVar(mv, TOBJECT, stateVar);
 572            mv.visitVarInsn(ALOAD, 0); // for state.self == this
 573            mv.visitFieldInsn(PUTFIELD, STATE_CLASS, "self", D_OBJECT);
 574        }
 575        int pc = methodWeaver.getPC(this);
 576        loadVar(mv, TOBJECT, stateVar); // state.pc
 577        if (pc < 6) {
 578            mv.visitInsn(ICONST_0 + pc);
 579        } else {
 580            mv.visitIntInsn(BIPUSH, pc);
 581        }
 582        mv.visitFieldInsn(PUTFIELD, STATE_CLASS, "pc", D_INT);
 583
 584        // First save bottom stack into state
 585        int i = getNumBottom() - 1;
 586        for (; i >= 0; i--) {
 587            Value v = f.getStack(i);
 588            ValInfo vi = valInfoList.find(v);
 589            if (vi == null) {
 590                // it must be a constant or a duplicate, which is why we don't
 591                // have any information on it. just pop it.
 592                mv.visitInsn(v.category() == 2 ? POP2 : POP);
 593            } else {
 594                /**
 595                 * xstore <scratchVar> ; send stack elem to some local var 
 596                 * aload <stateVar>    ; load state 
 597                 * xload <scratchVar>  ; get stack elem back 
 598                 * putfield ...        ; state.fx =
 599                 */
 600                int var = allocVar(vi.val.category());
 601                storeVar(mv, vi.vmt, var);
 602                loadVar(mv, VMType.TOBJECT, stateVar);
 603                loadVar(mv, vi.vmt, var);
 604                mv.visitFieldInsn(PUTFIELD, stateClassName, vi.fieldName, vi.fieldDesc());
 605                releaseVar(var, vi.val.category());
 606            }
 607        }
 608
 609        // Now load up registers into fields
 610        for (ValInfo vi : valInfoList) {
 611            // Ignore values on stack
 612            if (vi.var == -1)
 613                continue;
 614            // aload state var
 615            // xload <var>
 616            loadVar(mv, TOBJECT, stateVar);
 617            loadVar(mv, vi.vmt, vi.var);
 618            mv.visitFieldInsn(PUTFIELD, stateClassName, vi.fieldName, vi.fieldDesc());
 619        }
 620
 621        // Fiber.setState(state);
 622        loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
 623        loadVar(mv, TOBJECT, stateVar);
 624        mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "setState", "(" + D_STATE + ")V", false);
 625        releaseVar(stateVar, 1);
 626        // Figure out the return type of the calling method and issue the
 627        // appropriate xRETURN instruction
 628        retType = TypeDesc.getReturnTypeDesc(bb.flow.desc);
 629        if (retType == D_VOID) {
 630            mv.visitInsn(RETURN);
 631        } else {
 632            int vmt = VMType.toVmType(retType);
 633            // ICONST_0, DCONST_0 etc.
 634            mv.visitInsn(VMType.constInsn[vmt]);
 635            // IRETURN, DRETURN, etc.
 636            mv.visitInsn(VMType.retInsn[vmt]);
 637        }
 638    }
 639
 640    /**
 641     * Not yielding (resuming normally), but have stored state. We need to
 642     * restore from state before resuming. This is slightly more work than
 643     * saving state, because we have to restore constants and duplicates too.
 644     * <b>
 645     * 
 646     * Note that the return value (if any) has a real value, which is why it
 647     * needs to be saved away before we can get access to the bottom elements to
 648     * pop them out.
 649     * 
 650     * <pre>
 651     *           If there is anything at the bottom save return value in scratch register
 652     *                 pop unnecessary bottom stuff.
 653     *          
 654     *           load fiber.curState 
 655     *           cast to specific state (if necessary) 
 656     *           astore in scratch &lt;stateVar&gt;
 657     *          
 658     *          for each value in frame.var 
 659     *               if val is constant or is in valInfoList, 
 660     *                   push constant or load field (appropriately) 
 661     *          for each value in bottom stack
 662     *                 restore value similar to above 
 663     *          restore return value if any from scratch register
 664     * </pre>
 665     */
 666    private void genRestore(MethodVisitor mv, LabelNode restoreLabel) {
 667        restoreLabel.accept(mv);
 668        Frame f = bb.startFrame;
 669        int numBottom = getNumBottom();
 670        int retVar = -1;
 671        int retctype = -1;
 672        if (numBottom > 0) {
 673            // We have dummy values sitting in the stack. pop 'em.
 674            // But first, check if we have a real return value on top
 675            String retType = getReturnType();
 676            if (retType != D_VOID) {
 677                // .. yep. save it to scratch register
 678                retctype = VMType.toVmType(retType);
 679                retVar = allocVar(VMType.category[retctype]);
 680                storeVar(mv, retctype, retVar);
 681            }
 682            // pop dummy values from stack bottom
 683            for (int i = numBottom-1; i >= 0; i--) {
 684                Value v = f.getStack(i);
 685                int insn = v.isCategory1() ? POP : POP2;
 686                mv.visitInsn(insn);
 687            }
 688        }
 689        // Restore variables from state
 690        int stateVar = -1;
 691        if (valInfoList.size() > 0) {
 692            stateVar = allocVar(1);
 693        }
 694        genRestoreVars(mv, stateVar);
 695
 696        // Now restore the bottom values in the stack from state
 697        for (int i = 0; i < numBottom; i++) {
 698            Value v = f.getStack(i);
 699            if (v.isConstant()) {
 700                loadConstant(mv, v);
 701            } else {
 702                ValInfo vi = valInfoList.find(v);
 703                if (vi.var == -1) {
 704                    loadVar(mv, TOBJECT, stateVar);
 705                    mv.visitFieldInsn(GETFIELD, stateClassName, vi.fieldName, vi.fieldDesc());
 706                    checkcast(mv, v);
 707                } else {
 708                    // this stack value is a duplicate of a local var, which has
 709                    // already been loaded and is of the right type
 710                    loadVar(mv, vi.vmt, vi.var);
 711                }
 712            }
 713        }
 714
 715        // restore the saved return value, if any
 716        // But we would have popped and saved the return value only
 717        // if there were any dummy values in the stack bottom to be
 718        // cleared out. If not, we wouldn't have bothered
 719        // popping the return value in the first place.
 720        if (numBottom > 0) {
 721            if (retVar != -1) {
 722                loadVar(mv, retctype, retVar);
 723            }
 724        }
 725        releaseVar(stateVar, 1);
 726        if (retctype != -1) {
 727            releaseVar(retVar, VMType.category[retctype]);
 728        }
 729        // Fall through to the resumeLabel in genNY_NS, so no goto required.
 730    }
 731
 732    void genRestoreEx(MethodVisitor mv, LabelNode restoreLabel) {
 733        restoreLabel.accept(mv);
 734        int stateVar = -1;
 735        if (valInfoList.size() > 0) {
 736            stateVar = allocVar(1);
 737        }
 738        genRestoreVars(mv, stateVar);
 739        releaseVar(stateVar, 1);
 740    }
 741
 742    /*
 743     */
 744    private void genRestoreVars(MethodVisitor mv, int stateVar) {
 745        Frame f = bb.startFrame;
 746
 747        if (valInfoList.size() > 0) {
 748            // need to have state in a local variable
 749            loadVar(mv, TOBJECT, methodWeaver.getFiberVar());
 750            mv.visitFieldInsn(GETFIELD, FIBER_CLASS, "curState", D_STATE);
 751            if (!stateClassName.equals(STATE_CLASS)) {
 752                mv.visitTypeInsn(CHECKCAST, stateClassName);
 753            }
 754            storeVar(mv, TOBJECT, stateVar);
 755        }
 756
 757        // no need to restore "this" if it's already there.
 758        Usage u = bb.usage;
 759        int len = f.getMaxLocals();
 760        int i = bb.flow.isStatic() ? 0 : 1;
 761        for (; i < len; i++) {
 762            if (!u.isLiveIn(i))
 763                continue;
 764            Value v = f.getLocal(i);
 765            int vmt = VMType.toVmType(v.getTypeDesc());
 766            if (v.isConstant()) {
 767                loadConstant(mv, v);
 768            } else {
 769                ValInfo vi = valInfoList.find(v);
 770                if (vi.var == i) {
 771                    // load val from state
 772                    loadVar(mv, TOBJECT, stateVar);
 773                    mv.visitFieldInsn(GETFIELD, stateClassName, vi.fieldName, vi.fieldDesc());
 774                    checkcast(mv, v); // don't need to do this in the constant case
 775                } else {
 776                    // It is a duplicate of another var. No need to load this var from stack
 777                    assert vi.var < i;
 778                    loadVar(mv, vi.vmt, vi.var);
 779                }
 780            }
 781            // Convert types from vm types to value's real type, if necessary
 782            // store into local
 783            storeVar(mv, vmt, i);
 784        }
 785        releaseVar(stateVar, 1);
 786    }
 787
 788    private String getReceiverTypename() {
 789        MethodInsnNode min = getMethodInsn();
 790        return min.owner;
 791    }
 792
 793    /**
 794     * We have loaded a value of one of the five VM types into the stack and we
 795     * need to cast it to the value's type, if necessary
 796     * 
 797     * @param mv
 798     * @param v
 799     */
 800    private void checkcast(MethodVisitor mv, Value v) {
 801        String valType = v.getTypeDesc();
 802        int vmt = VMType.toVmType(valType);
 803        switch (vmt) {
 804            case VMType.TOBJECT:
 805                if (valType == D_OBJECT || valType == D_NULL) {
 806                    return;
 807                }
 808                mv.visitTypeInsn(CHECKCAST, TypeDesc.getInternalName(valType));
 809                break;
 810            case VMType.TINT:
 811                if (valType == D_INT)
 812                    return;
 813                int insn = 0;
 814                if (valType == D_SHORT)
 815                    insn = I2S;
 816                else if (valType == D_BYTE)
 817                    insn = I2B;
 818                else if (valType == D_CHAR)
 819                    insn = I2C;
 820                else
 821                    assert valType == D_BOOLEAN;
 822                mv.visitInsn(insn);
 823                break;
 824            default:
 825                break;
 826        }
 827    }
 828
 829    private void loadConstant(MethodVisitor mv, Value v) {
 830        if (v.getTypeDesc() == D_NULL) {
 831            mv.visitInsn(ACONST_NULL);
 832            return;
 833        }
 834        Object c = v.getConstVal();
 835        if (c instanceof Integer) {
 836            int i = (Integer) c;
 837            if (i > -1 && i <= 5) {
 838                mv.visitInsn(i + 1 + ICONST_M1);
 839                return;
 840            } else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
 841                mv.visitIntInsn(BIPUSH, i);
 842                return;
 843            } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
 844                mv.visitIntInsn(SIPUSH, i);
 845                return;
 846            }
 847        } else if (c instanceof Float) {
 848            Float f = ((Float) c).floatValue();
 849            int insn = 0;
 850            if (f == 0.0)
 851                insn = FCONST_0;
 852            else if (f == 1.0)
 853                insn = FCONST_1;
 854            else if (f == 2.0)
 855                insn = FCONST_2;
 856            if (insn != 0) {
 857                mv.visitInsn(insn);
 858                return;
 859            }
 860        } else if (c instanceof Long) {
 861            Long l = ((Long) c).longValue();
 862            int insn = 0;
 863            if (l == 0L)
 864                insn = LCONST_0;
 865            else if (l == 1L)
 866                insn = LCONST_1;
 867            if (insn != 0) {
 868                mv.visitInsn(insn);
 869                return;
 870            }
 871        } else if (c instanceof Double) {
 872            Double d = ((Double) c).doubleValue();
 873            int insn = 0;
 874            if (d == 0.0)
 875                insn = DCONST_0;
 876            else if (d == 1.0)
 877                insn = DCONST_1;
 878            if (insn != 0) {
 879                mv.visitInsn(insn);
 880                return;
 881            }
 882        }
 883        // No special constants found.
 884        mv.visitLdcInsn(c);
 885    }
 886
 887    private String createStateClass() {
 888        return valInfoList.size() == 0 ? STATE_CLASS :  
 889            methodWeaver.createStateClass(valInfoList);
 890    }
 891
 892    private int allocVar(int size) {
 893        int var;
 894        for (var = 0;; var++) {
 895            // if the var'th local is not set (if double, check the next word
 896            // too)
 897            if (!varUsage.get(var)) {
 898                if (size == 1 || !varUsage.get(var + 1)) {
 899                    break;
 900                }
 901            }
 902        }
 903        varUsage.set(var);
 904        if (size == 2) {
 905            varUsage.set(var + 1);
 906            methodWeaver.ensureMaxVars(var + 2); // var is 0-based
 907        } else {
 908            methodWeaver.ensureMaxVars(var + 1);
 909        }
 910        return var;
 911    }
 912
 913    private void releaseVar(int var, int size) {
 914        if (var == -1)
 915            return;
 916        varUsage.clear(var);
 917        if (size == 2) {
 918            varUsage.clear(var + 1);
 919        }
 920    }
 921
 922    BasicBlock getBasicBlock() {
 923        return bb;
 924    }
 925}
 926
 927class ValInfo implements Comparable<ValInfo> {
 928    /**
 929     * The var to which the value belongs. It remains undefined if it is a stack
 930     * item.
 931     */
 932    int    var = -1;
 933
 934    /**
 935     * The value to hold. This gives us information about the type, whether the
 936     * value is duplicated and whether it is a constant value.
 937     */
 938    Value  val;
 939
 940    /**
 941     * The type of value boiled down to one of the canonical types.
 942     */
 943    int    vmt;
 944
 945    /**
 946     * Names of the fields in the state var: "f0", "f1", etc, according to their
 947     * position in the call weaver's valInfoList.
 948     */
 949    String fieldName;
 950
 951    ValInfo(Value v) {
 952        val = v;
 953        vmt = VMType.toVmType(v.getTypeDesc());
 954    }
 955
 956    String fieldDesc() {
 957        return VMType.fieldDesc[vmt];
 958    }
 959
 960    public int compareTo(ValInfo that) {
 961        if (this == that)
 962            return 0;
 963        if (this.vmt < that.vmt)
 964            return -1;
 965        if (this.vmt > that.vmt)
 966            return 1;
 967        if (this.var < that.var)
 968            return -1;
 969        if (this.var > that.var)
 970            return 1;
 971        return 0;
 972    }
 973}
 974
 975class ValInfoList extends ArrayList<ValInfo> {
 976    private static final long serialVersionUID = -2339264992519046024L;
 977
 978    public ValInfo find(Value v) {
 979        int i = indexOf(v);
 980        return (i == -1) ? null : get(i);
 981    }
 982
 983    public int indexOf(Value v) {
 984        int len = size();
 985        for (int i = 0; i < len; i++) {
 986            if (get(i).val == v)
 987                return i;
 988        }
 989        return -1;
 990    }
 991
 992    public boolean contains(Value v) {
 993        return indexOf(v) != -1;
 994    }
 995
 996}
 997
 998class VMType {
 999
1000    static final int      TOBJECT   = 0;
1001
1002    static final int      TINT      = 1;
1003
1004    static final int      TLONG     = 2;
1005
1006    static final int      TDOUBLE   = 3;
1007
1008    static final int      TFLOAT    = 4;
1009
1010    static final int[]    constInsn = { ACONST_NULL, ICONST_0, LCONST_0,
1011            DCONST_0, FCONST_0     };
1012
1013    static final int[]    loadInsn  = { ALOAD, ILOAD, LLOAD, DLOAD, FLOAD };
1014
1015    static final int[]    retInsn   = { ARETURN, IRETURN, LRETURN, DRETURN,
1016            FRETURN                };
1017
1018
1019    static final int[]    ldInsn    = { ALOAD_0, ILOAD_0, LLOAD_0, DLOAD_0,
1020            FLOAD_0                };
1021
1022    static final int[]    stInsn    = { ASTORE_0, ISTORE_0, LSTORE_0, DSTORE_0,
1023            FSTORE_0               };
1024
1025    static final int[]    storeInsn = { ASTORE, ISTORE, LSTORE, DSTORE, FSTORE };
1026
1027    static final String[] fieldDesc = { D_OBJECT, D_INT, D_LONG, D_DOUBLE,
1028            D_FLOAT                };
1029
1030    static final String[] abbrev    = { "O", "I", "L", "D", "F" };
1031
1032    static final int[]    category  = { 1, 1, 2, 2, 1 };
1033
1034    static int toVmType(String type) {
1035        switch (type.charAt(0)) {
1036            case 'Z':
1037            case 'B':
1038            case 'C':
1039            case 'S':
1040            case 'I':
1041                return TINT;
1042
1043            case 'D':
1044                return TDOUBLE;
1045            case 'F':
1046                return TFLOAT;
1047            case 'J':
1048                return TLONG;
1049
1050            case 'N': // null
1051            case 'A': // catch handler return address 
1052            case 'L': // normal type
1053            case '[': // array
1054                return TOBJECT;
1055
1056            default:
1057                assert false : "Type " + type + " not handled";
1058        }
1059        return ' ';
1060    }
1061
1062    static void loadVar(MethodVisitor mv, int vmt, int var) {
1063        assert var >= 0 : "Got var = " + var;
1064        // ASM4.1 doesn't like short-form ALOAD_n instructions. Instead, we use ALOAD n. 
1065        
1066//        if (var < 4) {
1067//            // short instructions like ALOAD_n exist for n = 0 .. 4
1068//            mv.visitInsn(ldInsn[vmt] + var);
1069//        } else {
1070            mv.visitVarInsn(loadInsn[vmt], var);
1071//        }
1072    }
1073
1074    static void storeVar(MethodVisitor mv, int vmt, int var) {
1075        assert var >= 0;
1076//        if (var < 4) {
1077//            // short instructions like ALOAD_n exist for n = 0 .. 4
1078//            mv.visitInsn(stInsn[vmt] + var);
1079//        } else {
1080            mv.visitVarInsn(storeInsn[vmt], var);
1081//        }
1082    }
1083}