| /* |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * This file is available under and governed by the GNU General Public |
| * License version 2 only, as published by the Free Software Foundation. |
| * However, the following notice accompanied the original version of this |
| * file: |
| * |
| * ASM: a very small and fast Java bytecode manipulation framework |
| * Copyright (c) 2000-2011 INRIA, France Telecom |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package jdk.internal.org.objectweb.asm.commons; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import jdk.internal.org.objectweb.asm.ClassVisitor; |
| import jdk.internal.org.objectweb.asm.Handle; |
| import jdk.internal.org.objectweb.asm.Label; |
| import jdk.internal.org.objectweb.asm.MethodVisitor; |
| import jdk.internal.org.objectweb.asm.Opcodes; |
| import jdk.internal.org.objectweb.asm.Type; |
| |
| /** |
| * A {@link jdk.internal.org.objectweb.asm.MethodVisitor} with convenient methods to generate |
| * code. For example, using this adapter, the class below |
| * |
| * <pre> |
| * public class Example { |
| * public static void main(String[] args) { |
| * System.out.println("Hello world!"); |
| * } |
| * } |
| * </pre> |
| * |
| * can be generated as follows: |
| * |
| * <pre> |
| * ClassWriter cw = new ClassWriter(true); |
| * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null); |
| * |
| * Method m = Method.getMethod("void <init> ()"); |
| * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); |
| * mg.loadThis(); |
| * mg.invokeConstructor(Type.getType(Object.class), m); |
| * mg.returnValue(); |
| * mg.endMethod(); |
| * |
| * m = Method.getMethod("void main (String[])"); |
| * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw); |
| * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); |
| * mg.push("Hello world!"); |
| * mg.invokeVirtual(Type.getType(PrintStream.class), |
| * Method.getMethod("void println (String)")); |
| * mg.returnValue(); |
| * mg.endMethod(); |
| * |
| * cw.visitEnd(); |
| * </pre> |
| * |
| * @author Juozas Baliuka |
| * @author Chris Nokleberg |
| * @author Eric Bruneton |
| * @author Prashant Deva |
| */ |
| public class GeneratorAdapter extends LocalVariablesSorter { |
| |
| private static final String CLDESC = "Ljava/lang/Class;"; |
| |
| private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte"); |
| |
| private static final Type BOOLEAN_TYPE = Type |
| .getObjectType("java/lang/Boolean"); |
| |
| private static final Type SHORT_TYPE = Type |
| .getObjectType("java/lang/Short"); |
| |
| private static final Type CHARACTER_TYPE = Type |
| .getObjectType("java/lang/Character"); |
| |
| private static final Type INTEGER_TYPE = Type |
| .getObjectType("java/lang/Integer"); |
| |
| private static final Type FLOAT_TYPE = Type |
| .getObjectType("java/lang/Float"); |
| |
| private static final Type LONG_TYPE = Type.getObjectType("java/lang/Long"); |
| |
| private static final Type DOUBLE_TYPE = Type |
| .getObjectType("java/lang/Double"); |
| |
| private static final Type NUMBER_TYPE = Type |
| .getObjectType("java/lang/Number"); |
| |
| private static final Type OBJECT_TYPE = Type |
| .getObjectType("java/lang/Object"); |
| |
| private static final Method BOOLEAN_VALUE = Method |
| .getMethod("boolean booleanValue()"); |
| |
| private static final Method CHAR_VALUE = Method |
| .getMethod("char charValue()"); |
| |
| private static final Method INT_VALUE = Method.getMethod("int intValue()"); |
| |
| private static final Method FLOAT_VALUE = Method |
| .getMethod("float floatValue()"); |
| |
| private static final Method LONG_VALUE = Method |
| .getMethod("long longValue()"); |
| |
| private static final Method DOUBLE_VALUE = Method |
| .getMethod("double doubleValue()"); |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int ADD = Opcodes.IADD; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int SUB = Opcodes.ISUB; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int MUL = Opcodes.IMUL; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int DIV = Opcodes.IDIV; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int REM = Opcodes.IREM; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int NEG = Opcodes.INEG; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int SHL = Opcodes.ISHL; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int SHR = Opcodes.ISHR; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int USHR = Opcodes.IUSHR; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int AND = Opcodes.IAND; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int OR = Opcodes.IOR; |
| |
| /** |
| * Constant for the {@link #math math} method. |
| */ |
| public static final int XOR = Opcodes.IXOR; |
| |
| /** |
| * Constant for the {@link #ifCmp ifCmp} method. |
| */ |
| public static final int EQ = Opcodes.IFEQ; |
| |
| /** |
| * Constant for the {@link #ifCmp ifCmp} method. |
| */ |
| public static final int NE = Opcodes.IFNE; |
| |
| /** |
| * Constant for the {@link #ifCmp ifCmp} method. |
| */ |
| public static final int LT = Opcodes.IFLT; |
| |
| /** |
| * Constant for the {@link #ifCmp ifCmp} method. |
| */ |
| public static final int GE = Opcodes.IFGE; |
| |
| /** |
| * Constant for the {@link #ifCmp ifCmp} method. |
| */ |
| public static final int GT = Opcodes.IFGT; |
| |
| /** |
| * Constant for the {@link #ifCmp ifCmp} method. |
| */ |
| public static final int LE = Opcodes.IFLE; |
| |
| /** |
| * Access flags of the method visited by this adapter. |
| */ |
| private final int access; |
| |
| /** |
| * Return type of the method visited by this adapter. |
| */ |
| private final Type returnType; |
| |
| /** |
| * Argument types of the method visited by this adapter. |
| */ |
| private final Type[] argumentTypes; |
| |
| /** |
| * Types of the local variables of the method visited by this adapter. |
| */ |
| private final List<Type> localTypes = new ArrayList<Type>(); |
| |
| /** |
| * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this |
| * constructor</i>. Instead, they must use the |
| * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)} |
| * version. |
| * |
| * @param mv |
| * the method visitor to which this adapter delegates calls. |
| * @param access |
| * the method's access flags (see {@link Opcodes}). |
| * @param name |
| * the method's name. |
| * @param desc |
| * the method's descriptor (see {@link Type Type}). |
| * @throws IllegalStateException |
| * If a subclass calls this constructor. |
| */ |
| public GeneratorAdapter(final MethodVisitor mv, final int access, |
| final String name, final String desc) { |
| this(Opcodes.ASM5, mv, access, name, desc); |
| if (getClass() != GeneratorAdapter.class) { |
| throw new IllegalStateException(); |
| } |
| } |
| |
| /** |
| * Creates a new {@link GeneratorAdapter}. |
| * |
| * @param api |
| * the ASM API version implemented by this visitor. Must be one |
| * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. |
| * @param mv |
| * the method visitor to which this adapter delegates calls. |
| * @param access |
| * the method's access flags (see {@link Opcodes}). |
| * @param name |
| * the method's name. |
| * @param desc |
| * the method's descriptor (see {@link Type Type}). |
| */ |
| protected GeneratorAdapter(final int api, final MethodVisitor mv, |
| final int access, final String name, final String desc) { |
| super(api, access, desc, mv); |
| this.access = access; |
| this.returnType = Type.getReturnType(desc); |
| this.argumentTypes = Type.getArgumentTypes(desc); |
| } |
| |
| /** |
| * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this |
| * constructor</i>. Instead, they must use the |
| * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)} |
| * version. |
| * |
| * @param access |
| * access flags of the adapted method. |
| * @param method |
| * the adapted method. |
| * @param mv |
| * the method visitor to which this adapter delegates calls. |
| */ |
| public GeneratorAdapter(final int access, final Method method, |
| final MethodVisitor mv) { |
| this(mv, access, null, method.getDescriptor()); |
| } |
| |
| /** |
| * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this |
| * constructor</i>. Instead, they must use the |
| * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)} |
| * version. |
| * |
| * @param access |
| * access flags of the adapted method. |
| * @param method |
| * the adapted method. |
| * @param signature |
| * the signature of the adapted method (may be <tt>null</tt>). |
| * @param exceptions |
| * the exceptions thrown by the adapted method (may be |
| * <tt>null</tt>). |
| * @param cv |
| * the class visitor to which this adapter delegates calls. |
| */ |
| public GeneratorAdapter(final int access, final Method method, |
| final String signature, final Type[] exceptions, |
| final ClassVisitor cv) { |
| this(access, method, cv |
| .visitMethod(access, method.getName(), method.getDescriptor(), |
| signature, getInternalNames(exceptions))); |
| } |
| |
| /** |
| * Returns the internal names of the given types. |
| * |
| * @param types |
| * a set of types. |
| * @return the internal names of the given types. |
| */ |
| private static String[] getInternalNames(final Type[] types) { |
| if (types == null) { |
| return null; |
| } |
| String[] names = new String[types.length]; |
| for (int i = 0; i < names.length; ++i) { |
| names[i] = types[i].getInternalName(); |
| } |
| return names; |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to push constants on the stack |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Generates the instruction to push the given value on the stack. |
| * |
| * @param value |
| * the value to be pushed on the stack. |
| */ |
| public void push(final boolean value) { |
| push(value ? 1 : 0); |
| } |
| |
| /** |
| * Generates the instruction to push the given value on the stack. |
| * |
| * @param value |
| * the value to be pushed on the stack. |
| */ |
| public void push(final int value) { |
| if (value >= -1 && value <= 5) { |
| mv.visitInsn(Opcodes.ICONST_0 + value); |
| } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { |
| mv.visitIntInsn(Opcodes.BIPUSH, value); |
| } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { |
| mv.visitIntInsn(Opcodes.SIPUSH, value); |
| } else { |
| mv.visitLdcInsn(value); |
| } |
| } |
| |
| /** |
| * Generates the instruction to push the given value on the stack. |
| * |
| * @param value |
| * the value to be pushed on the stack. |
| */ |
| public void push(final long value) { |
| if (value == 0L || value == 1L) { |
| mv.visitInsn(Opcodes.LCONST_0 + (int) value); |
| } else { |
| mv.visitLdcInsn(value); |
| } |
| } |
| |
| /** |
| * Generates the instruction to push the given value on the stack. |
| * |
| * @param value |
| * the value to be pushed on the stack. |
| */ |
| public void push(final float value) { |
| int bits = Float.floatToIntBits(value); |
| if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2 |
| mv.visitInsn(Opcodes.FCONST_0 + (int) value); |
| } else { |
| mv.visitLdcInsn(value); |
| } |
| } |
| |
| /** |
| * Generates the instruction to push the given value on the stack. |
| * |
| * @param value |
| * the value to be pushed on the stack. |
| */ |
| public void push(final double value) { |
| long bits = Double.doubleToLongBits(value); |
| if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d |
| mv.visitInsn(Opcodes.DCONST_0 + (int) value); |
| } else { |
| mv.visitLdcInsn(value); |
| } |
| } |
| |
| /** |
| * Generates the instruction to push the given value on the stack. |
| * |
| * @param value |
| * the value to be pushed on the stack. May be <tt>null</tt>. |
| */ |
| public void push(final String value) { |
| if (value == null) { |
| mv.visitInsn(Opcodes.ACONST_NULL); |
| } else { |
| mv.visitLdcInsn(value); |
| } |
| } |
| |
| /** |
| * Generates the instruction to push the given value on the stack. |
| * |
| * @param value |
| * the value to be pushed on the stack. |
| */ |
| public void push(final Type value) { |
| if (value == null) { |
| mv.visitInsn(Opcodes.ACONST_NULL); |
| } else { |
| switch (value.getSort()) { |
| case Type.BOOLEAN: |
| mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", |
| "TYPE", CLDESC); |
| break; |
| case Type.CHAR: |
| mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", |
| "TYPE", CLDESC); |
| break; |
| case Type.BYTE: |
| mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", |
| CLDESC); |
| break; |
| case Type.SHORT: |
| mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", |
| CLDESC); |
| break; |
| case Type.INT: |
| mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", |
| "TYPE", CLDESC); |
| break; |
| case Type.FLOAT: |
| mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", |
| CLDESC); |
| break; |
| case Type.LONG: |
| mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", |
| CLDESC); |
| break; |
| case Type.DOUBLE: |
| mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", |
| "TYPE", CLDESC); |
| break; |
| default: |
| mv.visitLdcInsn(value); |
| } |
| } |
| } |
| |
| /** |
| * Generates the instruction to push a handle on the stack. |
| * |
| * @param handle |
| * the handle to be pushed on the stack. |
| */ |
| public void push(final Handle handle) { |
| mv.visitLdcInsn(handle); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to load and store method arguments |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Returns the index of the given method argument in the frame's local |
| * variables array. |
| * |
| * @param arg |
| * the index of a method argument. |
| * @return the index of the given method argument in the frame's local |
| * variables array. |
| */ |
| private int getArgIndex(final int arg) { |
| int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0; |
| for (int i = 0; i < arg; i++) { |
| index += argumentTypes[i].getSize(); |
| } |
| return index; |
| } |
| |
| /** |
| * Generates the instruction to push a local variable on the stack. |
| * |
| * @param type |
| * the type of the local variable to be loaded. |
| * @param index |
| * an index in the frame's local variables array. |
| */ |
| private void loadInsn(final Type type, final int index) { |
| mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index); |
| } |
| |
| /** |
| * Generates the instruction to store the top stack value in a local |
| * variable. |
| * |
| * @param type |
| * the type of the local variable to be stored. |
| * @param index |
| * an index in the frame's local variables array. |
| */ |
| private void storeInsn(final Type type, final int index) { |
| mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index); |
| } |
| |
| /** |
| * Generates the instruction to load 'this' on the stack. |
| */ |
| public void loadThis() { |
| if ((access & Opcodes.ACC_STATIC) != 0) { |
| throw new IllegalStateException( |
| "no 'this' pointer within static method"); |
| } |
| mv.visitVarInsn(Opcodes.ALOAD, 0); |
| } |
| |
| /** |
| * Generates the instruction to load the given method argument on the stack. |
| * |
| * @param arg |
| * the index of a method argument. |
| */ |
| public void loadArg(final int arg) { |
| loadInsn(argumentTypes[arg], getArgIndex(arg)); |
| } |
| |
| /** |
| * Generates the instructions to load the given method arguments on the |
| * stack. |
| * |
| * @param arg |
| * the index of the first method argument to be loaded. |
| * @param count |
| * the number of method arguments to be loaded. |
| */ |
| public void loadArgs(final int arg, final int count) { |
| int index = getArgIndex(arg); |
| for (int i = 0; i < count; ++i) { |
| Type t = argumentTypes[arg + i]; |
| loadInsn(t, index); |
| index += t.getSize(); |
| } |
| } |
| |
| /** |
| * Generates the instructions to load all the method arguments on the stack. |
| */ |
| public void loadArgs() { |
| loadArgs(0, argumentTypes.length); |
| } |
| |
| /** |
| * Generates the instructions to load all the method arguments on the stack, |
| * as a single object array. |
| */ |
| public void loadArgArray() { |
| push(argumentTypes.length); |
| newArray(OBJECT_TYPE); |
| for (int i = 0; i < argumentTypes.length; i++) { |
| dup(); |
| push(i); |
| loadArg(i); |
| box(argumentTypes[i]); |
| arrayStore(OBJECT_TYPE); |
| } |
| } |
| |
| /** |
| * Generates the instruction to store the top stack value in the given |
| * method argument. |
| * |
| * @param arg |
| * the index of a method argument. |
| */ |
| public void storeArg(final int arg) { |
| storeInsn(argumentTypes[arg], getArgIndex(arg)); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to load and store local variables |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Returns the type of the given local variable. |
| * |
| * @param local |
| * a local variable identifier, as returned by |
| * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. |
| * @return the type of the given local variable. |
| */ |
| public Type getLocalType(final int local) { |
| return localTypes.get(local - firstLocal); |
| } |
| |
| @Override |
| protected void setLocalType(final int local, final Type type) { |
| int index = local - firstLocal; |
| while (localTypes.size() < index + 1) { |
| localTypes.add(null); |
| } |
| localTypes.set(index, type); |
| } |
| |
| /** |
| * Generates the instruction to load the given local variable on the stack. |
| * |
| * @param local |
| * a local variable identifier, as returned by |
| * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. |
| */ |
| public void loadLocal(final int local) { |
| loadInsn(getLocalType(local), local); |
| } |
| |
| /** |
| * Generates the instruction to load the given local variable on the stack. |
| * |
| * @param local |
| * a local variable identifier, as returned by |
| * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. |
| * @param type |
| * the type of this local variable. |
| */ |
| public void loadLocal(final int local, final Type type) { |
| setLocalType(local, type); |
| loadInsn(type, local); |
| } |
| |
| /** |
| * Generates the instruction to store the top stack value in the given local |
| * variable. |
| * |
| * @param local |
| * a local variable identifier, as returned by |
| * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. |
| */ |
| public void storeLocal(final int local) { |
| storeInsn(getLocalType(local), local); |
| } |
| |
| /** |
| * Generates the instruction to store the top stack value in the given local |
| * variable. |
| * |
| * @param local |
| * a local variable identifier, as returned by |
| * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. |
| * @param type |
| * the type of this local variable. |
| */ |
| public void storeLocal(final int local, final Type type) { |
| setLocalType(local, type); |
| storeInsn(type, local); |
| } |
| |
| /** |
| * Generates the instruction to load an element from an array. |
| * |
| * @param type |
| * the type of the array element to be loaded. |
| */ |
| public void arrayLoad(final Type type) { |
| mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); |
| } |
| |
| /** |
| * Generates the instruction to store an element in an array. |
| * |
| * @param type |
| * the type of the array element to be stored. |
| */ |
| public void arrayStore(final Type type) { |
| mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to manage the stack |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Generates a POP instruction. |
| */ |
| public void pop() { |
| mv.visitInsn(Opcodes.POP); |
| } |
| |
| /** |
| * Generates a POP2 instruction. |
| */ |
| public void pop2() { |
| mv.visitInsn(Opcodes.POP2); |
| } |
| |
| /** |
| * Generates a DUP instruction. |
| */ |
| public void dup() { |
| mv.visitInsn(Opcodes.DUP); |
| } |
| |
| /** |
| * Generates a DUP2 instruction. |
| */ |
| public void dup2() { |
| mv.visitInsn(Opcodes.DUP2); |
| } |
| |
| /** |
| * Generates a DUP_X1 instruction. |
| */ |
| public void dupX1() { |
| mv.visitInsn(Opcodes.DUP_X1); |
| } |
| |
| /** |
| * Generates a DUP_X2 instruction. |
| */ |
| public void dupX2() { |
| mv.visitInsn(Opcodes.DUP_X2); |
| } |
| |
| /** |
| * Generates a DUP2_X1 instruction. |
| */ |
| public void dup2X1() { |
| mv.visitInsn(Opcodes.DUP2_X1); |
| } |
| |
| /** |
| * Generates a DUP2_X2 instruction. |
| */ |
| public void dup2X2() { |
| mv.visitInsn(Opcodes.DUP2_X2); |
| } |
| |
| /** |
| * Generates a SWAP instruction. |
| */ |
| public void swap() { |
| mv.visitInsn(Opcodes.SWAP); |
| } |
| |
| /** |
| * Generates the instructions to swap the top two stack values. |
| * |
| * @param prev |
| * type of the top - 1 stack value. |
| * @param type |
| * type of the top stack value. |
| */ |
| public void swap(final Type prev, final Type type) { |
| if (type.getSize() == 1) { |
| if (prev.getSize() == 1) { |
| swap(); // same as dupX1(), pop(); |
| } else { |
| dupX2(); |
| pop(); |
| } |
| } else { |
| if (prev.getSize() == 1) { |
| dup2X1(); |
| pop2(); |
| } else { |
| dup2X2(); |
| pop2(); |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to do mathematical and logical operations |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Generates the instruction to do the specified mathematical or logical |
| * operation. |
| * |
| * @param op |
| * a mathematical or logical operation. Must be one of ADD, SUB, |
| * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR. |
| * @param type |
| * the type of the operand(s) for this operation. |
| */ |
| public void math(final int op, final Type type) { |
| mv.visitInsn(type.getOpcode(op)); |
| } |
| |
| /** |
| * Generates the instructions to compute the bitwise negation of the top |
| * stack value. |
| */ |
| public void not() { |
| mv.visitInsn(Opcodes.ICONST_1); |
| mv.visitInsn(Opcodes.IXOR); |
| } |
| |
| /** |
| * Generates the instruction to increment the given local variable. |
| * |
| * @param local |
| * the local variable to be incremented. |
| * @param amount |
| * the amount by which the local variable must be incremented. |
| */ |
| public void iinc(final int local, final int amount) { |
| mv.visitIincInsn(local, amount); |
| } |
| |
| /** |
| * Generates the instructions to cast a numerical value from one type to |
| * another. |
| * |
| * @param from |
| * the type of the top stack value |
| * @param to |
| * the type into which this value must be cast. |
| */ |
| public void cast(final Type from, final Type to) { |
| if (from != to) { |
| if (from == Type.DOUBLE_TYPE) { |
| if (to == Type.FLOAT_TYPE) { |
| mv.visitInsn(Opcodes.D2F); |
| } else if (to == Type.LONG_TYPE) { |
| mv.visitInsn(Opcodes.D2L); |
| } else { |
| mv.visitInsn(Opcodes.D2I); |
| cast(Type.INT_TYPE, to); |
| } |
| } else if (from == Type.FLOAT_TYPE) { |
| if (to == Type.DOUBLE_TYPE) { |
| mv.visitInsn(Opcodes.F2D); |
| } else if (to == Type.LONG_TYPE) { |
| mv.visitInsn(Opcodes.F2L); |
| } else { |
| mv.visitInsn(Opcodes.F2I); |
| cast(Type.INT_TYPE, to); |
| } |
| } else if (from == Type.LONG_TYPE) { |
| if (to == Type.DOUBLE_TYPE) { |
| mv.visitInsn(Opcodes.L2D); |
| } else if (to == Type.FLOAT_TYPE) { |
| mv.visitInsn(Opcodes.L2F); |
| } else { |
| mv.visitInsn(Opcodes.L2I); |
| cast(Type.INT_TYPE, to); |
| } |
| } else { |
| if (to == Type.BYTE_TYPE) { |
| mv.visitInsn(Opcodes.I2B); |
| } else if (to == Type.CHAR_TYPE) { |
| mv.visitInsn(Opcodes.I2C); |
| } else if (to == Type.DOUBLE_TYPE) { |
| mv.visitInsn(Opcodes.I2D); |
| } else if (to == Type.FLOAT_TYPE) { |
| mv.visitInsn(Opcodes.I2F); |
| } else if (to == Type.LONG_TYPE) { |
| mv.visitInsn(Opcodes.I2L); |
| } else if (to == Type.SHORT_TYPE) { |
| mv.visitInsn(Opcodes.I2S); |
| } |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to do boxing and unboxing operations |
| // ------------------------------------------------------------------------ |
| |
| private static Type getBoxedType(final Type type) { |
| switch (type.getSort()) { |
| case Type.BYTE: |
| return BYTE_TYPE; |
| case Type.BOOLEAN: |
| return BOOLEAN_TYPE; |
| case Type.SHORT: |
| return SHORT_TYPE; |
| case Type.CHAR: |
| return CHARACTER_TYPE; |
| case Type.INT: |
| return INTEGER_TYPE; |
| case Type.FLOAT: |
| return FLOAT_TYPE; |
| case Type.LONG: |
| return LONG_TYPE; |
| case Type.DOUBLE: |
| return DOUBLE_TYPE; |
| } |
| return type; |
| } |
| |
| /** |
| * Generates the instructions to box the top stack value. This value is |
| * replaced by its boxed equivalent on top of the stack. |
| * |
| * @param type |
| * the type of the top stack value. |
| */ |
| public void box(final Type type) { |
| if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { |
| return; |
| } |
| if (type == Type.VOID_TYPE) { |
| push((String) null); |
| } else { |
| Type boxed = getBoxedType(type); |
| newInstance(boxed); |
| if (type.getSize() == 2) { |
| // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o |
| dupX2(); |
| dupX2(); |
| pop(); |
| } else { |
| // p -> po -> opo -> oop -> o |
| dupX1(); |
| swap(); |
| } |
| invokeConstructor(boxed, new Method("<init>", Type.VOID_TYPE, |
| new Type[] { type })); |
| } |
| } |
| |
| /** |
| * Generates the instructions to box the top stack value using Java 5's |
| * valueOf() method. This value is replaced by its boxed equivalent on top |
| * of the stack. |
| * |
| * @param type |
| * the type of the top stack value. |
| */ |
| public void valueOf(final Type type) { |
| if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { |
| return; |
| } |
| if (type == Type.VOID_TYPE) { |
| push((String) null); |
| } else { |
| Type boxed = getBoxedType(type); |
| invokeStatic(boxed, new Method("valueOf", boxed, |
| new Type[] { type })); |
| } |
| } |
| |
| /** |
| * Generates the instructions to unbox the top stack value. This value is |
| * replaced by its unboxed equivalent on top of the stack. |
| * |
| * @param type |
| * the type of the top stack value. |
| */ |
| public void unbox(final Type type) { |
| Type t = NUMBER_TYPE; |
| Method sig = null; |
| switch (type.getSort()) { |
| case Type.VOID: |
| return; |
| case Type.CHAR: |
| t = CHARACTER_TYPE; |
| sig = CHAR_VALUE; |
| break; |
| case Type.BOOLEAN: |
| t = BOOLEAN_TYPE; |
| sig = BOOLEAN_VALUE; |
| break; |
| case Type.DOUBLE: |
| sig = DOUBLE_VALUE; |
| break; |
| case Type.FLOAT: |
| sig = FLOAT_VALUE; |
| break; |
| case Type.LONG: |
| sig = LONG_VALUE; |
| break; |
| case Type.INT: |
| case Type.SHORT: |
| case Type.BYTE: |
| sig = INT_VALUE; |
| } |
| if (sig == null) { |
| checkCast(type); |
| } else { |
| checkCast(t); |
| invokeVirtual(t, sig); |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to jump to other instructions |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Creates a new {@link Label}. |
| * |
| * @return a new {@link Label}. |
| */ |
| public Label newLabel() { |
| return new Label(); |
| } |
| |
| /** |
| * Marks the current code position with the given label. |
| * |
| * @param label |
| * a label. |
| */ |
| public void mark(final Label label) { |
| mv.visitLabel(label); |
| } |
| |
| /** |
| * Marks the current code position with a new label. |
| * |
| * @return the label that was created to mark the current code position. |
| */ |
| public Label mark() { |
| Label label = new Label(); |
| mv.visitLabel(label); |
| return label; |
| } |
| |
| /** |
| * Generates the instructions to jump to a label based on the comparison of |
| * the top two stack values. |
| * |
| * @param type |
| * the type of the top two stack values. |
| * @param mode |
| * how these values must be compared. One of EQ, NE, LT, GE, GT, |
| * LE. |
| * @param label |
| * where to jump if the comparison result is <tt>true</tt>. |
| */ |
| public void ifCmp(final Type type, final int mode, final Label label) { |
| switch (type.getSort()) { |
| case Type.LONG: |
| mv.visitInsn(Opcodes.LCMP); |
| break; |
| case Type.DOUBLE: |
| mv.visitInsn(mode == GE || mode == GT ? Opcodes.DCMPL |
| : Opcodes.DCMPG); |
| break; |
| case Type.FLOAT: |
| mv.visitInsn(mode == GE || mode == GT ? Opcodes.FCMPL |
| : Opcodes.FCMPG); |
| break; |
| case Type.ARRAY: |
| case Type.OBJECT: |
| switch (mode) { |
| case EQ: |
| mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); |
| return; |
| case NE: |
| mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); |
| return; |
| } |
| throw new IllegalArgumentException("Bad comparison for type " |
| + type); |
| default: |
| int intOp = -1; |
| switch (mode) { |
| case EQ: |
| intOp = Opcodes.IF_ICMPEQ; |
| break; |
| case NE: |
| intOp = Opcodes.IF_ICMPNE; |
| break; |
| case GE: |
| intOp = Opcodes.IF_ICMPGE; |
| break; |
| case LT: |
| intOp = Opcodes.IF_ICMPLT; |
| break; |
| case LE: |
| intOp = Opcodes.IF_ICMPLE; |
| break; |
| case GT: |
| intOp = Opcodes.IF_ICMPGT; |
| break; |
| } |
| mv.visitJumpInsn(intOp, label); |
| return; |
| } |
| mv.visitJumpInsn(mode, label); |
| } |
| |
| /** |
| * Generates the instructions to jump to a label based on the comparison of |
| * the top two integer stack values. |
| * |
| * @param mode |
| * how these values must be compared. One of EQ, NE, LT, GE, GT, |
| * LE. |
| * @param label |
| * where to jump if the comparison result is <tt>true</tt>. |
| */ |
| public void ifICmp(final int mode, final Label label) { |
| ifCmp(Type.INT_TYPE, mode, label); |
| } |
| |
| /** |
| * Generates the instructions to jump to a label based on the comparison of |
| * the top integer stack value with zero. |
| * |
| * @param mode |
| * how these values must be compared. One of EQ, NE, LT, GE, GT, |
| * LE. |
| * @param label |
| * where to jump if the comparison result is <tt>true</tt>. |
| */ |
| public void ifZCmp(final int mode, final Label label) { |
| mv.visitJumpInsn(mode, label); |
| } |
| |
| /** |
| * Generates the instruction to jump to the given label if the top stack |
| * value is null. |
| * |
| * @param label |
| * where to jump if the condition is <tt>true</tt>. |
| */ |
| public void ifNull(final Label label) { |
| mv.visitJumpInsn(Opcodes.IFNULL, label); |
| } |
| |
| /** |
| * Generates the instruction to jump to the given label if the top stack |
| * value is not null. |
| * |
| * @param label |
| * where to jump if the condition is <tt>true</tt>. |
| */ |
| public void ifNonNull(final Label label) { |
| mv.visitJumpInsn(Opcodes.IFNONNULL, label); |
| } |
| |
| /** |
| * Generates the instruction to jump to the given label. |
| * |
| * @param label |
| * where to jump if the condition is <tt>true</tt>. |
| */ |
| public void goTo(final Label label) { |
| mv.visitJumpInsn(Opcodes.GOTO, label); |
| } |
| |
| /** |
| * Generates a RET instruction. |
| * |
| * @param local |
| * a local variable identifier, as returned by |
| * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. |
| */ |
| public void ret(final int local) { |
| mv.visitVarInsn(Opcodes.RET, local); |
| } |
| |
| /** |
| * Generates the instructions for a switch statement. |
| * |
| * @param keys |
| * the switch case keys. |
| * @param generator |
| * a generator to generate the code for the switch cases. |
| */ |
| public void tableSwitch(final int[] keys, |
| final TableSwitchGenerator generator) { |
| float density; |
| if (keys.length == 0) { |
| density = 0; |
| } else { |
| density = (float) keys.length |
| / (keys[keys.length - 1] - keys[0] + 1); |
| } |
| tableSwitch(keys, generator, density >= 0.5f); |
| } |
| |
| /** |
| * Generates the instructions for a switch statement. |
| * |
| * @param keys |
| * the switch case keys. |
| * @param generator |
| * a generator to generate the code for the switch cases. |
| * @param useTable |
| * <tt>true</tt> to use a TABLESWITCH instruction, or |
| * <tt>false</tt> to use a LOOKUPSWITCH instruction. |
| */ |
| public void tableSwitch(final int[] keys, |
| final TableSwitchGenerator generator, final boolean useTable) { |
| for (int i = 1; i < keys.length; ++i) { |
| if (keys[i] < keys[i - 1]) { |
| throw new IllegalArgumentException( |
| "keys must be sorted ascending"); |
| } |
| } |
| Label def = newLabel(); |
| Label end = newLabel(); |
| if (keys.length > 0) { |
| int len = keys.length; |
| int min = keys[0]; |
| int max = keys[len - 1]; |
| int range = max - min + 1; |
| if (useTable) { |
| Label[] labels = new Label[range]; |
| Arrays.fill(labels, def); |
| for (int i = 0; i < len; ++i) { |
| labels[keys[i] - min] = newLabel(); |
| } |
| mv.visitTableSwitchInsn(min, max, def, labels); |
| for (int i = 0; i < range; ++i) { |
| Label label = labels[i]; |
| if (label != def) { |
| mark(label); |
| generator.generateCase(i + min, end); |
| } |
| } |
| } else { |
| Label[] labels = new Label[len]; |
| for (int i = 0; i < len; ++i) { |
| labels[i] = newLabel(); |
| } |
| mv.visitLookupSwitchInsn(def, keys, labels); |
| for (int i = 0; i < len; ++i) { |
| mark(labels[i]); |
| generator.generateCase(keys[i], end); |
| } |
| } |
| } |
| mark(def); |
| generator.generateDefault(); |
| mark(end); |
| } |
| |
| /** |
| * Generates the instruction to return the top stack value to the caller. |
| */ |
| public void returnValue() { |
| mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to load and store fields |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Generates a get field or set field instruction. |
| * |
| * @param opcode |
| * the instruction's opcode. |
| * @param ownerType |
| * the class in which the field is defined. |
| * @param name |
| * the name of the field. |
| * @param fieldType |
| * the type of the field. |
| */ |
| private void fieldInsn(final int opcode, final Type ownerType, |
| final String name, final Type fieldType) { |
| mv.visitFieldInsn(opcode, ownerType.getInternalName(), name, |
| fieldType.getDescriptor()); |
| } |
| |
| /** |
| * Generates the instruction to push the value of a static field on the |
| * stack. |
| * |
| * @param owner |
| * the class in which the field is defined. |
| * @param name |
| * the name of the field. |
| * @param type |
| * the type of the field. |
| */ |
| public void getStatic(final Type owner, final String name, final Type type) { |
| fieldInsn(Opcodes.GETSTATIC, owner, name, type); |
| } |
| |
| /** |
| * Generates the instruction to store the top stack value in a static field. |
| * |
| * @param owner |
| * the class in which the field is defined. |
| * @param name |
| * the name of the field. |
| * @param type |
| * the type of the field. |
| */ |
| public void putStatic(final Type owner, final String name, final Type type) { |
| fieldInsn(Opcodes.PUTSTATIC, owner, name, type); |
| } |
| |
| /** |
| * Generates the instruction to push the value of a non static field on the |
| * stack. |
| * |
| * @param owner |
| * the class in which the field is defined. |
| * @param name |
| * the name of the field. |
| * @param type |
| * the type of the field. |
| */ |
| public void getField(final Type owner, final String name, final Type type) { |
| fieldInsn(Opcodes.GETFIELD, owner, name, type); |
| } |
| |
| /** |
| * Generates the instruction to store the top stack value in a non static |
| * field. |
| * |
| * @param owner |
| * the class in which the field is defined. |
| * @param name |
| * the name of the field. |
| * @param type |
| * the type of the field. |
| */ |
| public void putField(final Type owner, final String name, final Type type) { |
| fieldInsn(Opcodes.PUTFIELD, owner, name, type); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to invoke methods |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Generates an invoke method instruction. |
| * |
| * @param opcode |
| * the instruction's opcode. |
| * @param type |
| * the class in which the method is defined. |
| * @param method |
| * the method to be invoked. |
| */ |
| private void invokeInsn(final int opcode, final Type type, |
| final Method method, final boolean itf) { |
| String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() |
| : type.getInternalName(); |
| mv.visitMethodInsn(opcode, owner, method.getName(), |
| method.getDescriptor(), itf); |
| } |
| |
| /** |
| * Generates the instruction to invoke a normal method. |
| * |
| * @param owner |
| * the class in which the method is defined. |
| * @param method |
| * the method to be invoked. |
| */ |
| public void invokeVirtual(final Type owner, final Method method) { |
| invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method, false); |
| } |
| |
| /** |
| * Generates the instruction to invoke a constructor. |
| * |
| * @param type |
| * the class in which the constructor is defined. |
| * @param method |
| * the constructor to be invoked. |
| */ |
| public void invokeConstructor(final Type type, final Method method) { |
| invokeInsn(Opcodes.INVOKESPECIAL, type, method, false); |
| } |
| |
| /** |
| * Generates the instruction to invoke a static method. |
| * |
| * @param owner |
| * the class in which the method is defined. |
| * @param method |
| * the method to be invoked. |
| */ |
| public void invokeStatic(final Type owner, final Method method) { |
| invokeInsn(Opcodes.INVOKESTATIC, owner, method, false); |
| } |
| |
| /** |
| * Generates the instruction to invoke an interface method. |
| * |
| * @param owner |
| * the class in which the method is defined. |
| * @param method |
| * the method to be invoked. |
| */ |
| public void invokeInterface(final Type owner, final Method method) { |
| invokeInsn(Opcodes.INVOKEINTERFACE, owner, method, true); |
| } |
| |
| /** |
| * Generates an invokedynamic instruction. |
| * |
| * @param name |
| * the method's name. |
| * @param desc |
| * the method's descriptor (see {@link Type Type}). |
| * @param bsm |
| * the bootstrap method. |
| * @param bsmArgs |
| * the bootstrap method constant arguments. Each argument must be |
| * an {@link Integer}, {@link Float}, {@link Long}, |
| * {@link Double}, {@link String}, {@link Type} or {@link Handle} |
| * value. This method is allowed to modify the content of the |
| * array so a caller should expect that this array may change. |
| */ |
| public void invokeDynamic(String name, String desc, Handle bsm, |
| Object... bsmArgs) { |
| mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Instructions to create objects and arrays |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Generates a type dependent instruction. |
| * |
| * @param opcode |
| * the instruction's opcode. |
| * @param type |
| * the instruction's operand. |
| */ |
| private void typeInsn(final int opcode, final Type type) { |
| mv.visitTypeInsn(opcode, type.getInternalName()); |
| } |
| |
| /** |
| * Generates the instruction to create a new object. |
| * |
| * @param type |
| * the class of the object to be created. |
| */ |
| public void newInstance(final Type type) { |
| typeInsn(Opcodes.NEW, type); |
| } |
| |
| /** |
| * Generates the instruction to create a new array. |
| * |
| * @param type |
| * the type of the array elements. |
| */ |
| public void newArray(final Type type) { |
| int typ; |
| switch (type.getSort()) { |
| case Type.BOOLEAN: |
| typ = Opcodes.T_BOOLEAN; |
| break; |
| case Type.CHAR: |
| typ = Opcodes.T_CHAR; |
| break; |
| case Type.BYTE: |
| typ = Opcodes.T_BYTE; |
| break; |
| case Type.SHORT: |
| typ = Opcodes.T_SHORT; |
| break; |
| case Type.INT: |
| typ = Opcodes.T_INT; |
| break; |
| case Type.FLOAT: |
| typ = Opcodes.T_FLOAT; |
| break; |
| case Type.LONG: |
| typ = Opcodes.T_LONG; |
| break; |
| case Type.DOUBLE: |
| typ = Opcodes.T_DOUBLE; |
| break; |
| default: |
| typeInsn(Opcodes.ANEWARRAY, type); |
| return; |
| } |
| mv.visitIntInsn(Opcodes.NEWARRAY, typ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Miscelaneous instructions |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Generates the instruction to compute the length of an array. |
| */ |
| public void arrayLength() { |
| mv.visitInsn(Opcodes.ARRAYLENGTH); |
| } |
| |
| /** |
| * Generates the instruction to throw an exception. |
| */ |
| public void throwException() { |
| mv.visitInsn(Opcodes.ATHROW); |
| } |
| |
| /** |
| * Generates the instructions to create and throw an exception. The |
| * exception class must have a constructor with a single String argument. |
| * |
| * @param type |
| * the class of the exception to be thrown. |
| * @param msg |
| * the detailed message of the exception. |
| */ |
| public void throwException(final Type type, final String msg) { |
| newInstance(type); |
| dup(); |
| push(msg); |
| invokeConstructor(type, Method.getMethod("void <init> (String)")); |
| throwException(); |
| } |
| |
| /** |
| * Generates the instruction to check that the top stack value is of the |
| * given type. |
| * |
| * @param type |
| * a class or interface type. |
| */ |
| public void checkCast(final Type type) { |
| if (!type.equals(OBJECT_TYPE)) { |
| typeInsn(Opcodes.CHECKCAST, type); |
| } |
| } |
| |
| /** |
| * Generates the instruction to test if the top stack value is of the given |
| * type. |
| * |
| * @param type |
| * a class or interface type. |
| */ |
| public void instanceOf(final Type type) { |
| typeInsn(Opcodes.INSTANCEOF, type); |
| } |
| |
| /** |
| * Generates the instruction to get the monitor of the top stack value. |
| */ |
| public void monitorEnter() { |
| mv.visitInsn(Opcodes.MONITORENTER); |
| } |
| |
| /** |
| * Generates the instruction to release the monitor of the top stack value. |
| */ |
| public void monitorExit() { |
| mv.visitInsn(Opcodes.MONITOREXIT); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Non instructions |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Marks the end of the visited method. |
| */ |
| public void endMethod() { |
| if ((access & Opcodes.ACC_ABSTRACT) == 0) { |
| mv.visitMaxs(0, 0); |
| } |
| mv.visitEnd(); |
| } |
| |
| /** |
| * Marks the start of an exception handler. |
| * |
| * @param start |
| * beginning of the exception handler's scope (inclusive). |
| * @param end |
| * end of the exception handler's scope (exclusive). |
| * @param exception |
| * internal name of the type of exceptions handled by the |
| * handler. |
| */ |
| public void catchException(final Label start, final Label end, |
| final Type exception) { |
| Label doCatch = new Label(); |
| if (exception == null) { |
| mv.visitTryCatchBlock(start, end, doCatch, null); |
| } else { |
| mv.visitTryCatchBlock(start, end, doCatch, |
| exception.getInternalName()); |
| } |
| mark(doCatch); |
| } |
| } |