Example usage for org.objectweb.asm.commons InstructionAdapter visitLocalVariable

List of usage examples for org.objectweb.asm.commons InstructionAdapter visitLocalVariable

Introduction

In this page you can find the example usage for org.objectweb.asm.commons InstructionAdapter visitLocalVariable.

Prototype

public void visitLocalVariable(final String name, final String descriptor, final String signature,
        final Label start, final Label end, final int index) 

Source Link

Document

Visits a local variable declaration.

Usage

From source file:com.gargoylesoftware.js.nashorn.internal.runtime.linker.JavaAdapterBytecodeGenerator.java

License:Open Source License

/**
 * Generates a method in the adapter class that adapts a method from the original class. The generated methods will
 * inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation
 * for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an
 * {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is
 * invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the
 * current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter
 * instance, the creating global is set to be the current global. In this case, the previously current global is
 * restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared
 * exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime
 * exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of
 * the method; this is guaranteed by the way constructors of the adapter class obtain them using
 * {@link #getHandle(Object, String, MethodType, boolean)}.
 * @param mi the method info describing the method to be generated.
 *///ww  w  .j  a  v  a  2 s  . c  o  m
private void generateMethod(final MethodInfo mi) {
    final Method method = mi.method;
    final Class<?>[] exceptions = method.getExceptionTypes();
    final String[] exceptionNames = getExceptionNames(exceptions);
    final MethodType type = mi.type;
    final String methodDesc = type.toMethodDescriptorString();
    final String name = mi.getName();

    final Type asmType = Type.getMethodType(methodDesc);
    final Type[] asmArgTypes = asmType.getArgumentTypes();

    final InstructionAdapter mv = new InstructionAdapter(
            cw.visitMethod(getAccessModifiers(method), name, methodDesc, null, exceptionNames));
    mv.visitCode();

    final Label handleDefined = new Label();

    final Class<?> returnType = type.returnType();
    final Type asmReturnType = Type.getType(returnType);

    // See if we have overriding method handle defined
    if (classOverride) {
        mv.getstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
    } else {
        mv.visitVarInsn(ALOAD, 0);
        mv.getfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
    }
    // stack: [handle]
    mv.visitInsn(DUP);
    mv.visitJumpInsn(IFNONNULL, handleDefined);

    // No handle is available, fall back to default behavior
    if (Modifier.isAbstract(method.getModifiers())) {
        // If the super method is abstract, throw an exception
        mv.anew(UNSUPPORTED_OPERATION_TYPE);
        mv.dup();
        mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR, false);
        mv.athrow();
    } else {
        mv.visitInsn(POP);
        // If the super method is not abstract, delegate to it.
        emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc);
    }

    mv.visitLabel(handleDefined);
    // Load the creatingGlobal object
    if (classOverride) {
        // If class handle is defined, load the static defining global
        mv.getstatic(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR);
    } else {
        mv.visitVarInsn(ALOAD, 0);
        mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR);
    }
    // stack: [creatingGlobal, handle]
    final Label setupGlobal = new Label();
    mv.visitLabel(setupGlobal);

    // Determine the first index for a local variable
    int nextLocalVar = 1; // "this" is at 0
    for (final Type t : asmArgTypes) {
        nextLocalVar += t.getSize();
    }
    // Set our local variable indices
    final int currentGlobalVar = nextLocalVar++;
    final int globalsDifferVar = nextLocalVar++;

    mv.dup();
    // stack: [creatingGlobal, creatingGlobal, handle]

    // Emit code for switching to the creating global
    // Global currentGlobal = Context.getGlobal();
    invokeGetGlobal(mv);
    mv.dup();

    mv.visitVarInsn(ASTORE, currentGlobalVar);
    // stack: [currentGlobal, creatingGlobal, creatingGlobal, handle]
    // if(definingGlobal == currentGlobal) {
    final Label globalsDiffer = new Label();
    mv.ifacmpne(globalsDiffer);
    // stack: [creatingGlobal, handle]
    //     globalsDiffer = false
    mv.pop();
    // stack: [handle]
    mv.iconst(0); // false
    // stack: [false, handle]
    final Label invokeHandle = new Label();
    mv.goTo(invokeHandle);
    mv.visitLabel(globalsDiffer);
    // } else {
    //     Context.setGlobal(definingGlobal);
    // stack: [creatingGlobal, handle]
    invokeSetGlobal(mv);
    // stack: [handle]
    //     globalsDiffer = true
    mv.iconst(1);
    // stack: [true, handle]

    mv.visitLabel(invokeHandle);
    mv.visitVarInsn(ISTORE, globalsDifferVar);
    // stack: [handle]

    // Load all parameters back on stack for dynamic invocation. NOTE: since we're using a generic
    // Object(Object, Object, ...) type signature for the method, we must box all arguments here.
    int varOffset = 1;
    for (final Type t : asmArgTypes) {
        mv.load(varOffset, t);
        boxStackTop(mv, t);
        varOffset += t.getSize();
    }

    // Invoke the target method handle
    final Label tryBlockStart = new Label();
    mv.visitLabel(tryBlockStart);
    emitInvokeExact(mv, type.generic());
    convertReturnValue(mv, returnType, asmReturnType);
    final Label tryBlockEnd = new Label();
    mv.visitLabel(tryBlockEnd);
    emitFinally(mv, currentGlobalVar, globalsDifferVar);
    mv.areturn(asmReturnType);

    // If Throwable is not declared, we need an adapter from Throwable to RuntimeException
    final boolean throwableDeclared = isThrowableDeclared(exceptions);
    final Label throwableHandler;
    if (!throwableDeclared) {
        // Add "throw new RuntimeException(Throwable)" handler for Throwable
        throwableHandler = new Label();
        mv.visitLabel(throwableHandler);
        mv.anew(RUNTIME_EXCEPTION_TYPE);
        mv.dupX1();
        mv.swap();
        mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT,
                Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE), false);
        // Fall through to rethrow handler
    } else {
        throwableHandler = null;
    }
    final Label rethrowHandler = new Label();
    mv.visitLabel(rethrowHandler);
    // Rethrow handler for RuntimeException, Error, and all declared exception types
    emitFinally(mv, currentGlobalVar, globalsDifferVar);
    mv.athrow();
    final Label methodEnd = new Label();
    mv.visitLabel(methodEnd);

    mv.visitLocalVariable("currentGlobal", GLOBAL_TYPE_DESCRIPTOR, null, setupGlobal, methodEnd,
            currentGlobalVar);
    mv.visitLocalVariable("globalsDiffer", Type.BOOLEAN_TYPE.getDescriptor(), null, setupGlobal, methodEnd,
            globalsDifferVar);

    if (throwableDeclared) {
        mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME);
        assert throwableHandler == null;
    } else {
        mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME);
        mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME);
        for (final String excName : exceptionNames) {
            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName);
        }
        mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME);
    }
    endMethod(mv);
}