/* * Copyright 2001-2008 Geert Bevin (gbevin[remove] at uwyn dot com) * Licensed under the Apache License, Version 2.0 (the "License") * $Id: ResumableMethodAdapter.java 3918 2008-04-14 17:35:35Z gbevin $ */ package com.uwyn.rife.continuations.instrument; import com.uwyn.rife.asm.*; import com.uwyn.rife.continuations.ContinuationConfigInstrument; import java.util.Stack; import java.util.logging.Level; import static com.uwyn.rife.continuations.instrument.ContinuationDebug.*; class ResumableMethodAdapter implements MethodVisitor, Opcodes { private ContinuationConfigInstrument mConfig = null; private TypesClassVisitor mTypes = null; private MethodVisitor mMethodVisitor = null; private String mClassName = null; private String mClassNameInternal = null; private boolean mVisit = false; private boolean mAdapt = false; private int mContextIndex = -1; private int mCallTargetIndex = -1; private int mAnswerIndex = -1; private int mTempIndex = -1; private Label mDefaultLabel = null; private Label mRethrowLabel = null; private boolean mVisitRethrowLabel = false; private Label[] mLabels = null; private int mLabelIndex = 0; private int mMaxLocalIndex = 0; private TypesContext mLabelContext = null; private boolean mDisableCodeguideBackInTime = false; private boolean mDisabledCodeguideBackInTime = false; private NoOpAnnotationVisitor mAnnotationVisitor = new NoOpAnnotationVisitor(); private void debugMessage(String message) { if (ContinuationDebug.sTrace && ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) { mMethodVisitor.visitFieldInsn(GETSTATIC, "com/uwyn/rife/continuations/instrument/ContinuationDebug", "LOGGER", "Ljava/util/logging/Logger;"); mMethodVisitor.visitLdcInsn(message); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/util/logging/Logger", "finest", "(Ljava/lang/String;)V"); } } ResumableMethodAdapter(ContinuationConfigInstrument config, TypesClassVisitor types, MethodVisitor methodVisitor, String className, boolean adapt, int maxLocals, int pauseCount) { mConfig = config; mTypes = types; mMethodVisitor = methodVisitor; mClassName = className; if (className != null) { mClassNameInternal = mClassName.replace('.', '/'); } mVisit = (mMethodVisitor != null); mAdapt = adapt; mContextIndex = maxLocals; mCallTargetIndex = mContextIndex+1; mAnswerIndex = mCallTargetIndex+1; mTempIndex = mAnswerIndex+1; if (mAdapt) { // create all the labels beforehand mDefaultLabel = new Label(); if (pauseCount > 0) { mLabels = new Label[pauseCount]; for (int i = 0; i < pauseCount; i++) { mLabels[i] = new Label(); } } debugMessage("CONT: context initializing"); // get the current context for the current method and register it // after the last local variable mMethodVisitor.visitVarInsn(ALOAD, 0); mMethodVisitor.visitMethodInsn(INVOKESTATIC, "com/uwyn/rife/continuations/ContinuationContext", "createOrResetContext", "(Ljava/lang/Object;)Lcom/uwyn/rife/continuations/ContinuationContext;"); mMethodVisitor.visitVarInsn(ASTORE, mContextIndex); debugMessage("CONT: context set up"); if (pauseCount > 0) { debugMessage("CONT: context obtain label"); // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // retrieve the current label index mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLabel", "()I"); debugMessage("CONT: evaluate tableswitch"); mMethodVisitor.visitTableSwitchInsn(0, pauseCount-1, mDefaultLabel, mLabels); } // set the default label to the start of the code mMethodVisitor.visitLabel(mDefaultLabel); debugMessage("CONT: begin of code"); } } // store an integer on the stack private void addIntegerConst(int value) { switch (value) { case 0: mMethodVisitor.visitInsn(ICONST_0); break; case 1: mMethodVisitor.visitInsn(ICONST_1); break; case 2: mMethodVisitor.visitInsn(ICONST_2); break; case 3: mMethodVisitor.visitInsn(ICONST_3); break; case 4: mMethodVisitor.visitInsn(ICONST_4); break; case 5: mMethodVisitor.visitInsn(ICONST_5); break; default: mMethodVisitor.visitLdcInsn(value); break; } } /** * Visits a local variable instruction. A local variable instruction is an * instruction that loads or stores the value of a local variable. * * @param opcode the opcode of the local variable instruction to be visited. * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, * LSTORE, FSTORE, DSTORE, ASTORE or RET. * @param var the operand of the instruction to be visited. This operand is * the index of a local variable. */ public void visitVarInsn(int opcode, int var) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitVarInsn ("+OPCODES[opcode]+", "+var+")"); ///CLOVER:ON if (mAdapt) { // execute the original opcode mMethodVisitor.visitVarInsn(opcode, var); // if this is an exception block, check if the caught exception is a // pause exception, if it is, just throw it further again. if (mLabelContext != null && TypesNode.EXCEPTION == mLabelContext.getSort() && ASTORE == opcode) { if (null == mRethrowLabel) { mRethrowLabel = new Label(); mVisitRethrowLabel = true; } Label label = new Label(); mMethodVisitor.visitVarInsn(ALOAD, var); mMethodVisitor.visitTypeInsn(INSTANCEOF, "com/uwyn/rife/tools/exceptions/ControlFlowRuntimeException"); mMethodVisitor.visitJumpInsn(IFEQ, label); mMethodVisitor.visitVarInsn(ALOAD, var); mMethodVisitor.visitJumpInsn(GOTO, mRethrowLabel); mMethodVisitor.visitLabel(label); } // catch local variable store opcodes so that they can also be // stored in the context object if (opcode == ISTORE || opcode == LSTORE || opcode == FSTORE || opcode == DSTORE || opcode == ASTORE) { // retain the maximum index of the local var storage if (var > mMaxLocalIndex) { mMaxLocalIndex = var; } // prepare the arguments of the context storage method // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // get a reference to the local variable stack mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalVars", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); // push the index of local var that has to be stored on the // stack addIntegerConst(var); // detect the opcode and handle the different local variable // types correctly switch (opcode) { // store ints case ISTORE: mMethodVisitor.visitVarInsn(ILOAD, var); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "storeInt", "(II)V"); break; // store longs case LSTORE: mMethodVisitor.visitVarInsn(LLOAD, var); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "storeLong", "(IJ)V"); break; // store floats case FSTORE: mMethodVisitor.visitVarInsn(FLOAD, var); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "storeFloat", "(IF)V"); break; // store doubles case DSTORE: mMethodVisitor.visitVarInsn(DLOAD, var); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "storeDouble", "(ID)V"); break; // store references case ASTORE: mMethodVisitor.visitVarInsn(ALOAD, var); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "storeReference", "(ILjava/lang/Object;)V"); break; } } // if this was the first ASTORE of an exception block, restore // the local types and change the type of the block so that this // isn't executed anymore if (mLabelContext != null && TypesNode.EXCEPTION == mLabelContext.getSort() && ASTORE == opcode) { // restore the local variable stack restoreLocalStack(mLabelContext); mLabelContext.setSort(TypesNode.REGULAR); } } else if (mVisit) { mMethodVisitor.visitVarInsn(opcode, var); } } /** * Visits a method instruction. A method instruction is an instruction that * invokes a method. * * @param opcode the opcode of the type instruction to be visited. This opcode * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or * INVOKEINTERFACE. * @param owner the internal name of the method's owner class (see {@link * Type#getInternalName getInternalName}). * @param name the method's name. * @param desc the method's descriptor (see {@link Type Type}). */ public void visitMethodInsn(int opcode, String owner, String name, String desc) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitMethodInsn ("+OPCODES[opcode]+", \""+owner+"\", \""+name+"\", \""+desc+"\")"); ///CLOVER:ON if (mAdapt) { String owner_classname = owner.replace('/', '.'); if (owner_classname.equals(mConfig.getContinuableSupportClassName()) || mClassName.equals(owner_classname)) { if (mConfig.getPauseMethodName().equals(name) && "()V".equals(desc)) { debugMessage("CONT: pause : undoing method call"); // pop the ALOAD opcode off the stack mMethodVisitor.visitInsn(POP); TypesContext context = mTypes.nextPauseContext(); Stack<String> stack = context.getStackClone(); debugMessage("CONT: pause : saving operand stack"); saveOperandStack(stack); debugMessage("CONT: pause : storing resume label"); // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // set the index of the current label addIntegerConst(mLabelIndex); // set the new label index mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "setLabel", "(I)V"); // generate the pause exception debugMessage("CONT: pause : throwing pause exception"); mMethodVisitor.visitTypeInsn(NEW, "com/uwyn/rife/continuations/exceptions/PauseException"); mMethodVisitor.visitInsn(DUP); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKESPECIAL, "com/uwyn/rife/continuations/exceptions/PauseException", "<init>", "(Lcom/uwyn/rife/continuations/ContinuationContext;)V"); mMethodVisitor.visitInsn(ATHROW); // add label for skipping over resumed code mMethodVisitor.visitLabel(mLabels[mLabelIndex]); debugMessage("CONT: pause : resumed execution"); // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // clear the label mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "clearLabel", "()V"); // restore the local variable stack debugMessage("CONT: pause : restoring local stack"); restoreLocalStack(context); // restore the local operand stack debugMessage("CONT: pause : restoring operand stack"); restoreOperandStack(stack); mLabelIndex++; return; } else if (mConfig.getStepbackMethodName().equals(name) && "()V".equals(desc)) { debugMessage("CONT: stepBack : undoing method call"); // pop the ALOAD opcode off the stack mMethodVisitor.visitInsn(POP); TypesContext context = mTypes.nextPauseContext(); Stack<String> stack = context.getStackClone(); debugMessage("CONT: stepBack : saving operand stack"); saveOperandStack(stack); // generate the stepBack exception debugMessage("CONT: stepBack : throwing pause exception"); mMethodVisitor.visitTypeInsn(NEW, "com/uwyn/rife/continuations/exceptions/StepBackException"); mMethodVisitor.visitInsn(DUP); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKESPECIAL, "com/uwyn/rife/continuations/exceptions/StepBackException", "<init>", "(Lcom/uwyn/rife/continuations/ContinuationContext;)V"); mMethodVisitor.visitInsn(ATHROW); // add label for skipping over resumed code mMethodVisitor.visitLabel(mLabels[mLabelIndex]); debugMessage("CONT: stepBack : resumed execution"); // restore the local variable stack debugMessage("CONT: stepBack : restoring local stack"); restoreLocalStack(context); // restore the local operand stack debugMessage("CONT: stepBack : restoring operand stack"); restoreOperandStack(stack); mLabelIndex++; return; } else if (mConfig.getCallMethodName().equals(name) && Type.getMethodDescriptor(mConfig.getCallMethodReturnType(), mConfig.getCallMethodArgumentTypes()).equals(desc)) { // store the call target debugMessage("CONT: call : storing call target"); mMethodVisitor.visitVarInsn(ASTORE, mCallTargetIndex); // pop the ALOAD opcode off the stack debugMessage("CONT: call : undoing method call"); mMethodVisitor.visitInsn(POP); TypesContext context = mTypes.nextPauseContext(); Stack<String> stack = context.getStackClone(); stack.pop(); debugMessage("CONT: call : saving operand stack"); saveOperandStack(stack); debugMessage("CONT: call : storing resume label"); // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // set the index of the current label addIntegerConst(mLabelIndex); // set the new label index mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "setLabel", "(I)V"); // generate the pause exception debugMessage("CONT: call : throwing call exception"); mMethodVisitor.visitTypeInsn(NEW, "com/uwyn/rife/continuations/exceptions/CallException"); mMethodVisitor.visitInsn(DUP); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitVarInsn(ALOAD, mCallTargetIndex); mMethodVisitor.visitMethodInsn(INVOKESPECIAL, "com/uwyn/rife/continuations/exceptions/CallException", "<init>", "(Lcom/uwyn/rife/continuations/ContinuationContext;Ljava/lang/Object;)V"); mMethodVisitor.visitInsn(ATHROW); // add label for skipping over resumed code mMethodVisitor.visitLabel(mLabels[mLabelIndex]); debugMessage("CONT: call : resumed execution"); // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // clear the label mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "clearLabel", "()V"); // restore the local variable stack debugMessage("CONT: call : restoring local stack"); restoreLocalStack(context); // restore the local operand stack debugMessage("CONT: call : restoring operand stack"); restoreOperandStack(stack); debugMessage("CONT: call : retrieving call answer"); // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // get the call answer mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getCallAnswer", "()Ljava/lang/Object;"); mMethodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(mConfig.getCallMethodReturnType())); mLabelIndex++; return; } else if (mConfig.getAnswerMethodName().equals(name) && ("()V".equals(desc) || "(Ljava/lang/Object;)V".equals(desc))) { if ("()V".equals(desc)) { mMethodVisitor.visitInsn(ACONST_NULL); } // store the answer debugMessage("CONT: call : storing answer"); mMethodVisitor.visitVarInsn(ASTORE, mAnswerIndex); // pop the ALOAD opcode off the stack debugMessage("CONT: call : undoing method call"); mMethodVisitor.visitInsn(POP); // generate the answer exception debugMessage("CONT: answer : throwing answer exception"); mMethodVisitor.visitTypeInsn(NEW, "com/uwyn/rife/continuations/exceptions/AnswerException"); mMethodVisitor.visitInsn(DUP); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitVarInsn(ALOAD, mAnswerIndex); mMethodVisitor.visitMethodInsn(INVOKESPECIAL, "com/uwyn/rife/continuations/exceptions/AnswerException", "<init>", "(Lcom/uwyn/rife/continuations/ContinuationContext;Ljava/lang/Object;)V"); mMethodVisitor.visitInsn(ATHROW); return; } } } if (mVisit) { mMethodVisitor.visitMethodInsn(opcode, owner, name, desc); } } /** * Restore the local variable stack, first the computation * types of category 1 and afterwards those of category 2 */ private void restoreLocalStack(TypesContext context) { for (int i = 1; i <= mMaxLocalIndex; i++) { if (!context.hasVar(i)) { continue; } switch (context.getVarType(i)) { case Type.INT: debugMessage("CONT: restore local : "+i+", int"); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalVars", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); addIntegerConst(i); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "getInt", "(I)I"); mMethodVisitor.visitVarInsn(ISTORE, i); break; case Type.FLOAT: debugMessage("CONT: restore local : "+i+", float"); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalVars", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); addIntegerConst(i); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "getFloat", "(I)F"); mMethodVisitor.visitVarInsn(FSTORE, i); break; case Type.OBJECT: debugMessage("CONT: restore local : "+i+", "+context.getVar(i)+""); String type = context.getVar(i); if (TypesContext.TYPE_NULL.equals(type)) { mMethodVisitor.visitInsn(ACONST_NULL); mMethodVisitor.visitVarInsn(ASTORE, i); } else { mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalVars", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); addIntegerConst(i); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "getReference", "(I)Ljava/lang/Object;"); mMethodVisitor.visitTypeInsn(CHECKCAST, type); mMethodVisitor.visitVarInsn(ASTORE, i); } break; } } for (int i = 1; i <= mMaxLocalIndex; i++) { if (!context.hasVar(i)) { continue; } switch (context.getVarType(i)) { case Type.LONG: debugMessage("CONT: restore local : "+i+", long"); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalVars", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); addIntegerConst(i); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "getLong", "(I)J"); mMethodVisitor.visitVarInsn(LSTORE, i); break; case Type.DOUBLE: debugMessage("CONT: restore local : "+i+", double"); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalVars", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); addIntegerConst(i); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "getDouble", "(I)D"); mMethodVisitor.visitVarInsn(DSTORE, i); break; } } } /** * Save the operand stack */ private void saveOperandStack(Stack<String> stack) { String tupe = null; // save all stack entries besides the last one pushed, it's the // element's object reference that is used for the stub continuation // methods for (int i = stack.size()-1; i >= 0 ; i--) { tupe = stack.get(i); if (tupe.equals(TypesContext.CAT1_BOOLEAN) || tupe.equals(TypesContext.CAT1_CHAR) || tupe.equals(TypesContext.CAT1_BYTE) || tupe.equals(TypesContext.CAT1_SHORT) || tupe.equals(TypesContext.CAT1_INT)) { mMethodVisitor.visitVarInsn(ISTORE, mTempIndex); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitVarInsn(ILOAD, mTempIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "pushInt", "(I)V"); } else if (tupe.equals(TypesContext.CAT1_FLOAT)) { mMethodVisitor.visitVarInsn(FSTORE, mTempIndex); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitVarInsn(FLOAD, mTempIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "pushFloat", "(F)V"); } else if (tupe.equals(TypesContext.CAT2_DOUBLE)) { mMethodVisitor.visitVarInsn(DSTORE, mTempIndex); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitVarInsn(DLOAD, mTempIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "pushDouble", "(D)V"); } else if (tupe.equals(TypesContext.CAT2_LONG)) { mMethodVisitor.visitVarInsn(LSTORE, mTempIndex); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitVarInsn(LLOAD, mTempIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "pushLong", "(J)V"); } else if (tupe.equals(TypesContext.CAT1_ADDRESS)) { // this should never happen throw new RuntimeException("Invalid local stack type"); } else { mMethodVisitor.visitVarInsn(ASTORE, mTempIndex); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitVarInsn(ALOAD, mTempIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "pushReference", "(Ljava/lang/Object;)V"); } } } /** * Restore the operand stack */ private void restoreOperandStack(Stack<String> stack) { String type = null; // restore all stack entries besides the last one pushed, it's the // element's object reference that is used for the stub continuation // methods for (int i = 0; i < stack.size() ; i++) { type = stack.get(i); if (type.equals(TypesContext.CAT1_BOOLEAN) || type.equals(TypesContext.CAT1_CHAR) || type.equals(TypesContext.CAT1_BYTE) || type.equals(TypesContext.CAT1_SHORT) || type.equals(TypesContext.CAT1_INT)) { debugMessage("CONT: restore operand : "+i+", int"); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "popInt", "()I"); } else if (type.equals(TypesContext.CAT1_FLOAT)) { debugMessage("CONT: restore operand : "+i+", float"); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "popFloat", "()F"); } else if (type.equals(TypesContext.CAT2_DOUBLE)) { debugMessage("CONT: restore operand : "+i+", double"); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "popDouble", "()D"); } else if (type.equals(TypesContext.CAT2_LONG)) { debugMessage("CONT: restore operand : "+i+", long"); mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "popLong", "()J"); } else if (type.equals(TypesContext.CAT1_ADDRESS)) { // this should never happen throw new RuntimeException("Invalid local stack type"); } else { debugMessage("CONT: restore operand : "+i+", "+type); if (TypesContext.TYPE_NULL.equals(type)) { mMethodVisitor.visitInsn(ACONST_NULL); mMethodVisitor.visitVarInsn(ASTORE, i); } else { mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalStack", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "popReference", "()Ljava/lang/Object;"); mMethodVisitor.visitTypeInsn(CHECKCAST, type); } } } } /** * Visits a type instruction. A type instruction is an instruction that * takes a type descriptor as parameter. * * @param opcode the opcode of the type instruction to be visited. This opcode * is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. * @param desc the operand of the instruction to be visited. This operand is * must be a fully qualified class name in internal form, or the type * descriptor of an array type (see {@link Type Type}). */ public void visitTypeInsn(int opcode, String desc) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitTypeInsn ("+OPCODES[opcode]+", \""+desc+"\")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitTypeInsn(opcode, desc); } } /** * Visits a LDC instruction. * * @param cst the constant to be loaded on the stack. This parameter must be * a non null {@link java.lang.Integer Integer}, a {@link java.lang.Float * Float}, a {@link java.lang.Long Long}, a {@link java.lang.Double * Double} or a {@link String String}. */ public void visitLdcInsn(Object cst) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitLdcInsn ("+cst+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitLdcInsn(cst); } } /** * Visits a MULTIANEWARRAY instruction. * * @param desc an array type descriptor (see {@link Type Type}). * @param dims number of dimensions of the array to allocate. */ public void visitMultiANewArrayInsn(String desc, int dims) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitMultiANewArrayInsn (\""+desc+"\", "+dims+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitMultiANewArrayInsn(desc, dims); } } /** * Visits a zero operand instruction. * * @param opcode the opcode of the instruction to be visited. This opcode is * either NOP, ACONST_NULL, ICONST_1, ICONST_0, ICONST_1, ICONST_2, * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, * FCONST_2, DCONST_0, DCONST_1, * * IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, * SASTORE, * * POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, * * IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, * DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, * FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, * LOR, IXOR, LXOR, * * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, * I2S, * * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, * * IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN, * * ARRAYLENGTH, * * ATHROW, * * MONITORENTER, or MONITOREXIT. */ public void visitInsn(int opcode) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitInsn ("+OPCODES[opcode]+")"); ///CLOVER:ON if (mAdapt && RETURN == opcode) { debugMessage("CONT: context deactivation"); // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // remove the context from the manager mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "registerContext", "()V"); // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // remove the context from the manager mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "deactivate", "()V"); mMethodVisitor.visitInsn(opcode); if (mRethrowLabel != null && mVisitRethrowLabel) { mMethodVisitor.visitLabel(mRethrowLabel); debugMessage("CONT: rethrowing exception"); mMethodVisitor.visitInsn(ATHROW); mVisitRethrowLabel = false; } } else if (mVisit) { mMethodVisitor.visitInsn(opcode); } } /** * Visits an IINC instruction. * * @param var index of the local variable to be incremented. * @param increment amount to increment the local variable by. */ public void visitIincInsn(int var, int increment) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitIincInsn ("+var+", "+increment+")"); ///CLOVER:ON if (mAdapt) { // execute the original opcode mMethodVisitor.visitIincInsn(var, increment); // retain the maximum index of the local var storage if (var > mMaxLocalIndex) { mMaxLocalIndex = var; } // prepare the arguments of the context storage method // get a reference to the context object mMethodVisitor.visitVarInsn(ALOAD, mContextIndex); // get a reference to the local variable stack mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationContext", "getLocalVars", "()Lcom/uwyn/rife/continuations/ContinuationStack;"); // push the index of local var that has to be stored on the // stack and put the increment amount on it also addIntegerConst(var); addIntegerConst(increment); mMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/uwyn/rife/continuations/ContinuationStack", "incrementInt", "(II)V"); } else if (mVisit) { mMethodVisitor.visitIincInsn(var, increment); } } /** * Visits a field instruction. A field instruction is an instruction that * loads or stores the value of a field of an object. * * @param opcode the opcode of the type instruction to be visited. This opcode * is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. * @param owner the internal name of the field's owner class (see {@link * Type#getInternalName getInternalName}). * @param name the field's name. * @param desc the field's descriptor (see {@link Type Type}). */ public void visitFieldInsn(int opcode, String owner, String name, String desc) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitFieldInsn ("+OPCODES[opcode]+", \""+owner+"\", \""+name+"\", \""+desc+"\")"); ///CLOVER:ON if (mAdapt && !mDisabledCodeguideBackInTime && opcode == GETSTATIC && mClassNameInternal.equals(owner) && name.startsWith("debugEnabled$") && "Z".equals(desc)) { mDisableCodeguideBackInTime = true; } if (mVisit) { mMethodVisitor.visitFieldInsn(opcode, owner, name, desc); } } /** * Visits an instruction with a single int operand. * * @param opcode the opcode of the instruction to be visited. This opcode is * either BIPUSH, SIPUSH or NEWARRAY. * @param operand the operand of the instruction to be visited. */ public void visitIntInsn(int opcode, int operand) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitIntInsn ("+OPCODES[opcode]+", "+operand+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitIntInsn(opcode, operand); } } /** * Visits a try catch block. * * @param start beginning of the exception handler's scope (inclusive). * @param end end of the exception handler's scope (exclusive). * @param handler beginning of the exception handler's code. * @param type internal name of the type of exceptions handled by the handler, * or <tt>null</tt> to catch any exceptions (for "finally" blocks). * @throws IllegalArgumentException if one of the labels has not already been * visited by this visitor (by the {@link #visitLabel visitLabel} * method). */ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitTryCatchBlock ("+start+", "+end+", "+handler+", \""+type+"\")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitTryCatchBlock(start, end, handler, type); } } /** * Visits a LOOKUPSWITCH instruction. * * @param dflt beginning of the default handler block. * @param keys the values of the keys. * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is the * beginning of the handler block for the <tt>keys[i]</tt> key. */ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitLookupSwitchInsn ("+dflt+", "+(null == keys ? null : join(keys, ","))+", "+(null == labels ? null : join(labels, ","))+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitLookupSwitchInsn(dflt, keys, labels); } } /** * Visits a jump instruction. A jump instruction is an instruction that may * jump to another instruction. * * @param opcode the opcode of the type instruction to be visited. This opcode * is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, * IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, * GOTO, JSR, IFNULL or IFNONNULL. * @param label the operand of the instruction to be visited. This operand is * a label that designates the instruction to which the jump instruction * may jump. */ public void visitJumpInsn(int opcode, Label label) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitJumpInsn ("+OPCODES[opcode]+", "+label+")"); ///CLOVER:ON if (mAdapt && mDisableCodeguideBackInTime && opcode == IFEQ) { // pop the condition value off the stack mMethodVisitor.visitInsn(POP); mMethodVisitor.visitJumpInsn(GOTO, label); mDisableCodeguideBackInTime = false; mDisabledCodeguideBackInTime = true; } else if (mVisit) { mMethodVisitor.visitJumpInsn(opcode, label); } } /** * Visits a label. A label designates the instruction that will be visited * just after it. * * @param label a {@link Label Label} object. */ public void visitLabel(Label label) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitLabel ("+label+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitLabel(label); } if (mAdapt) { mLabelContext = mTypes.nextLabelTypes(); } } /** * Visits a TABLESWITCH instruction. * * @param min the minimum key value. * @param max the maximum key value. * @param dflt beginning of the default handler block. * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is the * beginning of the handler block for the <tt>min + i</tt> key. */ public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitTableSwitchInsn ("+min+", "+max+", "+dflt+", "+(null == labels ? null : join(labels, ","))+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitTableSwitchInsn(min, max, dflt, labels); } } /** * Visits the maximum stack size and the maximum number of local variables of * the method. * * @param maxStack maximum stack size of the method. * @param maxLocals maximum number of local variables for the method. */ public void visitMaxs(int maxStack, int maxLocals) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitMaxs ("+maxStack+", "+maxLocals+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitMaxs(maxStack, maxLocals); } } /** * Visits a local variable declaration. * * @param name the name of a local variable. * @param desc the type descriptor of this local variable. * @param signature the type signature of this local variable. May be * <tt>null</tt> if the local variable type does not use generic types. * @param start the first instruction corresponding to the scope of this * local variable (inclusive). * @param end the last instruction corresponding to the scope of this * local variable (exclusive). * @param index the local variable's index. * @throws IllegalArgumentException if one of the labels has not already been * visited by this visitor (by the {@link #visitLabel visitLabel} * method). */ public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitLocalVariable (\""+name+"\", \""+desc+", \""+signature+"\", "+start+", "+end+", "+index+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitLocalVariable( name, desc, signature, start, end, index); } } /** * Visits a line number declaration. * * @param line a line number. This number refers to the source file * from which the class was compiled. * @param start the first instruction corresponding to this line number. * @throws IllegalArgumentException if <tt>start</tt> has not already been * visited by this visitor (by the {@link #visitLabel visitLabel} * method). */ public void visitLineNumber(int line, Label start) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitLineNumber ("+line+", "+start+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitLineNumber(line, start); } } /** * Visits a non standard attribute of the code. This method must visit only * the first attribute in the given attribute list. * * @param attr a non standard code attribute. Must not be <tt>null</tt>. */ public void visitAttribute(Attribute attr) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitAttribute ("+attr+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitAttribute(attr); } } public void visitCode() { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitCode ()"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitCode(); } } public AnnotationVisitor visitAnnotationDefault() { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitAnnotationDefault ()"); ///CLOVER:ON if (mVisit) { return mMethodVisitor.visitAnnotationDefault(); } return mAnnotationVisitor; } public AnnotationVisitor visitAnnotation(String desc, boolean visible) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitAnnotation (\""+desc+"\", "+visible+")"); ///CLOVER:ON if (mVisit) { return mMethodVisitor.visitAnnotation(desc, visible); } return mAnnotationVisitor; } public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitAnnotation ("+parameter+", \""+desc+"\", "+visible+")"); ///CLOVER:ON if (mVisit) { return mMethodVisitor.visitParameterAnnotation(parameter, desc, visible); } return mAnnotationVisitor; } public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitFrame ("+type+", "+nLocal+", "+local+", "+nStack+", "+stack+")"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitFrame(type, nLocal, local, nStack, stack); } } public void visitEnd() { ///CLOVER:OFF if (ContinuationDebug.LOGGER.isLoggable(Level.FINEST)) ContinuationDebug.LOGGER.finest(" Code:visitEnd ()"); ///CLOVER:ON if (mVisit) { mMethodVisitor.visitEnd(); } } }