/***
* 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 org.objectweb.asm.commons;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* A {@link org.objectweb.asm.MethodVisitor} to insert before, after and around
* advices in methods and constructors.
*
* The behavior for constructors is like this:
*
*
* as long as the INVOKESPECIAL for the object initialization has not been
* reached, every bytecode instruction is dispatched in the ctor code visitor
*
* when this one is reached, it is only added in the ctor code visitor and a
* JP invoke is added
*
* after that, only the other code visitor receives the instructions
*
*
*
* @author Eugene Kuleshov
* @author Eric Bruneton
*/
public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes {
private static final Object THIS = new Object();
private static final Object OTHER = new Object();
protected int methodAccess;
protected String methodDesc;
private boolean constructor;
private boolean superInitialized;
private List stackFrame;
private Map> branches;
/**
* Creates a new {@link AdviceAdapter}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @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 AdviceAdapter(final int api, final MethodVisitor mv,
final int access, final String name, final String desc) {
super(api, mv, access, name, desc);
methodAccess = access;
methodDesc = desc;
constructor = "".equals(name);
}
@Override
public void visitCode() {
mv.visitCode();
if (constructor) {
stackFrame = new ArrayList();
branches = new HashMap>();
} else {
superInitialized = true;
onMethodEnter();
}
}
@Override
public void visitLabel(final Label label) {
mv.visitLabel(label);
if (constructor && branches != null) {
List frame = branches.get(label);
if (frame != null) {
stackFrame = frame;
branches.remove(label);
}
}
}
@Override
public void visitInsn(final int opcode) {
if (constructor) {
int s;
switch (opcode) {
case RETURN: // empty stack
onMethodExit(opcode);
break;
case IRETURN: // 1 before n/a after
case FRETURN: // 1 before n/a after
case ARETURN: // 1 before n/a after
case ATHROW: // 1 before n/a after
popValue();
onMethodExit(opcode);
break;
case LRETURN: // 2 before n/a after
case DRETURN: // 2 before n/a after
popValue();
popValue();
onMethodExit(opcode);
break;
case NOP:
case LALOAD: // remove 2 add 2
case DALOAD: // remove 2 add 2
case LNEG:
case DNEG:
case FNEG:
case INEG:
case L2D:
case D2L:
case F2I:
case I2B:
case I2C:
case I2S:
case I2F:
case ARRAYLENGTH:
break;
case ACONST_NULL:
case ICONST_M1:
case ICONST_0:
case ICONST_1:
case ICONST_2:
case ICONST_3:
case ICONST_4:
case ICONST_5:
case FCONST_0:
case FCONST_1:
case FCONST_2:
case F2L: // 1 before 2 after
case F2D:
case I2L:
case I2D:
pushValue(OTHER);
break;
case LCONST_0:
case LCONST_1:
case DCONST_0:
case DCONST_1:
pushValue(OTHER);
pushValue(OTHER);
break;
case IALOAD: // remove 2 add 1
case FALOAD: // remove 2 add 1
case AALOAD: // remove 2 add 1
case BALOAD: // remove 2 add 1
case CALOAD: // remove 2 add 1
case SALOAD: // remove 2 add 1
case POP:
case IADD:
case FADD:
case ISUB:
case LSHL: // 3 before 2 after
case LSHR: // 3 before 2 after
case LUSHR: // 3 before 2 after
case L2I: // 2 before 1 after
case L2F: // 2 before 1 after
case D2I: // 2 before 1 after
case D2F: // 2 before 1 after
case FSUB:
case FMUL:
case FDIV:
case FREM:
case FCMPL: // 2 before 1 after
case FCMPG: // 2 before 1 after
case IMUL:
case IDIV:
case IREM:
case ISHL:
case ISHR:
case IUSHR:
case IAND:
case IOR:
case IXOR:
case MONITORENTER:
case MONITOREXIT:
popValue();
break;
case POP2:
case LSUB:
case LMUL:
case LDIV:
case LREM:
case LADD:
case LAND:
case LOR:
case LXOR:
case DADD:
case DMUL:
case DSUB:
case DDIV:
case DREM:
popValue();
popValue();
break;
case IASTORE:
case FASTORE:
case AASTORE:
case BASTORE:
case CASTORE:
case SASTORE:
case LCMP: // 4 before 1 after
case DCMPL:
case DCMPG:
popValue();
popValue();
popValue();
break;
case LASTORE:
case DASTORE:
popValue();
popValue();
popValue();
popValue();
break;
case DUP:
pushValue(peekValue());
break;
case DUP_X1:
s = stackFrame.size();
stackFrame.add(s - 2, stackFrame.get(s - 1));
break;
case DUP_X2:
s = stackFrame.size();
stackFrame.add(s - 3, stackFrame.get(s - 1));
break;
case DUP2:
s = stackFrame.size();
stackFrame.add(s - 2, stackFrame.get(s - 1));
stackFrame.add(s - 2, stackFrame.get(s - 1));
break;
case DUP2_X1:
s = stackFrame.size();
stackFrame.add(s - 3, stackFrame.get(s - 1));
stackFrame.add(s - 3, stackFrame.get(s - 1));
break;
case DUP2_X2:
s = stackFrame.size();
stackFrame.add(s - 4, stackFrame.get(s - 1));
stackFrame.add(s - 4, stackFrame.get(s - 1));
break;
case SWAP:
s = stackFrame.size();
stackFrame.add(s - 2, stackFrame.get(s - 1));
stackFrame.remove(s);
break;
}
} else {
switch (opcode) {
case RETURN:
case IRETURN:
case FRETURN:
case ARETURN:
case LRETURN:
case DRETURN:
case ATHROW:
onMethodExit(opcode);
break;
}
}
mv.visitInsn(opcode);
}
@Override
public void visitVarInsn(final int opcode, final int var) {
super.visitVarInsn(opcode, var);
if (constructor) {
switch (opcode) {
case ILOAD:
case FLOAD:
pushValue(OTHER);
break;
case LLOAD:
case DLOAD:
pushValue(OTHER);
pushValue(OTHER);
break;
case ALOAD:
pushValue(var == 0 ? THIS : OTHER);
break;
case ASTORE:
case ISTORE:
case FSTORE:
popValue();
break;
case LSTORE:
case DSTORE:
popValue();
popValue();
break;
}
}
}
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
mv.visitFieldInsn(opcode, owner, name, desc);
if (constructor) {
char c = desc.charAt(0);
boolean longOrDouble = c == 'J' || c == 'D';
switch (opcode) {
case GETSTATIC:
pushValue(OTHER);
if (longOrDouble) {
pushValue(OTHER);
}
break;
case PUTSTATIC:
popValue();
if (longOrDouble) {
popValue();
}
break;
case PUTFIELD:
popValue();
if (longOrDouble) {
popValue();
popValue();
}
break;
// case GETFIELD:
default:
if (longOrDouble) {
pushValue(OTHER);
}
}
}
}
@Override
public void visitIntInsn(final int opcode, final int operand) {
mv.visitIntInsn(opcode, operand);
if (constructor && opcode != NEWARRAY) {
pushValue(OTHER);
}
}
@Override
public void visitLdcInsn(final Object cst) {
mv.visitLdcInsn(cst);
if (constructor) {
pushValue(OTHER);
if (cst instanceof Double || cst instanceof Long) {
pushValue(OTHER);
}
}
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
mv.visitMultiANewArrayInsn(desc, dims);
if (constructor) {
for (int i = 0; i < dims; i++) {
popValue();
}
pushValue(OTHER);
}
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
mv.visitTypeInsn(opcode, type);
// ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
if (constructor && opcode == NEW) {
pushValue(OTHER);
}
}
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
mv.visitMethodInsn(opcode, owner, name, desc);
if (constructor) {
Type[] types = Type.getArgumentTypes(desc);
for (int i = 0; i < types.length; i++) {
popValue();
if (types[i].getSize() == 2) {
popValue();
}
}
switch (opcode) {
// case INVOKESTATIC:
// break;
case INVOKEINTERFACE:
case INVOKEVIRTUAL:
popValue(); // objectref
break;
case INVOKESPECIAL:
Object type = popValue(); // objectref
if (type == THIS && !superInitialized) {
onMethodEnter();
superInitialized = true;
// once super has been initialized it is no longer
// necessary to keep track of stack state
constructor = false;
}
break;
}
Type returnType = Type.getReturnType(desc);
if (returnType != Type.VOID_TYPE) {
pushValue(OTHER);
if (returnType.getSize() == 2) {
pushValue(OTHER);
}
}
}
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
if (constructor) {
Type[] types = Type.getArgumentTypes(desc);
for (int i = 0; i < types.length; i++) {
popValue();
if (types[i].getSize() == 2) {
popValue();
}
}
Type returnType = Type.getReturnType(desc);
if (returnType != Type.VOID_TYPE) {
pushValue(OTHER);
if (returnType.getSize() == 2) {
pushValue(OTHER);
}
}
}
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
mv.visitJumpInsn(opcode, label);
if (constructor) {
switch (opcode) {
case IFEQ:
case IFNE:
case IFLT:
case IFGE:
case IFGT:
case IFLE:
case IFNULL:
case IFNONNULL:
popValue();
break;
case IF_ICMPEQ:
case IF_ICMPNE:
case IF_ICMPLT:
case IF_ICMPGE:
case IF_ICMPGT:
case IF_ICMPLE:
case IF_ACMPEQ:
case IF_ACMPNE:
popValue();
popValue();
break;
case JSR:
pushValue(OTHER);
break;
}
addBranch(label);
}
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
mv.visitLookupSwitchInsn(dflt, keys, labels);
if (constructor) {
popValue();
addBranches(dflt, labels);
}
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
mv.visitTableSwitchInsn(min, max, dflt, labels);
if (constructor) {
popValue();
addBranches(dflt, labels);
}
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler,
String type) {
super.visitTryCatchBlock(start, end, handler, type);
if (constructor && !branches.containsKey(handler)) {
List stackFrame = new ArrayList();
stackFrame.add(OTHER);
branches.put(handler, stackFrame);
}
}
private void addBranches(final Label dflt, final Label[] labels) {
addBranch(dflt);
for (int i = 0; i < labels.length; i++) {
addBranch(labels[i]);
}
}
private void addBranch(final Label label) {
if (branches.containsKey(label)) {
return;
}
branches.put(label, new ArrayList(stackFrame));
}
private Object popValue() {
return stackFrame.remove(stackFrame.size() - 1);
}
private Object peekValue() {
return stackFrame.get(stackFrame.size() - 1);
}
private void pushValue(final Object o) {
stackFrame.add(o);
}
/**
* Called at the beginning of the method or after super class class call in
* the constructor.
*
*
* Custom code can use or change all the local variables, but should not
* change state of the stack.
*/
protected void onMethodEnter() {
}
/**
* Called before explicit exit from the method using either return or throw.
* Top element on the stack contains the return value or exception instance.
* For example:
*
*
* public void onMethodExit(int opcode) {
* if(opcode==RETURN) {
* visitInsn(ACONST_NULL);
* } else if(opcode==ARETURN || opcode==ATHROW) {
* dup();
* } else {
* if(opcode==LRETURN || opcode==DRETURN) {
* dup2();
* } else {
* dup();
* }
* box(Type.getReturnType(this.methodDesc));
* }
* visitIntInsn(SIPUSH, opcode);
* visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
* }
*
* // an actual call back method
* public static void onExit(Object param, int opcode) {
* ...
*
*
*
*
*
* Custom code can use or change all the local variables, but should not
* change state of the stack.
*
* @param opcode
* one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN
* or ATHROW
*
*/
protected void onMethodExit(int opcode) {
}
// TODO onException, onMethodCall
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 28523
Content-Disposition: inline; filename="AnalyzerAdapter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "156af0cbd5c7da4faa82503c4d94d064c26530b6"
/***
* 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 org.objectweb.asm.commons;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* A {@link MethodVisitor} that keeps track of stack map frame changes between
* {@link #visitFrame(int, int, Object[], int, Object[]) visitFrame} calls. This
* adapter must be used with the
* {@link org.objectweb.asm.ClassReader#EXPAND_FRAMES} option. Each
* visitX instruction delegates to the next visitor in the chain, if any,
* and then simulates the effect of this instruction on the stack map frame,
* represented by {@link #locals} and {@link #stack}. The next visitor in the
* chain can get the state of the stack map frame before each instruction
* by reading the value of these fields in its visitX methods (this
* requires a reference to the AnalyzerAdapter that is before it in the chain).
* If this adapter is used with a class that does not contain stack map table
* attributes (i.e., pre Java 6 classes) then this adapter may not be able to
* compute the stack map frame for each instruction. In this case no exception
* is thrown but the {@link #locals} and {@link #stack} fields will be null for
* these instructions.
*
* @author Eric Bruneton
*/
public class AnalyzerAdapter extends MethodVisitor {
/**
* List
of the local variable slots for current execution
* frame. Primitive types are represented by {@link Opcodes#TOP},
* {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
* {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
* {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by
* two elements, the second one being TOP). Reference types are represented
* by String objects (representing internal names), and uninitialized types
* by Label objects (this label designates the NEW instruction that created
* this uninitialized value). This field is null for unreachable
* instructions.
*/
public List locals;
/**
* List
of the operand stack slots for current execution frame.
* Primitive types are represented by {@link Opcodes#TOP},
* {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
* {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
* {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by
* two elements, the second one being TOP). Reference types are represented
* by String objects (representing internal names), and uninitialized types
* by Label objects (this label designates the NEW instruction that created
* this uninitialized value). This field is null for unreachable
* instructions.
*/
public List stack;
/**
* The labels that designate the next instruction to be visited. May be
* null .
*/
private List labels;
/**
* Information about uninitialized types in the current execution frame.
* This map associates internal names to Label objects. Each label
* designates a NEW instruction that created the currently uninitialized
* types, and the associated internal name represents the NEW operand, i.e.
* the final, initialized type value.
*/
public Map uninitializedTypes;
/**
* The maximum stack size of this method.
*/
private int maxStack;
/**
* The maximum number of local variables of this method.
*/
private int maxLocals;
/**
* The owner's class name.
*/
private String owner;
/**
* Creates a new {@link AnalyzerAdapter}. Subclasses must not use this
* constructor . Instead, they must use the
* {@link #AnalyzerAdapter(int, String, int, String, String, MethodVisitor)}
* version.
*
* @param owner
* the owner's class name.
* @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}).
* @param mv
* the method visitor to which this adapter delegates calls. May
* be null .
*/
public AnalyzerAdapter(final String owner, final int access,
final String name, final String desc, final MethodVisitor mv) {
this(Opcodes.ASM4, owner, access, name, desc, mv);
}
/**
* Creates a new {@link AnalyzerAdapter}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @param owner
* the owner's class name.
* @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}).
* @param mv
* the method visitor to which this adapter delegates calls. May
* be null .
*/
protected AnalyzerAdapter(final int api, final String owner,
final int access, final String name, final String desc,
final MethodVisitor mv) {
super(api, mv);
this.owner = owner;
locals = new ArrayList();
stack = new ArrayList();
uninitializedTypes = new HashMap();
if ((access & Opcodes.ACC_STATIC) == 0) {
if ("".equals(name)) {
locals.add(Opcodes.UNINITIALIZED_THIS);
} else {
locals.add(owner);
}
}
Type[] types = Type.getArgumentTypes(desc);
for (int i = 0; i < types.length; ++i) {
Type type = types[i];
switch (type.getSort()) {
case Type.BOOLEAN:
case Type.CHAR:
case Type.BYTE:
case Type.SHORT:
case Type.INT:
locals.add(Opcodes.INTEGER);
break;
case Type.FLOAT:
locals.add(Opcodes.FLOAT);
break;
case Type.LONG:
locals.add(Opcodes.LONG);
locals.add(Opcodes.TOP);
break;
case Type.DOUBLE:
locals.add(Opcodes.DOUBLE);
locals.add(Opcodes.TOP);
break;
case Type.ARRAY:
locals.add(types[i].getDescriptor());
break;
// case Type.OBJECT:
default:
locals.add(types[i].getInternalName());
}
}
}
@Override
public void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack) {
if (type != Opcodes.F_NEW) { // uncompressed frame
throw new IllegalStateException(
"ClassReader.accept() should be called with EXPAND_FRAMES flag");
}
if (mv != null) {
mv.visitFrame(type, nLocal, local, nStack, stack);
}
if (this.locals != null) {
this.locals.clear();
this.stack.clear();
} else {
this.locals = new ArrayList();
this.stack = new ArrayList();
}
visitFrameTypes(nLocal, local, this.locals);
visitFrameTypes(nStack, stack, this.stack);
maxStack = Math.max(maxStack, this.stack.size());
}
private static void visitFrameTypes(final int n, final Object[] types,
final List result) {
for (int i = 0; i < n; ++i) {
Object type = types[i];
result.add(type);
if (type == Opcodes.LONG || type == Opcodes.DOUBLE) {
result.add(Opcodes.TOP);
}
}
}
@Override
public void visitInsn(final int opcode) {
if (mv != null) {
mv.visitInsn(opcode);
}
execute(opcode, 0, null);
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
|| opcode == Opcodes.ATHROW) {
this.locals = null;
this.stack = null;
}
}
@Override
public void visitIntInsn(final int opcode, final int operand) {
if (mv != null) {
mv.visitIntInsn(opcode, operand);
}
execute(opcode, operand, null);
}
@Override
public void visitVarInsn(final int opcode, final int var) {
if (mv != null) {
mv.visitVarInsn(opcode, var);
}
execute(opcode, var, null);
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
if (opcode == Opcodes.NEW) {
if (labels == null) {
Label l = new Label();
labels = new ArrayList(3);
labels.add(l);
if (mv != null) {
mv.visitLabel(l);
}
}
for (int i = 0; i < labels.size(); ++i) {
uninitializedTypes.put(labels.get(i), type);
}
}
if (mv != null) {
mv.visitTypeInsn(opcode, type);
}
execute(opcode, 0, type);
}
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
if (mv != null) {
mv.visitFieldInsn(opcode, owner, name, desc);
}
execute(opcode, 0, desc);
}
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc);
}
if (this.locals == null) {
labels = null;
return;
}
pop(desc);
if (opcode != Opcodes.INVOKESTATIC) {
Object t = pop();
if (opcode == Opcodes.INVOKESPECIAL && name.charAt(0) == '<') {
Object u;
if (t == Opcodes.UNINITIALIZED_THIS) {
u = this.owner;
} else {
u = uninitializedTypes.get(t);
}
for (int i = 0; i < locals.size(); ++i) {
if (locals.get(i) == t) {
locals.set(i, u);
}
}
for (int i = 0; i < stack.size(); ++i) {
if (stack.get(i) == t) {
stack.set(i, u);
}
}
}
}
pushDesc(desc);
labels = null;
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
if (mv != null) {
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
if (this.locals == null) {
labels = null;
return;
}
pop(desc);
pushDesc(desc);
labels = null;
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
if (mv != null) {
mv.visitJumpInsn(opcode, label);
}
execute(opcode, 0, null);
if (opcode == Opcodes.GOTO) {
this.locals = null;
this.stack = null;
}
}
@Override
public void visitLabel(final Label label) {
if (mv != null) {
mv.visitLabel(label);
}
if (labels == null) {
labels = new ArrayList(3);
}
labels.add(label);
}
@Override
public void visitLdcInsn(final Object cst) {
if (mv != null) {
mv.visitLdcInsn(cst);
}
if (this.locals == null) {
labels = null;
return;
}
if (cst instanceof Integer) {
push(Opcodes.INTEGER);
} else if (cst instanceof Long) {
push(Opcodes.LONG);
push(Opcodes.TOP);
} else if (cst instanceof Float) {
push(Opcodes.FLOAT);
} else if (cst instanceof Double) {
push(Opcodes.DOUBLE);
push(Opcodes.TOP);
} else if (cst instanceof String) {
push("java/lang/String");
} else if (cst instanceof Type) {
int sort = ((Type) cst).getSort();
if (sort == Type.OBJECT || sort == Type.ARRAY) {
push("java/lang/Class");
} else if (sort == Type.METHOD) {
push("java/lang/invoke/MethodType");
} else {
throw new IllegalArgumentException();
}
} else if (cst instanceof Handle) {
push("java/lang/invoke/MethodHandle");
} else {
throw new IllegalArgumentException();
}
labels = null;
}
@Override
public void visitIincInsn(final int var, final int increment) {
if (mv != null) {
mv.visitIincInsn(var, increment);
}
execute(Opcodes.IINC, var, null);
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
if (mv != null) {
mv.visitTableSwitchInsn(min, max, dflt, labels);
}
execute(Opcodes.TABLESWITCH, 0, null);
this.locals = null;
this.stack = null;
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
if (mv != null) {
mv.visitLookupSwitchInsn(dflt, keys, labels);
}
execute(Opcodes.LOOKUPSWITCH, 0, null);
this.locals = null;
this.stack = null;
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
if (mv != null) {
mv.visitMultiANewArrayInsn(desc, dims);
}
execute(Opcodes.MULTIANEWARRAY, dims, desc);
}
@Override
public void visitMaxs(final int maxStack, final int maxLocals) {
if (mv != null) {
this.maxStack = Math.max(this.maxStack, maxStack);
this.maxLocals = Math.max(this.maxLocals, maxLocals);
mv.visitMaxs(this.maxStack, this.maxLocals);
}
}
// ------------------------------------------------------------------------
private Object get(final int local) {
maxLocals = Math.max(maxLocals, local);
return local < locals.size() ? locals.get(local) : Opcodes.TOP;
}
private void set(final int local, final Object type) {
maxLocals = Math.max(maxLocals, local);
while (local >= locals.size()) {
locals.add(Opcodes.TOP);
}
locals.set(local, type);
}
private void push(final Object type) {
stack.add(type);
maxStack = Math.max(maxStack, stack.size());
}
private void pushDesc(final String desc) {
int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0;
switch (desc.charAt(index)) {
case 'V':
return;
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
push(Opcodes.INTEGER);
return;
case 'F':
push(Opcodes.FLOAT);
return;
case 'J':
push(Opcodes.LONG);
push(Opcodes.TOP);
return;
case 'D':
push(Opcodes.DOUBLE);
push(Opcodes.TOP);
return;
case '[':
if (index == 0) {
push(desc);
} else {
push(desc.substring(index, desc.length()));
}
break;
// case 'L':
default:
if (index == 0) {
push(desc.substring(1, desc.length() - 1));
} else {
push(desc.substring(index + 1, desc.length() - 1));
}
}
}
private Object pop() {
return stack.remove(stack.size() - 1);
}
private void pop(final int n) {
int size = stack.size();
int end = size - n;
for (int i = size - 1; i >= end; --i) {
stack.remove(i);
}
}
private void pop(final String desc) {
char c = desc.charAt(0);
if (c == '(') {
int n = 0;
Type[] types = Type.getArgumentTypes(desc);
for (int i = 0; i < types.length; ++i) {
n += types[i].getSize();
}
pop(n);
} else if (c == 'J' || c == 'D') {
pop(2);
} else {
pop(1);
}
}
private void execute(final int opcode, final int iarg, final String sarg) {
if (this.locals == null) {
labels = null;
return;
}
Object t1, t2, t3, t4;
switch (opcode) {
case Opcodes.NOP:
case Opcodes.INEG:
case Opcodes.LNEG:
case Opcodes.FNEG:
case Opcodes.DNEG:
case Opcodes.I2B:
case Opcodes.I2C:
case Opcodes.I2S:
case Opcodes.GOTO:
case Opcodes.RETURN:
break;
case Opcodes.ACONST_NULL:
push(Opcodes.NULL);
break;
case Opcodes.ICONST_M1:
case Opcodes.ICONST_0:
case Opcodes.ICONST_1:
case Opcodes.ICONST_2:
case Opcodes.ICONST_3:
case Opcodes.ICONST_4:
case Opcodes.ICONST_5:
case Opcodes.BIPUSH:
case Opcodes.SIPUSH:
push(Opcodes.INTEGER);
break;
case Opcodes.LCONST_0:
case Opcodes.LCONST_1:
push(Opcodes.LONG);
push(Opcodes.TOP);
break;
case Opcodes.FCONST_0:
case Opcodes.FCONST_1:
case Opcodes.FCONST_2:
push(Opcodes.FLOAT);
break;
case Opcodes.DCONST_0:
case Opcodes.DCONST_1:
push(Opcodes.DOUBLE);
push(Opcodes.TOP);
break;
case Opcodes.ILOAD:
case Opcodes.FLOAD:
case Opcodes.ALOAD:
push(get(iarg));
break;
case Opcodes.LLOAD:
case Opcodes.DLOAD:
push(get(iarg));
push(Opcodes.TOP);
break;
case Opcodes.IALOAD:
case Opcodes.BALOAD:
case Opcodes.CALOAD:
case Opcodes.SALOAD:
pop(2);
push(Opcodes.INTEGER);
break;
case Opcodes.LALOAD:
case Opcodes.D2L:
pop(2);
push(Opcodes.LONG);
push(Opcodes.TOP);
break;
case Opcodes.FALOAD:
pop(2);
push(Opcodes.FLOAT);
break;
case Opcodes.DALOAD:
case Opcodes.L2D:
pop(2);
push(Opcodes.DOUBLE);
push(Opcodes.TOP);
break;
case Opcodes.AALOAD:
pop(1);
t1 = pop();
if (t1 instanceof String) {
pushDesc(((String) t1).substring(1));
} else {
push("java/lang/Object");
}
break;
case Opcodes.ISTORE:
case Opcodes.FSTORE:
case Opcodes.ASTORE:
t1 = pop();
set(iarg, t1);
if (iarg > 0) {
t2 = get(iarg - 1);
if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) {
set(iarg - 1, Opcodes.TOP);
}
}
break;
case Opcodes.LSTORE:
case Opcodes.DSTORE:
pop(1);
t1 = pop();
set(iarg, t1);
set(iarg + 1, Opcodes.TOP);
if (iarg > 0) {
t2 = get(iarg - 1);
if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) {
set(iarg - 1, Opcodes.TOP);
}
}
break;
case Opcodes.IASTORE:
case Opcodes.BASTORE:
case Opcodes.CASTORE:
case Opcodes.SASTORE:
case Opcodes.FASTORE:
case Opcodes.AASTORE:
pop(3);
break;
case Opcodes.LASTORE:
case Opcodes.DASTORE:
pop(4);
break;
case Opcodes.POP:
case Opcodes.IFEQ:
case Opcodes.IFNE:
case Opcodes.IFLT:
case Opcodes.IFGE:
case Opcodes.IFGT:
case Opcodes.IFLE:
case Opcodes.IRETURN:
case Opcodes.FRETURN:
case Opcodes.ARETURN:
case Opcodes.TABLESWITCH:
case Opcodes.LOOKUPSWITCH:
case Opcodes.ATHROW:
case Opcodes.MONITORENTER:
case Opcodes.MONITOREXIT:
case Opcodes.IFNULL:
case Opcodes.IFNONNULL:
pop(1);
break;
case Opcodes.POP2:
case Opcodes.IF_ICMPEQ:
case Opcodes.IF_ICMPNE:
case Opcodes.IF_ICMPLT:
case Opcodes.IF_ICMPGE:
case Opcodes.IF_ICMPGT:
case Opcodes.IF_ICMPLE:
case Opcodes.IF_ACMPEQ:
case Opcodes.IF_ACMPNE:
case Opcodes.LRETURN:
case Opcodes.DRETURN:
pop(2);
break;
case Opcodes.DUP:
t1 = pop();
push(t1);
push(t1);
break;
case Opcodes.DUP_X1:
t1 = pop();
t2 = pop();
push(t1);
push(t2);
push(t1);
break;
case Opcodes.DUP_X2:
t1 = pop();
t2 = pop();
t3 = pop();
push(t1);
push(t3);
push(t2);
push(t1);
break;
case Opcodes.DUP2:
t1 = pop();
t2 = pop();
push(t2);
push(t1);
push(t2);
push(t1);
break;
case Opcodes.DUP2_X1:
t1 = pop();
t2 = pop();
t3 = pop();
push(t2);
push(t1);
push(t3);
push(t2);
push(t1);
break;
case Opcodes.DUP2_X2:
t1 = pop();
t2 = pop();
t3 = pop();
t4 = pop();
push(t2);
push(t1);
push(t4);
push(t3);
push(t2);
push(t1);
break;
case Opcodes.SWAP:
t1 = pop();
t2 = pop();
push(t1);
push(t2);
break;
case Opcodes.IADD:
case Opcodes.ISUB:
case Opcodes.IMUL:
case Opcodes.IDIV:
case Opcodes.IREM:
case Opcodes.IAND:
case Opcodes.IOR:
case Opcodes.IXOR:
case Opcodes.ISHL:
case Opcodes.ISHR:
case Opcodes.IUSHR:
case Opcodes.L2I:
case Opcodes.D2I:
case Opcodes.FCMPL:
case Opcodes.FCMPG:
pop(2);
push(Opcodes.INTEGER);
break;
case Opcodes.LADD:
case Opcodes.LSUB:
case Opcodes.LMUL:
case Opcodes.LDIV:
case Opcodes.LREM:
case Opcodes.LAND:
case Opcodes.LOR:
case Opcodes.LXOR:
pop(4);
push(Opcodes.LONG);
push(Opcodes.TOP);
break;
case Opcodes.FADD:
case Opcodes.FSUB:
case Opcodes.FMUL:
case Opcodes.FDIV:
case Opcodes.FREM:
case Opcodes.L2F:
case Opcodes.D2F:
pop(2);
push(Opcodes.FLOAT);
break;
case Opcodes.DADD:
case Opcodes.DSUB:
case Opcodes.DMUL:
case Opcodes.DDIV:
case Opcodes.DREM:
pop(4);
push(Opcodes.DOUBLE);
push(Opcodes.TOP);
break;
case Opcodes.LSHL:
case Opcodes.LSHR:
case Opcodes.LUSHR:
pop(3);
push(Opcodes.LONG);
push(Opcodes.TOP);
break;
case Opcodes.IINC:
set(iarg, Opcodes.INTEGER);
break;
case Opcodes.I2L:
case Opcodes.F2L:
pop(1);
push(Opcodes.LONG);
push(Opcodes.TOP);
break;
case Opcodes.I2F:
pop(1);
push(Opcodes.FLOAT);
break;
case Opcodes.I2D:
case Opcodes.F2D:
pop(1);
push(Opcodes.DOUBLE);
push(Opcodes.TOP);
break;
case Opcodes.F2I:
case Opcodes.ARRAYLENGTH:
case Opcodes.INSTANCEOF:
pop(1);
push(Opcodes.INTEGER);
break;
case Opcodes.LCMP:
case Opcodes.DCMPL:
case Opcodes.DCMPG:
pop(4);
push(Opcodes.INTEGER);
break;
case Opcodes.JSR:
case Opcodes.RET:
throw new RuntimeException("JSR/RET are not supported");
case Opcodes.GETSTATIC:
pushDesc(sarg);
break;
case Opcodes.PUTSTATIC:
pop(sarg);
break;
case Opcodes.GETFIELD:
pop(1);
pushDesc(sarg);
break;
case Opcodes.PUTFIELD:
pop(sarg);
pop();
break;
case Opcodes.NEW:
push(labels.get(0));
break;
case Opcodes.NEWARRAY:
pop();
switch (iarg) {
case Opcodes.T_BOOLEAN:
pushDesc("[Z");
break;
case Opcodes.T_CHAR:
pushDesc("[C");
break;
case Opcodes.T_BYTE:
pushDesc("[B");
break;
case Opcodes.T_SHORT:
pushDesc("[S");
break;
case Opcodes.T_INT:
pushDesc("[I");
break;
case Opcodes.T_FLOAT:
pushDesc("[F");
break;
case Opcodes.T_DOUBLE:
pushDesc("[D");
break;
// case Opcodes.T_LONG:
default:
pushDesc("[J");
break;
}
break;
case Opcodes.ANEWARRAY:
pop();
pushDesc("[" + Type.getObjectType(sarg));
break;
case Opcodes.CHECKCAST:
pop();
pushDesc(Type.getObjectType(sarg).getDescriptor());
break;
// case Opcodes.MULTIANEWARRAY:
default:
pop(iarg);
pushDesc(sarg);
break;
}
labels = null;
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 6397
Content-Disposition: inline; filename="CodeSizeEvaluator.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "ddeb26db2af29ebc167eaa5c834a0de2342b4e43"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link MethodVisitor} that can be used to approximate method size.
*
* @author Eugene Kuleshov
*/
public class CodeSizeEvaluator extends MethodVisitor implements Opcodes {
private int minSize;
private int maxSize;
public CodeSizeEvaluator(final MethodVisitor mv) {
this(Opcodes.ASM4, mv);
}
protected CodeSizeEvaluator(final int api, final MethodVisitor mv) {
super(api, mv);
}
public int getMinSize() {
return this.minSize;
}
public int getMaxSize() {
return this.maxSize;
}
@Override
public void visitInsn(final int opcode) {
minSize += 1;
maxSize += 1;
if (mv != null) {
mv.visitInsn(opcode);
}
}
@Override
public void visitIntInsn(final int opcode, final int operand) {
if (opcode == SIPUSH) {
minSize += 3;
maxSize += 3;
} else {
minSize += 2;
maxSize += 2;
}
if (mv != null) {
mv.visitIntInsn(opcode, operand);
}
}
@Override
public void visitVarInsn(final int opcode, final int var) {
if (var < 4 && opcode != RET) {
minSize += 1;
maxSize += 1;
} else if (var >= 256) {
minSize += 4;
maxSize += 4;
} else {
minSize += 2;
maxSize += 2;
}
if (mv != null) {
mv.visitVarInsn(opcode, var);
}
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
minSize += 3;
maxSize += 3;
if (mv != null) {
mv.visitTypeInsn(opcode, type);
}
}
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
minSize += 3;
maxSize += 3;
if (mv != null) {
mv.visitFieldInsn(opcode, owner, name, desc);
}
}
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
if (opcode == INVOKEINTERFACE) {
minSize += 5;
maxSize += 5;
} else {
minSize += 3;
maxSize += 3;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc);
}
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
minSize += 5;
maxSize += 5;
if (mv != null) {
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
minSize += 3;
if (opcode == GOTO || opcode == JSR) {
maxSize += 5;
} else {
maxSize += 8;
}
if (mv != null) {
mv.visitJumpInsn(opcode, label);
}
}
@Override
public void visitLdcInsn(final Object cst) {
if (cst instanceof Long || cst instanceof Double) {
minSize += 3;
maxSize += 3;
} else {
minSize += 2;
maxSize += 3;
}
if (mv != null) {
mv.visitLdcInsn(cst);
}
}
@Override
public void visitIincInsn(final int var, final int increment) {
if (var > 255 || increment > 127 || increment < -128) {
minSize += 6;
maxSize += 6;
} else {
minSize += 3;
maxSize += 3;
}
if (mv != null) {
mv.visitIincInsn(var, increment);
}
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
minSize += 13 + labels.length * 4;
maxSize += 16 + labels.length * 4;
if (mv != null) {
mv.visitTableSwitchInsn(min, max, dflt, labels);
}
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
minSize += 9 + keys.length * 8;
maxSize += 12 + keys.length * 8;
if (mv != null) {
mv.visitLookupSwitchInsn(dflt, keys, labels);
}
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
minSize += 4;
maxSize += 4;
if (mv != null) {
mv.visitMultiANewArrayInsn(desc, dims);
}
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 50570
Content-Disposition: inline; filename="GeneratorAdapter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "ea2a09743f537857e4d7d33848851db03f50bdd7"
/***
* 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 org.objectweb.asm.commons;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* A {@link org.objectweb.asm.MethodVisitor} with convenient methods to generate
* code. For example, using this adapter, the class below
*
*
* public class Example {
* public static void main(String[] args) {
* System.out.println("Hello world!");
* }
* }
*
*
* can be generated as follows:
*
*
* 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();
*
*
* @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 localTypes = new ArrayList();
/**
* Creates a new {@link GeneratorAdapter}. Subclasses must not use this
* constructor . 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}).
*/
public GeneratorAdapter(final MethodVisitor mv, final int access,
final String name, final String desc) {
this(Opcodes.ASM4, mv, access, name, desc);
}
/**
* Creates a new {@link GeneratorAdapter}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @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}. Subclasses must not use this
* constructor . 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}. Subclasses must not use this
* constructor . 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 null ).
* @param exceptions
* the exceptions thrown by the adapted method (may be
* null ).
* @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(new Integer(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(new Long(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(new Float(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(new Double(value));
}
}
/**
* Generates the instruction to push the given value on the stack.
*
* @param value
* the value to be pushed on the stack. May be null .
*/
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("", 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 true .
*/
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 true .
*/
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 true .
*/
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 true .
*/
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 true .
*/
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 true .
*/
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
* true to use a TABLESWITCH instruction, or
* false 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) {
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor()
: type.getInternalName();
mv.visitMethodInsn(opcode, owner, method.getName(),
method.getDescriptor());
}
/**
* 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);
}
/**
* 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);
}
/**
* 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);
}
/**
* 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);
}
/**
* 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 (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) {
if (exception == null) {
mv.visitTryCatchBlock(start, end, mark(), null);
} else {
mv.visitTryCatchBlock(start, end, mark(),
exception.getInternalName());
}
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 32497
Content-Disposition: inline; filename="InstructionAdapter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "82469ba030a9a6cc6ba4bde392b1f3183e353a0e"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* A {@link MethodVisitor} providing a more detailed API to generate and
* transform instructions.
*
* @author Eric Bruneton
*/
public class InstructionAdapter extends MethodVisitor {
public final static Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;");
/**
* Creates a new {@link InstructionAdapter}. Subclasses must not use this
* constructor . Instead, they must use the
* {@link #InstructionAdapter(int, MethodVisitor)} version.
*
* @param mv
* the method visitor to which this adapter delegates calls.
*/
public InstructionAdapter(final MethodVisitor mv) {
this(Opcodes.ASM4, mv);
}
/**
* Creates a new {@link InstructionAdapter}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @param mv
* the method visitor to which this adapter delegates calls.
*/
protected InstructionAdapter(final int api, final MethodVisitor mv) {
super(api, mv);
}
@Override
public void visitInsn(final int opcode) {
switch (opcode) {
case Opcodes.NOP:
nop();
break;
case Opcodes.ACONST_NULL:
aconst(null);
break;
case Opcodes.ICONST_M1:
case Opcodes.ICONST_0:
case Opcodes.ICONST_1:
case Opcodes.ICONST_2:
case Opcodes.ICONST_3:
case Opcodes.ICONST_4:
case Opcodes.ICONST_5:
iconst(opcode - Opcodes.ICONST_0);
break;
case Opcodes.LCONST_0:
case Opcodes.LCONST_1:
lconst(opcode - Opcodes.LCONST_0);
break;
case Opcodes.FCONST_0:
case Opcodes.FCONST_1:
case Opcodes.FCONST_2:
fconst(opcode - Opcodes.FCONST_0);
break;
case Opcodes.DCONST_0:
case Opcodes.DCONST_1:
dconst(opcode - Opcodes.DCONST_0);
break;
case Opcodes.IALOAD:
aload(Type.INT_TYPE);
break;
case Opcodes.LALOAD:
aload(Type.LONG_TYPE);
break;
case Opcodes.FALOAD:
aload(Type.FLOAT_TYPE);
break;
case Opcodes.DALOAD:
aload(Type.DOUBLE_TYPE);
break;
case Opcodes.AALOAD:
aload(OBJECT_TYPE);
break;
case Opcodes.BALOAD:
aload(Type.BYTE_TYPE);
break;
case Opcodes.CALOAD:
aload(Type.CHAR_TYPE);
break;
case Opcodes.SALOAD:
aload(Type.SHORT_TYPE);
break;
case Opcodes.IASTORE:
astore(Type.INT_TYPE);
break;
case Opcodes.LASTORE:
astore(Type.LONG_TYPE);
break;
case Opcodes.FASTORE:
astore(Type.FLOAT_TYPE);
break;
case Opcodes.DASTORE:
astore(Type.DOUBLE_TYPE);
break;
case Opcodes.AASTORE:
astore(OBJECT_TYPE);
break;
case Opcodes.BASTORE:
astore(Type.BYTE_TYPE);
break;
case Opcodes.CASTORE:
astore(Type.CHAR_TYPE);
break;
case Opcodes.SASTORE:
astore(Type.SHORT_TYPE);
break;
case Opcodes.POP:
pop();
break;
case Opcodes.POP2:
pop2();
break;
case Opcodes.DUP:
dup();
break;
case Opcodes.DUP_X1:
dupX1();
break;
case Opcodes.DUP_X2:
dupX2();
break;
case Opcodes.DUP2:
dup2();
break;
case Opcodes.DUP2_X1:
dup2X1();
break;
case Opcodes.DUP2_X2:
dup2X2();
break;
case Opcodes.SWAP:
swap();
break;
case Opcodes.IADD:
add(Type.INT_TYPE);
break;
case Opcodes.LADD:
add(Type.LONG_TYPE);
break;
case Opcodes.FADD:
add(Type.FLOAT_TYPE);
break;
case Opcodes.DADD:
add(Type.DOUBLE_TYPE);
break;
case Opcodes.ISUB:
sub(Type.INT_TYPE);
break;
case Opcodes.LSUB:
sub(Type.LONG_TYPE);
break;
case Opcodes.FSUB:
sub(Type.FLOAT_TYPE);
break;
case Opcodes.DSUB:
sub(Type.DOUBLE_TYPE);
break;
case Opcodes.IMUL:
mul(Type.INT_TYPE);
break;
case Opcodes.LMUL:
mul(Type.LONG_TYPE);
break;
case Opcodes.FMUL:
mul(Type.FLOAT_TYPE);
break;
case Opcodes.DMUL:
mul(Type.DOUBLE_TYPE);
break;
case Opcodes.IDIV:
div(Type.INT_TYPE);
break;
case Opcodes.LDIV:
div(Type.LONG_TYPE);
break;
case Opcodes.FDIV:
div(Type.FLOAT_TYPE);
break;
case Opcodes.DDIV:
div(Type.DOUBLE_TYPE);
break;
case Opcodes.IREM:
rem(Type.INT_TYPE);
break;
case Opcodes.LREM:
rem(Type.LONG_TYPE);
break;
case Opcodes.FREM:
rem(Type.FLOAT_TYPE);
break;
case Opcodes.DREM:
rem(Type.DOUBLE_TYPE);
break;
case Opcodes.INEG:
neg(Type.INT_TYPE);
break;
case Opcodes.LNEG:
neg(Type.LONG_TYPE);
break;
case Opcodes.FNEG:
neg(Type.FLOAT_TYPE);
break;
case Opcodes.DNEG:
neg(Type.DOUBLE_TYPE);
break;
case Opcodes.ISHL:
shl(Type.INT_TYPE);
break;
case Opcodes.LSHL:
shl(Type.LONG_TYPE);
break;
case Opcodes.ISHR:
shr(Type.INT_TYPE);
break;
case Opcodes.LSHR:
shr(Type.LONG_TYPE);
break;
case Opcodes.IUSHR:
ushr(Type.INT_TYPE);
break;
case Opcodes.LUSHR:
ushr(Type.LONG_TYPE);
break;
case Opcodes.IAND:
and(Type.INT_TYPE);
break;
case Opcodes.LAND:
and(Type.LONG_TYPE);
break;
case Opcodes.IOR:
or(Type.INT_TYPE);
break;
case Opcodes.LOR:
or(Type.LONG_TYPE);
break;
case Opcodes.IXOR:
xor(Type.INT_TYPE);
break;
case Opcodes.LXOR:
xor(Type.LONG_TYPE);
break;
case Opcodes.I2L:
cast(Type.INT_TYPE, Type.LONG_TYPE);
break;
case Opcodes.I2F:
cast(Type.INT_TYPE, Type.FLOAT_TYPE);
break;
case Opcodes.I2D:
cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
break;
case Opcodes.L2I:
cast(Type.LONG_TYPE, Type.INT_TYPE);
break;
case Opcodes.L2F:
cast(Type.LONG_TYPE, Type.FLOAT_TYPE);
break;
case Opcodes.L2D:
cast(Type.LONG_TYPE, Type.DOUBLE_TYPE);
break;
case Opcodes.F2I:
cast(Type.FLOAT_TYPE, Type.INT_TYPE);
break;
case Opcodes.F2L:
cast(Type.FLOAT_TYPE, Type.LONG_TYPE);
break;
case Opcodes.F2D:
cast(Type.FLOAT_TYPE, Type.DOUBLE_TYPE);
break;
case Opcodes.D2I:
cast(Type.DOUBLE_TYPE, Type.INT_TYPE);
break;
case Opcodes.D2L:
cast(Type.DOUBLE_TYPE, Type.LONG_TYPE);
break;
case Opcodes.D2F:
cast(Type.DOUBLE_TYPE, Type.FLOAT_TYPE);
break;
case Opcodes.I2B:
cast(Type.INT_TYPE, Type.BYTE_TYPE);
break;
case Opcodes.I2C:
cast(Type.INT_TYPE, Type.CHAR_TYPE);
break;
case Opcodes.I2S:
cast(Type.INT_TYPE, Type.SHORT_TYPE);
break;
case Opcodes.LCMP:
lcmp();
break;
case Opcodes.FCMPL:
cmpl(Type.FLOAT_TYPE);
break;
case Opcodes.FCMPG:
cmpg(Type.FLOAT_TYPE);
break;
case Opcodes.DCMPL:
cmpl(Type.DOUBLE_TYPE);
break;
case Opcodes.DCMPG:
cmpg(Type.DOUBLE_TYPE);
break;
case Opcodes.IRETURN:
areturn(Type.INT_TYPE);
break;
case Opcodes.LRETURN:
areturn(Type.LONG_TYPE);
break;
case Opcodes.FRETURN:
areturn(Type.FLOAT_TYPE);
break;
case Opcodes.DRETURN:
areturn(Type.DOUBLE_TYPE);
break;
case Opcodes.ARETURN:
areturn(OBJECT_TYPE);
break;
case Opcodes.RETURN:
areturn(Type.VOID_TYPE);
break;
case Opcodes.ARRAYLENGTH:
arraylength();
break;
case Opcodes.ATHROW:
athrow();
break;
case Opcodes.MONITORENTER:
monitorenter();
break;
case Opcodes.MONITOREXIT:
monitorexit();
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public void visitIntInsn(final int opcode, final int operand) {
switch (opcode) {
case Opcodes.BIPUSH:
iconst(operand);
break;
case Opcodes.SIPUSH:
iconst(operand);
break;
case Opcodes.NEWARRAY:
switch (operand) {
case Opcodes.T_BOOLEAN:
newarray(Type.BOOLEAN_TYPE);
break;
case Opcodes.T_CHAR:
newarray(Type.CHAR_TYPE);
break;
case Opcodes.T_BYTE:
newarray(Type.BYTE_TYPE);
break;
case Opcodes.T_SHORT:
newarray(Type.SHORT_TYPE);
break;
case Opcodes.T_INT:
newarray(Type.INT_TYPE);
break;
case Opcodes.T_FLOAT:
newarray(Type.FLOAT_TYPE);
break;
case Opcodes.T_LONG:
newarray(Type.LONG_TYPE);
break;
case Opcodes.T_DOUBLE:
newarray(Type.DOUBLE_TYPE);
break;
default:
throw new IllegalArgumentException();
}
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public void visitVarInsn(final int opcode, final int var) {
switch (opcode) {
case Opcodes.ILOAD:
load(var, Type.INT_TYPE);
break;
case Opcodes.LLOAD:
load(var, Type.LONG_TYPE);
break;
case Opcodes.FLOAD:
load(var, Type.FLOAT_TYPE);
break;
case Opcodes.DLOAD:
load(var, Type.DOUBLE_TYPE);
break;
case Opcodes.ALOAD:
load(var, OBJECT_TYPE);
break;
case Opcodes.ISTORE:
store(var, Type.INT_TYPE);
break;
case Opcodes.LSTORE:
store(var, Type.LONG_TYPE);
break;
case Opcodes.FSTORE:
store(var, Type.FLOAT_TYPE);
break;
case Opcodes.DSTORE:
store(var, Type.DOUBLE_TYPE);
break;
case Opcodes.ASTORE:
store(var, OBJECT_TYPE);
break;
case Opcodes.RET:
ret(var);
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
Type t = Type.getObjectType(type);
switch (opcode) {
case Opcodes.NEW:
anew(t);
break;
case Opcodes.ANEWARRAY:
newarray(t);
break;
case Opcodes.CHECKCAST:
checkcast(t);
break;
case Opcodes.INSTANCEOF:
instanceOf(t);
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
switch (opcode) {
case Opcodes.GETSTATIC:
getstatic(owner, name, desc);
break;
case Opcodes.PUTSTATIC:
putstatic(owner, name, desc);
break;
case Opcodes.GETFIELD:
getfield(owner, name, desc);
break;
case Opcodes.PUTFIELD:
putfield(owner, name, desc);
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
switch (opcode) {
case Opcodes.INVOKESPECIAL:
invokespecial(owner, name, desc);
break;
case Opcodes.INVOKEVIRTUAL:
invokevirtual(owner, name, desc);
break;
case Opcodes.INVOKESTATIC:
invokestatic(owner, name, desc);
break;
case Opcodes.INVOKEINTERFACE:
invokeinterface(owner, name, desc);
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
invokedynamic(name, desc, bsm, bsmArgs);
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
switch (opcode) {
case Opcodes.IFEQ:
ifeq(label);
break;
case Opcodes.IFNE:
ifne(label);
break;
case Opcodes.IFLT:
iflt(label);
break;
case Opcodes.IFGE:
ifge(label);
break;
case Opcodes.IFGT:
ifgt(label);
break;
case Opcodes.IFLE:
ifle(label);
break;
case Opcodes.IF_ICMPEQ:
ificmpeq(label);
break;
case Opcodes.IF_ICMPNE:
ificmpne(label);
break;
case Opcodes.IF_ICMPLT:
ificmplt(label);
break;
case Opcodes.IF_ICMPGE:
ificmpge(label);
break;
case Opcodes.IF_ICMPGT:
ificmpgt(label);
break;
case Opcodes.IF_ICMPLE:
ificmple(label);
break;
case Opcodes.IF_ACMPEQ:
ifacmpeq(label);
break;
case Opcodes.IF_ACMPNE:
ifacmpne(label);
break;
case Opcodes.GOTO:
goTo(label);
break;
case Opcodes.JSR:
jsr(label);
break;
case Opcodes.IFNULL:
ifnull(label);
break;
case Opcodes.IFNONNULL:
ifnonnull(label);
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public void visitLabel(final Label label) {
mark(label);
}
@Override
public void visitLdcInsn(final Object cst) {
if (cst instanceof Integer) {
int val = ((Integer) cst).intValue();
iconst(val);
} else if (cst instanceof Byte) {
int val = ((Byte) cst).intValue();
iconst(val);
} else if (cst instanceof Character) {
int val = ((Character) cst).charValue();
iconst(val);
} else if (cst instanceof Short) {
int val = ((Short) cst).intValue();
iconst(val);
} else if (cst instanceof Boolean) {
int val = ((Boolean) cst).booleanValue() ? 1 : 0;
iconst(val);
} else if (cst instanceof Float) {
float val = ((Float) cst).floatValue();
fconst(val);
} else if (cst instanceof Long) {
long val = ((Long) cst).longValue();
lconst(val);
} else if (cst instanceof Double) {
double val = ((Double) cst).doubleValue();
dconst(val);
} else if (cst instanceof String) {
aconst(cst);
} else if (cst instanceof Type) {
tconst((Type) cst);
} else if (cst instanceof Handle) {
hconst((Handle) cst);
} else {
throw new IllegalArgumentException();
}
}
@Override
public void visitIincInsn(final int var, final int increment) {
iinc(var, increment);
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
tableswitch(min, max, dflt, labels);
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
lookupswitch(dflt, keys, labels);
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
multianewarray(desc, dims);
}
// -----------------------------------------------------------------------
public void nop() {
mv.visitInsn(Opcodes.NOP);
}
public void aconst(final Object cst) {
if (cst == null) {
mv.visitInsn(Opcodes.ACONST_NULL);
} else {
mv.visitLdcInsn(cst);
}
}
public void iconst(final int cst) {
if (cst >= -1 && cst <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + cst);
} else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, cst);
} else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, cst);
} else {
mv.visitLdcInsn(new Integer(cst));
}
}
public void lconst(final long cst) {
if (cst == 0L || cst == 1L) {
mv.visitInsn(Opcodes.LCONST_0 + (int) cst);
} else {
mv.visitLdcInsn(new Long(cst));
}
}
public void fconst(final float cst) {
int bits = Float.floatToIntBits(cst);
if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
mv.visitInsn(Opcodes.FCONST_0 + (int) cst);
} else {
mv.visitLdcInsn(new Float(cst));
}
}
public void dconst(final double cst) {
long bits = Double.doubleToLongBits(cst);
if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
mv.visitInsn(Opcodes.DCONST_0 + (int) cst);
} else {
mv.visitLdcInsn(new Double(cst));
}
}
public void tconst(final Type type) {
mv.visitLdcInsn(type);
}
public void hconst(final Handle handle) {
mv.visitLdcInsn(handle);
}
public void load(final int var, final Type type) {
mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), var);
}
public void aload(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
}
public void store(final int var, final Type type) {
mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), var);
}
public void astore(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
}
public void pop() {
mv.visitInsn(Opcodes.POP);
}
public void pop2() {
mv.visitInsn(Opcodes.POP2);
}
public void dup() {
mv.visitInsn(Opcodes.DUP);
}
public void dup2() {
mv.visitInsn(Opcodes.DUP2);
}
public void dupX1() {
mv.visitInsn(Opcodes.DUP_X1);
}
public void dupX2() {
mv.visitInsn(Opcodes.DUP_X2);
}
public void dup2X1() {
mv.visitInsn(Opcodes.DUP2_X1);
}
public void dup2X2() {
mv.visitInsn(Opcodes.DUP2_X2);
}
public void swap() {
mv.visitInsn(Opcodes.SWAP);
}
public void add(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IADD));
}
public void sub(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.ISUB));
}
public void mul(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IMUL));
}
public void div(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IDIV));
}
public void rem(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IREM));
}
public void neg(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.INEG));
}
public void shl(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.ISHL));
}
public void shr(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.ISHR));
}
public void ushr(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IUSHR));
}
public void and(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IAND));
}
public void or(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IOR));
}
public void xor(final Type type) {
mv.visitInsn(type.getOpcode(Opcodes.IXOR));
}
public void iinc(final int var, final int increment) {
mv.visitIincInsn(var, increment);
}
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);
}
}
}
}
public void lcmp() {
mv.visitInsn(Opcodes.LCMP);
}
public void cmpl(final Type type) {
mv.visitInsn(type == Type.FLOAT_TYPE ? Opcodes.FCMPL : Opcodes.DCMPL);
}
public void cmpg(final Type type) {
mv.visitInsn(type == Type.FLOAT_TYPE ? Opcodes.FCMPG : Opcodes.DCMPG);
}
public void ifeq(final Label label) {
mv.visitJumpInsn(Opcodes.IFEQ, label);
}
public void ifne(final Label label) {
mv.visitJumpInsn(Opcodes.IFNE, label);
}
public void iflt(final Label label) {
mv.visitJumpInsn(Opcodes.IFLT, label);
}
public void ifge(final Label label) {
mv.visitJumpInsn(Opcodes.IFGE, label);
}
public void ifgt(final Label label) {
mv.visitJumpInsn(Opcodes.IFGT, label);
}
public void ifle(final Label label) {
mv.visitJumpInsn(Opcodes.IFLE, label);
}
public void ificmpeq(final Label label) {
mv.visitJumpInsn(Opcodes.IF_ICMPEQ, label);
}
public void ificmpne(final Label label) {
mv.visitJumpInsn(Opcodes.IF_ICMPNE, label);
}
public void ificmplt(final Label label) {
mv.visitJumpInsn(Opcodes.IF_ICMPLT, label);
}
public void ificmpge(final Label label) {
mv.visitJumpInsn(Opcodes.IF_ICMPGE, label);
}
public void ificmpgt(final Label label) {
mv.visitJumpInsn(Opcodes.IF_ICMPGT, label);
}
public void ificmple(final Label label) {
mv.visitJumpInsn(Opcodes.IF_ICMPLE, label);
}
public void ifacmpeq(final Label label) {
mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
}
public void ifacmpne(final Label label) {
mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
}
public void goTo(final Label label) {
mv.visitJumpInsn(Opcodes.GOTO, label);
}
public void jsr(final Label label) {
mv.visitJumpInsn(Opcodes.JSR, label);
}
public void ret(final int var) {
mv.visitVarInsn(Opcodes.RET, var);
}
public void tableswitch(final int min, final int max, final Label dflt,
final Label... labels) {
mv.visitTableSwitchInsn(min, max, dflt, labels);
}
public void lookupswitch(final Label dflt, final int[] keys,
final Label[] labels) {
mv.visitLookupSwitchInsn(dflt, keys, labels);
}
public void areturn(final Type t) {
mv.visitInsn(t.getOpcode(Opcodes.IRETURN));
}
public void getstatic(final String owner, final String name,
final String desc) {
mv.visitFieldInsn(Opcodes.GETSTATIC, owner, name, desc);
}
public void putstatic(final String owner, final String name,
final String desc) {
mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, name, desc);
}
public void getfield(final String owner, final String name,
final String desc) {
mv.visitFieldInsn(Opcodes.GETFIELD, owner, name, desc);
}
public void putfield(final String owner, final String name,
final String desc) {
mv.visitFieldInsn(Opcodes.PUTFIELD, owner, name, desc);
}
public void invokevirtual(final String owner, final String name,
final String desc) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc);
}
public void invokespecial(final String owner, final String name,
final String desc) {
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc);
}
public void invokestatic(final String owner, final String name,
final String desc) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc);
}
public void invokeinterface(final String owner, final String name,
final String desc) {
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc);
}
public void invokedynamic(String name, String desc, Handle bsm,
Object[] bsmArgs) {
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
public void anew(final Type type) {
mv.visitTypeInsn(Opcodes.NEW, type.getInternalName());
}
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:
mv.visitTypeInsn(Opcodes.ANEWARRAY, type.getInternalName());
return;
}
mv.visitIntInsn(Opcodes.NEWARRAY, typ);
}
public void arraylength() {
mv.visitInsn(Opcodes.ARRAYLENGTH);
}
public void athrow() {
mv.visitInsn(Opcodes.ATHROW);
}
public void checkcast(final Type type) {
mv.visitTypeInsn(Opcodes.CHECKCAST, type.getInternalName());
}
public void instanceOf(final Type type) {
mv.visitTypeInsn(Opcodes.INSTANCEOF, type.getInternalName());
}
public void monitorenter() {
mv.visitInsn(Opcodes.MONITORENTER);
}
public void monitorexit() {
mv.visitInsn(Opcodes.MONITOREXIT);
}
public void multianewarray(final String desc, final int dims) {
mv.visitMultiANewArrayInsn(desc, dims);
}
public void ifnull(final Label label) {
mv.visitJumpInsn(Opcodes.IFNULL, label);
}
public void ifnonnull(final Label label) {
mv.visitJumpInsn(Opcodes.IFNONNULL, label);
}
public void mark(final Label label) {
mv.visitLabel(label);
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 30549
Content-Disposition: inline; filename="JSRInlinerAdapter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "8fd5d45590642b215e3f0d56da97e7e1c5625184"
/***
* 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 org.objectweb.asm.commons;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
/**
* A {@link org.objectweb.asm.MethodVisitor} that removes JSR instructions and
* inlines the referenced subroutines.
*
* Explanation of how it works TODO
*
* @author Niko Matsakis
*/
public class JSRInlinerAdapter extends MethodNode implements Opcodes {
private static final boolean LOGGING = false;
/**
* For each label that is jumped to by a JSR, we create a BitSet instance.
*/
private final Map subroutineHeads = new HashMap();
/**
* This subroutine instance denotes the line of execution that is not
* contained within any subroutine; i.e., the "subroutine" that is executing
* when a method first begins.
*/
private final BitSet mainSubroutine = new BitSet();
/**
* This BitSet contains the index of every instruction that belongs to more
* than one subroutine. This should not happen often.
*/
final BitSet dualCitizens = new BitSet();
/**
* Creates a new JSRInliner. Subclasses must not use this
* constructor . Instead, they must use the
* {@link #JSRInlinerAdapter(int, MethodVisitor, int, String, String, String, String[])}
* version.
*
* @param mv
* the MethodVisitor
to send the resulting inlined
* method code to (use null
for none).
* @param access
* the method's access flags (see {@link Opcodes}). This
* parameter also indicates if the method is synthetic and/or
* deprecated.
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type}).
* @param signature
* the method's signature. May be null .
* @param exceptions
* the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be
* null .
*/
public JSRInlinerAdapter(final MethodVisitor mv, final int access,
final String name, final String desc, final String signature,
final String[] exceptions) {
this(Opcodes.ASM4, mv, access, name, desc, signature, exceptions);
}
/**
* Creates a new JSRInliner.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @param mv
* the MethodVisitor
to send the resulting inlined
* method code to (use null
for none).
* @param access
* the method's access flags (see {@link Opcodes}). This
* parameter also indicates if the method is synthetic and/or
* deprecated.
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type}).
* @param signature
* the method's signature. May be null .
* @param exceptions
* the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be
* null .
*/
protected JSRInlinerAdapter(final int api, final MethodVisitor mv,
final int access, final String name, final String desc,
final String signature, final String[] exceptions) {
super(api, access, name, desc, signature, exceptions);
this.mv = mv;
}
/**
* Detects a JSR instruction and sets a flag to indicate we will need to do
* inlining.
*/
@Override
public void visitJumpInsn(final int opcode, final Label lbl) {
super.visitJumpInsn(opcode, lbl);
LabelNode ln = ((JumpInsnNode) instructions.getLast()).label;
if (opcode == JSR && !subroutineHeads.containsKey(ln)) {
subroutineHeads.put(ln, new BitSet());
}
}
/**
* If any JSRs were seen, triggers the inlining process. Otherwise, forwards
* the byte codes untouched.
*/
@Override
public void visitEnd() {
if (!subroutineHeads.isEmpty()) {
markSubroutines();
if (LOGGING) {
log(mainSubroutine.toString());
Iterator it = subroutineHeads.values().iterator();
while (it.hasNext()) {
BitSet sub = it.next();
log(sub.toString());
}
}
emitCode();
}
// Forward the translate opcodes on if appropriate:
if (mv != null) {
accept(mv);
}
}
/**
* Walks the method and determines which internal subroutine(s), if any,
* each instruction is a method of.
*/
private void markSubroutines() {
BitSet anyvisited = new BitSet();
// First walk the main subroutine and find all those instructions which
// can be reached without invoking any JSR at all
markSubroutineWalk(mainSubroutine, 0, anyvisited);
// Go through the head of each subroutine and find any nodes reachable
// to that subroutine without following any JSR links.
for (Iterator> it = subroutineHeads
.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = it.next();
LabelNode lab = entry.getKey();
BitSet sub = entry.getValue();
int index = instructions.indexOf(lab);
markSubroutineWalk(sub, index, anyvisited);
}
}
/**
* Performs a depth first search walking the normal byte code path starting
* at index
, and adding each instruction encountered into the
* subroutine sub
. After this walk is complete, iterates over
* the exception handlers to ensure that we also include those byte codes
* which are reachable through an exception that may be thrown during the
* execution of the subroutine. Invoked from markSubroutines()
.
*
* @param sub
* the subroutine whose instructions must be computed.
* @param index
* an instruction of this subroutine.
* @param anyvisited
* indexes of the already visited instructions, i.e. marked as
* part of this subroutine or any previously computed subroutine.
*/
private void markSubroutineWalk(final BitSet sub, final int index,
final BitSet anyvisited) {
if (LOGGING) {
log("markSubroutineWalk: sub=" + sub + " index=" + index);
}
// First find those instructions reachable via normal execution
markSubroutineWalkDFS(sub, index, anyvisited);
// Now, make sure we also include any applicable exception handlers
boolean loop = true;
while (loop) {
loop = false;
for (Iterator it = tryCatchBlocks.iterator(); it
.hasNext();) {
TryCatchBlockNode trycatch = it.next();
if (LOGGING) {
// TODO use of default toString().
log("Scanning try/catch " + trycatch);
}
// If the handler has already been processed, skip it.
int handlerindex = instructions.indexOf(trycatch.handler);
if (sub.get(handlerindex)) {
continue;
}
int startindex = instructions.indexOf(trycatch.start);
int endindex = instructions.indexOf(trycatch.end);
int nextbit = sub.nextSetBit(startindex);
if (nextbit != -1 && nextbit < endindex) {
if (LOGGING) {
log("Adding exception handler: " + startindex + '-'
+ endindex + " due to " + nextbit + " handler "
+ handlerindex);
}
markSubroutineWalkDFS(sub, handlerindex, anyvisited);
loop = true;
}
}
}
}
/**
* Performs a simple DFS of the instructions, assigning each to the
* subroutine sub
. Starts from index
. Invoked only
* by markSubroutineWalk()
.
*
* @param sub
* the subroutine whose instructions must be computed.
* @param index
* an instruction of this subroutine.
* @param anyvisited
* indexes of the already visited instructions, i.e. marked as
* part of this subroutine or any previously computed subroutine.
*/
private void markSubroutineWalkDFS(final BitSet sub, int index,
final BitSet anyvisited) {
while (true) {
AbstractInsnNode node = instructions.get(index);
// don't visit a node twice
if (sub.get(index)) {
return;
}
sub.set(index);
// check for those nodes already visited by another subroutine
if (anyvisited.get(index)) {
dualCitizens.set(index);
if (LOGGING) {
log("Instruction #" + index + " is dual citizen.");
}
}
anyvisited.set(index);
if (node.getType() == AbstractInsnNode.JUMP_INSN
&& node.getOpcode() != JSR) {
// we do not follow recursively called subroutines here; but any
// other sort of branch we do follow
JumpInsnNode jnode = (JumpInsnNode) node;
int destidx = instructions.indexOf(jnode.label);
markSubroutineWalkDFS(sub, destidx, anyvisited);
}
if (node.getType() == AbstractInsnNode.TABLESWITCH_INSN) {
TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node;
int destidx = instructions.indexOf(tsnode.dflt);
markSubroutineWalkDFS(sub, destidx, anyvisited);
for (int i = tsnode.labels.size() - 1; i >= 0; --i) {
LabelNode l = tsnode.labels.get(i);
destidx = instructions.indexOf(l);
markSubroutineWalkDFS(sub, destidx, anyvisited);
}
}
if (node.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN) {
LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node;
int destidx = instructions.indexOf(lsnode.dflt);
markSubroutineWalkDFS(sub, destidx, anyvisited);
for (int i = lsnode.labels.size() - 1; i >= 0; --i) {
LabelNode l = lsnode.labels.get(i);
destidx = instructions.indexOf(l);
markSubroutineWalkDFS(sub, destidx, anyvisited);
}
}
// check to see if this opcode falls through to the next instruction
// or not; if not, return.
switch (instructions.get(index).getOpcode()) {
case GOTO:
case RET:
case TABLESWITCH:
case LOOKUPSWITCH:
case IRETURN:
case LRETURN:
case FRETURN:
case DRETURN:
case ARETURN:
case RETURN:
case ATHROW:
/*
* note: this either returns from this subroutine, or a parent
* subroutine which invoked it
*/
return;
}
// Use tail recursion here in the form of an outer while loop to
// avoid our stack growing needlessly:
index++;
}
}
/**
* Creates the new instructions, inlining each instantiation of each
* subroutine until the code is fully elaborated.
*/
private void emitCode() {
LinkedList worklist = new LinkedList();
// Create an instantiation of the "root" subroutine, which is just the
// main routine
worklist.add(new Instantiation(null, mainSubroutine));
// Emit instantiations of each subroutine we encounter, including the
// main subroutine
InsnList newInstructions = new InsnList();
List newTryCatchBlocks = new ArrayList();
List newLocalVariables = new ArrayList();
while (!worklist.isEmpty()) {
Instantiation inst = worklist.removeFirst();
emitSubroutine(inst, worklist, newInstructions, newTryCatchBlocks,
newLocalVariables);
}
instructions = newInstructions;
tryCatchBlocks = newTryCatchBlocks;
localVariables = newLocalVariables;
}
/**
* Emits one instantiation of one subroutine, specified by
* instant
. May add new instantiations that are invoked by this
* one to the worklist
parameter, and new try/catch blocks to
* newTryCatchBlocks
.
*
* @param instant
* the instantiation that must be performed.
* @param worklist
* list of the instantiations that remain to be done.
* @param newInstructions
* the instruction list to which the instantiated code must be
* appended.
* @param newTryCatchBlocks
* the exception handler list to which the instantiated handlers
* must be appended.
*/
private void emitSubroutine(final Instantiation instant,
final List worklist, final InsnList newInstructions,
final List newTryCatchBlocks,
final List newLocalVariables) {
LabelNode duplbl = null;
if (LOGGING) {
log("--------------------------------------------------------");
log("Emitting instantiation of subroutine " + instant.subroutine);
}
// Emit the relevant instructions for this instantiation, translating
// labels and jump targets as we go:
for (int i = 0, c = instructions.size(); i < c; i++) {
AbstractInsnNode insn = instructions.get(i);
Instantiation owner = instant.findOwner(i);
// Always remap labels:
if (insn.getType() == AbstractInsnNode.LABEL) {
// Translate labels into their renamed equivalents.
// Avoid adding the same label more than once. Note
// that because we own this instruction the gotoTable
// and the rangeTable will always agree.
LabelNode ilbl = (LabelNode) insn;
LabelNode remap = instant.rangeLabel(ilbl);
if (LOGGING) {
// TODO use of default toString().
log("Translating lbl #" + i + ':' + ilbl + " to " + remap);
}
if (remap != duplbl) {
newInstructions.add(remap);
duplbl = remap;
}
continue;
}
// We don't want to emit instructions that were already
// emitted by a subroutine higher on the stack. Note that
// it is still possible for a given instruction to be
// emitted twice because it may belong to two subroutines
// that do not invoke each other.
if (owner != instant) {
continue;
}
if (LOGGING) {
log("Emitting inst #" + i);
}
if (insn.getOpcode() == RET) {
// Translate RET instruction(s) to a jump to the return label
// for the appropriate instantiation. The problem is that the
// subroutine may "fall through" to the ret of a parent
// subroutine; therefore, to find the appropriate ret label we
// find the lowest subroutine on the stack that claims to own
// this instruction. See the class javadoc comment for an
// explanation on why this technique is safe (note: it is only
// safe if the input is verifiable).
LabelNode retlabel = null;
for (Instantiation p = instant; p != null; p = p.previous) {
if (p.subroutine.get(i)) {
retlabel = p.returnLabel;
}
}
if (retlabel == null) {
// This is only possible if the mainSubroutine owns a RET
// instruction, which should never happen for verifiable
// code.
throw new RuntimeException("Instruction #" + i
+ " is a RET not owned by any subroutine");
}
newInstructions.add(new JumpInsnNode(GOTO, retlabel));
} else if (insn.getOpcode() == JSR) {
LabelNode lbl = ((JumpInsnNode) insn).label;
BitSet sub = subroutineHeads.get(lbl);
Instantiation newinst = new Instantiation(instant, sub);
LabelNode startlbl = newinst.gotoLabel(lbl);
if (LOGGING) {
log(" Creating instantiation of subr " + sub);
}
// Rather than JSRing, we will jump to the inline version and
// push NULL for what was once the return value. This hack
// allows us to avoid doing any sort of data flow analysis to
// figure out which instructions manipulate the old return value
// pointer which is now known to be unneeded.
newInstructions.add(new InsnNode(ACONST_NULL));
newInstructions.add(new JumpInsnNode(GOTO, startlbl));
newInstructions.add(newinst.returnLabel);
// Insert this new instantiation into the queue to be emitted
// later.
worklist.add(newinst);
} else {
newInstructions.add(insn.clone(instant));
}
}
// Emit try/catch blocks that are relevant to this method.
for (Iterator it = tryCatchBlocks.iterator(); it
.hasNext();) {
TryCatchBlockNode trycatch = it.next();
if (LOGGING) {
// TODO use of default toString().
log("try catch block original labels=" + trycatch.start + '-'
+ trycatch.end + "->" + trycatch.handler);
}
final LabelNode start = instant.rangeLabel(trycatch.start);
final LabelNode end = instant.rangeLabel(trycatch.end);
// Ignore empty try/catch regions
if (start == end) {
if (LOGGING) {
log(" try catch block empty in this subroutine");
}
continue;
}
final LabelNode handler = instant.gotoLabel(trycatch.handler);
if (LOGGING) {
// TODO use of default toString().
log(" try catch block new labels=" + start + '-' + end + "->"
+ handler);
}
if (start == null || end == null || handler == null) {
throw new RuntimeException("Internal error!");
}
newTryCatchBlocks.add(new TryCatchBlockNode(start, end, handler,
trycatch.type));
}
for (Iterator it = localVariables.iterator(); it
.hasNext();) {
LocalVariableNode lvnode = it.next();
if (LOGGING) {
log("local var " + lvnode.name);
}
final LabelNode start = instant.rangeLabel(lvnode.start);
final LabelNode end = instant.rangeLabel(lvnode.end);
if (start == end) {
if (LOGGING) {
log(" local variable empty in this sub");
}
continue;
}
newLocalVariables.add(new LocalVariableNode(lvnode.name,
lvnode.desc, lvnode.signature, start, end, lvnode.index));
}
}
private static void log(final String str) {
System.err.println(str);
}
/**
* A class that represents an instantiation of a subroutine. Each
* instantiation has an associate "stack" --- which is a listing of those
* instantiations that were active when this particular instance of this
* subroutine was invoked. Each instantiation also has a map from the
* original labels of the program to the labels appropriate for this
* instantiation, and finally a label to return to.
*/
private class Instantiation extends AbstractMap {
/**
* Previous instantiations; the stack must be statically predictable to
* be inlinable.
*/
final Instantiation previous;
/**
* The subroutine this is an instantiation of.
*/
public final BitSet subroutine;
/**
* This table maps Labels from the original source to Labels pointing at
* code specific to this instantiation, for use in remapping try/catch
* blocks,as well as gotos.
*
* Note that in the presence of dual citizens instructions, that is,
* instructions which belong to more than one subroutine due to the
* merging of control flow without a RET instruction, we will map the
* target label of a GOTO to the label used by the instantiation lowest
* on the stack. This avoids code duplication during inlining in most
* cases.
*
* @see #findOwner(int)
*/
public final Map rangeTable = new HashMap();
/**
* All returns for this instantiation will be mapped to this label
*/
public final LabelNode returnLabel;
Instantiation(final Instantiation prev, final BitSet sub) {
previous = prev;
subroutine = sub;
for (Instantiation p = prev; p != null; p = p.previous) {
if (p.subroutine == sub) {
throw new RuntimeException("Recursive invocation of " + sub);
}
}
// Determine the label to return to when this subroutine terminates
// via RET: note that the main subroutine never terminates via RET.
if (prev != null) {
returnLabel = new LabelNode();
} else {
returnLabel = null;
}
// Each instantiation will remap the labels from the code above to
// refer to its particular copy of its own instructions. Note that
// we collapse labels which point at the same instruction into one:
// this is fairly common as we are often ignoring large chunks of
// instructions, so what were previously distinct labels become
// duplicates.
LabelNode duplbl = null;
for (int i = 0, c = instructions.size(); i < c; i++) {
AbstractInsnNode insn = instructions.get(i);
if (insn.getType() == AbstractInsnNode.LABEL) {
LabelNode ilbl = (LabelNode) insn;
if (duplbl == null) {
// if we already have a label pointing at this spot,
// don't recreate it.
duplbl = new LabelNode();
}
// Add an entry in the rangeTable for every label
// in the original code which points at the next
// instruction of our own to be emitted.
rangeTable.put(ilbl, duplbl);
} else if (findOwner(i) == this) {
// We will emit this instruction, so clear the 'duplbl' flag
// since the next Label will refer to a distinct
// instruction.
duplbl = null;
}
}
}
/**
* Returns the "owner" of a particular instruction relative to this
* instantiation: the owner referes to the Instantiation which will emit
* the version of this instruction that we will execute.
*
* Typically, the return value is either this
or
* null
. this
indicates that this
* instantiation will generate the version of this instruction that we
* will execute, and null
indicates that this instantiation
* never executes the given instruction.
*
* Sometimes, however, an instruction can belong to multiple
* subroutines; this is called a "dual citizen" instruction (though it
* may belong to more than 2 subroutines), and occurs when multiple
* subroutines branch to common points of control. In this case, the
* owner is the subroutine that appears lowest on the stack, and which
* also owns the instruction in question.
*
* @param i
* the index of the instruction in the original code
* @return the "owner" of a particular instruction relative to this
* instantiation.
*/
public Instantiation findOwner(final int i) {
if (!subroutine.get(i)) {
return null;
}
if (!dualCitizens.get(i)) {
return this;
}
Instantiation own = this;
for (Instantiation p = previous; p != null; p = p.previous) {
if (p.subroutine.get(i)) {
own = p;
}
}
return own;
}
/**
* Looks up the label l
in the gotoTable
, thus
* translating it from a Label in the original code, to a Label in the
* inlined code that is appropriate for use by an instruction that
* branched to the original label.
*
* @param l
* The label we will be translating
* @return a label for use by a branch instruction in the inlined code
* @see #rangeLabel
*/
public LabelNode gotoLabel(final LabelNode l) {
// owner should never be null, because owner is only null
// if an instruction cannot be reached from this subroutine
Instantiation owner = findOwner(instructions.indexOf(l));
return owner.rangeTable.get(l);
}
/**
* Looks up the label l
in the rangeTable
,
* thus translating it from a Label in the original code, to a Label in
* the inlined code that is appropriate for use by an try/catch or
* variable use annotation.
*
* @param l
* The label we will be translating
* @return a label for use by a try/catch or variable annotation in the
* original code
* @see #rangeTable
*/
public LabelNode rangeLabel(final LabelNode l) {
return rangeTable.get(l);
}
// AbstractMap implementation
@Override
public Set> entrySet() {
return null;
}
@Override
public LabelNode get(final Object o) {
return gotoLabel((LabelNode) o);
}
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 12783
Content-Disposition: inline; filename="LocalVariablesSorter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "e228d9e93ff6d01bab745e8e082093e724898a89"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* A {@link MethodVisitor} that renumbers local variables in their order of
* appearance. This adapter allows one to easily add new local variables to a
* method. It may be used by inheriting from this class, but the preferred way
* of using it is via delegation: the next visitor in the chain can indeed add
* new locals when needed by calling {@link #newLocal} on this adapter (this
* requires a reference back to this {@link LocalVariablesSorter}).
*
* @author Chris Nokleberg
* @author Eugene Kuleshov
* @author Eric Bruneton
*/
public class LocalVariablesSorter extends MethodVisitor {
private static final Type OBJECT_TYPE = Type
.getObjectType("java/lang/Object");
/**
* Mapping from old to new local variable indexes. A local variable at index
* i of size 1 is remapped to 'mapping[2*i]', while a local variable at
* index i of size 2 is remapped to 'mapping[2*i+1]'.
*/
private int[] mapping = new int[40];
/**
* Array used to store stack map local variable types after remapping.
*/
private Object[] newLocals = new Object[20];
/**
* Index of the first local variable, after formal parameters.
*/
protected final int firstLocal;
/**
* Index of the next local variable to be created by {@link #newLocal}.
*/
protected int nextLocal;
/**
* Indicates if at least one local variable has moved due to remapping.
*/
private boolean changed;
/**
* Creates a new {@link LocalVariablesSorter}. Subclasses must not use
* this constructor . Instead, they must use the
* {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} version.
*
* @param access
* access flags of the adapted method.
* @param desc
* the method's descriptor (see {@link Type Type}).
* @param mv
* the method visitor to which this adapter delegates calls.
*/
public LocalVariablesSorter(final int access, final String desc,
final MethodVisitor mv) {
this(Opcodes.ASM4, access, desc, mv);
}
/**
* Creates a new {@link LocalVariablesSorter}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @param access
* access flags of the adapted method.
* @param desc
* the method's descriptor (see {@link Type Type}).
* @param mv
* the method visitor to which this adapter delegates calls.
*/
protected LocalVariablesSorter(final int api, final int access,
final String desc, final MethodVisitor mv) {
super(api, mv);
Type[] args = Type.getArgumentTypes(desc);
nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
for (int i = 0; i < args.length; i++) {
nextLocal += args[i].getSize();
}
firstLocal = nextLocal;
}
@Override
public void visitVarInsn(final int opcode, final int var) {
Type type;
switch (opcode) {
case Opcodes.LLOAD:
case Opcodes.LSTORE:
type = Type.LONG_TYPE;
break;
case Opcodes.DLOAD:
case Opcodes.DSTORE:
type = Type.DOUBLE_TYPE;
break;
case Opcodes.FLOAD:
case Opcodes.FSTORE:
type = Type.FLOAT_TYPE;
break;
case Opcodes.ILOAD:
case Opcodes.ISTORE:
type = Type.INT_TYPE;
break;
default:
// case Opcodes.ALOAD:
// case Opcodes.ASTORE:
// case RET:
type = OBJECT_TYPE;
break;
}
mv.visitVarInsn(opcode, remap(var, type));
}
@Override
public void visitIincInsn(final int var, final int increment) {
mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
}
@Override
public void visitMaxs(final int maxStack, final int maxLocals) {
mv.visitMaxs(maxStack, nextLocal);
}
@Override
public void visitLocalVariable(final String name, final String desc,
final String signature, final Label start, final Label end,
final int index) {
int newIndex = remap(index, Type.getType(desc));
mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
}
@Override
public void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack) {
if (type != Opcodes.F_NEW) { // uncompressed frame
throw new IllegalStateException(
"ClassReader.accept() should be called with EXPAND_FRAMES flag");
}
if (!changed) { // optimization for the case where mapping = identity
mv.visitFrame(type, nLocal, local, nStack, stack);
return;
}
// creates a copy of newLocals
Object[] oldLocals = new Object[newLocals.length];
System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
updateNewLocals(newLocals);
// copies types from 'local' to 'newLocals'
// 'newLocals' already contains the variables added with 'newLocal'
int index = 0; // old local variable index
int number = 0; // old local variable number
for (; number < nLocal; ++number) {
Object t = local[number];
int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
if (t != Opcodes.TOP) {
Type typ = OBJECT_TYPE;
if (t == Opcodes.INTEGER) {
typ = Type.INT_TYPE;
} else if (t == Opcodes.FLOAT) {
typ = Type.FLOAT_TYPE;
} else if (t == Opcodes.LONG) {
typ = Type.LONG_TYPE;
} else if (t == Opcodes.DOUBLE) {
typ = Type.DOUBLE_TYPE;
} else if (t instanceof String) {
typ = Type.getObjectType((String) t);
}
setFrameLocal(remap(index, typ), t);
}
index += size;
}
// removes TOP after long and double types as well as trailing TOPs
index = 0;
number = 0;
for (int i = 0; index < newLocals.length; ++i) {
Object t = newLocals[index++];
if (t != null && t != Opcodes.TOP) {
newLocals[i] = t;
number = i + 1;
if (t == Opcodes.LONG || t == Opcodes.DOUBLE) {
index += 1;
}
} else {
newLocals[i] = Opcodes.TOP;
}
}
// visits remapped frame
mv.visitFrame(type, number, newLocals, nStack, stack);
// restores original value of 'newLocals'
newLocals = oldLocals;
}
// -------------
/**
* Creates a new local variable of the given type.
*
* @param type
* the type of the local variable to be created.
* @return the identifier of the newly created local variable.
*/
public int newLocal(final Type type) {
Object t;
switch (type.getSort()) {
case Type.BOOLEAN:
case Type.CHAR:
case Type.BYTE:
case Type.SHORT:
case Type.INT:
t = Opcodes.INTEGER;
break;
case Type.FLOAT:
t = Opcodes.FLOAT;
break;
case Type.LONG:
t = Opcodes.LONG;
break;
case Type.DOUBLE:
t = Opcodes.DOUBLE;
break;
case Type.ARRAY:
t = type.getDescriptor();
break;
// case Type.OBJECT:
default:
t = type.getInternalName();
break;
}
int local = newLocalMapping(type);
setLocalType(local, type);
setFrameLocal(local, t);
return local;
}
/**
* Notifies subclasses that a new stack map frame is being visited. The
* array argument contains the stack map frame types corresponding to the
* local variables added with {@link #newLocal}. This method can update
* these types in place for the stack map frame being visited. The default
* implementation of this method does nothing, i.e. a local variable added
* with {@link #newLocal} will have the same type in all stack map frames.
* But this behavior is not always the desired one, for instance if a local
* variable is added in the middle of a try/catch block: the frame for the
* exception handler should have a TOP type for this new local.
*
* @param newLocals
* the stack map frame types corresponding to the local variables
* added with {@link #newLocal} (and null for the others). The
* format of this array is the same as in
* {@link MethodVisitor#visitFrame}, except that long and double
* types use two slots. The types for the current stack map frame
* must be updated in place in this array.
*/
protected void updateNewLocals(Object[] newLocals) {
}
/**
* Notifies subclasses that a local variable has been added or remapped. The
* default implementation of this method does nothing.
*
* @param local
* a local variable identifier, as returned by {@link #newLocal
* newLocal()}.
* @param type
* the type of the value being stored in the local variable.
*/
protected void setLocalType(final int local, final Type type) {
}
private void setFrameLocal(final int local, final Object type) {
int l = newLocals.length;
if (local >= l) {
Object[] a = new Object[Math.max(2 * l, local + 1)];
System.arraycopy(newLocals, 0, a, 0, l);
newLocals = a;
}
newLocals[local] = type;
}
private int remap(final int var, final Type type) {
if (var + type.getSize() <= firstLocal) {
return var;
}
int key = 2 * var + type.getSize() - 1;
int size = mapping.length;
if (key >= size) {
int[] newMapping = new int[Math.max(2 * size, key + 1)];
System.arraycopy(mapping, 0, newMapping, 0, size);
mapping = newMapping;
}
int value = mapping[key];
if (value == 0) {
value = newLocalMapping(type);
setLocalType(value, type);
mapping[key] = value + 1;
} else {
value--;
}
if (value != var) {
changed = true;
}
return value;
}
protected int newLocalMapping(final Type type) {
int local = nextLocal;
nextLocal += type.getSize();
return local;
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 9536
Content-Disposition: inline; filename="Method.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "25c3660cfacafd56bb8149678b65fa39bcf28157"
/***
* 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 org.objectweb.asm.commons;
import java.util.HashMap;
import java.util.Map;
import org.objectweb.asm.Type;
/**
* A named method descriptor.
*
* @author Juozas Baliuka
* @author Chris Nokleberg
* @author Eric Bruneton
*/
public class Method {
/**
* The method name.
*/
private final String name;
/**
* The method descriptor.
*/
private final String desc;
/**
* Maps primitive Java type names to their descriptors.
*/
private static final Map DESCRIPTORS;
static {
DESCRIPTORS = new HashMap();
DESCRIPTORS.put("void", "V");
DESCRIPTORS.put("byte", "B");
DESCRIPTORS.put("char", "C");
DESCRIPTORS.put("double", "D");
DESCRIPTORS.put("float", "F");
DESCRIPTORS.put("int", "I");
DESCRIPTORS.put("long", "J");
DESCRIPTORS.put("short", "S");
DESCRIPTORS.put("boolean", "Z");
}
/**
* Creates a new {@link Method}.
*
* @param name
* the method's name.
* @param desc
* the method's descriptor.
*/
public Method(final String name, final String desc) {
this.name = name;
this.desc = desc;
}
/**
* Creates a new {@link Method}.
*
* @param name
* the method's name.
* @param returnType
* the method's return type.
* @param argumentTypes
* the method's argument types.
*/
public Method(final String name, final Type returnType,
final Type[] argumentTypes) {
this(name, Type.getMethodDescriptor(returnType, argumentTypes));
}
/**
* Creates a new {@link Method}.
*
* @param m
* a java.lang.reflect method descriptor
* @return a {@link Method} corresponding to the given Java method
* declaration.
*/
public static Method getMethod(java.lang.reflect.Method m) {
return new Method(m.getName(), Type.getMethodDescriptor(m));
}
/**
* Creates a new {@link Method}.
*
* @param c
* a java.lang.reflect constructor descriptor
* @return a {@link Method} corresponding to the given Java constructor
* declaration.
*/
public static Method getMethod(java.lang.reflect.Constructor> c) {
return new Method("", Type.getConstructorDescriptor(c));
}
/**
* Returns a {@link Method} corresponding to the given Java method
* declaration.
*
* @param method
* a Java method declaration, without argument names, of the form
* "returnType name (argumentType1, ... argumentTypeN)", where
* the types are in plain Java (e.g. "int", "float",
* "java.util.List", ...). Classes of the java.lang package can
* be specified by their unqualified name; all other classes
* names must be fully qualified.
* @return a {@link Method} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException
* if method
could not get parsed.
*/
public static Method getMethod(final String method)
throws IllegalArgumentException {
return getMethod(method, false);
}
/**
* Returns a {@link Method} corresponding to the given Java method
* declaration.
*
* @param method
* a Java method declaration, without argument names, of the form
* "returnType name (argumentType1, ... argumentTypeN)", where
* the types are in plain Java (e.g. "int", "float",
* "java.util.List", ...). Classes of the java.lang package may
* be specified by their unqualified name, depending on the
* defaultPackage argument; all other classes names must be fully
* qualified.
* @param defaultPackage
* true if unqualified class names belong to the default package,
* or false if they correspond to java.lang classes. For instance
* "Object" means "Object" if this option is true, or
* "java.lang.Object" otherwise.
* @return a {@link Method} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException
* if method
could not get parsed.
*/
public static Method getMethod(final String method,
final boolean defaultPackage) throws IllegalArgumentException {
int space = method.indexOf(' ');
int start = method.indexOf('(', space) + 1;
int end = method.indexOf(')', start);
if (space == -1 || start == -1 || end == -1) {
throw new IllegalArgumentException();
}
String returnType = method.substring(0, space);
String methodName = method.substring(space + 1, start - 1).trim();
StringBuffer sb = new StringBuffer();
sb.append('(');
int p;
do {
String s;
p = method.indexOf(',', start);
if (p == -1) {
s = map(method.substring(start, end).trim(), defaultPackage);
} else {
s = map(method.substring(start, p).trim(), defaultPackage);
start = p + 1;
}
sb.append(s);
} while (p != -1);
sb.append(')');
sb.append(map(returnType, defaultPackage));
return new Method(methodName, sb.toString());
}
private static String map(final String type, final boolean defaultPackage) {
if ("".equals(type)) {
return type;
}
StringBuffer sb = new StringBuffer();
int index = 0;
while ((index = type.indexOf("[]", index) + 1) > 0) {
sb.append('[');
}
String t = type.substring(0, type.length() - sb.length() * 2);
String desc = DESCRIPTORS.get(t);
if (desc != null) {
sb.append(desc);
} else {
sb.append('L');
if (t.indexOf('.') < 0) {
if (!defaultPackage) {
sb.append("java/lang/");
}
sb.append(t);
} else {
sb.append(t.replace('.', '/'));
}
sb.append(';');
}
return sb.toString();
}
/**
* Returns the name of the method described by this object.
*
* @return the name of the method described by this object.
*/
public String getName() {
return name;
}
/**
* Returns the descriptor of the method described by this object.
*
* @return the descriptor of the method described by this object.
*/
public String getDescriptor() {
return desc;
}
/**
* Returns the return type of the method described by this object.
*
* @return the return type of the method described by this object.
*/
public Type getReturnType() {
return Type.getReturnType(desc);
}
/**
* Returns the argument types of the method described by this object.
*
* @return the argument types of the method described by this object.
*/
public Type[] getArgumentTypes() {
return Type.getArgumentTypes(desc);
}
@Override
public String toString() {
return name + desc;
}
@Override
public boolean equals(final Object o) {
if (!(o instanceof Method)) {
return false;
}
Method other = (Method) o;
return name.equals(other.name) && desc.equals(other.desc);
}
@Override
public int hashCode() {
return name.hashCode() ^ desc.hashCode();
}
}X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 7527
Content-Disposition: inline; filename="Remapper.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "9c359fb00e39fb254b3e01bac9ec18d651966ba8"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Type;
import org.objectweb.asm.signature.SignatureReader;
import org.objectweb.asm.signature.SignatureVisitor;
import org.objectweb.asm.signature.SignatureWriter;
/**
* A class responsible for remapping types and names. Subclasses can override
* the following methods:
*
*
* {@link #map(String)} - map type
* {@link #mapFieldName(String, String, String)} - map field name
* {@link #mapMethodName(String, String, String)} - map method name
*
*
* @author Eugene Kuleshov
*/
public abstract class Remapper {
public String mapDesc(String desc) {
Type t = Type.getType(desc);
switch (t.getSort()) {
case Type.ARRAY:
String s = mapDesc(t.getElementType().getDescriptor());
for (int i = 0; i < t.getDimensions(); ++i) {
s = '[' + s;
}
return s;
case Type.OBJECT:
String newType = map(t.getInternalName());
if (newType != null) {
return 'L' + newType + ';';
}
}
return desc;
}
private Type mapType(Type t) {
switch (t.getSort()) {
case Type.ARRAY:
String s = mapDesc(t.getElementType().getDescriptor());
for (int i = 0; i < t.getDimensions(); ++i) {
s = '[' + s;
}
return Type.getType(s);
case Type.OBJECT:
s = map(t.getInternalName());
return s != null ? Type.getObjectType(s) : t;
case Type.METHOD:
return Type.getMethodType(mapMethodDesc(t.getDescriptor()));
}
return t;
}
public String mapType(String type) {
if (type == null) {
return null;
}
return mapType(Type.getObjectType(type)).getInternalName();
}
public String[] mapTypes(String[] types) {
String[] newTypes = null;
boolean needMapping = false;
for (int i = 0; i < types.length; i++) {
String type = types[i];
String newType = map(type);
if (newType != null && newTypes == null) {
newTypes = new String[types.length];
if (i > 0) {
System.arraycopy(types, 0, newTypes, 0, i);
}
needMapping = true;
}
if (needMapping) {
newTypes[i] = newType == null ? type : newType;
}
}
return needMapping ? newTypes : types;
}
public String mapMethodDesc(String desc) {
if ("()V".equals(desc)) {
return desc;
}
Type[] args = Type.getArgumentTypes(desc);
StringBuffer s = new StringBuffer("(");
for (int i = 0; i < args.length; i++) {
s.append(mapDesc(args[i].getDescriptor()));
}
Type returnType = Type.getReturnType(desc);
if (returnType == Type.VOID_TYPE) {
s.append(")V");
return s.toString();
}
s.append(')').append(mapDesc(returnType.getDescriptor()));
return s.toString();
}
public Object mapValue(Object value) {
if (value instanceof Type) {
return mapType((Type) value);
}
if (value instanceof Handle) {
Handle h = (Handle) value;
return new Handle(h.getTag(), mapType(h.getOwner()), mapMethodName(
h.getOwner(), h.getName(), h.getDesc()),
mapMethodDesc(h.getDesc()));
}
return value;
}
/**
*
* @param typeSignature
* true if signature is a FieldTypeSignature, such as the
* signature parameter of the ClassVisitor.visitField or
* MethodVisitor.visitLocalVariable methods
*/
public String mapSignature(String signature, boolean typeSignature) {
if (signature == null) {
return null;
}
SignatureReader r = new SignatureReader(signature);
SignatureWriter w = new SignatureWriter();
SignatureVisitor a = createRemappingSignatureAdapter(w);
if (typeSignature) {
r.acceptType(a);
} else {
r.accept(a);
}
return w.toString();
}
protected SignatureVisitor createRemappingSignatureAdapter(
SignatureVisitor v) {
return new RemappingSignatureAdapter(v, this);
}
/**
* Map method name to the new name. Subclasses can override.
*
* @param owner
* owner of the method.
* @param name
* name of the method.
* @param desc
* descriptor of the method.
* @return new name of the method
*/
public String mapMethodName(String owner, String name, String desc) {
return name;
}
/**
* Map invokedynamic method name to the new name. Subclasses can override.
*
* @param name
* name of the invokedynamic.
* @param desc
* descriptor of the invokedynamic.
* @return new invokdynamic name.
*/
public String mapInvokeDynamicMethodName(String name, String desc) {
return name;
}
/**
* Map field name to the new name. Subclasses can override.
*
* @param owner
* owner of the field.
* @param name
* name of the field
* @param desc
* descriptor of the field
* @return new name of the field.
*/
public String mapFieldName(String owner, String name, String desc) {
return name;
}
/**
* Map type name to the new name. Subclasses can override.
*/
public String map(String typeName) {
return typeName;
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 3126
Content-Disposition: inline; filename="RemappingAnnotationAdapter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "8fff2d1d0942d2f242ffef6be29e315ab73ab0dc"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
/**
* An {@link AnnotationVisitor} adapter for type remapping.
*
* @author Eugene Kuleshov
*/
public class RemappingAnnotationAdapter extends AnnotationVisitor {
protected final Remapper remapper;
public RemappingAnnotationAdapter(final AnnotationVisitor av,
final Remapper remapper) {
this(Opcodes.ASM4, av, remapper);
}
protected RemappingAnnotationAdapter(final int api,
final AnnotationVisitor av, final Remapper remapper) {
super(api, av);
this.remapper = remapper;
}
@Override
public void visit(String name, Object value) {
av.visit(name, remapper.mapValue(value));
}
@Override
public void visitEnum(String name, String desc, String value) {
av.visitEnum(name, remapper.mapDesc(desc), value);
}
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
AnnotationVisitor v = av.visitAnnotation(name, remapper.mapDesc(desc));
return v == null ? null : (v == av ? this
: new RemappingAnnotationAdapter(v, remapper));
}
@Override
public AnnotationVisitor visitArray(String name) {
AnnotationVisitor v = av.visitArray(name);
return v == null ? null : (v == av ? this
: new RemappingAnnotationAdapter(v, remapper));
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 5233
Content-Disposition: inline; filename="RemappingClassAdapter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "f9e577dfb3f4591685c99fc974961fc73b99ee85"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link ClassVisitor} for type remapping.
*
* @author Eugene Kuleshov
*/
public class RemappingClassAdapter extends ClassVisitor {
protected final Remapper remapper;
protected String className;
public RemappingClassAdapter(final ClassVisitor cv, final Remapper remapper) {
this(Opcodes.ASM4, cv, remapper);
}
protected RemappingClassAdapter(final int api, final ClassVisitor cv,
final Remapper remapper) {
super(api, cv);
this.remapper = remapper;
}
@Override
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
this.className = name;
super.visit(version, access, remapper.mapType(name), remapper
.mapSignature(signature, false), remapper.mapType(superName),
interfaces == null ? null : remapper.mapTypes(interfaces));
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
AnnotationVisitor av;
av = super.visitAnnotation(remapper.mapDesc(desc), visible);
return av == null ? null : createRemappingAnnotationAdapter(av);
}
@Override
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
FieldVisitor fv = super.visitField(access,
remapper.mapFieldName(className, name, desc),
remapper.mapDesc(desc), remapper.mapSignature(signature, true),
remapper.mapValue(value));
return fv == null ? null : createRemappingFieldAdapter(fv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
String newDesc = remapper.mapMethodDesc(desc);
MethodVisitor mv = super.visitMethod(access, remapper.mapMethodName(
className, name, desc), newDesc, remapper.mapSignature(
signature, false),
exceptions == null ? null : remapper.mapTypes(exceptions));
return mv == null ? null : createRemappingMethodAdapter(access,
newDesc, mv);
}
@Override
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
// TODO should innerName be changed?
super.visitInnerClass(remapper.mapType(name), outerName == null ? null
: remapper.mapType(outerName), innerName, access);
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
super.visitOuterClass(remapper.mapType(owner), name == null ? null
: remapper.mapMethodName(owner, name, desc),
desc == null ? null : remapper.mapMethodDesc(desc));
}
protected FieldVisitor createRemappingFieldAdapter(FieldVisitor fv) {
return new RemappingFieldAdapter(fv, remapper);
}
protected MethodVisitor createRemappingMethodAdapter(int access,
String newDesc, MethodVisitor mv) {
return new RemappingMethodAdapter(access, newDesc, mv, remapper);
}
protected AnnotationVisitor createRemappingAnnotationAdapter(
AnnotationVisitor av) {
return new RemappingAnnotationAdapter(av, remapper);
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 2595
Content-Disposition: inline; filename="RemappingFieldAdapter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "1dbee7e6d6a2e6bbd46a61539991023321f81c7e"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link FieldVisitor} adapter for type remapping.
*
* @author Eugene Kuleshov
*/
public class RemappingFieldAdapter extends FieldVisitor {
private final Remapper remapper;
public RemappingFieldAdapter(final FieldVisitor fv, final Remapper remapper) {
this(Opcodes.ASM4, fv, remapper);
}
protected RemappingFieldAdapter(final int api, final FieldVisitor fv,
final Remapper remapper) {
super(api, fv);
this.remapper = remapper;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
AnnotationVisitor av = fv.visitAnnotation(remapper.mapDesc(desc),
visible);
return av == null ? null : new RemappingAnnotationAdapter(av, remapper);
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 6170
Content-Disposition: inline; filename="RemappingMethodAdapter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "e1c3b9c9fc4b002f3470f1ec47d7b2cdcf9d0852"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link LocalVariablesSorter} for type mapping.
*
* @author Eugene Kuleshov
*/
public class RemappingMethodAdapter extends LocalVariablesSorter {
protected final Remapper remapper;
public RemappingMethodAdapter(final int access, final String desc,
final MethodVisitor mv, final Remapper remapper) {
this(Opcodes.ASM4, access, desc, mv, remapper);
}
protected RemappingMethodAdapter(final int api, final int access,
final String desc, final MethodVisitor mv, final Remapper remapper) {
super(api, access, desc, mv);
this.remapper = remapper;
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
AnnotationVisitor av = mv.visitAnnotationDefault();
return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
AnnotationVisitor av = mv.visitAnnotation(remapper.mapDesc(desc),
visible);
return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter,
String desc, boolean visible) {
AnnotationVisitor av = mv.visitParameterAnnotation(parameter,
remapper.mapDesc(desc), visible);
return av == null ? av : new RemappingAnnotationAdapter(av, remapper);
}
@Override
public void visitFrame(int type, int nLocal, Object[] local, int nStack,
Object[] stack) {
super.visitFrame(type, nLocal, remapEntries(nLocal, local), nStack,
remapEntries(nStack, stack));
}
private Object[] remapEntries(int n, Object[] entries) {
for (int i = 0; i < n; i++) {
if (entries[i] instanceof String) {
Object[] newEntries = new Object[n];
if (i > 0) {
System.arraycopy(entries, 0, newEntries, 0, i);
}
do {
Object t = entries[i];
newEntries[i++] = t instanceof String ? remapper
.mapType((String) t) : t;
} while (i < n);
return newEntries;
}
}
return entries;
}
@Override
public void visitFieldInsn(int opcode, String owner, String name,
String desc) {
super.visitFieldInsn(opcode, remapper.mapType(owner),
remapper.mapFieldName(owner, name, desc),
remapper.mapDesc(desc));
}
@Override
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
super.visitMethodInsn(opcode, remapper.mapType(owner),
remapper.mapMethodName(owner, name, desc),
remapper.mapMethodDesc(desc));
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
for (int i = 0; i < bsmArgs.length; i++) {
bsmArgs[i] = remapper.mapValue(bsmArgs[i]);
}
super.visitInvokeDynamicInsn(
remapper.mapInvokeDynamicMethodName(name, desc),
remapper.mapMethodDesc(desc), (Handle) remapper.mapValue(bsm),
bsmArgs);
}
@Override
public void visitTypeInsn(int opcode, String type) {
super.visitTypeInsn(opcode, remapper.mapType(type));
}
@Override
public void visitLdcInsn(Object cst) {
super.visitLdcInsn(remapper.mapValue(cst));
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
super.visitMultiANewArrayInsn(remapper.mapDesc(desc), dims);
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler,
String type) {
super.visitTryCatchBlock(start, end, handler, type == null ? null
: remapper.mapType(type));
}
@Override
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index) {
super.visitLocalVariable(name, remapper.mapDesc(desc),
remapper.mapSignature(signature, true), start, end, index);
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 4437
Content-Disposition: inline; filename="RemappingSignatureAdapter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "88d5315d3ad4eb9cd4ac18add911c05027a836fe"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.signature.SignatureVisitor;
/**
* A {@link SignatureVisitor} adapter for type mapping.
*
* @author Eugene Kuleshov
*/
public class RemappingSignatureAdapter extends SignatureVisitor {
private final SignatureVisitor v;
private final Remapper remapper;
private String className;
public RemappingSignatureAdapter(final SignatureVisitor v,
final Remapper remapper) {
this(Opcodes.ASM4, v, remapper);
}
protected RemappingSignatureAdapter(final int api,
final SignatureVisitor v, final Remapper remapper) {
super(api);
this.v = v;
this.remapper = remapper;
}
@Override
public void visitClassType(String name) {
className = name;
v.visitClassType(remapper.mapType(name));
}
@Override
public void visitInnerClassType(String name) {
className = className + '$' + name;
String remappedName = remapper.mapType(className);
v.visitInnerClassType(remappedName.substring(remappedName
.lastIndexOf('$') + 1));
}
@Override
public void visitFormalTypeParameter(String name) {
v.visitFormalTypeParameter(name);
}
@Override
public void visitTypeVariable(String name) {
v.visitTypeVariable(name);
}
@Override
public SignatureVisitor visitArrayType() {
v.visitArrayType();
return this;
}
@Override
public void visitBaseType(char descriptor) {
v.visitBaseType(descriptor);
}
@Override
public SignatureVisitor visitClassBound() {
v.visitClassBound();
return this;
}
@Override
public SignatureVisitor visitExceptionType() {
v.visitExceptionType();
return this;
}
@Override
public SignatureVisitor visitInterface() {
v.visitInterface();
return this;
}
@Override
public SignatureVisitor visitInterfaceBound() {
v.visitInterfaceBound();
return this;
}
@Override
public SignatureVisitor visitParameterType() {
v.visitParameterType();
return this;
}
@Override
public SignatureVisitor visitReturnType() {
v.visitReturnType();
return this;
}
@Override
public SignatureVisitor visitSuperclass() {
v.visitSuperclass();
return this;
}
@Override
public void visitTypeArgument() {
v.visitTypeArgument();
}
@Override
public SignatureVisitor visitTypeArgument(char wildcard) {
v.visitTypeArgument(wildcard);
return this;
}
@Override
public void visitEnd() {
v.visitEnd();
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 20096
Content-Disposition: inline; filename="SerialVersionUIDAdder.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "29a6696ee6d7a83ef5677f29d8c9b3f36a19f5e1"
/***
* 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 org.objectweb.asm.commons;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link ClassVisitor} that adds a serial version unique identifier to a
* class if missing. Here is typical usage of this class:
*
*
* ClassWriter cw = new ClassWriter(...);
* ClassVisitor sv = new SerialVersionUIDAdder(cw);
* ClassVisitor ca = new MyClassAdapter(sv);
* new ClassReader(orginalClass).accept(ca, false);
*
*
* The SVUID algorithm can be found http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html :
*
*
* The serialVersionUID is computed using the signature of a stream of bytes
* that reflect the class definition. The National Institute of Standards and
* Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a
* signature for the stream. The first two 32-bit quantities are used to form a
* 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data
* types to a sequence of bytes. The values input to the stream are defined by
* the Java Virtual Machine (VM) specification for classes.
*
* The sequence of items in the stream is as follows:
*
* 1. The class name written using UTF encoding.
* 2. The class modifiers written as a 32-bit integer.
* 3. The name of each interface sorted by name written using UTF encoding.
* 4. For each field of the class sorted by field name (except private static
* and private transient fields):
* 1. The name of the field in UTF encoding.
* 2. The modifiers of the field written as a 32-bit integer.
* 3. The descriptor of the field in UTF encoding
* 5. If a class initializer exists, write out the following:
* 1. The name of the method, <clinit>, in UTF encoding.
* 2. The modifier of the method, java.lang.reflect.Modifier.STATIC,
* written as a 32-bit integer.
* 3. The descriptor of the method, ()V, in UTF encoding.
* 6. For each non-private constructor sorted by method name and signature:
* 1. The name of the method, <init>, in UTF encoding.
* 2. The modifiers of the method written as a 32-bit integer.
* 3. The descriptor of the method in UTF encoding.
* 7. For each non-private method sorted by method name and signature:
* 1. The name of the method in UTF encoding.
* 2. The modifiers of the method written as a 32-bit integer.
* 3. The descriptor of the method in UTF encoding.
* 8. The SHA-1 algorithm is executed on the stream of bytes produced by
* DataOutputStream and produces five 32-bit values sha[0..4].
*
* 9. The hash value is assembled from the first and second 32-bit values of
* the SHA-1 message digest. If the result of the message digest, the five
* 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named
* sha, the hash value would be computed as follows:
*
* long hash = ((sha[0] >>> 24) & 0xFF) |
* ((sha[0] >>> 16) & 0xFF) << 8 |
* ((sha[0] >>> 8) & 0xFF) << 16 |
* ((sha[0] >>> 0) & 0xFF) << 24 |
* ((sha[1] >>> 24) & 0xFF) << 32 |
* ((sha[1] >>> 16) & 0xFF) << 40 |
* ((sha[1] >>> 8) & 0xFF) << 48 |
* ((sha[1] >>> 0) & 0xFF) << 56;
*
*
* @author Rajendra Inamdar, Vishal Vishnoi
*/
public class SerialVersionUIDAdder extends ClassVisitor {
/**
* Flag that indicates if we need to compute SVUID.
*/
private boolean computeSVUID;
/**
* Set to true if the class already has SVUID.
*/
private boolean hasSVUID;
/**
* Classes access flags.
*/
private int access;
/**
* Internal name of the class
*/
private String name;
/**
* Interfaces implemented by the class.
*/
private String[] interfaces;
/**
* Collection of fields. (except private static and private transient
* fields)
*/
private Collection- svuidFields;
/**
* Set to true if the class has static initializer.
*/
private boolean hasStaticInitializer;
/**
* Collection of non-private constructors.
*/
private Collection
- svuidConstructors;
/**
* Collection of non-private methods.
*/
private Collection
- svuidMethods;
/**
* Creates a new {@link SerialVersionUIDAdder}.
Subclasses must not use
* this constructor . Instead, they must use the
* {@link #SerialVersionUIDAdder(int, ClassVisitor)} version.
*
* @param cv
* a {@link ClassVisitor} to which this visitor will delegate
* calls.
*/
public SerialVersionUIDAdder(final ClassVisitor cv) {
this(Opcodes.ASM4, cv);
}
/**
* Creates a new {@link SerialVersionUIDAdder}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @param cv
* a {@link ClassVisitor} to which this visitor will delegate
* calls.
*/
protected SerialVersionUIDAdder(final int api, final ClassVisitor cv) {
super(api, cv);
svuidFields = new ArrayList- ();
svuidConstructors = new ArrayList
- ();
svuidMethods = new ArrayList
- ();
}
// ------------------------------------------------------------------------
// Overriden methods
// ------------------------------------------------------------------------
/*
* Visit class header and get class name, access , and interfaces
* information (step 1,2, and 3) for SVUID computation.
*/
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName,
final String[] interfaces) {
computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0;
if (computeSVUID) {
this.name = name;
this.access = access;
this.interfaces = interfaces;
}
super.visit(version, access, name, signature, superName, interfaces);
}
/*
* Visit the methods and get constructor and method information (step 5 and
* 7). Also determine if there is a class initializer (step 6).
*/
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
if (computeSVUID) {
if ("
".equals(name)) {
hasStaticInitializer = true;
}
/*
* Remembers non private constructors and methods for SVUID
* computation For constructor and method modifiers, only the
* ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
* ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags
* are used.
*/
int mods = access
& (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
| Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
| Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED
| Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT);
// all non private methods
if ((access & Opcodes.ACC_PRIVATE) == 0) {
if ("".equals(name)) {
svuidConstructors.add(new Item(name, mods, desc));
} else if (!"".equals(name)) {
svuidMethods.add(new Item(name, mods, desc));
}
}
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
/*
* Gets class field information for step 4 of the algorithm. Also determines
* if the class already has a SVUID.
*/
@Override
public FieldVisitor visitField(final int access, final String name,
final String desc, final String signature, final Object value) {
if (computeSVUID) {
if ("serialVersionUID".equals(name)) {
// since the class already has SVUID, we won't be computing it.
computeSVUID = false;
hasSVUID = true;
}
/*
* Remember field for SVUID computation For field modifiers, only
* the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC,
* ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when
* computing serialVersionUID values.
*/
if ((access & Opcodes.ACC_PRIVATE) == 0
|| (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0) {
int mods = access
& (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
| Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
| Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT);
svuidFields.add(new Item(name, mods, desc));
}
}
return super.visitField(access, name, desc, signature, value);
}
/**
* Handle a bizarre special case. Nested classes (static classes declared
* inside another class) that are protected have their access bit set to
* public in their class files to deal with some odd reflection situation.
* Our SVUID computation must do as the JVM does and ignore access bits in
* the class file in favor of the access bits InnerClass attribute.
*/
@Override
public void visitInnerClass(final String aname, final String outerName,
final String innerName, final int attr_access) {
if ((name != null) && name.equals(aname)) {
this.access = attr_access;
}
super.visitInnerClass(aname, outerName, innerName, attr_access);
}
/*
* Add the SVUID if class doesn't have one
*/
@Override
public void visitEnd() {
// compute SVUID and add it to the class
if (computeSVUID && !hasSVUID) {
try {
addSVUID(computeSVUID());
} catch (Throwable e) {
throw new RuntimeException("Error while computing SVUID for "
+ name, e);
}
}
super.visitEnd();
}
// ------------------------------------------------------------------------
// Utility methods
// ------------------------------------------------------------------------
/**
* Returns true if the class already has a SVUID field. The result of this
* method is only valid when visitEnd is or has been called.
*
* @return true if the class already has a SVUID field.
*/
public boolean hasSVUID() {
return hasSVUID;
}
protected void addSVUID(long svuid) {
FieldVisitor fv = super.visitField(Opcodes.ACC_FINAL
+ Opcodes.ACC_STATIC, "serialVersionUID", "J", null, new Long(
svuid));
if (fv != null) {
fv.visitEnd();
}
}
/**
* Computes and returns the value of SVUID.
*
* @return Returns the serial version UID
* @throws IOException
* if an I/O error occurs
*/
protected long computeSVUID() throws IOException {
ByteArrayOutputStream bos;
DataOutputStream dos = null;
long svuid = 0;
try {
bos = new ByteArrayOutputStream();
dos = new DataOutputStream(bos);
/*
* 1. The class name written using UTF encoding.
*/
dos.writeUTF(name.replace('/', '.'));
/*
* 2. The class modifiers written as a 32-bit integer.
*/
dos.writeInt(access
& (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
| Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT));
/*
* 3. The name of each interface sorted by name written using UTF
* encoding.
*/
Arrays.sort(interfaces);
for (int i = 0; i < interfaces.length; i++) {
dos.writeUTF(interfaces[i].replace('/', '.'));
}
/*
* 4. For each field of the class sorted by field name (except
* private static and private transient fields):
*
* 1. The name of the field in UTF encoding. 2. The modifiers of the
* field written as a 32-bit integer. 3. The descriptor of the field
* in UTF encoding
*
* Note that field signatures are not dot separated. Method and
* constructor signatures are dot separated. Go figure...
*/
writeItems(svuidFields, dos, false);
/*
* 5. If a class initializer exists, write out the following: 1. The
* name of the method, , in UTF encoding. 2. The modifier of
* the method, java.lang.reflect.Modifier.STATIC, written as a
* 32-bit integer. 3. The descriptor of the method, ()V, in UTF
* encoding.
*/
if (hasStaticInitializer) {
dos.writeUTF("");
dos.writeInt(Opcodes.ACC_STATIC);
dos.writeUTF("()V");
} // if..
/*
* 6. For each non-private constructor sorted by method name and
* signature: 1. The name of the method, , in UTF encoding. 2.
* The modifiers of the method written as a 32-bit integer. 3. The
* descriptor of the method in UTF encoding.
*/
writeItems(svuidConstructors, dos, true);
/*
* 7. For each non-private method sorted by method name and
* signature: 1. The name of the method in UTF encoding. 2. The
* modifiers of the method written as a 32-bit integer. 3. The
* descriptor of the method in UTF encoding.
*/
writeItems(svuidMethods, dos, true);
dos.flush();
/*
* 8. The SHA-1 algorithm is executed on the stream of bytes
* produced by DataOutputStream and produces five 32-bit values
* sha[0..4].
*/
byte[] hashBytes = computeSHAdigest(bos.toByteArray());
/*
* 9. The hash value is assembled from the first and second 32-bit
* values of the SHA-1 message digest. If the result of the message
* digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of
* five int values named sha, the hash value would be computed as
* follows:
*
* long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF)
* << 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) <<
* 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) <<
* 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) <<
* 56;
*/
for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
svuid = (svuid << 8) | (hashBytes[i] & 0xFF);
}
} finally {
// close the stream (if open)
if (dos != null) {
dos.close();
}
}
return svuid;
}
/**
* Returns the SHA-1 message digest of the given value.
*
* @param value
* the value whose SHA message digest must be computed.
* @return the SHA-1 message digest of the given value.
*/
protected byte[] computeSHAdigest(final byte[] value) {
try {
return MessageDigest.getInstance("SHA").digest(value);
} catch (Exception e) {
throw new UnsupportedOperationException(e.toString());
}
}
/**
* Sorts the items in the collection and writes it to the data output stream
*
* @param itemCollection
* collection of items
* @param dos
* a DataOutputStream
value
* @param dotted
* a boolean
value
* @exception IOException
* if an error occurs
*/
private static void writeItems(final Collection- itemCollection,
final DataOutput dos, final boolean dotted) throws IOException {
int size = itemCollection.size();
Item[] items = itemCollection.toArray(new Item[size]);
Arrays.sort(items);
for (int i = 0; i < size; i++) {
dos.writeUTF(items[i].name);
dos.writeInt(items[i].access);
dos.writeUTF(dotted ? items[i].desc.replace('/', '.')
: items[i].desc);
}
}
// ------------------------------------------------------------------------
// Inner classes
// ------------------------------------------------------------------------
private static class Item implements Comparable
- {
final String name;
final int access;
final String desc;
Item(final String name, final int access, final String desc) {
this.name = name;
this.access = access;
this.desc = desc;
}
public int compareTo(final Item other) {
int retVal = name.compareTo(other.name);
if (retVal == 0) {
retVal = desc.compareTo(other.desc);
}
return retVal;
}
@Override
public boolean equals(final Object o) {
if (o instanceof Item) {
return compareTo((Item) o) == 0;
}
return false;
}
@Override
public int hashCode() {
return (name + desc).hashCode();
}
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 2629
Content-Disposition: inline; filename="SimpleRemapper.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "95b43e52cfabed78c7467778442abe5011d0d99a"
/***
* 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 org.objectweb.asm.commons;
import java.util.Collections;
import java.util.Map;
/**
* A {@link Remapper} using a {@link Map} to define its mapping.
*
* @author Eugene Kuleshov
*/
public class SimpleRemapper extends Remapper {
private final Map
mapping;
public SimpleRemapper(Map mapping) {
this.mapping = mapping;
}
public SimpleRemapper(String oldName, String newName) {
this.mapping = Collections.singletonMap(oldName, newName);
}
@Override
public String mapMethodName(String owner, String name, String desc) {
String s = map(owner + '.' + name + desc);
return s == null ? name : s;
}
@Override
public String mapFieldName(String owner, String name, String desc) {
String s = map(owner + '.' + name);
return s == null ? name : s;
}
@Override
public String map(String key) {
return mapping.get(key);
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 3611
Content-Disposition: inline; filename="StaticInitMerger.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "86c5ae3cbd668e2264f5676b1966380cae4a8d0a"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link ClassVisitor} that merges clinit methods into a single one.
*
* @author Eric Bruneton
*/
public class StaticInitMerger extends ClassVisitor {
private String name;
private MethodVisitor clinit;
private final String prefix;
private int counter;
public StaticInitMerger(final String prefix, final ClassVisitor cv) {
this(Opcodes.ASM4, prefix, cv);
}
protected StaticInitMerger(final int api, final String prefix,
final ClassVisitor cv) {
super(api, cv);
this.prefix = prefix;
}
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName,
final String[] interfaces) {
cv.visit(version, access, name, signature, superName, interfaces);
this.name = name;
}
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
MethodVisitor mv;
if ("".equals(name)) {
int a = Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC;
String n = prefix + counter++;
mv = cv.visitMethod(a, n, desc, signature, exceptions);
if (clinit == null) {
clinit = cv.visitMethod(a, name, desc, null, null);
}
clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc);
} else {
mv = cv.visitMethod(access, name, desc, signature, exceptions);
}
return mv;
}
@Override
public void visitEnd() {
if (clinit != null) {
clinit.visitInsn(Opcodes.RETURN);
clinit.visitMaxs(0, 0);
}
cv.visitEnd();
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 2271
Content-Disposition: inline; filename="TableSwitchGenerator.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "52bd3160cf846e751b70542bc5f0598f693f3dfc"
/***
* 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 org.objectweb.asm.commons;
import org.objectweb.asm.Label;
/**
* A code generator for switch statements.
*
* @author Juozas Baliuka
* @author Chris Nokleberg
* @author Eric Bruneton
*/
public interface TableSwitchGenerator {
/**
* Generates the code for a switch case.
*
* @param key
* the switch case key.
* @param end
* a label that corresponds to the end of the switch statement.
*/
void generateCase(int key, Label end);
/**
* Generates the code for the default switch case.
*/
void generateDefault();
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 3992
Content-Disposition: inline; filename="TryCatchBlockSorter.java"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "079c0cf014a870f2225810ecdabfd4b006aa6543"
/***
* 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 org.objectweb.asm.commons;
import java.util.Collections;
import java.util.Comparator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
/**
* A {@link MethodVisitor} adapter to sort the exception handlers. The handlers
* are sorted in a method innermost-to-outermost. This allows the programmer to
* add handlers without worrying about ordering them correctly with respect to
* existing, in-code handlers.
*
* Behavior is only defined for properly-nested handlers. If any "try" blocks
* overlap (something that isn't possible in Java code) then this may not do
* what you want. In fact, this adapter just sorts by the length of the "try"
* block, taking advantage of the fact that a given try block must be larger
* than any block it contains).
*
* @author Adrian Sampson
*/
public class TryCatchBlockSorter extends MethodNode {
public TryCatchBlockSorter(final MethodVisitor mv, final int access,
final String name, final String desc, final String signature,
final String[] exceptions) {
this(Opcodes.ASM4, mv, access, name, desc, signature, exceptions);
}
protected TryCatchBlockSorter(final int api, final MethodVisitor mv,
final int access, final String name, final String desc,
final String signature, final String[] exceptions) {
super(api, access, name, desc, signature, exceptions);
this.mv = mv;
}
@Override
public void visitEnd() {
// Compares TryCatchBlockNodes by the length of their "try" block.
Comparator comp = new Comparator() {
public int compare(TryCatchBlockNode t1, TryCatchBlockNode t2) {
int len1 = blockLength(t1);
int len2 = blockLength(t2);
return len1 - len2;
}
private int blockLength(TryCatchBlockNode block) {
int startidx = instructions.indexOf(block.start);
int endidx = instructions.indexOf(block.end);
return endidx - startidx;
}
};
Collections.sort(tryCatchBlocks, comp);
if (mv != null) {
accept(mv);
}
}
}
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 2871
Content-Disposition: inline; filename="package.html"
Last-Modified: Mon, 14 Jul 2025 18:41:28 GMT
Expires: Mon, 14 Jul 2025 18:46:28 GMT
ETag: "4ce0db854b180dc920b0982763a0f7943318c259"
Provides some useful class and method adapters. The preferred way of using
these adapters is by chaining them together and to custom adapters (instead of
inheriting from them) . Indeed this approach provides more combination
possibilities than inheritance. For instance, suppose you want to implement an
adapter MyAdapter than needs sorted local variables and intermediate stack map
frame values taking into account the local variables sort. By using inheritance,
this would require MyAdapter to extend AnalyzerAdapter, itself extending
LocalVariablesSorter. But AnalyzerAdapter is not a subclass of
LocalVariablesSorter, so this is not possible. On the contrary, by using
delegation, you can make LocalVariablesSorter delegate to AnalyzerAdapter,
itself delegating to MyAdapter. In this case AnalyzerAdapter computes
intermediate frames based on the output of LocalVariablesSorter, and MyAdapter
can add new locals by calling the newLocal method on LocalVariablesSorter, and
can get the stack map frame state before each instruction by reading the locals
and stack fields in AnalyzerAdapter (this requires references from MyAdapter
back to LocalVariablesSorter and AnalyzerAdapter).