This commit is contained in:
Clisprail
2014-02-20 01:19:41 +01:00
parent ff85cbffde
commit cbfc4ea0f6
55 changed files with 20594 additions and 0 deletions
@@ -0,0 +1,48 @@
<html>
<!--
* 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.
-->
<body>
Provides an implementation for optional class, field and method attributes.
<p>
By default ASM strips optional attributes, in order to keep them in
the bytecode that is being readed you should pass an array of required attribute
instances to {@link org.objectweb.asm.ClassReader#accept(org.objectweb.asm.ClassVisitor, org.objectweb.asm.Attribute[], boolean) ClassReader.accept()} method.
In order to add custom attributes to the manually constructed bytecode concrete
subclasses of the {@link org.objectweb.asm.Attribute Attribute} can be passed to
the visitAttribute methods of the
{@link org.objectweb.asm.ClassVisitor ClassVisitor},
{@link org.objectweb.asm.FieldVisitor FieldVisitor} and
{@link org.objectweb.asm.MethodVisitor MethodVisitor} interfaces.
@since ASM 1.4.1
</body>
</html>
@@ -0,0 +1,625 @@
/***
* 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.
* <p>
* The behavior for constructors is like this:
* <ol>
*
* <li>as long as the INVOKESPECIAL for the object initialization has not been
* reached, every bytecode instruction is dispatched in the ctor code visitor</li>
*
* <li>when this one is reached, it is only added in the ctor code visitor and a
* JP invoke is added</li>
*
* <li>after that, only the other code visitor receives the instructions</li>
*
* </ol>
*
* @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<Object> stackFrame;
private Map<Label, List<Object>> branches;
/**
* Creates a new {@link org.objectweb.asm.commons.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 = "<init>".equals(name);
}
@Override
public void visitCode() {
mv.visitCode();
if (constructor) {
stackFrame = new ArrayList<Object>();
branches = new HashMap<Label, List<Object>>();
} else {
superInitialized = true;
onMethodEnter();
}
}
@Override
public void visitLabel(final Label label) {
mv.visitLabel(label);
if (constructor && branches != null) {
List<Object> 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<Object> stackFrame = new ArrayList<Object>();
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<Object>(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. <br>
* <br>
*
* <i>Custom code can use or change all the local variables, but should not
* change state of the stack.</i>
*/
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:
*
* <pre>
* 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) {
* ...
* </pre>
*
* <br>
* <br>
*
* <i>Custom code can use or change all the local variables, but should not
* change state of the stack.</i>
*
* @param opcode
* one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN
* or ATHROW
*
*/
protected void onMethodExit(int opcode) {
}
// TODO onException, onMethodCall
}
@@ -0,0 +1,920 @@
/***
* 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
* visit<i>X</i> 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 <i>before</i> each instruction
* by reading the value of these fields in its visit<i>X</i> 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 {
/**
* <code>List</code> 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 <tt>null</tt> for unreachable
* instructions.
*/
public List<Object> locals;
/**
* <code>List</code> 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 <tt>null</tt> for unreachable
* instructions.
*/
public List<Object> stack;
/**
* The labels that designate the next instruction to be visited. May be
* <tt>null</tt>.
*/
private List<Label> 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<Object, Object> 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 org.objectweb.asm.commons.AnalyzerAdapter}. <i>Subclasses must not use this
* constructor</i>. 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 <tt>null</tt>.
*/
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 org.objectweb.asm.commons.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 <tt>null</tt>.
*/
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<Object>();
stack = new ArrayList<Object>();
uninitializedTypes = new HashMap<Object, Object>();
if ((access & Opcodes.ACC_STATIC) == 0) {
if ("<init>".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<Object>();
this.stack = new ArrayList<Object>();
}
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<Object> 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<Label>(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<Label>(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;
}
}
@@ -0,0 +1,217 @@
/***
* 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);
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,736 @@
/***
* 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.
*
* <b>Explanation of how it works</b> 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<LabelNode, BitSet> subroutineHeads = new HashMap<LabelNode, BitSet>();
/**
* 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. <i>Subclasses must not use this
* constructor</i>. Instead, they must use the
* {@link #JSRInlinerAdapter(int, MethodVisitor, int, String, String, String, String[])}
* version.
*
* @param mv
* the <code>MethodVisitor</code> to send the resulting inlined
* method code to (use <code>null</code> 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 <tt>null</tt>.
* @param exceptions
* the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>.
*/
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 <code>MethodVisitor</code> to send the resulting inlined
* method code to (use <code>null</code> 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 <tt>null</tt>.
* @param exceptions
* the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>.
*/
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<BitSet> 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<Map.Entry<LabelNode, BitSet>> it = subroutineHeads
.entrySet().iterator(); it.hasNext();) {
Map.Entry<LabelNode, BitSet> 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 <code>index</code>, and adding each instruction encountered into the
* subroutine <code>sub</code>. 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 <code>markSubroutines()</code>.
*
* @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<TryCatchBlockNode> 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 <code>sub</code>. Starts from <code>index</code>. Invoked only
* by <code>markSubroutineWalk()</code>.
*
* @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<Instantiation> worklist = new LinkedList<Instantiation>();
// 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<TryCatchBlockNode> newTryCatchBlocks = new ArrayList<TryCatchBlockNode>();
List<LocalVariableNode> newLocalVariables = new ArrayList<LocalVariableNode>();
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
* <code>instant</code>. May add new instantiations that are invoked by this
* one to the <code>worklist</code> parameter, and new try/catch blocks to
* <code>newTryCatchBlocks</code>.
*
* @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<Instantiation> worklist, final InsnList newInstructions,
final List<TryCatchBlockNode> newTryCatchBlocks,
final List<LocalVariableNode> 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<TryCatchBlockNode> 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<LocalVariableNode> 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<LabelNode, LabelNode> {
/**
* 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<LabelNode, LabelNode> rangeTable = new HashMap<LabelNode, LabelNode>();
/**
* 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 <code>this</code> or
* <code>null</code>. <code>this</code> indicates that this
* instantiation will generate the version of this instruction that we
* will execute, and <code>null</code> 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 <code>l</code> in the <code>gotoTable</code>, 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 <code>l</code> in the <code>rangeTable</code>,
* 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<Entry<LabelNode, LabelNode>> entrySet() {
return null;
}
@Override
public LabelNode get(final Object o) {
return gotoLabel((LabelNode) o);
}
}
}
@@ -0,0 +1,282 @@
/***
* 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<String, String> DESCRIPTORS;
static {
DESCRIPTORS = new HashMap<String, String>();
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 org.objectweb.asm.commons.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 org.objectweb.asm.commons.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 org.objectweb.asm.commons.Method}.
*
* @param m
* a java.lang.reflect method descriptor
* @return a {@link org.objectweb.asm.commons.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 org.objectweb.asm.commons.Method}.
*
* @param c
* a java.lang.reflect constructor descriptor
* @return a {@link org.objectweb.asm.commons.Method} corresponding to the given Java constructor
* declaration.
*/
public static Method getMethod(java.lang.reflect.Constructor<?> c) {
return new Method("<init>", Type.getConstructorDescriptor(c));
}
/**
* Returns a {@link org.objectweb.asm.commons.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 org.objectweb.asm.commons.Method} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException
* if <code>method</code> could not get parsed.
*/
public static Method getMethod(final String method)
throws IllegalArgumentException {
return getMethod(method, false);
}
/**
* Returns a {@link org.objectweb.asm.commons.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 org.objectweb.asm.commons.Method} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException
* if <code>method</code> 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();
}
}
@@ -0,0 +1,533 @@
/***
* 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 org.objectweb.asm.ClassVisitor} that adds a serial version unique identifier to a
* class if missing. Here is typical usage of this class:
*
* <pre>
* ClassWriter cw = new ClassWriter(...);
* ClassVisitor sv = new SerialVersionUIDAdder(cw);
* ClassVisitor ca = new MyClassAdapter(sv);
* new ClassReader(orginalClass).accept(ca, false);
* </pre>
*
* The SVUID algorithm can be found <a href=
* "http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html"
* >http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html</a>:
*
* <pre>
* 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, &lt;clinit&gt;, 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, &lt;init&gt;, 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] &gt;&gt;&gt; 24) &amp; 0xFF) |
* ((sha[0] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 8 |
* ((sha[0] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 16 |
* ((sha[0] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 24 |
* ((sha[1] &gt;&gt;&gt; 24) &amp; 0xFF) &lt;&lt; 32 |
* ((sha[1] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 40 |
* ((sha[1] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 48 |
* ((sha[1] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 56;
* </pre>
*
* @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<Item> svuidFields;
/**
* Set to true if the class has static initializer.
*/
private boolean hasStaticInitializer;
/**
* Collection of non-private constructors.
*/
private Collection<Item> svuidConstructors;
/**
* Collection of non-private methods.
*/
private Collection<Item> svuidMethods;
/**
* Creates a new {@link org.objectweb.asm.commons.SerialVersionUIDAdder}. <i>Subclasses must not use
* this constructor</i>. Instead, they must use the
* {@link #SerialVersionUIDAdder(int, org.objectweb.asm.ClassVisitor)} version.
*
* @param cv
* a {@link org.objectweb.asm.ClassVisitor} to which this visitor will delegate
* calls.
*/
public SerialVersionUIDAdder(final ClassVisitor cv) {
this(Opcodes.ASM4, cv);
}
/**
* Creates a new {@link org.objectweb.asm.commons.SerialVersionUIDAdder}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link Opcodes#ASM4}.
* @param cv
* a {@link org.objectweb.asm.ClassVisitor} to which this visitor will delegate
* calls.
*/
protected SerialVersionUIDAdder(final int api, final ClassVisitor cv) {
super(api, cv);
svuidFields = new ArrayList<Item>();
svuidConstructors = new ArrayList<Item>();
svuidMethods = new ArrayList<Item>();
}
// ------------------------------------------------------------------------
// 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 ("<clinit>".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 ("<init>".equals(name)) {
svuidConstructors.add(new Item(name, mods, desc));
} else if (!"<clinit>".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 java.io.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, <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.
*/
if (hasStaticInitializer) {
dos.writeUTF("<clinit>");
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, <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.
*/
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 <code>DataOutputStream</code> value
* @param dotted
* a <code>boolean</code> value
* @exception java.io.IOException
* if an error occurs
*/
private static void writeItems(final Collection<Item> 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<Item> {
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();
}
}
}
@@ -0,0 +1,69 @@
/***
* 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 org.objectweb.asm.commons.Remapper} using a {@link java.util.Map} to define its mapping.
*
* @author Eugene Kuleshov
*/
public class SimpleRemapper extends Remapper {
private final Map<String, String> mapping;
public SimpleRemapper(Map<String, String> 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);
}
}
@@ -0,0 +1,96 @@
/***
* 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 org.objectweb.asm.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 ("<clinit>".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();
}
}
@@ -0,0 +1,57 @@
/***
* 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();
}
@@ -0,0 +1,92 @@
/***
* 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<TryCatchBlockNode> comp = new Comparator<TryCatchBlockNode>() {
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);
}
}
}
@@ -0,0 +1,48 @@
<html>
<!--
* 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.
-->
<body>
Provides some useful class and method adapters. <i>The preferred way of using
these adapters is by chaining them together and to custom adapters (instead of
inheriting from them)</i>. 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).
</body>
@@ -0,0 +1,87 @@
<html>
<!--
* 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.
-->
<body>
Provides a small and fast bytecode manipulation framework.
<p>
The <a href="http://www.objectweb.org/asm">ASM</a> framework is organized
around the {@link org.objectweb.asm.ClassVisitor ClassVisitor},
{@link org.objectweb.asm.FieldVisitor FieldVisitor},
{@link org.objectweb.asm.MethodVisitor MethodVisitor} and
{@link org.objectweb.asm.AnnotationVisitor AnnotationVisitor} abstract classes,
which allow one to visit the fields, methods and annotations of a class,
including the bytecode instructions of each method.
<p>
In addition to these main abstract classes, ASM provides a {@link
org.objectweb.asm.ClassReader ClassReader} class, that can parse an
existing class and make a given visitor visit it. ASM also provides
a {@link org.objectweb.asm.ClassWriter ClassWriter} class, which is
a visitor that generates Java class files.
<p>
In order to generate a class from scratch, only the {@link
org.objectweb.asm.ClassWriter ClassWriter} class is necessary. Indeed,
in order to generate a class, one must just call its visit<i>Xxx</i>
methods with the appropriate arguments to generate the desired fields
and methods. See the "helloworld" example in the ASM distribution for
more details about class generation.
<p>
In order to modify existing classes, one must use a {@link
org.objectweb.asm.ClassReader ClassReader} class to analyze
the original class, a class modifier, and a {@link org.objectweb.asm.ClassWriter
ClassWriter} to construct the modified class. The class modifier
is just a {@link org.objectweb.asm.ClassVisitor ClassVisitor}
that delegates most of the work to another {@link org.objectweb.asm.ClassVisitor
ClassVisitor}, but that sometimes changes some parameter values,
or call additional methods, in order to implement the desired
modification process. In order to make it easier to implement such
class modifiers, the {@link org.objectweb.asm.ClassVisitor
ClassVisitor} and {@link org.objectweb.asm.MethodVisitor MethodVisitor}
classes delegate by default all the method calls they receive to an
optional visitor. See the "adapt" example in the ASM
distribution for more details about class modification.
<p>
The size of the core ASM library, <tt>asm.jar</tt>, is only 45KB, which is much
smaller than the size of the
<a href="http://jakarta.apache.org/bcel">BCEL</a> library (504KB), and than the
size of the
<a href="http://serp.sourceforge.net">SERP</a> library (150KB). ASM is also
much faster than these tools. Indeed the overhead of a load time class
transformation process is of the order of 60% with ASM, 700% or more with BCEL,
and 1100% or more with SERP (see the <tt>test/perf</tt> directory in the ASM
distribution)!
@since ASM 1.3
</body>
</html>
@@ -0,0 +1,36 @@
<html>
<!--
* 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.
-->
<body>
Provides support for type signatures.
@since ASM 2.0
</body>
</html>
@@ -0,0 +1,550 @@
/***
* 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.tree.analysis;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.VarInsnNode;
/**
* A semantic bytecode analyzer. <i>This class does not fully check that JSR and
* RET instructions are valid.</i>
*
* @param <V>
* type of the Value used for the analysis.
*
* @author Eric Bruneton
*/
public class Analyzer<V extends Value> implements Opcodes {
private final Interpreter<V> interpreter;
private int n;
private InsnList insns;
private List<TryCatchBlockNode>[] handlers;
private Frame<V>[] frames;
private Subroutine[] subroutines;
private boolean[] queued;
private int[] queue;
private int top;
/**
* Constructs a new {@link org.objectweb.asm.tree.analysis.Analyzer}.
*
* @param interpreter
* the interpreter to be used to symbolically interpret the
* bytecode instructions.
*/
public Analyzer(final Interpreter<V> interpreter) {
this.interpreter = interpreter;
}
/**
* Analyzes the given method.
*
* @param owner
* the internal name of the class to which the method belongs.
* @param m
* the method to be analyzed.
* @return the symbolic state of the execution stack frame at each bytecode
* instruction of the method. The size of the returned array is
* equal to the number of instructions (and labels) of the method. A
* given frame is <tt>null</tt> if and only if the corresponding
* instruction cannot be reached (dead code).
* @throws AnalyzerException
* if a problem occurs during the analysis.
*/
@SuppressWarnings("unchecked")
public Frame<V>[] analyze(final String owner, final MethodNode m)
throws AnalyzerException {
if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {
frames = (Frame<V>[]) new Frame<?>[0];
return frames;
}
n = m.instructions.size();
insns = m.instructions;
handlers = (List<TryCatchBlockNode>[]) new List<?>[n];
frames = (Frame<V>[]) new Frame<?>[n];
subroutines = new Subroutine[n];
queued = new boolean[n];
queue = new int[n];
top = 0;
// computes exception handlers for each instruction
for (int i = 0; i < m.tryCatchBlocks.size(); ++i) {
TryCatchBlockNode tcb = m.tryCatchBlocks.get(i);
int begin = insns.indexOf(tcb.start);
int end = insns.indexOf(tcb.end);
for (int j = begin; j < end; ++j) {
List<TryCatchBlockNode> insnHandlers = handlers[j];
if (insnHandlers == null) {
insnHandlers = new ArrayList<TryCatchBlockNode>();
handlers[j] = insnHandlers;
}
insnHandlers.add(tcb);
}
}
// computes the subroutine for each instruction:
Subroutine main = new Subroutine(null, m.maxLocals, null);
List<AbstractInsnNode> subroutineCalls = new ArrayList<AbstractInsnNode>();
Map<LabelNode, Subroutine> subroutineHeads = new HashMap<LabelNode, Subroutine>();
findSubroutine(0, main, subroutineCalls);
while (!subroutineCalls.isEmpty()) {
JumpInsnNode jsr = (JumpInsnNode) subroutineCalls.remove(0);
Subroutine sub = subroutineHeads.get(jsr.label);
if (sub == null) {
sub = new Subroutine(jsr.label, m.maxLocals, jsr);
subroutineHeads.put(jsr.label, sub);
findSubroutine(insns.indexOf(jsr.label), sub, subroutineCalls);
} else {
sub.callers.add(jsr);
}
}
for (int i = 0; i < n; ++i) {
if (subroutines[i] != null && subroutines[i].start == null) {
subroutines[i] = null;
}
}
// initializes the data structures for the control flow analysis
Frame<V> current = newFrame(m.maxLocals, m.maxStack);
Frame<V> handler = newFrame(m.maxLocals, m.maxStack);
current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)));
Type[] args = Type.getArgumentTypes(m.desc);
int local = 0;
if ((m.access & ACC_STATIC) == 0) {
Type ctype = Type.getObjectType(owner);
current.setLocal(local++, interpreter.newValue(ctype));
}
for (int i = 0; i < args.length; ++i) {
current.setLocal(local++, interpreter.newValue(args[i]));
if (args[i].getSize() == 2) {
current.setLocal(local++, interpreter.newValue(null));
}
}
while (local < m.maxLocals) {
current.setLocal(local++, interpreter.newValue(null));
}
merge(0, current, null);
init(owner, m);
// control flow analysis
while (top > 0) {
int insn = queue[--top];
Frame<V> f = frames[insn];
Subroutine subroutine = subroutines[insn];
queued[insn] = false;
AbstractInsnNode insnNode = null;
try {
insnNode = m.instructions.get(insn);
int insnOpcode = insnNode.getOpcode();
int insnType = insnNode.getType();
if (insnType == AbstractInsnNode.LABEL
|| insnType == AbstractInsnNode.LINE
|| insnType == AbstractInsnNode.FRAME) {
merge(insn + 1, f, subroutine);
newControlFlowEdge(insn, insn + 1);
} else {
current.init(f).execute(insnNode, interpreter);
subroutine = subroutine == null ? null : subroutine.copy();
if (insnNode instanceof JumpInsnNode) {
JumpInsnNode j = (JumpInsnNode) insnNode;
if (insnOpcode != GOTO && insnOpcode != JSR) {
merge(insn + 1, current, subroutine);
newControlFlowEdge(insn, insn + 1);
}
int jump = insns.indexOf(j.label);
if (insnOpcode == JSR) {
merge(jump, current, new Subroutine(j.label,
m.maxLocals, j));
} else {
merge(jump, current, subroutine);
}
newControlFlowEdge(insn, jump);
} else if (insnNode instanceof LookupSwitchInsnNode) {
LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;
int jump = insns.indexOf(lsi.dflt);
merge(jump, current, subroutine);
newControlFlowEdge(insn, jump);
for (int j = 0; j < lsi.labels.size(); ++j) {
LabelNode label = lsi.labels.get(j);
jump = insns.indexOf(label);
merge(jump, current, subroutine);
newControlFlowEdge(insn, jump);
}
} else if (insnNode instanceof TableSwitchInsnNode) {
TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;
int jump = insns.indexOf(tsi.dflt);
merge(jump, current, subroutine);
newControlFlowEdge(insn, jump);
for (int j = 0; j < tsi.labels.size(); ++j) {
LabelNode label = tsi.labels.get(j);
jump = insns.indexOf(label);
merge(jump, current, subroutine);
newControlFlowEdge(insn, jump);
}
} else if (insnOpcode == RET) {
if (subroutine == null) {
throw new AnalyzerException(insnNode,
"RET instruction outside of a sub routine");
}
for (int i = 0; i < subroutine.callers.size(); ++i) {
JumpInsnNode caller = subroutine.callers.get(i);
int call = insns.indexOf(caller);
if (frames[call] != null) {
merge(call + 1, frames[call], current,
subroutines[call], subroutine.access);
newControlFlowEdge(insn, call + 1);
}
}
} else if (insnOpcode != ATHROW
&& (insnOpcode < IRETURN || insnOpcode > RETURN)) {
if (subroutine != null) {
if (insnNode instanceof VarInsnNode) {
int var = ((VarInsnNode) insnNode).var;
subroutine.access[var] = true;
if (insnOpcode == LLOAD || insnOpcode == DLOAD
|| insnOpcode == LSTORE
|| insnOpcode == DSTORE) {
subroutine.access[var + 1] = true;
}
} else if (insnNode instanceof IincInsnNode) {
int var = ((IincInsnNode) insnNode).var;
subroutine.access[var] = true;
}
}
merge(insn + 1, current, subroutine);
newControlFlowEdge(insn, insn + 1);
}
}
List<TryCatchBlockNode> insnHandlers = handlers[insn];
if (insnHandlers != null) {
for (int i = 0; i < insnHandlers.size(); ++i) {
TryCatchBlockNode tcb = insnHandlers.get(i);
Type type;
if (tcb.type == null) {
type = Type.getObjectType("java/lang/Throwable");
} else {
type = Type.getObjectType(tcb.type);
}
int jump = insns.indexOf(tcb.handler);
if (newControlFlowExceptionEdge(insn, tcb)) {
handler.init(f);
handler.clearStack();
handler.push(interpreter.newValue(type));
merge(jump, handler, subroutine);
}
}
}
} catch (AnalyzerException e) {
throw new AnalyzerException(e.node, "Error at instruction "
+ insn + ": " + e.getMessage(), e);
} catch (Exception e) {
throw new AnalyzerException(insnNode, "Error at instruction "
+ insn + ": " + e.getMessage(), e);
}
}
return frames;
}
private void findSubroutine(int insn, final Subroutine sub,
final List<AbstractInsnNode> calls) throws AnalyzerException {
while (true) {
if (insn < 0 || insn >= n) {
throw new AnalyzerException(null,
"Execution can fall off end of the code");
}
if (subroutines[insn] != null) {
return;
}
subroutines[insn] = sub.copy();
AbstractInsnNode node = insns.get(insn);
// calls findSubroutine recursively on normal successors
if (node instanceof JumpInsnNode) {
if (node.getOpcode() == JSR) {
// do not follow a JSR, it leads to another subroutine!
calls.add(node);
} else {
JumpInsnNode jnode = (JumpInsnNode) node;
findSubroutine(insns.indexOf(jnode.label), sub, calls);
}
} else if (node instanceof TableSwitchInsnNode) {
TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node;
findSubroutine(insns.indexOf(tsnode.dflt), sub, calls);
for (int i = tsnode.labels.size() - 1; i >= 0; --i) {
LabelNode l = tsnode.labels.get(i);
findSubroutine(insns.indexOf(l), sub, calls);
}
} else if (node instanceof LookupSwitchInsnNode) {
LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node;
findSubroutine(insns.indexOf(lsnode.dflt), sub, calls);
for (int i = lsnode.labels.size() - 1; i >= 0; --i) {
LabelNode l = lsnode.labels.get(i);
findSubroutine(insns.indexOf(l), sub, calls);
}
}
// calls findSubroutine recursively on exception handler successors
List<TryCatchBlockNode> insnHandlers = handlers[insn];
if (insnHandlers != null) {
for (int i = 0; i < insnHandlers.size(); ++i) {
TryCatchBlockNode tcb = insnHandlers.get(i);
findSubroutine(insns.indexOf(tcb.handler), sub, calls);
}
}
// if insn does not falls through to the next instruction, return.
switch (node.getOpcode()) {
case GOTO:
case RET:
case TABLESWITCH:
case LOOKUPSWITCH:
case IRETURN:
case LRETURN:
case FRETURN:
case DRETURN:
case ARETURN:
case RETURN:
case ATHROW:
return;
}
insn++;
}
}
/**
* Returns the symbolic stack frame for each instruction of the last
* recently analyzed method.
*
* @return the symbolic state of the execution stack frame at each bytecode
* instruction of the method. The size of the returned array is
* equal to the number of instructions (and labels) of the method. A
* given frame is <tt>null</tt> if the corresponding instruction
* cannot be reached, or if an error occured during the analysis of
* the method.
*/
public Frame<V>[] getFrames() {
return frames;
}
/**
* Returns the exception handlers for the given instruction.
*
* @param insn
* the index of an instruction of the last recently analyzed
* method.
* @return a list of {@link TryCatchBlockNode} objects.
*/
public List<TryCatchBlockNode> getHandlers(final int insn) {
return handlers[insn];
}
/**
* Initializes this analyzer. This method is called just before the
* execution of control flow analysis loop in #analyze. The default
* implementation of this method does nothing.
*
* @param owner
* the internal name of the class to which the method belongs.
* @param m
* the method to be analyzed.
* @throws AnalyzerException
* if a problem occurs.
*/
protected void init(String owner, MethodNode m) throws AnalyzerException {
}
/**
* Constructs a new frame with the given size.
*
* @param nLocals
* the maximum number of local variables of the frame.
* @param nStack
* the maximum stack size of the frame.
* @return the created frame.
*/
protected Frame<V> newFrame(final int nLocals, final int nStack) {
return new Frame<V>(nLocals, nStack);
}
/**
* Constructs a new frame that is identical to the given frame.
*
* @param src
* a frame.
* @return the created frame.
*/
protected Frame<V> newFrame(final Frame<? extends V> src) {
return new Frame<V>(src);
}
/**
* Creates a control flow graph edge. The default implementation of this
* method does nothing. It can be overriden in order to construct the
* control flow graph of a method (this method is called by the
* {@link #analyze analyze} method during its visit of the method's code).
*
* @param insn
* an instruction index.
* @param successor
* index of a successor instruction.
*/
protected void newControlFlowEdge(final int insn, final int successor) {
}
/**
* Creates a control flow graph edge corresponding to an exception handler.
* The default implementation of this method does nothing. It can be
* overridden in order to construct the control flow graph of a method (this
* method is called by the {@link #analyze analyze} method during its visit
* of the method's code).
*
* @param insn
* an instruction index.
* @param successor
* index of a successor instruction.
* @return true if this edge must be considered in the data flow analysis
* performed by this analyzer, or false otherwise. The default
* implementation of this method always returns true.
*/
protected boolean newControlFlowExceptionEdge(final int insn,
final int successor) {
return true;
}
/**
* Creates a control flow graph edge corresponding to an exception handler.
* The default implementation of this method delegates to
* {@link #newControlFlowExceptionEdge(int, int)
* newControlFlowExceptionEdge(int, int)}. It can be overridden in order to
* construct the control flow graph of a method (this method is called by
* the {@link #analyze analyze} method during its visit of the method's
* code).
*
* @param insn
* an instruction index.
* @param tcb
* TryCatchBlockNode corresponding to this edge.
* @return true if this edge must be considered in the data flow analysis
* performed by this analyzer, or false otherwise. The default
* implementation of this method delegates to
* {@link #newControlFlowExceptionEdge(int, int)
* newControlFlowExceptionEdge(int, int)}.
*/
protected boolean newControlFlowExceptionEdge(final int insn,
final TryCatchBlockNode tcb) {
return newControlFlowExceptionEdge(insn, insns.indexOf(tcb.handler));
}
// -------------------------------------------------------------------------
private void merge(final int insn, final Frame<V> frame,
final Subroutine subroutine) throws AnalyzerException {
Frame<V> oldFrame = frames[insn];
Subroutine oldSubroutine = subroutines[insn];
boolean changes;
if (oldFrame == null) {
frames[insn] = newFrame(frame);
changes = true;
} else {
changes = oldFrame.merge(frame, interpreter);
}
if (oldSubroutine == null) {
if (subroutine != null) {
subroutines[insn] = subroutine.copy();
changes = true;
}
} else {
if (subroutine != null) {
changes |= oldSubroutine.merge(subroutine);
}
}
if (changes && !queued[insn]) {
queued[insn] = true;
queue[top++] = insn;
}
}
private void merge(final int insn, final Frame<V> beforeJSR,
final Frame<V> afterRET, final Subroutine subroutineBeforeJSR,
final boolean[] access) throws AnalyzerException {
Frame<V> oldFrame = frames[insn];
Subroutine oldSubroutine = subroutines[insn];
boolean changes;
afterRET.merge(beforeJSR, access);
if (oldFrame == null) {
frames[insn] = newFrame(afterRET);
changes = true;
} else {
changes = oldFrame.merge(afterRET, interpreter);
}
if (oldSubroutine != null && subroutineBeforeJSR != null) {
changes |= oldSubroutine.merge(subroutineBeforeJSR);
}
if (changes && !queued[insn]) {
queued[insn] = true;
queue[top++] = insn;
}
}
}
@@ -0,0 +1,61 @@
/***
* 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.tree.analysis;
import org.objectweb.asm.tree.AbstractInsnNode;
/**
* Thrown if a problem occurs during the analysis of a method.
*
* @author Bing Ran
* @author Eric Bruneton
*/
public class AnalyzerException extends Exception {
private static final long serialVersionUID = -1788355977291059681L;
public final AbstractInsnNode node;
public AnalyzerException(final AbstractInsnNode node, final String msg) {
super(msg);
this.node = node;
}
public AnalyzerException(final AbstractInsnNode node, final String msg,
final Throwable exception) {
super(msg, exception);
this.node = node;
}
public AnalyzerException(final AbstractInsnNode node, final String msg,
final Object expected, final Value encountered) {
super((msg == null ? "Expected " : msg + ": expected ") + expected
+ ", but found " + encountered);
this.node = node;
}
}
@@ -0,0 +1,358 @@
/***
* 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.tree.analysis;
import java.util.List;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
/**
* An {@link Interpreter} for {@link BasicValue} values.
*
* @author Eric Bruneton
* @author Bing Ran
*/
public class BasicInterpreter extends Interpreter<BasicValue> implements
Opcodes {
public BasicInterpreter() {
super(ASM4);
}
protected BasicInterpreter(final int api) {
super(api);
}
@Override
public BasicValue newValue(final Type type) {
if (type == null) {
return BasicValue.UNINITIALIZED_VALUE;
}
switch (type.getSort()) {
case Type.VOID:
return null;
case Type.BOOLEAN:
case Type.CHAR:
case Type.BYTE:
case Type.SHORT:
case Type.INT:
return BasicValue.INT_VALUE;
case Type.FLOAT:
return BasicValue.FLOAT_VALUE;
case Type.LONG:
return BasicValue.LONG_VALUE;
case Type.DOUBLE:
return BasicValue.DOUBLE_VALUE;
case Type.ARRAY:
case Type.OBJECT:
return BasicValue.REFERENCE_VALUE;
default:
throw new Error("Internal error");
}
}
@Override
public BasicValue newOperation(final AbstractInsnNode insn)
throws AnalyzerException {
switch (insn.getOpcode()) {
case ACONST_NULL:
return newValue(Type.getObjectType("null"));
case ICONST_M1:
case ICONST_0:
case ICONST_1:
case ICONST_2:
case ICONST_3:
case ICONST_4:
case ICONST_5:
return BasicValue.INT_VALUE;
case LCONST_0:
case LCONST_1:
return BasicValue.LONG_VALUE;
case FCONST_0:
case FCONST_1:
case FCONST_2:
return BasicValue.FLOAT_VALUE;
case DCONST_0:
case DCONST_1:
return BasicValue.DOUBLE_VALUE;
case BIPUSH:
case SIPUSH:
return BasicValue.INT_VALUE;
case LDC:
Object cst = ((LdcInsnNode) insn).cst;
if (cst instanceof Integer) {
return BasicValue.INT_VALUE;
} else if (cst instanceof Float) {
return BasicValue.FLOAT_VALUE;
} else if (cst instanceof Long) {
return BasicValue.LONG_VALUE;
} else if (cst instanceof Double) {
return BasicValue.DOUBLE_VALUE;
} else if (cst instanceof String) {
return newValue(Type.getObjectType("java/lang/String"));
} else if (cst instanceof Type) {
int sort = ((Type) cst).getSort();
if (sort == Type.OBJECT || sort == Type.ARRAY) {
return newValue(Type.getObjectType("java/lang/Class"));
} else if (sort == Type.METHOD) {
return newValue(Type
.getObjectType("java/lang/invoke/MethodType"));
} else {
throw new IllegalArgumentException("Illegal LDC constant "
+ cst);
}
} else if (cst instanceof Handle) {
return newValue(Type
.getObjectType("java/lang/invoke/MethodHandle"));
} else {
throw new IllegalArgumentException("Illegal LDC constant "
+ cst);
}
case JSR:
return BasicValue.RETURNADDRESS_VALUE;
case GETSTATIC:
return newValue(Type.getType(((FieldInsnNode) insn).desc));
case NEW:
return newValue(Type.getObjectType(((TypeInsnNode) insn).desc));
default:
throw new Error("Internal error.");
}
}
@Override
public BasicValue copyOperation(final AbstractInsnNode insn,
final BasicValue value) throws AnalyzerException {
return value;
}
@Override
public BasicValue unaryOperation(final AbstractInsnNode insn,
final BasicValue value) throws AnalyzerException {
switch (insn.getOpcode()) {
case INEG:
case IINC:
case L2I:
case F2I:
case D2I:
case I2B:
case I2C:
case I2S:
return BasicValue.INT_VALUE;
case FNEG:
case I2F:
case L2F:
case D2F:
return BasicValue.FLOAT_VALUE;
case LNEG:
case I2L:
case F2L:
case D2L:
return BasicValue.LONG_VALUE;
case DNEG:
case I2D:
case L2D:
case F2D:
return BasicValue.DOUBLE_VALUE;
case IFEQ:
case IFNE:
case IFLT:
case IFGE:
case IFGT:
case IFLE:
case TABLESWITCH:
case LOOKUPSWITCH:
case IRETURN:
case LRETURN:
case FRETURN:
case DRETURN:
case ARETURN:
case PUTSTATIC:
return null;
case GETFIELD:
return newValue(Type.getType(((FieldInsnNode) insn).desc));
case NEWARRAY:
switch (((IntInsnNode) insn).operand) {
case T_BOOLEAN:
return newValue(Type.getType("[Z"));
case T_CHAR:
return newValue(Type.getType("[C"));
case T_BYTE:
return newValue(Type.getType("[B"));
case T_SHORT:
return newValue(Type.getType("[S"));
case T_INT:
return newValue(Type.getType("[I"));
case T_FLOAT:
return newValue(Type.getType("[F"));
case T_DOUBLE:
return newValue(Type.getType("[D"));
case T_LONG:
return newValue(Type.getType("[J"));
default:
throw new AnalyzerException(insn, "Invalid array type");
}
case ANEWARRAY:
String desc = ((TypeInsnNode) insn).desc;
return newValue(Type.getType("[" + Type.getObjectType(desc)));
case ARRAYLENGTH:
return BasicValue.INT_VALUE;
case ATHROW:
return null;
case CHECKCAST:
desc = ((TypeInsnNode) insn).desc;
return newValue(Type.getObjectType(desc));
case INSTANCEOF:
return BasicValue.INT_VALUE;
case MONITORENTER:
case MONITOREXIT:
case IFNULL:
case IFNONNULL:
return null;
default:
throw new Error("Internal error.");
}
}
@Override
public BasicValue binaryOperation(final AbstractInsnNode insn,
final BasicValue value1, final BasicValue value2)
throws AnalyzerException {
switch (insn.getOpcode()) {
case IALOAD:
case BALOAD:
case CALOAD:
case SALOAD:
case IADD:
case ISUB:
case IMUL:
case IDIV:
case IREM:
case ISHL:
case ISHR:
case IUSHR:
case IAND:
case IOR:
case IXOR:
return BasicValue.INT_VALUE;
case FALOAD:
case FADD:
case FSUB:
case FMUL:
case FDIV:
case FREM:
return BasicValue.FLOAT_VALUE;
case LALOAD:
case LADD:
case LSUB:
case LMUL:
case LDIV:
case LREM:
case LSHL:
case LSHR:
case LUSHR:
case LAND:
case LOR:
case LXOR:
return BasicValue.LONG_VALUE;
case DALOAD:
case DADD:
case DSUB:
case DMUL:
case DDIV:
case DREM:
return BasicValue.DOUBLE_VALUE;
case AALOAD:
return BasicValue.REFERENCE_VALUE;
case LCMP:
case FCMPL:
case FCMPG:
case DCMPL:
case DCMPG:
return BasicValue.INT_VALUE;
case IF_ICMPEQ:
case IF_ICMPNE:
case IF_ICMPLT:
case IF_ICMPGE:
case IF_ICMPGT:
case IF_ICMPLE:
case IF_ACMPEQ:
case IF_ACMPNE:
case PUTFIELD:
return null;
default:
throw new Error("Internal error.");
}
}
@Override
public BasicValue ternaryOperation(final AbstractInsnNode insn,
final BasicValue value1, final BasicValue value2,
final BasicValue value3) throws AnalyzerException {
return null;
}
@Override
public BasicValue naryOperation(final AbstractInsnNode insn,
final List<? extends BasicValue> values) throws AnalyzerException {
int opcode = insn.getOpcode();
if (opcode == MULTIANEWARRAY) {
return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc));
} else if (opcode == INVOKEDYNAMIC) {
return newValue(Type
.getReturnType(((InvokeDynamicInsnNode) insn).desc));
} else {
return newValue(Type.getReturnType(((MethodInsnNode) insn).desc));
}
}
@Override
public void returnOperation(final AbstractInsnNode insn,
final BasicValue value, final BasicValue expected)
throws AnalyzerException {
}
@Override
public BasicValue merge(final BasicValue v, final BasicValue w) {
if (!v.equals(w)) {
return BasicValue.UNINITIALIZED_VALUE;
}
return v;
}
}
@@ -0,0 +1,111 @@
/***
* 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.tree.analysis;
import org.objectweb.asm.Type;
/**
* A {@link Value} that is represented by its type in a seven types type system.
* This type system distinguishes the UNINITIALZED, INT, FLOAT, LONG, DOUBLE,
* REFERENCE and RETURNADDRESS types.
*
* @author Eric Bruneton
*/
public class BasicValue implements Value {
public static final BasicValue UNINITIALIZED_VALUE = new BasicValue(null);
public static final BasicValue INT_VALUE = new BasicValue(Type.INT_TYPE);
public static final BasicValue FLOAT_VALUE = new BasicValue(Type.FLOAT_TYPE);
public static final BasicValue LONG_VALUE = new BasicValue(Type.LONG_TYPE);
public static final BasicValue DOUBLE_VALUE = new BasicValue(
Type.DOUBLE_TYPE);
public static final BasicValue REFERENCE_VALUE = new BasicValue(
Type.getObjectType("java/lang/Object"));
public static final BasicValue RETURNADDRESS_VALUE = new BasicValue(
Type.VOID_TYPE);
private final Type type;
public BasicValue(final Type type) {
this.type = type;
}
public Type getType() {
return type;
}
public int getSize() {
return type == Type.LONG_TYPE || type == Type.DOUBLE_TYPE ? 2 : 1;
}
public boolean isReference() {
return type != null
&& (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY);
}
@Override
public boolean equals(final Object value) {
if (value == this) {
return true;
} else if (value instanceof BasicValue) {
if (type == null) {
return ((BasicValue) value).type == null;
} else {
return type.equals(((BasicValue) value).type);
}
} else {
return false;
}
}
@Override
public int hashCode() {
return type == null ? 0 : type.hashCode();
}
@Override
public String toString() {
if (this == UNINITIALIZED_VALUE) {
return ".";
} else if (this == RETURNADDRESS_VALUE) {
return "A";
} else if (this == REFERENCE_VALUE) {
return "R";
} else {
return type.getDescriptor();
}
}
}
@@ -0,0 +1,433 @@
/***
* 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.tree.analysis;
import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
/**
* An extended {@link org.objectweb.asm.tree.analysis.BasicInterpreter} that checks that bytecode instructions
* are correctly used.
*
* @author Eric Bruneton
* @author Bing Ran
*/
public class BasicVerifier extends BasicInterpreter {
public BasicVerifier() {
super(ASM4);
}
protected BasicVerifier(final int api) {
super(api);
}
@Override
public BasicValue copyOperation(final AbstractInsnNode insn,
final BasicValue value) throws AnalyzerException {
Value expected;
switch (insn.getOpcode()) {
case ILOAD:
case ISTORE:
expected = BasicValue.INT_VALUE;
break;
case FLOAD:
case FSTORE:
expected = BasicValue.FLOAT_VALUE;
break;
case LLOAD:
case LSTORE:
expected = BasicValue.LONG_VALUE;
break;
case DLOAD:
case DSTORE:
expected = BasicValue.DOUBLE_VALUE;
break;
case ALOAD:
if (!value.isReference()) {
throw new AnalyzerException(insn, null, "an object reference",
value);
}
return value;
case ASTORE:
if (!value.isReference()
&& !BasicValue.RETURNADDRESS_VALUE.equals(value)) {
throw new AnalyzerException(insn, null,
"an object reference or a return address", value);
}
return value;
default:
return value;
}
if (!expected.equals(value)) {
throw new AnalyzerException(insn, null, expected, value);
}
return value;
}
@Override
public BasicValue unaryOperation(final AbstractInsnNode insn,
final BasicValue value) throws AnalyzerException {
BasicValue expected;
switch (insn.getOpcode()) {
case INEG:
case IINC:
case I2F:
case I2L:
case I2D:
case I2B:
case I2C:
case I2S:
case IFEQ:
case IFNE:
case IFLT:
case IFGE:
case IFGT:
case IFLE:
case TABLESWITCH:
case LOOKUPSWITCH:
case IRETURN:
case NEWARRAY:
case ANEWARRAY:
expected = BasicValue.INT_VALUE;
break;
case FNEG:
case F2I:
case F2L:
case F2D:
case FRETURN:
expected = BasicValue.FLOAT_VALUE;
break;
case LNEG:
case L2I:
case L2F:
case L2D:
case LRETURN:
expected = BasicValue.LONG_VALUE;
break;
case DNEG:
case D2I:
case D2F:
case D2L:
case DRETURN:
expected = BasicValue.DOUBLE_VALUE;
break;
case GETFIELD:
expected = newValue(Type
.getObjectType(((FieldInsnNode) insn).owner));
break;
case CHECKCAST:
if (!value.isReference()) {
throw new AnalyzerException(insn, null, "an object reference",
value);
}
return super.unaryOperation(insn, value);
case ARRAYLENGTH:
if (!isArrayValue(value)) {
throw new AnalyzerException(insn, null, "an array reference",
value);
}
return super.unaryOperation(insn, value);
case ARETURN:
case ATHROW:
case INSTANCEOF:
case MONITORENTER:
case MONITOREXIT:
case IFNULL:
case IFNONNULL:
if (!value.isReference()) {
throw new AnalyzerException(insn, null, "an object reference",
value);
}
return super.unaryOperation(insn, value);
case PUTSTATIC:
expected = newValue(Type.getType(((FieldInsnNode) insn).desc));
break;
default:
throw new Error("Internal error.");
}
if (!isSubTypeOf(value, expected)) {
throw new AnalyzerException(insn, null, expected, value);
}
return super.unaryOperation(insn, value);
}
@Override
public BasicValue binaryOperation(final AbstractInsnNode insn,
final BasicValue value1, final BasicValue value2)
throws AnalyzerException {
BasicValue expected1;
BasicValue expected2;
switch (insn.getOpcode()) {
case IALOAD:
expected1 = newValue(Type.getType("[I"));
expected2 = BasicValue.INT_VALUE;
break;
case BALOAD:
if (isSubTypeOf(value1, newValue(Type.getType("[Z")))) {
expected1 = newValue(Type.getType("[Z"));
} else {
expected1 = newValue(Type.getType("[B"));
}
expected2 = BasicValue.INT_VALUE;
break;
case CALOAD:
expected1 = newValue(Type.getType("[C"));
expected2 = BasicValue.INT_VALUE;
break;
case SALOAD:
expected1 = newValue(Type.getType("[S"));
expected2 = BasicValue.INT_VALUE;
break;
case LALOAD:
expected1 = newValue(Type.getType("[J"));
expected2 = BasicValue.INT_VALUE;
break;
case FALOAD:
expected1 = newValue(Type.getType("[F"));
expected2 = BasicValue.INT_VALUE;
break;
case DALOAD:
expected1 = newValue(Type.getType("[D"));
expected2 = BasicValue.INT_VALUE;
break;
case AALOAD:
expected1 = newValue(Type.getType("[Ljava/lang/Object;"));
expected2 = BasicValue.INT_VALUE;
break;
case IADD:
case ISUB:
case IMUL:
case IDIV:
case IREM:
case ISHL:
case ISHR:
case IUSHR:
case IAND:
case IOR:
case IXOR:
case IF_ICMPEQ:
case IF_ICMPNE:
case IF_ICMPLT:
case IF_ICMPGE:
case IF_ICMPGT:
case IF_ICMPLE:
expected1 = BasicValue.INT_VALUE;
expected2 = BasicValue.INT_VALUE;
break;
case FADD:
case FSUB:
case FMUL:
case FDIV:
case FREM:
case FCMPL:
case FCMPG:
expected1 = BasicValue.FLOAT_VALUE;
expected2 = BasicValue.FLOAT_VALUE;
break;
case LADD:
case LSUB:
case LMUL:
case LDIV:
case LREM:
case LAND:
case LOR:
case LXOR:
case LCMP:
expected1 = BasicValue.LONG_VALUE;
expected2 = BasicValue.LONG_VALUE;
break;
case LSHL:
case LSHR:
case LUSHR:
expected1 = BasicValue.LONG_VALUE;
expected2 = BasicValue.INT_VALUE;
break;
case DADD:
case DSUB:
case DMUL:
case DDIV:
case DREM:
case DCMPL:
case DCMPG:
expected1 = BasicValue.DOUBLE_VALUE;
expected2 = BasicValue.DOUBLE_VALUE;
break;
case IF_ACMPEQ:
case IF_ACMPNE:
expected1 = BasicValue.REFERENCE_VALUE;
expected2 = BasicValue.REFERENCE_VALUE;
break;
case PUTFIELD:
FieldInsnNode fin = (FieldInsnNode) insn;
expected1 = newValue(Type.getObjectType(fin.owner));
expected2 = newValue(Type.getType(fin.desc));
break;
default:
throw new Error("Internal error.");
}
if (!isSubTypeOf(value1, expected1)) {
throw new AnalyzerException(insn, "First argument", expected1,
value1);
} else if (!isSubTypeOf(value2, expected2)) {
throw new AnalyzerException(insn, "Second argument", expected2,
value2);
}
if (insn.getOpcode() == AALOAD) {
return getElementValue(value1);
} else {
return super.binaryOperation(insn, value1, value2);
}
}
@Override
public BasicValue ternaryOperation(final AbstractInsnNode insn,
final BasicValue value1, final BasicValue value2,
final BasicValue value3) throws AnalyzerException {
BasicValue expected1;
BasicValue expected3;
switch (insn.getOpcode()) {
case IASTORE:
expected1 = newValue(Type.getType("[I"));
expected3 = BasicValue.INT_VALUE;
break;
case BASTORE:
if (isSubTypeOf(value1, newValue(Type.getType("[Z")))) {
expected1 = newValue(Type.getType("[Z"));
} else {
expected1 = newValue(Type.getType("[B"));
}
expected3 = BasicValue.INT_VALUE;
break;
case CASTORE:
expected1 = newValue(Type.getType("[C"));
expected3 = BasicValue.INT_VALUE;
break;
case SASTORE:
expected1 = newValue(Type.getType("[S"));
expected3 = BasicValue.INT_VALUE;
break;
case LASTORE:
expected1 = newValue(Type.getType("[J"));
expected3 = BasicValue.LONG_VALUE;
break;
case FASTORE:
expected1 = newValue(Type.getType("[F"));
expected3 = BasicValue.FLOAT_VALUE;
break;
case DASTORE:
expected1 = newValue(Type.getType("[D"));
expected3 = BasicValue.DOUBLE_VALUE;
break;
case AASTORE:
expected1 = value1;
expected3 = BasicValue.REFERENCE_VALUE;
break;
default:
throw new Error("Internal error.");
}
if (!isSubTypeOf(value1, expected1)) {
throw new AnalyzerException(insn, "First argument", "a "
+ expected1 + " array reference", value1);
} else if (!BasicValue.INT_VALUE.equals(value2)) {
throw new AnalyzerException(insn, "Second argument",
BasicValue.INT_VALUE, value2);
} else if (!isSubTypeOf(value3, expected3)) {
throw new AnalyzerException(insn, "Third argument", expected3,
value3);
}
return null;
}
@Override
public BasicValue naryOperation(final AbstractInsnNode insn,
final List<? extends BasicValue> values) throws AnalyzerException {
int opcode = insn.getOpcode();
if (opcode == MULTIANEWARRAY) {
for (int i = 0; i < values.size(); ++i) {
if (!BasicValue.INT_VALUE.equals(values.get(i))) {
throw new AnalyzerException(insn, null,
BasicValue.INT_VALUE, values.get(i));
}
}
} else {
int i = 0;
int j = 0;
if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) {
Type owner = Type.getObjectType(((MethodInsnNode) insn).owner);
if (!isSubTypeOf(values.get(i++), newValue(owner))) {
throw new AnalyzerException(insn, "Method owner",
newValue(owner), values.get(0));
}
}
String desc = (opcode == INVOKEDYNAMIC) ? ((InvokeDynamicInsnNode) insn).desc
: ((MethodInsnNode) insn).desc;
Type[] args = Type.getArgumentTypes(desc);
while (i < values.size()) {
BasicValue expected = newValue(args[j++]);
BasicValue encountered = values.get(i++);
if (!isSubTypeOf(encountered, expected)) {
throw new AnalyzerException(insn, "Argument " + j,
expected, encountered);
}
}
}
return super.naryOperation(insn, values);
}
@Override
public void returnOperation(final AbstractInsnNode insn,
final BasicValue value, final BasicValue expected)
throws AnalyzerException {
if (!isSubTypeOf(value, expected)) {
throw new AnalyzerException(insn, "Incompatible return type",
expected, value);
}
}
protected boolean isArrayValue(final BasicValue value) {
return value.isReference();
}
protected BasicValue getElementValue(final BasicValue objectArrayValue)
throws AnalyzerException {
return BasicValue.REFERENCE_VALUE;
}
protected boolean isSubTypeOf(final BasicValue value,
final BasicValue expected) {
return value.equals(expected);
}
}
@@ -0,0 +1,730 @@
/***
* 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.tree.analysis;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
/**
* A symbolic execution stack frame. A stack frame contains a set of local
* variable slots, and an operand stack. Warning: long and double values are
* represented by <i>two</i> slots in local variables, and by <i>one</i> slot in
* the operand stack.
*
* @param <V>
* type of the Value used for the analysis.
*
* @author Eric Bruneton
*/
public class Frame<V extends Value> {
/**
* The expected return type of the analyzed method, or <tt>null</tt> if the
* method returns void.
*/
private V returnValue;
/**
* The local variables and operand stack of this frame.
*/
private V[] values;
/**
* The number of local variables of this frame.
*/
private int locals;
/**
* The number of elements in the operand stack.
*/
private int top;
/**
* Constructs a new frame with the given size.
*
* @param nLocals
* the maximum number of local variables of the frame.
* @param nStack
* the maximum stack size of the frame.
*/
@SuppressWarnings("unchecked")
public Frame(final int nLocals, final int nStack) {
this.values = (V[]) new Value[nLocals + nStack];
this.locals = nLocals;
}
/**
* Constructs a new frame that is identical to the given frame.
*
* @param src
* a frame.
*/
public Frame(final Frame<? extends V> src) {
this(src.locals, src.values.length - src.locals);
init(src);
}
/**
* Copies the state of the given frame into this frame.
*
* @param src
* a frame.
* @return this frame.
*/
public Frame<V> init(final Frame<? extends V> src) {
returnValue = src.returnValue;
System.arraycopy(src.values, 0, values, 0, values.length);
top = src.top;
return this;
}
/**
* Sets the expected return type of the analyzed method.
*
* @param v
* the expected return type of the analyzed method, or
* <tt>null</tt> if the method returns void.
*/
public void setReturn(final V v) {
returnValue = v;
}
/**
* Returns the maximum number of local variables of this frame.
*
* @return the maximum number of local variables of this frame.
*/
public int getLocals() {
return locals;
}
/**
* Returns the value of the given local variable.
*
* @param i
* a local variable index.
* @return the value of the given local variable.
* @throws IndexOutOfBoundsException
* if the variable does not exist.
*/
public V getLocal(final int i) throws IndexOutOfBoundsException {
if (i >= locals) {
throw new IndexOutOfBoundsException(
"Trying to access an inexistant local variable");
}
return values[i];
}
/**
* Sets the value of the given local variable.
*
* @param i
* a local variable index.
* @param value
* the new value of this local variable.
* @throws IndexOutOfBoundsException
* if the variable does not exist.
*/
public void setLocal(final int i, final V value)
throws IndexOutOfBoundsException {
if (i >= locals) {
throw new IndexOutOfBoundsException(
"Trying to access an inexistant local variable " + i);
}
values[i] = value;
}
/**
* Returns the number of values in the operand stack of this frame. Long and
* double values are treated as single values.
*
* @return the number of values in the operand stack of this frame.
*/
public int getStackSize() {
return top;
}
/**
* Returns the value of the given operand stack slot.
*
* @param i
* the index of an operand stack slot.
* @return the value of the given operand stack slot.
* @throws IndexOutOfBoundsException
* if the operand stack slot does not exist.
*/
public V getStack(final int i) throws IndexOutOfBoundsException {
return values[i + locals];
}
/**
* Clears the operand stack of this frame.
*/
public void clearStack() {
top = 0;
}
/**
* Pops a value from the operand stack of this frame.
*
* @return the value that has been popped from the stack.
* @throws IndexOutOfBoundsException
* if the operand stack is empty.
*/
public V pop() throws IndexOutOfBoundsException {
if (top == 0) {
throw new IndexOutOfBoundsException(
"Cannot pop operand off an empty stack.");
}
return values[--top + locals];
}
/**
* Pushes a value into the operand stack of this frame.
*
* @param value
* the value that must be pushed into the stack.
* @throws IndexOutOfBoundsException
* if the operand stack is full.
*/
public void push(final V value) throws IndexOutOfBoundsException {
if (top + locals >= values.length) {
throw new IndexOutOfBoundsException(
"Insufficient maximum stack size.");
}
values[top++ + locals] = value;
}
public void execute(final AbstractInsnNode insn,
final Interpreter<V> interpreter) throws AnalyzerException {
V value1, value2, value3, value4;
List<V> values;
int var;
switch (insn.getOpcode()) {
case Opcodes.NOP:
break;
case Opcodes.ACONST_NULL:
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.LCONST_0:
case Opcodes.LCONST_1:
case Opcodes.FCONST_0:
case Opcodes.FCONST_1:
case Opcodes.FCONST_2:
case Opcodes.DCONST_0:
case Opcodes.DCONST_1:
case Opcodes.BIPUSH:
case Opcodes.SIPUSH:
case Opcodes.LDC:
push(interpreter.newOperation(insn));
break;
case Opcodes.ILOAD:
case Opcodes.LLOAD:
case Opcodes.FLOAD:
case Opcodes.DLOAD:
case Opcodes.ALOAD:
push(interpreter.copyOperation(insn,
getLocal(((VarInsnNode) insn).var)));
break;
case Opcodes.IALOAD:
case Opcodes.LALOAD:
case Opcodes.FALOAD:
case Opcodes.DALOAD:
case Opcodes.AALOAD:
case Opcodes.BALOAD:
case Opcodes.CALOAD:
case Opcodes.SALOAD:
value2 = pop();
value1 = pop();
push(interpreter.binaryOperation(insn, value1, value2));
break;
case Opcodes.ISTORE:
case Opcodes.LSTORE:
case Opcodes.FSTORE:
case Opcodes.DSTORE:
case Opcodes.ASTORE:
value1 = interpreter.copyOperation(insn, pop());
var = ((VarInsnNode) insn).var;
setLocal(var, value1);
if (value1.getSize() == 2) {
setLocal(var + 1, interpreter.newValue(null));
}
if (var > 0) {
Value local = getLocal(var - 1);
if (local != null && local.getSize() == 2) {
setLocal(var - 1, interpreter.newValue(null));
}
}
break;
case Opcodes.IASTORE:
case Opcodes.LASTORE:
case Opcodes.FASTORE:
case Opcodes.DASTORE:
case Opcodes.AASTORE:
case Opcodes.BASTORE:
case Opcodes.CASTORE:
case Opcodes.SASTORE:
value3 = pop();
value2 = pop();
value1 = pop();
interpreter.ternaryOperation(insn, value1, value2, value3);
break;
case Opcodes.POP:
if (pop().getSize() == 2) {
throw new AnalyzerException(insn, "Illegal use of POP");
}
break;
case Opcodes.POP2:
if (pop().getSize() == 1) {
if (pop().getSize() != 1) {
throw new AnalyzerException(insn, "Illegal use of POP2");
}
}
break;
case Opcodes.DUP:
value1 = pop();
if (value1.getSize() != 1) {
throw new AnalyzerException(insn, "Illegal use of DUP");
}
push(value1);
push(interpreter.copyOperation(insn, value1));
break;
case Opcodes.DUP_X1:
value1 = pop();
value2 = pop();
if (value1.getSize() != 1 || value2.getSize() != 1) {
throw new AnalyzerException(insn, "Illegal use of DUP_X1");
}
push(interpreter.copyOperation(insn, value1));
push(value2);
push(value1);
break;
case Opcodes.DUP_X2:
value1 = pop();
if (value1.getSize() == 1) {
value2 = pop();
if (value2.getSize() == 1) {
value3 = pop();
if (value3.getSize() == 1) {
push(interpreter.copyOperation(insn, value1));
push(value3);
push(value2);
push(value1);
break;
}
} else {
push(interpreter.copyOperation(insn, value1));
push(value2);
push(value1);
break;
}
}
throw new AnalyzerException(insn, "Illegal use of DUP_X2");
case Opcodes.DUP2:
value1 = pop();
if (value1.getSize() == 1) {
value2 = pop();
if (value2.getSize() == 1) {
push(value2);
push(value1);
push(interpreter.copyOperation(insn, value2));
push(interpreter.copyOperation(insn, value1));
break;
}
} else {
push(value1);
push(interpreter.copyOperation(insn, value1));
break;
}
throw new AnalyzerException(insn, "Illegal use of DUP2");
case Opcodes.DUP2_X1:
value1 = pop();
if (value1.getSize() == 1) {
value2 = pop();
if (value2.getSize() == 1) {
value3 = pop();
if (value3.getSize() == 1) {
push(interpreter.copyOperation(insn, value2));
push(interpreter.copyOperation(insn, value1));
push(value3);
push(value2);
push(value1);
break;
}
}
} else {
value2 = pop();
if (value2.getSize() == 1) {
push(interpreter.copyOperation(insn, value1));
push(value2);
push(value1);
break;
}
}
throw new AnalyzerException(insn, "Illegal use of DUP2_X1");
case Opcodes.DUP2_X2:
value1 = pop();
if (value1.getSize() == 1) {
value2 = pop();
if (value2.getSize() == 1) {
value3 = pop();
if (value3.getSize() == 1) {
value4 = pop();
if (value4.getSize() == 1) {
push(interpreter.copyOperation(insn, value2));
push(interpreter.copyOperation(insn, value1));
push(value4);
push(value3);
push(value2);
push(value1);
break;
}
} else {
push(interpreter.copyOperation(insn, value2));
push(interpreter.copyOperation(insn, value1));
push(value3);
push(value2);
push(value1);
break;
}
}
} else {
value2 = pop();
if (value2.getSize() == 1) {
value3 = pop();
if (value3.getSize() == 1) {
push(interpreter.copyOperation(insn, value1));
push(value3);
push(value2);
push(value1);
break;
}
} else {
push(interpreter.copyOperation(insn, value1));
push(value2);
push(value1);
break;
}
}
throw new AnalyzerException(insn, "Illegal use of DUP2_X2");
case Opcodes.SWAP:
value2 = pop();
value1 = pop();
if (value1.getSize() != 1 || value2.getSize() != 1) {
throw new AnalyzerException(insn, "Illegal use of SWAP");
}
push(interpreter.copyOperation(insn, value2));
push(interpreter.copyOperation(insn, value1));
break;
case Opcodes.IADD:
case Opcodes.LADD:
case Opcodes.FADD:
case Opcodes.DADD:
case Opcodes.ISUB:
case Opcodes.LSUB:
case Opcodes.FSUB:
case Opcodes.DSUB:
case Opcodes.IMUL:
case Opcodes.LMUL:
case Opcodes.FMUL:
case Opcodes.DMUL:
case Opcodes.IDIV:
case Opcodes.LDIV:
case Opcodes.FDIV:
case Opcodes.DDIV:
case Opcodes.IREM:
case Opcodes.LREM:
case Opcodes.FREM:
case Opcodes.DREM:
value2 = pop();
value1 = pop();
push(interpreter.binaryOperation(insn, value1, value2));
break;
case Opcodes.INEG:
case Opcodes.LNEG:
case Opcodes.FNEG:
case Opcodes.DNEG:
push(interpreter.unaryOperation(insn, pop()));
break;
case Opcodes.ISHL:
case Opcodes.LSHL:
case Opcodes.ISHR:
case Opcodes.LSHR:
case Opcodes.IUSHR:
case Opcodes.LUSHR:
case Opcodes.IAND:
case Opcodes.LAND:
case Opcodes.IOR:
case Opcodes.LOR:
case Opcodes.IXOR:
case Opcodes.LXOR:
value2 = pop();
value1 = pop();
push(interpreter.binaryOperation(insn, value1, value2));
break;
case Opcodes.IINC:
var = ((IincInsnNode) insn).var;
setLocal(var, interpreter.unaryOperation(insn, getLocal(var)));
break;
case Opcodes.I2L:
case Opcodes.I2F:
case Opcodes.I2D:
case Opcodes.L2I:
case Opcodes.L2F:
case Opcodes.L2D:
case Opcodes.F2I:
case Opcodes.F2L:
case Opcodes.F2D:
case Opcodes.D2I:
case Opcodes.D2L:
case Opcodes.D2F:
case Opcodes.I2B:
case Opcodes.I2C:
case Opcodes.I2S:
push(interpreter.unaryOperation(insn, pop()));
break;
case Opcodes.LCMP:
case Opcodes.FCMPL:
case Opcodes.FCMPG:
case Opcodes.DCMPL:
case Opcodes.DCMPG:
value2 = pop();
value1 = pop();
push(interpreter.binaryOperation(insn, value1, value2));
break;
case Opcodes.IFEQ:
case Opcodes.IFNE:
case Opcodes.IFLT:
case Opcodes.IFGE:
case Opcodes.IFGT:
case Opcodes.IFLE:
interpreter.unaryOperation(insn, pop());
break;
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:
value2 = pop();
value1 = pop();
interpreter.binaryOperation(insn, value1, value2);
break;
case Opcodes.GOTO:
break;
case Opcodes.JSR:
push(interpreter.newOperation(insn));
break;
case Opcodes.RET:
break;
case Opcodes.TABLESWITCH:
case Opcodes.LOOKUPSWITCH:
interpreter.unaryOperation(insn, pop());
break;
case Opcodes.IRETURN:
case Opcodes.LRETURN:
case Opcodes.FRETURN:
case Opcodes.DRETURN:
case Opcodes.ARETURN:
value1 = pop();
interpreter.unaryOperation(insn, value1);
interpreter.returnOperation(insn, value1, returnValue);
break;
case Opcodes.RETURN:
if (returnValue != null) {
throw new AnalyzerException(insn, "Incompatible return type");
}
break;
case Opcodes.GETSTATIC:
push(interpreter.newOperation(insn));
break;
case Opcodes.PUTSTATIC:
interpreter.unaryOperation(insn, pop());
break;
case Opcodes.GETFIELD:
push(interpreter.unaryOperation(insn, pop()));
break;
case Opcodes.PUTFIELD:
value2 = pop();
value1 = pop();
interpreter.binaryOperation(insn, value1, value2);
break;
case Opcodes.INVOKEVIRTUAL:
case Opcodes.INVOKESPECIAL:
case Opcodes.INVOKESTATIC:
case Opcodes.INVOKEINTERFACE: {
values = new ArrayList<V>();
String desc = ((MethodInsnNode) insn).desc;
for (int i = Type.getArgumentTypes(desc).length; i > 0; --i) {
values.add(0, pop());
}
if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
values.add(0, pop());
}
if (Type.getReturnType(desc) == Type.VOID_TYPE) {
interpreter.naryOperation(insn, values);
} else {
push(interpreter.naryOperation(insn, values));
}
break;
}
case Opcodes.INVOKEDYNAMIC: {
values = new ArrayList<V>();
String desc = ((InvokeDynamicInsnNode) insn).desc;
for (int i = Type.getArgumentTypes(desc).length; i > 0; --i) {
values.add(0, pop());
}
if (Type.getReturnType(desc) == Type.VOID_TYPE) {
interpreter.naryOperation(insn, values);
} else {
push(interpreter.naryOperation(insn, values));
}
break;
}
case Opcodes.NEW:
push(interpreter.newOperation(insn));
break;
case Opcodes.NEWARRAY:
case Opcodes.ANEWARRAY:
case Opcodes.ARRAYLENGTH:
push(interpreter.unaryOperation(insn, pop()));
break;
case Opcodes.ATHROW:
interpreter.unaryOperation(insn, pop());
break;
case Opcodes.CHECKCAST:
case Opcodes.INSTANCEOF:
push(interpreter.unaryOperation(insn, pop()));
break;
case Opcodes.MONITORENTER:
case Opcodes.MONITOREXIT:
interpreter.unaryOperation(insn, pop());
break;
case Opcodes.MULTIANEWARRAY:
values = new ArrayList<V>();
for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) {
values.add(0, pop());
}
push(interpreter.naryOperation(insn, values));
break;
case Opcodes.IFNULL:
case Opcodes.IFNONNULL:
interpreter.unaryOperation(insn, pop());
break;
default:
throw new RuntimeException("Illegal opcode " + insn.getOpcode());
}
}
/**
* Merges this frame with the given frame.
*
* @param frame
* a frame.
* @param interpreter
* the interpreter used to merge values.
* @return <tt>true</tt> if this frame has been changed as a result of the
* merge operation, or <tt>false</tt> otherwise.
* @throws org.objectweb.asm.tree.analysis.AnalyzerException
* if the frames have incompatible sizes.
*/
public boolean merge(final Frame<? extends V> frame,
final Interpreter<V> interpreter) throws AnalyzerException {
if (top != frame.top) {
throw new AnalyzerException(null, "Incompatible stack heights");
}
boolean changes = false;
for (int i = 0; i < locals + top; ++i) {
V v = interpreter.merge(values[i], frame.values[i]);
if (!v.equals(values[i])) {
values[i] = v;
changes = true;
}
}
return changes;
}
/**
* Merges this frame with the given frame (case of a RET instruction).
*
* @param frame
* a frame
* @param access
* the local variables that have been accessed by the subroutine
* to which the RET instruction corresponds.
* @return <tt>true</tt> if this frame has been changed as a result of the
* merge operation, or <tt>false</tt> otherwise.
*/
public boolean merge(final Frame<? extends V> frame, final boolean[] access) {
boolean changes = false;
for (int i = 0; i < locals; ++i) {
if (!access[i] && !values[i].equals(frame.values[i])) {
values[i] = frame.values[i];
changes = true;
}
}
return changes;
}
/**
* Returns a string representation of this frame.
*
* @return a string representation of this frame.
*/
@Override
public String toString() {
StringBuffer b = new StringBuffer();
for (int i = 0; i < getLocals(); ++i) {
b.append(getLocal(i));
}
b.append(' ');
for (int i = 0; i < getStackSize(); ++i) {
b.append(getStack(i).toString());
}
return b.toString();
}
}
@@ -0,0 +1,226 @@
/***
* 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.tree.analysis;
import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
/**
* A semantic bytecode interpreter. More precisely, this interpreter only
* manages the computation of values from other values: it does not manage the
* transfer of values to or from the stack, and to or from the local variables.
* This separation allows a generic bytecode {@link org.objectweb.asm.tree.analysis.Analyzer} to work with
* various semantic interpreters, without needing to duplicate the code to
* simulate the transfer of values.
*
* @param <V>
* type of the Value used for the analysis.
*
* @author Eric Bruneton
*/
public abstract class Interpreter<V extends Value> {
protected final int api;
protected Interpreter(final int api) {
this.api = api;
}
/**
* Creates a new value that represents the given type.
*
* Called for method parameters (including <code>this</code>), exception
* handler variable and with <code>null</code> type for variables reserved
* by long and double types.
*
* @param type
* a primitive or reference type, or <tt>null</tt> to represent
* an uninitialized value.
* @return a value that represents the given type. The size of the returned
* value must be equal to the size of the given type.
*/
public abstract V newValue(Type type);
/**
* Interprets a bytecode instruction without arguments. This method is
* called for the following opcodes:
*
* ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4,
* ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0,
* DCONST_1, BIPUSH, SIPUSH, LDC, JSR, GETSTATIC, NEW
*
* @param insn
* the bytecode instruction to be interpreted.
* @return the result of the interpretation of the given instruction.
* @throws org.objectweb.asm.tree.analysis.AnalyzerException
* if an error occured during the interpretation.
*/
public abstract V newOperation(AbstractInsnNode insn)
throws AnalyzerException;
/**
* Interprets a bytecode instruction that moves a value on the stack or to
* or from local variables. This method is called for the following opcodes:
*
* ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE,
* ASTORE, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP
*
* @param insn
* the bytecode instruction to be interpreted.
* @param value
* the value that must be moved by the instruction.
* @return the result of the interpretation of the given instruction. The
* returned value must be <tt>equal</tt> to the given value.
* @throws org.objectweb.asm.tree.analysis.AnalyzerException
* if an error occured during the interpretation.
*/
public abstract V copyOperation(AbstractInsnNode insn, V value)
throws AnalyzerException;
/**
* Interprets a bytecode instruction with a single argument. This method is
* called for the following opcodes:
*
* INEG, LNEG, FNEG, DNEG, IINC, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L,
* F2D, D2I, D2L, D2F, I2B, I2C, I2S, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE,
* TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN,
* PUTSTATIC, GETFIELD, NEWARRAY, ANEWARRAY, ARRAYLENGTH, ATHROW, CHECKCAST,
* INSTANCEOF, MONITORENTER, MONITOREXIT, IFNULL, IFNONNULL
*
* @param insn
* the bytecode instruction to be interpreted.
* @param value
* the argument of the instruction to be interpreted.
* @return the result of the interpretation of the given instruction.
* @throws org.objectweb.asm.tree.analysis.AnalyzerException
* if an error occured during the interpretation.
*/
public abstract V unaryOperation(AbstractInsnNode insn, V value)
throws AnalyzerException;
/**
* Interprets a bytecode instruction with two arguments. This method is
* called for the following opcodes:
*
* IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IADD,
* LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV,
* LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, ISHL, LSHL, ISHR, LSHR, IUSHR,
* LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, LCMP, FCMPL, FCMPG, DCMPL,
* DCMPG, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE,
* IF_ACMPEQ, IF_ACMPNE, PUTFIELD
*
* @param insn
* the bytecode instruction to be interpreted.
* @param value1
* the first argument of the instruction to be interpreted.
* @param value2
* the second argument of the instruction to be interpreted.
* @return the result of the interpretation of the given instruction.
* @throws org.objectweb.asm.tree.analysis.AnalyzerException
* if an error occured during the interpretation.
*/
public abstract V binaryOperation(AbstractInsnNode insn, V value1, V value2)
throws AnalyzerException;
/**
* Interprets a bytecode instruction with three arguments. This method is
* called for the following opcodes:
*
* IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE
*
* @param insn
* the bytecode instruction to be interpreted.
* @param value1
* the first argument of the instruction to be interpreted.
* @param value2
* the second argument of the instruction to be interpreted.
* @param value3
* the third argument of the instruction to be interpreted.
* @return the result of the interpretation of the given instruction.
* @throws org.objectweb.asm.tree.analysis.AnalyzerException
* if an error occured during the interpretation.
*/
public abstract V ternaryOperation(AbstractInsnNode insn, V value1,
V value2, V value3) throws AnalyzerException;
/**
* Interprets a bytecode instruction with a variable number of arguments.
* This method is called for the following opcodes:
*
* INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE,
* MULTIANEWARRAY and INVOKEDYNAMIC
*
* @param insn
* the bytecode instruction to be interpreted.
* @param values
* the arguments of the instruction to be interpreted.
* @return the result of the interpretation of the given instruction.
* @throws org.objectweb.asm.tree.analysis.AnalyzerException
* if an error occured during the interpretation.
*/
public abstract V naryOperation(AbstractInsnNode insn,
List<? extends V> values) throws AnalyzerException;
/**
* Interprets a bytecode return instruction. This method is called for the
* following opcodes:
*
* IRETURN, LRETURN, FRETURN, DRETURN, ARETURN
*
* @param insn
* the bytecode instruction to be interpreted.
* @param value
* the argument of the instruction to be interpreted.
* @param expected
* the expected return type of the analyzed method.
* @throws org.objectweb.asm.tree.analysis.AnalyzerException
* if an error occured during the interpretation.
*/
public abstract void returnOperation(AbstractInsnNode insn, V value,
V expected) throws AnalyzerException;
/**
* Merges two values. The merge operation must return a value that
* represents both values (for instance, if the two values are two types,
* the merged value must be a common super type of the two types. If the two
* values are integer intervals, the merged value must be an interval that
* contains the previous ones. Likewise for other types of values).
*
* @param v
* a value.
* @param w
* another value.
* @return the merged value. If the merged value is equal to <tt>v</tt>,
* this method <i>must</i> return <tt>v</tt>.
*/
public abstract V merge(V v, V w);
}
@@ -0,0 +1,320 @@
/***
* 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.tree.analysis;
import java.util.List;
import org.objectweb.asm.Type;
/**
* An extended {@link org.objectweb.asm.tree.analysis.BasicVerifier} that performs more precise verifications.
* This verifier computes exact class types, instead of using a single "object
* reference" type (as done in the {@link org.objectweb.asm.tree.analysis.BasicVerifier}).
*
* @author Eric Bruneton
* @author Bing Ran
*/
public class SimpleVerifier extends BasicVerifier {
/**
* The class that is verified.
*/
private final Type currentClass;
/**
* The super class of the class that is verified.
*/
private final Type currentSuperClass;
/**
* The interfaces implemented by the class that is verified.
*/
private final List<Type> currentClassInterfaces;
/**
* If the class that is verified is an interface.
*/
private final boolean isInterface;
/**
* The loader to use for referenced classes.
*/
private ClassLoader loader = getClass().getClassLoader();
/**
* Constructs a new {@link org.objectweb.asm.tree.analysis.SimpleVerifier}.
*/
public SimpleVerifier() {
this(null, null, false);
}
/**
* Constructs a new {@link org.objectweb.asm.tree.analysis.SimpleVerifier} to verify a specific class. This
* class will not be loaded into the JVM since it may be incorrect.
*
* @param currentClass
* the class that is verified.
* @param currentSuperClass
* the super class of the class that is verified.
* @param isInterface
* if the class that is verified is an interface.
*/
public SimpleVerifier(final Type currentClass,
final Type currentSuperClass, final boolean isInterface) {
this(currentClass, currentSuperClass, null, isInterface);
}
/**
* Constructs a new {@link org.objectweb.asm.tree.analysis.SimpleVerifier} to verify a specific class. This
* class will not be loaded into the JVM since it may be incorrect.
*
* @param currentClass
* the class that is verified.
* @param currentSuperClass
* the super class of the class that is verified.
* @param currentClassInterfaces
* the interfaces implemented by the class that is verified.
* @param isInterface
* if the class that is verified is an interface.
*/
public SimpleVerifier(final Type currentClass,
final Type currentSuperClass,
final List<Type> currentClassInterfaces, final boolean isInterface) {
this(ASM4, currentClass, currentSuperClass, currentClassInterfaces,
isInterface);
}
protected SimpleVerifier(final int api, final Type currentClass,
final Type currentSuperClass,
final List<Type> currentClassInterfaces, final boolean isInterface) {
super(api);
this.currentClass = currentClass;
this.currentSuperClass = currentSuperClass;
this.currentClassInterfaces = currentClassInterfaces;
this.isInterface = isInterface;
}
/**
* Set the <code>ClassLoader</code> which will be used to load referenced
* classes. This is useful if you are verifying multiple interdependent
* classes.
*
* @param loader
* a <code>ClassLoader</code> to use
*/
public void setClassLoader(final ClassLoader loader) {
this.loader = loader;
}
@Override
public BasicValue newValue(final Type type) {
if (type == null) {
return BasicValue.UNINITIALIZED_VALUE;
}
boolean isArray = type.getSort() == Type.ARRAY;
if (isArray) {
switch (type.getElementType().getSort()) {
case Type.BOOLEAN:
case Type.CHAR:
case Type.BYTE:
case Type.SHORT:
return new BasicValue(type);
}
}
BasicValue v = super.newValue(type);
if (BasicValue.REFERENCE_VALUE.equals(v)) {
if (isArray) {
v = newValue(type.getElementType());
String desc = v.getType().getDescriptor();
for (int i = 0; i < type.getDimensions(); ++i) {
desc = '[' + desc;
}
v = new BasicValue(Type.getType(desc));
} else {
v = new BasicValue(type);
}
}
return v;
}
@Override
protected boolean isArrayValue(final BasicValue value) {
Type t = value.getType();
return t != null
&& ("Lnull;".equals(t.getDescriptor()) || t.getSort() == Type.ARRAY);
}
@Override
protected BasicValue getElementValue(final BasicValue objectArrayValue)
throws AnalyzerException {
Type arrayType = objectArrayValue.getType();
if (arrayType != null) {
if (arrayType.getSort() == Type.ARRAY) {
return newValue(Type.getType(arrayType.getDescriptor()
.substring(1)));
} else if ("Lnull;".equals(arrayType.getDescriptor())) {
return objectArrayValue;
}
}
throw new Error("Internal error");
}
@Override
protected boolean isSubTypeOf(final BasicValue value,
final BasicValue expected) {
Type expectedType = expected.getType();
Type type = value.getType();
switch (expectedType.getSort()) {
case Type.INT:
case Type.FLOAT:
case Type.LONG:
case Type.DOUBLE:
return type.equals(expectedType);
case Type.ARRAY:
case Type.OBJECT:
if ("Lnull;".equals(type.getDescriptor())) {
return true;
} else if (type.getSort() == Type.OBJECT
|| type.getSort() == Type.ARRAY) {
return isAssignableFrom(expectedType, type);
} else {
return false;
}
default:
throw new Error("Internal error");
}
}
@Override
public BasicValue merge(final BasicValue v, final BasicValue w) {
if (!v.equals(w)) {
Type t = v.getType();
Type u = w.getType();
if (t != null
&& (t.getSort() == Type.OBJECT || t.getSort() == Type.ARRAY)) {
if (u != null
&& (u.getSort() == Type.OBJECT || u.getSort() == Type.ARRAY)) {
if ("Lnull;".equals(t.getDescriptor())) {
return w;
}
if ("Lnull;".equals(u.getDescriptor())) {
return v;
}
if (isAssignableFrom(t, u)) {
return v;
}
if (isAssignableFrom(u, t)) {
return w;
}
// TODO case of array classes of the same dimension
// TODO should we look also for a common super interface?
// problem: there may be several possible common super
// interfaces
do {
if (t == null || isInterface(t)) {
return BasicValue.REFERENCE_VALUE;
}
t = getSuperClass(t);
if (isAssignableFrom(t, u)) {
return newValue(t);
}
} while (true);
}
}
return BasicValue.UNINITIALIZED_VALUE;
}
return v;
}
protected boolean isInterface(final Type t) {
if (currentClass != null && t.equals(currentClass)) {
return isInterface;
}
return getClass(t).isInterface();
}
protected Type getSuperClass(final Type t) {
if (currentClass != null && t.equals(currentClass)) {
return currentSuperClass;
}
Class<?> c = getClass(t).getSuperclass();
return c == null ? null : Type.getType(c);
}
protected boolean isAssignableFrom(final Type t, final Type u) {
if (t.equals(u)) {
return true;
}
if (currentClass != null && t.equals(currentClass)) {
if (getSuperClass(u) == null) {
return false;
} else {
if (isInterface) {
return u.getSort() == Type.OBJECT
|| u.getSort() == Type.ARRAY;
}
return isAssignableFrom(t, getSuperClass(u));
}
}
if (currentClass != null && u.equals(currentClass)) {
if (isAssignableFrom(t, currentSuperClass)) {
return true;
}
if (currentClassInterfaces != null) {
for (int i = 0; i < currentClassInterfaces.size(); ++i) {
Type v = currentClassInterfaces.get(i);
if (isAssignableFrom(t, v)) {
return true;
}
}
}
return false;
}
Class<?> tc = getClass(t);
if (tc.isInterface()) {
tc = Object.class;
}
return tc.isAssignableFrom(getClass(u));
}
protected Class<?> getClass(final Type t) {
try {
if (t.getSort() == Type.ARRAY) {
return Class.forName(t.getDescriptor().replace('/', '.'),
false, loader);
}
return Class.forName(t.getClassName(), false, loader);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e.toString());
}
}
}
@@ -0,0 +1,134 @@
/***
* 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.tree.analysis;
import java.util.AbstractSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A set of at most two elements.
*
* @author Eric Bruneton
*/
class SmallSet<E> extends AbstractSet<E> implements Iterator<E> {
// if e1 is null, e2 must be null; otherwise e2 must be different from e1
E e1, e2;
static final <T> Set<T> emptySet() {
return new SmallSet<T>(null, null);
}
SmallSet(final E e1, final E e2) {
this.e1 = e1;
this.e2 = e2;
}
// -------------------------------------------------------------------------
// Implementation of inherited abstract methods
// -------------------------------------------------------------------------
@Override
public Iterator<E> iterator() {
return new SmallSet<E>(e1, e2);
}
@Override
public int size() {
return e1 == null ? 0 : (e2 == null ? 1 : 2);
}
// -------------------------------------------------------------------------
// Implementation of the Iterator interface
// -------------------------------------------------------------------------
public boolean hasNext() {
return e1 != null;
}
public E next() {
if (e1 == null) {
throw new NoSuchElementException();
}
E e = e1;
e1 = e2;
e2 = null;
return e;
}
public void remove() {
}
// -------------------------------------------------------------------------
// Utility methods
// -------------------------------------------------------------------------
Set<E> union(final SmallSet<E> s) {
if ((s.e1 == e1 && s.e2 == e2) || (s.e1 == e2 && s.e2 == e1)) {
return this; // if the two sets are equal, return this
}
if (s.e1 == null) {
return this; // if s is empty, return this
}
if (e1 == null) {
return s; // if this is empty, return s
}
if (s.e2 == null) { // s contains exactly one element
if (e2 == null) {
return new SmallSet<E>(e1, s.e1); // necessarily e1 != s.e1
} else if (s.e1 == e1 || s.e1 == e2) { // s is included in this
return this;
}
}
if (e2 == null) { // this contains exactly one element
// if (s.e2 == null) { // cannot happen
// return new SmallSet(e1, s.e1); // necessarily e1 != s.e1
// } else
if (e1 == s.e1 || e1 == s.e2) { // this in included in s
return s;
}
}
// here we know that there are at least 3 distinct elements
HashSet<E> r = new HashSet<E>(4);
r.add(e1);
if (e2 != null) {
r.add(e2);
}
r.add(s.e1);
if (s.e2 != null) {
r.add(s.e2);
}
return r;
}
}
@@ -0,0 +1,198 @@
/***
* 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.tree.analysis;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
/**
* An {@link org.objectweb.asm.tree.analysis.Interpreter} for {@link SourceValue} values.
*
* @author Eric Bruneton
*/
public class SourceInterpreter extends Interpreter<SourceValue> implements
Opcodes {
public SourceInterpreter() {
super(ASM4);
}
protected SourceInterpreter(final int api) {
super(api);
}
@Override
public SourceValue newValue(final Type type) {
if (type == Type.VOID_TYPE) {
return null;
}
return new SourceValue(type == null ? 1 : type.getSize());
}
@Override
public SourceValue newOperation(final AbstractInsnNode insn) {
int size;
switch (insn.getOpcode()) {
case LCONST_0:
case LCONST_1:
case DCONST_0:
case DCONST_1:
size = 2;
break;
case LDC:
Object cst = ((LdcInsnNode) insn).cst;
size = cst instanceof Long || cst instanceof Double ? 2 : 1;
break;
case GETSTATIC:
size = Type.getType(((FieldInsnNode) insn).desc).getSize();
break;
default:
size = 1;
}
return new SourceValue(size, insn);
}
@Override
public SourceValue copyOperation(final AbstractInsnNode insn,
final SourceValue value) {
return new SourceValue(value.getSize(), insn);
}
@Override
public SourceValue unaryOperation(final AbstractInsnNode insn,
final SourceValue value) {
int size;
switch (insn.getOpcode()) {
case LNEG:
case DNEG:
case I2L:
case I2D:
case L2D:
case F2L:
case F2D:
case D2L:
size = 2;
break;
case GETFIELD:
size = Type.getType(((FieldInsnNode) insn).desc).getSize();
break;
default:
size = 1;
}
return new SourceValue(size, insn);
}
@Override
public SourceValue binaryOperation(final AbstractInsnNode insn,
final SourceValue value1, final SourceValue value2) {
int size;
switch (insn.getOpcode()) {
case LALOAD:
case DALOAD:
case LADD:
case DADD:
case LSUB:
case DSUB:
case LMUL:
case DMUL:
case LDIV:
case DDIV:
case LREM:
case DREM:
case LSHL:
case LSHR:
case LUSHR:
case LAND:
case LOR:
case LXOR:
size = 2;
break;
default:
size = 1;
}
return new SourceValue(size, insn);
}
@Override
public SourceValue ternaryOperation(final AbstractInsnNode insn,
final SourceValue value1, final SourceValue value2,
final SourceValue value3) {
return new SourceValue(1, insn);
}
@Override
public SourceValue naryOperation(final AbstractInsnNode insn,
final List<? extends SourceValue> values) {
int size;
int opcode = insn.getOpcode();
if (opcode == MULTIANEWARRAY) {
size = 1;
} else {
String desc = (opcode == INVOKEDYNAMIC) ? ((InvokeDynamicInsnNode) insn).desc
: ((MethodInsnNode) insn).desc;
size = Type.getReturnType(desc).getSize();
}
return new SourceValue(size, insn);
}
@Override
public void returnOperation(final AbstractInsnNode insn,
final SourceValue value, final SourceValue expected) {
}
@Override
public SourceValue merge(final SourceValue d, final SourceValue w) {
if (d.insns instanceof SmallSet && w.insns instanceof SmallSet) {
Set<AbstractInsnNode> s = ((SmallSet<AbstractInsnNode>) d.insns)
.union((SmallSet<AbstractInsnNode>) w.insns);
if (s == d.insns && d.size == w.size) {
return d;
} else {
return new SourceValue(Math.min(d.size, w.size), s);
}
}
if (d.size != w.size || !d.insns.containsAll(w.insns)) {
HashSet<AbstractInsnNode> s = new HashSet<AbstractInsnNode>();
s.addAll(d.insns);
s.addAll(w.insns);
return new SourceValue(Math.min(d.size, w.size), s);
}
return d;
}
}
@@ -0,0 +1,97 @@
/***
* 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.tree.analysis;
import java.util.Set;
import org.objectweb.asm.tree.AbstractInsnNode;
/**
* A {@link Value} that is represented by its type in a two types type system.
* This type system distinguishes the ONEWORD and TWOWORDS types.
*
* @author Eric Bruneton
*/
public class SourceValue implements Value {
/**
* The size of this value.
*/
public final int size;
/**
* The instructions that can produce this value. For example, for the Java
* code below, the instructions that can produce the value of <tt>i</tt> at
* line 5 are the txo ISTORE instructions at line 1 and 3:
*
* <pre>
* 1: i = 0;
* 2: if (...) {
* 3: i = 1;
* 4: }
* 5: return i;
* </pre>
*
* This field is a set of {@link org.objectweb.asm.tree.AbstractInsnNode} objects.
*/
public final Set<AbstractInsnNode> insns;
public SourceValue(final int size) {
this(size, SmallSet.<AbstractInsnNode> emptySet());
}
public SourceValue(final int size, final AbstractInsnNode insn) {
this.size = size;
this.insns = new SmallSet<AbstractInsnNode>(insn, null);
}
public SourceValue(final int size, final Set<AbstractInsnNode> insns) {
this.size = size;
this.insns = insns;
}
public int getSize() {
return size;
}
@Override
public boolean equals(final Object value) {
if (!(value instanceof SourceValue)) {
return false;
}
SourceValue v = (SourceValue) value;
return size == v.size && insns.equals(v.insns);
}
@Override
public int hashCode() {
return insns.hashCode();
}
}
@@ -0,0 +1,90 @@
/***
* 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.tree.analysis;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
/**
* A method subroutine (corresponds to a JSR instruction).
*
* @author Eric Bruneton
*/
class Subroutine {
LabelNode start;
boolean[] access;
List<JumpInsnNode> callers;
private Subroutine() {
}
Subroutine(final LabelNode start, final int maxLocals,
final JumpInsnNode caller) {
this.start = start;
this.access = new boolean[maxLocals];
this.callers = new ArrayList<JumpInsnNode>();
callers.add(caller);
}
public Subroutine copy() {
Subroutine result = new Subroutine();
result.start = start;
result.access = new boolean[access.length];
System.arraycopy(access, 0, result.access, 0, access.length);
result.callers = new ArrayList<JumpInsnNode>(callers);
return result;
}
public boolean merge(final Subroutine subroutine) throws AnalyzerException {
boolean changes = false;
for (int i = 0; i < access.length; ++i) {
if (subroutine.access[i] && !access[i]) {
access[i] = true;
changes = true;
}
}
if (subroutine.start == start) {
for (int i = 0; i < subroutine.callers.size(); ++i) {
JumpInsnNode caller = subroutine.callers.get(i);
if (!callers.contains(caller)) {
callers.add(caller);
changes = true;
}
}
}
return changes;
}
}
@@ -0,0 +1,45 @@
/***
* 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.tree.analysis;
/**
* An immutable symbolic value for semantic interpretation of bytecode.
*
* @author Eric Bruneton
*/
public interface Value {
/**
* Returns the size of this value in words.
*
* @return either 1 or 2.
*/
int getSize();
}
@@ -0,0 +1,67 @@
<html>
<!--
* 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.
-->
<body>
<p>
Provides a framework for static code analysis based on the asm.tree package.
</p>
<p>
Basic usage:
</p>
<pre>
ClassReader cr = new ClassReader(bytecode);
ClassNode cn = new ClassNode();
cr.accept(cn, ClassReader.SKIP_DEBUG);
List methods = cn.methods;
for (int i = 0; i < methods.size(); ++i) {
MethodNode method = (MethodNode) methods.get(i);
if (method.instructions.size() > 0) {
Analyzer a = new Analyzer(new BasicInterpreter());
a.analyze(cn.name, method);
Frame[] frames = a.getFrames();
// Elements of the frames arrray now contains info for each instruction
// from the analyzed method. BasicInterpreter creates BasicValue, that
// is using simplified type system that distinguishes the UNINITIALZED,
// INT, FLOAT, LONG, DOUBLE, REFERENCE and RETURNADDRESS types.
...
}
}
</pre>
<p>
@since ASM 1.4.3
</p>
</body>
</html>
@@ -0,0 +1,56 @@
/**
* 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.util;
import java.util.Map;
import org.objectweb.asm.Label;
/**
* An {@link org.objectweb.asm.Attribute Attribute} that can print the ASM code
* to create an equivalent attribute.
*
* @author Eugene Kuleshov
*/
public interface ASMifiable {
/**
* Prints the ASM code to create an attribute equal to this attribute.
*
* @param buf
* a buffer used for printing Java code.
* @param varName
* name of the variable in a printed code used to store attribute
* instance.
* @param labelNames
* map of label instances to their names.
*/
void asmify(StringBuffer buf, String varName, Map<Label, String> labelNames);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,136 @@
/***
* 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.util;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* An {@link org.objectweb.asm.AnnotationVisitor} that checks that its methods are properly used.
*
* @author Eric Bruneton
*/
public class CheckAnnotationAdapter extends AnnotationVisitor {
private final boolean named;
private boolean end;
public CheckAnnotationAdapter(final AnnotationVisitor av) {
this(av, true);
}
CheckAnnotationAdapter(final AnnotationVisitor av, final boolean named) {
super(Opcodes.ASM4, av);
this.named = named;
}
@Override
public void visit(final String name, final Object value) {
checkEnd();
checkName(name);
if (!(value instanceof Byte || value instanceof Boolean
|| value instanceof Character || value instanceof Short
|| value instanceof Integer || value instanceof Long
|| value instanceof Float || value instanceof Double
|| value instanceof String || value instanceof Type
|| value instanceof byte[] || value instanceof boolean[]
|| value instanceof char[] || value instanceof short[]
|| value instanceof int[] || value instanceof long[]
|| value instanceof float[] || value instanceof double[])) {
throw new IllegalArgumentException("Invalid annotation value");
}
if (value instanceof Type) {
int sort = ((Type) value).getSort();
if (sort != Type.OBJECT && sort != Type.ARRAY) {
throw new IllegalArgumentException("Invalid annotation value");
}
}
if (av != null) {
av.visit(name, value);
}
}
@Override
public void visitEnum(final String name, final String desc,
final String value) {
checkEnd();
checkName(name);
CheckMethodAdapter.checkDesc(desc, false);
if (value == null) {
throw new IllegalArgumentException("Invalid enum value");
}
if (av != null) {
av.visitEnum(name, desc, value);
}
}
@Override
public AnnotationVisitor visitAnnotation(final String name,
final String desc) {
checkEnd();
checkName(name);
CheckMethodAdapter.checkDesc(desc, false);
return new CheckAnnotationAdapter(av == null ? null
: av.visitAnnotation(name, desc));
}
@Override
public AnnotationVisitor visitArray(final String name) {
checkEnd();
checkName(name);
return new CheckAnnotationAdapter(av == null ? null
: av.visitArray(name), false);
}
@Override
public void visitEnd() {
checkEnd();
end = true;
if (av != null) {
av.visitEnd();
}
}
private void checkEnd() {
if (end) {
throw new IllegalStateException(
"Cannot call a visit method after visitEnd has been called");
}
}
private void checkName(final String name) {
if (named && name == null) {
throw new IllegalArgumentException(
"Annotation value name must not be null");
}
}
}
@@ -0,0 +1,906 @@
/***
* 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.util;
import java.io.FileInputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
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.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.tree.analysis.SimpleVerifier;
/**
* A {@link org.objectweb.asm.ClassVisitor} that checks that its methods are properly used. More
* precisely this class adapter checks each method call individually, based
* <i>only</i> on its arguments, but does <i>not</i> check the <i>sequence</i>
* of method calls. For example, the invalid sequence
* <tt>visitField(ACC_PUBLIC, "i", "I", null)</tt> <tt>visitField(ACC_PUBLIC,
* "i", "D", null)</tt> will <i>not</i> be detected by this class adapter.
*
* <p>
* <code>CheckClassAdapter</code> can be also used to verify bytecode
* transformations in order to make sure transformed bytecode is sane. For
* example:
*
* <pre>
* InputStream is = ...; // get bytes for the source class
* ClassReader cr = new ClassReader(is);
* ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
* ClassVisitor cv = new <b>MyClassAdapter</b>(new CheckClassAdapter(cw));
* cr.accept(cv, 0);
*
* StringWriter sw = new StringWriter();
* PrintWriter pw = new PrintWriter(sw);
* CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), false, pw);
* assertTrue(sw.toString(), sw.toString().length()==0);
* </pre>
*
* Above code runs transformed bytecode trough the
* <code>CheckClassAdapter</code>. It won't be exactly the same verification as
* JVM does, but it run data flow analysis for the code of each method and
* checks that expectations are met for each method instruction.
*
* <p>
* If method bytecode has errors, assertion text will show the erroneous
* instruction number and dump of the failed method with information about
* locals and stack slot for each instruction. For example (format is -
* insnNumber locals : stack):
*
* <pre>
* org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
* at org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289)
* at org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135)
* ...
* remove()V
* 00000 LinkedBlockingQueue$Itr . . . . . . . . :
* ICONST_0
* 00001 LinkedBlockingQueue$Itr . . . . . . . . : I
* ISTORE 2
* 00001 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . :
* ...
*
* 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . :
* ILOAD 1
* 00072 <b>?</b>
* INVOKESPECIAL java/lang/Integer.<init> (I)V
* ...
* </pre>
*
* In the above output you can see that variable 1 loaded by
* <code>ILOAD 1</code> instruction at position <code>00071</code> is not
* initialized. You can also see that at the beginning of the method (code
* inserted by the transformation) variable 2 is initialized.
*
* <p>
* Note that when used like that, <code>CheckClassAdapter.verify()</code> can
* trigger additional class loading, because it is using
* <code>SimpleVerifier</code>.
*
* @author Eric Bruneton
*/
public class CheckClassAdapter extends ClassVisitor {
/**
* The class version number.
*/
private int version;
/**
* <tt>true</tt> if the visit method has been called.
*/
private boolean start;
/**
* <tt>true</tt> if the visitSource method has been called.
*/
private boolean source;
/**
* <tt>true</tt> if the visitOuterClass method has been called.
*/
private boolean outer;
/**
* <tt>true</tt> if the visitEnd method has been called.
*/
private boolean end;
/**
* The already visited labels. This map associate Integer values to Label
* keys.
*/
private Map<Label, Integer> labels;
/**
* <tt>true</tt> if the method code must be checked with a BasicVerifier.
*/
private boolean checkDataFlow;
/**
* Checks a given class.
* <p>
* Usage: CheckClassAdapter &lt;binary class name or class file name&gt;
*
* @param args
* the command line arguments.
*
* @throws Exception
* if the class cannot be found, or if an IO exception occurs.
*/
public static void main(final String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Verifies the given class.");
System.err.println("Usage: CheckClassAdapter "
+ "<fully qualified class name or class file name>");
return;
}
ClassReader cr;
if (args[0].endsWith(".class")) {
cr = new ClassReader(new FileInputStream(args[0]));
} else {
cr = new ClassReader(args[0]);
}
verify(cr, false, new PrintWriter(System.err));
}
/**
* Checks a given class.
*
* @param cr
* a <code>ClassReader</code> that contains bytecode for the
* analysis.
* @param loader
* a <code>ClassLoader</code> which will be used to load
* referenced classes. This is useful if you are verifiying
* multiple interdependent classes.
* @param dump
* true if bytecode should be printed out not only when errors
* are found.
* @param pw
* write where results going to be printed
*/
public static void verify(final ClassReader cr, final ClassLoader loader,
final boolean dump, final PrintWriter pw) {
ClassNode cn = new ClassNode();
cr.accept(new CheckClassAdapter(cn, false), ClassReader.SKIP_DEBUG);
Type syperType = cn.superName == null ? null : Type
.getObjectType(cn.superName);
List<MethodNode> methods = cn.methods;
List<Type> interfaces = new ArrayList<Type>();
for (Iterator<String> i = cn.interfaces.iterator(); i.hasNext();) {
interfaces.add(Type.getObjectType(i.next().toString()));
}
for (int i = 0; i < methods.size(); ++i) {
MethodNode method = methods.get(i);
SimpleVerifier verifier = new SimpleVerifier(
Type.getObjectType(cn.name), syperType, interfaces,
(cn.access & Opcodes.ACC_INTERFACE) != 0);
Analyzer<BasicValue> a = new Analyzer<BasicValue>(verifier);
if (loader != null) {
verifier.setClassLoader(loader);
}
try {
a.analyze(cn.name, method);
if (!dump) {
continue;
}
} catch (Exception e) {
e.printStackTrace(pw);
}
printAnalyzerResult(method, a, pw);
}
pw.flush();
}
/**
* Checks a given class
*
* @param cr
* a <code>ClassReader</code> that contains bytecode for the
* analysis.
* @param dump
* true if bytecode should be printed out not only when errors
* are found.
* @param pw
* write where results going to be printed
*/
public static void verify(final ClassReader cr, final boolean dump,
final PrintWriter pw) {
verify(cr, null, dump, pw);
}
static void printAnalyzerResult(MethodNode method, Analyzer<BasicValue> a,
final PrintWriter pw) {
Frame<BasicValue>[] frames = a.getFrames();
Textifier t = new Textifier();
TraceMethodVisitor mv = new TraceMethodVisitor(t);
pw.println(method.name + method.desc);
for (int j = 0; j < method.instructions.size(); ++j) {
method.instructions.get(j).accept(mv);
StringBuffer s = new StringBuffer();
Frame<BasicValue> f = frames[j];
if (f == null) {
s.append('?');
} else {
for (int k = 0; k < f.getLocals(); ++k) {
s.append(getShortName(f.getLocal(k).toString()))
.append(' ');
}
s.append(" : ");
for (int k = 0; k < f.getStackSize(); ++k) {
s.append(getShortName(f.getStack(k).toString()))
.append(' ');
}
}
while (s.length() < method.maxStack + method.maxLocals + 1) {
s.append(' ');
}
pw.print(Integer.toString(j + 100000).substring(1));
pw.print(" " + s + " : " + t.text.get(t.text.size() - 1));
}
for (int j = 0; j < method.tryCatchBlocks.size(); ++j) {
method.tryCatchBlocks.get(j).accept(mv);
pw.print(" " + t.text.get(t.text.size() - 1));
}
pw.println();
}
private static String getShortName(final String name) {
int n = name.lastIndexOf('/');
int k = name.length();
if (name.charAt(k - 1) == ';') {
k--;
}
return n == -1 ? name : name.substring(n + 1, k);
}
/**
* Constructs a new {@link org.objectweb.asm.util.CheckClassAdapter}. <i>Subclasses must not use
* this constructor</i>. Instead, they must use the
* {@link #CheckClassAdapter(int, org.objectweb.asm.ClassVisitor, boolean)} version.
*
* @param cv
* the class visitor to which this adapter must delegate calls.
*/
public CheckClassAdapter(final ClassVisitor cv) {
this(cv, true);
}
/**
* Constructs a new {@link org.objectweb.asm.util.CheckClassAdapter}. <i>Subclasses must not use
* this constructor</i>. Instead, they must use the
* {@link #CheckClassAdapter(int, org.objectweb.asm.ClassVisitor, boolean)} version.
*
* @param cv
* the class visitor to which this adapter must delegate calls.
* @param checkDataFlow
* <tt>true</tt> to perform basic data flow checks, or
* <tt>false</tt> to not perform any data flow check (see
* {@link CheckMethodAdapter}). This option requires valid
* maxLocals and maxStack values.
*/
public CheckClassAdapter(final ClassVisitor cv, final boolean checkDataFlow) {
this(Opcodes.ASM4, cv, checkDataFlow);
}
/**
* Constructs a new {@link org.objectweb.asm.util.CheckClassAdapter}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link org.objectweb.asm.Opcodes#ASM4}.
* @param cv
* the class visitor to which this adapter must delegate calls.
* @param checkDataFlow
* <tt>true</tt> to perform basic data flow checks, or
* <tt>false</tt> to not perform any data flow check (see
* {@link CheckMethodAdapter}). This option requires valid
* maxLocals and maxStack values.
*/
protected CheckClassAdapter(final int api, final ClassVisitor cv,
final boolean checkDataFlow) {
super(api, cv);
this.labels = new HashMap<Label, Integer>();
this.checkDataFlow = checkDataFlow;
}
// ------------------------------------------------------------------------
// Implementation of the ClassVisitor interface
// ------------------------------------------------------------------------
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName,
final String[] interfaces) {
if (start) {
throw new IllegalStateException("visit must be called only once");
}
start = true;
checkState();
checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL
+ Opcodes.ACC_SUPER + Opcodes.ACC_INTERFACE
+ Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
+ Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM
+ Opcodes.ACC_DEPRECATED + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
if (name == null || !name.endsWith("package-info")) {
CheckMethodAdapter.checkInternalName(name, "class name");
}
if ("java/lang/Object".equals(name)) {
if (superName != null) {
throw new IllegalArgumentException(
"The super class name of the Object class must be 'null'");
}
} else {
CheckMethodAdapter.checkInternalName(superName, "super class name");
}
if (signature != null) {
checkClassSignature(signature);
}
if ((access & Opcodes.ACC_INTERFACE) != 0) {
if (!"java/lang/Object".equals(superName)) {
throw new IllegalArgumentException(
"The super class name of interfaces must be 'java/lang/Object'");
}
}
if (interfaces != null) {
for (int i = 0; i < interfaces.length; ++i) {
CheckMethodAdapter.checkInternalName(interfaces[i],
"interface name at index " + i);
}
}
this.version = version;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public void visitSource(final String file, final String debug) {
checkState();
if (source) {
throw new IllegalStateException(
"visitSource can be called only once.");
}
source = true;
super.visitSource(file, debug);
}
@Override
public void visitOuterClass(final String owner, final String name,
final String desc) {
checkState();
if (outer) {
throw new IllegalStateException(
"visitOuterClass can be called only once.");
}
outer = true;
if (owner == null) {
throw new IllegalArgumentException("Illegal outer class owner");
}
if (desc != null) {
CheckMethodAdapter.checkMethodDesc(desc);
}
super.visitOuterClass(owner, name, desc);
}
@Override
public void visitInnerClass(final String name, final String outerName,
final String innerName, final int access) {
checkState();
CheckMethodAdapter.checkInternalName(name, "class name");
if (outerName != null) {
CheckMethodAdapter.checkInternalName(outerName, "outer class name");
}
if (innerName != null) {
CheckMethodAdapter.checkIdentifier(innerName, "inner class name");
}
checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
+ Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
+ Opcodes.ACC_FINAL + Opcodes.ACC_INTERFACE
+ Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
+ Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM);
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public FieldVisitor visitField(final int access, final String name,
final String desc, final String signature, final Object value) {
checkState();
checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
+ Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
+ Opcodes.ACC_FINAL + Opcodes.ACC_VOLATILE
+ Opcodes.ACC_TRANSIENT + Opcodes.ACC_SYNTHETIC
+ Opcodes.ACC_ENUM + Opcodes.ACC_DEPRECATED + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
CheckMethodAdapter.checkUnqualifiedName(version, name, "field name");
CheckMethodAdapter.checkDesc(desc, false);
if (signature != null) {
checkFieldSignature(signature);
}
if (value != null) {
CheckMethodAdapter.checkConstant(value);
}
FieldVisitor av = super
.visitField(access, name, desc, signature, value);
return new CheckFieldAdapter(av);
}
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
checkState();
checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
+ Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
+ Opcodes.ACC_FINAL + Opcodes.ACC_SYNCHRONIZED
+ Opcodes.ACC_BRIDGE + Opcodes.ACC_VARARGS + Opcodes.ACC_NATIVE
+ Opcodes.ACC_ABSTRACT + Opcodes.ACC_STRICT
+ Opcodes.ACC_SYNTHETIC + Opcodes.ACC_DEPRECATED + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
CheckMethodAdapter.checkMethodIdentifier(version, name,
"method name");
}
CheckMethodAdapter.checkMethodDesc(desc);
if (signature != null) {
checkMethodSignature(signature);
}
if (exceptions != null) {
for (int i = 0; i < exceptions.length; ++i) {
CheckMethodAdapter.checkInternalName(exceptions[i],
"exception name at index " + i);
}
}
CheckMethodAdapter cma;
if (checkDataFlow) {
cma = new CheckMethodAdapter(access, name, desc, super.visitMethod(
access, name, desc, signature, exceptions), labels);
} else {
cma = new CheckMethodAdapter(super.visitMethod(access, name, desc,
signature, exceptions), labels);
}
cma.version = version;
return cma;
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
checkState();
CheckMethodAdapter.checkDesc(desc, false);
return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible));
}
@Override
public void visitAttribute(final Attribute attr) {
checkState();
if (attr == null) {
throw new IllegalArgumentException(
"Invalid attribute (must not be null)");
}
super.visitAttribute(attr);
}
@Override
public void visitEnd() {
checkState();
end = true;
super.visitEnd();
}
// ------------------------------------------------------------------------
// Utility methods
// ------------------------------------------------------------------------
/**
* Checks that the visit method has been called and that visitEnd has not
* been called.
*/
private void checkState() {
if (!start) {
throw new IllegalStateException(
"Cannot visit member before visit has been called.");
}
if (end) {
throw new IllegalStateException(
"Cannot visit member after visitEnd has been called.");
}
}
/**
* Checks that the given access flags do not contain invalid flags. This
* method also checks that mutually incompatible flags are not set
* simultaneously.
*
* @param access
* the access flags to be checked
* @param possibleAccess
* the valid access flags.
*/
static void checkAccess(final int access, final int possibleAccess) {
if ((access & ~possibleAccess) != 0) {
throw new IllegalArgumentException("Invalid access flags: "
+ access);
}
int pub = (access & Opcodes.ACC_PUBLIC) == 0 ? 0 : 1;
int pri = (access & Opcodes.ACC_PRIVATE) == 0 ? 0 : 1;
int pro = (access & Opcodes.ACC_PROTECTED) == 0 ? 0 : 1;
if (pub + pri + pro > 1) {
throw new IllegalArgumentException(
"public private and protected are mutually exclusive: "
+ access);
}
int fin = (access & Opcodes.ACC_FINAL) == 0 ? 0 : 1;
int abs = (access & Opcodes.ACC_ABSTRACT) == 0 ? 0 : 1;
if (fin + abs > 1) {
throw new IllegalArgumentException(
"final and abstract are mutually exclusive: " + access);
}
}
/**
* Checks a class signature.
*
* @param signature
* a string containing the signature that must be checked.
*/
public static void checkClassSignature(final String signature) {
// ClassSignature:
// FormalTypeParameters? ClassTypeSignature ClassTypeSignature*
int pos = 0;
if (getChar(signature, 0) == '<') {
pos = checkFormalTypeParameters(signature, pos);
}
pos = checkClassTypeSignature(signature, pos);
while (getChar(signature, pos) == 'L') {
pos = checkClassTypeSignature(signature, pos);
}
if (pos != signature.length()) {
throw new IllegalArgumentException(signature + ": error at index "
+ pos);
}
}
/**
* Checks a method signature.
*
* @param signature
* a string containing the signature that must be checked.
*/
public static void checkMethodSignature(final String signature) {
// MethodTypeSignature:
// FormalTypeParameters? ( TypeSignature* ) ( TypeSignature | V ) (
// ^ClassTypeSignature | ^TypeVariableSignature )*
int pos = 0;
if (getChar(signature, 0) == '<') {
pos = checkFormalTypeParameters(signature, pos);
}
pos = checkChar('(', signature, pos);
while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) {
pos = checkTypeSignature(signature, pos);
}
pos = checkChar(')', signature, pos);
if (getChar(signature, pos) == 'V') {
++pos;
} else {
pos = checkTypeSignature(signature, pos);
}
while (getChar(signature, pos) == '^') {
++pos;
if (getChar(signature, pos) == 'L') {
pos = checkClassTypeSignature(signature, pos);
} else {
pos = checkTypeVariableSignature(signature, pos);
}
}
if (pos != signature.length()) {
throw new IllegalArgumentException(signature + ": error at index "
+ pos);
}
}
/**
* Checks a field signature.
*
* @param signature
* a string containing the signature that must be checked.
*/
public static void checkFieldSignature(final String signature) {
int pos = checkFieldTypeSignature(signature, 0);
if (pos != signature.length()) {
throw new IllegalArgumentException(signature + ": error at index "
+ pos);
}
}
/**
* Checks the formal type parameters of a class or method signature.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkFormalTypeParameters(final String signature, int pos) {
// FormalTypeParameters:
// < FormalTypeParameter+ >
pos = checkChar('<', signature, pos);
pos = checkFormalTypeParameter(signature, pos);
while (getChar(signature, pos) != '>') {
pos = checkFormalTypeParameter(signature, pos);
}
return pos + 1;
}
/**
* Checks a formal type parameter of a class or method signature.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkFormalTypeParameter(final String signature, int pos) {
// FormalTypeParameter:
// Identifier : FieldTypeSignature? (: FieldTypeSignature)*
pos = checkIdentifier(signature, pos);
pos = checkChar(':', signature, pos);
if ("L[T".indexOf(getChar(signature, pos)) != -1) {
pos = checkFieldTypeSignature(signature, pos);
}
while (getChar(signature, pos) == ':') {
pos = checkFieldTypeSignature(signature, pos + 1);
}
return pos;
}
/**
* Checks a field type signature.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkFieldTypeSignature(final String signature, int pos) {
// FieldTypeSignature:
// ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature
//
// ArrayTypeSignature:
// [ TypeSignature
switch (getChar(signature, pos)) {
case 'L':
return checkClassTypeSignature(signature, pos);
case '[':
return checkTypeSignature(signature, pos + 1);
default:
return checkTypeVariableSignature(signature, pos);
}
}
/**
* Checks a class type signature.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkClassTypeSignature(final String signature, int pos) {
// ClassTypeSignature:
// L Identifier ( / Identifier )* TypeArguments? ( . Identifier
// TypeArguments? )* ;
pos = checkChar('L', signature, pos);
pos = checkIdentifier(signature, pos);
while (getChar(signature, pos) == '/') {
pos = checkIdentifier(signature, pos + 1);
}
if (getChar(signature, pos) == '<') {
pos = checkTypeArguments(signature, pos);
}
while (getChar(signature, pos) == '.') {
pos = checkIdentifier(signature, pos + 1);
if (getChar(signature, pos) == '<') {
pos = checkTypeArguments(signature, pos);
}
}
return checkChar(';', signature, pos);
}
/**
* Checks the type arguments in a class type signature.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkTypeArguments(final String signature, int pos) {
// TypeArguments:
// < TypeArgument+ >
pos = checkChar('<', signature, pos);
pos = checkTypeArgument(signature, pos);
while (getChar(signature, pos) != '>') {
pos = checkTypeArgument(signature, pos);
}
return pos + 1;
}
/**
* Checks a type argument in a class type signature.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkTypeArgument(final String signature, int pos) {
// TypeArgument:
// * | ( ( + | - )? FieldTypeSignature )
char c = getChar(signature, pos);
if (c == '*') {
return pos + 1;
} else if (c == '+' || c == '-') {
pos++;
}
return checkFieldTypeSignature(signature, pos);
}
/**
* Checks a type variable signature.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkTypeVariableSignature(final String signature,
int pos) {
// TypeVariableSignature:
// T Identifier ;
pos = checkChar('T', signature, pos);
pos = checkIdentifier(signature, pos);
return checkChar(';', signature, pos);
}
/**
* Checks a type signature.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkTypeSignature(final String signature, int pos) {
// TypeSignature:
// Z | C | B | S | I | F | J | D | FieldTypeSignature
switch (getChar(signature, pos)) {
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
case 'F':
case 'J':
case 'D':
return pos + 1;
default:
return checkFieldTypeSignature(signature, pos);
}
}
/**
* Checks an identifier.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkIdentifier(final String signature, int pos) {
if (!Character.isJavaIdentifierStart(getChar(signature, pos))) {
throw new IllegalArgumentException(signature
+ ": identifier expected at index " + pos);
}
++pos;
while (Character.isJavaIdentifierPart(getChar(signature, pos))) {
++pos;
}
return pos;
}
/**
* Checks a single character.
*
* @param signature
* a string containing the signature that must be checked.
* @param pos
* index of first character to be checked.
* @return the index of the first character after the checked part.
*/
private static int checkChar(final char c, final String signature, int pos) {
if (getChar(signature, pos) == c) {
return pos + 1;
}
throw new IllegalArgumentException(signature + ": '" + c
+ "' expected at index " + pos);
}
/**
* Returns the signature car at the given index.
*
* @param signature
* a signature.
* @param pos
* an index in signature.
* @return the character at the given index, or 0 if there is no such
* character.
*/
private static char getChar(final String signature, int pos) {
return pos < signature.length() ? signature.charAt(pos) : (char) 0;
}
}
@@ -0,0 +1,100 @@
/***
* 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.util;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link org.objectweb.asm.FieldVisitor} that checks that its methods are properly used.
*/
public class CheckFieldAdapter extends FieldVisitor {
private boolean end;
/**
* Constructs a new {@link org.objectweb.asm.util.CheckFieldAdapter}. <i>Subclasses must not use
* this constructor</i>. Instead, they must use the
* {@link #CheckFieldAdapter(int, org.objectweb.asm.FieldVisitor)} version.
*
* @param fv
* the field visitor to which this adapter must delegate calls.
*/
public CheckFieldAdapter(final FieldVisitor fv) {
this(Opcodes.ASM4, fv);
}
/**
* Constructs a new {@link org.objectweb.asm.util.CheckFieldAdapter}.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link org.objectweb.asm.Opcodes#ASM4}.
* @param fv
* the field visitor to which this adapter must delegate calls.
*/
protected CheckFieldAdapter(final int api, final FieldVisitor fv) {
super(api, fv);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
checkEnd();
CheckMethodAdapter.checkDesc(desc, false);
return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible));
}
@Override
public void visitAttribute(final Attribute attr) {
checkEnd();
if (attr == null) {
throw new IllegalArgumentException(
"Invalid attribute (must not be null)");
}
super.visitAttribute(attr);
}
@Override
public void visitEnd() {
checkEnd();
end = true;
super.visitEnd();
}
private void checkEnd() {
if (end) {
throw new IllegalStateException(
"Cannot call a visit method after visitEnd has been called");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,330 @@
/***
* 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.util;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.signature.SignatureVisitor;
/**
* A {@link org.objectweb.asm.signature.SignatureVisitor} that checks that its methods are properly used.
*
* @author Eric Bruneton
*/
public class CheckSignatureAdapter extends SignatureVisitor {
/**
* Type to be used to check class signatures. See
* {@link #CheckSignatureAdapter(int, org.objectweb.asm.signature.SignatureVisitor)
* CheckSignatureAdapter}.
*/
public static final int CLASS_SIGNATURE = 0;
/**
* Type to be used to check method signatures. See
* {@link #CheckSignatureAdapter(int, org.objectweb.asm.signature.SignatureVisitor)
* CheckSignatureAdapter}.
*/
public static final int METHOD_SIGNATURE = 1;
/**
* Type to be used to check type signatures.See
* {@link #CheckSignatureAdapter(int, org.objectweb.asm.signature.SignatureVisitor)
* CheckSignatureAdapter}.
*/
public static final int TYPE_SIGNATURE = 2;
private static final int EMPTY = 1;
private static final int FORMAL = 2;
private static final int BOUND = 4;
private static final int SUPER = 8;
private static final int PARAM = 16;
private static final int RETURN = 32;
private static final int SIMPLE_TYPE = 64;
private static final int CLASS_TYPE = 128;
private static final int END = 256;
/**
* Type of the signature to be checked.
*/
private final int type;
/**
* State of the automaton used to check the order of method calls.
*/
private int state;
/**
* <tt>true</tt> if the checked type signature can be 'V'.
*/
private boolean canBeVoid;
/**
* The visitor to which this adapter must delegate calls. May be
* <tt>null</tt>.
*/
private final SignatureVisitor sv;
/**
* Creates a new {@link org.objectweb.asm.util.CheckSignatureAdapter} object. <i>Subclasses must
* not use this constructor</i>. Instead, they must use the
* {@link #CheckSignatureAdapter(int, int, org.objectweb.asm.signature.SignatureVisitor)} version.
*
* @param type
* the type of signature to be checked. See
* {@link #CLASS_SIGNATURE}, {@link #METHOD_SIGNATURE} and
* {@link #TYPE_SIGNATURE}.
* @param sv
* the visitor to which this adapter must delegate calls. May be
* <tt>null</tt>.
*/
public CheckSignatureAdapter(final int type, final SignatureVisitor sv) {
this(Opcodes.ASM4, type, sv);
}
/**
* Creates a new {@link org.objectweb.asm.util.CheckSignatureAdapter} object.
*
* @param api
* the ASM API version implemented by this visitor. Must be one
* of {@link org.objectweb.asm.Opcodes#ASM4}.
* @param type
* the type of signature to be checked. See
* {@link #CLASS_SIGNATURE}, {@link #METHOD_SIGNATURE} and
* {@link #TYPE_SIGNATURE}.
* @param sv
* the visitor to which this adapter must delegate calls. May be
* <tt>null</tt>.
*/
protected CheckSignatureAdapter(final int api, final int type,
final SignatureVisitor sv) {
super(api);
this.type = type;
this.state = EMPTY;
this.sv = sv;
}
// class and method signatures
@Override
public void visitFormalTypeParameter(final String name) {
if (type == TYPE_SIGNATURE
|| (state != EMPTY && state != FORMAL && state != BOUND)) {
throw new IllegalStateException();
}
CheckMethodAdapter.checkIdentifier(name, "formal type parameter");
state = FORMAL;
if (sv != null) {
sv.visitFormalTypeParameter(name);
}
}
@Override
public SignatureVisitor visitClassBound() {
if (state != FORMAL) {
throw new IllegalStateException();
}
state = BOUND;
SignatureVisitor v = sv == null ? null : sv.visitClassBound();
return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
}
@Override
public SignatureVisitor visitInterfaceBound() {
if (state != FORMAL && state != BOUND) {
throw new IllegalArgumentException();
}
SignatureVisitor v = sv == null ? null : sv.visitInterfaceBound();
return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
}
// class signatures
@Override
public SignatureVisitor visitSuperclass() {
if (type != CLASS_SIGNATURE || (state & (EMPTY | FORMAL | BOUND)) == 0) {
throw new IllegalArgumentException();
}
state = SUPER;
SignatureVisitor v = sv == null ? null : sv.visitSuperclass();
return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
}
@Override
public SignatureVisitor visitInterface() {
if (state != SUPER) {
throw new IllegalStateException();
}
SignatureVisitor v = sv == null ? null : sv.visitInterface();
return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
}
// method signatures
@Override
public SignatureVisitor visitParameterType() {
if (type != METHOD_SIGNATURE
|| (state & (EMPTY | FORMAL | BOUND | PARAM)) == 0) {
throw new IllegalArgumentException();
}
state = PARAM;
SignatureVisitor v = sv == null ? null : sv.visitParameterType();
return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
}
@Override
public SignatureVisitor visitReturnType() {
if (type != METHOD_SIGNATURE
|| (state & (EMPTY | FORMAL | BOUND | PARAM)) == 0) {
throw new IllegalArgumentException();
}
state = RETURN;
SignatureVisitor v = sv == null ? null : sv.visitReturnType();
CheckSignatureAdapter cv = new CheckSignatureAdapter(TYPE_SIGNATURE, v);
cv.canBeVoid = true;
return cv;
}
@Override
public SignatureVisitor visitExceptionType() {
if (state != RETURN) {
throw new IllegalStateException();
}
SignatureVisitor v = sv == null ? null : sv.visitExceptionType();
return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
}
// type signatures
@Override
public void visitBaseType(final char descriptor) {
if (type != TYPE_SIGNATURE || state != EMPTY) {
throw new IllegalStateException();
}
if (descriptor == 'V') {
if (!canBeVoid) {
throw new IllegalArgumentException();
}
} else {
if ("ZCBSIFJD".indexOf(descriptor) == -1) {
throw new IllegalArgumentException();
}
}
state = SIMPLE_TYPE;
if (sv != null) {
sv.visitBaseType(descriptor);
}
}
@Override
public void visitTypeVariable(final String name) {
if (type != TYPE_SIGNATURE || state != EMPTY) {
throw new IllegalStateException();
}
CheckMethodAdapter.checkIdentifier(name, "type variable");
state = SIMPLE_TYPE;
if (sv != null) {
sv.visitTypeVariable(name);
}
}
@Override
public SignatureVisitor visitArrayType() {
if (type != TYPE_SIGNATURE || state != EMPTY) {
throw new IllegalStateException();
}
state = SIMPLE_TYPE;
SignatureVisitor v = sv == null ? null : sv.visitArrayType();
return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
}
@Override
public void visitClassType(final String name) {
if (type != TYPE_SIGNATURE || state != EMPTY) {
throw new IllegalStateException();
}
CheckMethodAdapter.checkInternalName(name, "class name");
state = CLASS_TYPE;
if (sv != null) {
sv.visitClassType(name);
}
}
@Override
public void visitInnerClassType(final String name) {
if (state != CLASS_TYPE) {
throw new IllegalStateException();
}
CheckMethodAdapter.checkIdentifier(name, "inner class name");
if (sv != null) {
sv.visitInnerClassType(name);
}
}
@Override
public void visitTypeArgument() {
if (state != CLASS_TYPE) {
throw new IllegalStateException();
}
if (sv != null) {
sv.visitTypeArgument();
}
}
@Override
public SignatureVisitor visitTypeArgument(final char wildcard) {
if (state != CLASS_TYPE) {
throw new IllegalStateException();
}
if ("+-=".indexOf(wildcard) == -1) {
throw new IllegalArgumentException();
}
SignatureVisitor v = sv == null ? null : sv.visitTypeArgument(wildcard);
return new CheckSignatureAdapter(TYPE_SIGNATURE, v);
}
@Override
public void visitEnd() {
if (state != CLASS_TYPE) {
throw new IllegalStateException();
}
state = END;
if (sv != null) {
sv.visitEnd();
}
}
}
@@ -0,0 +1,499 @@
/***
* 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.util;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
/**
* An abstract converter from visit events to text.
*
* @author Eric Bruneton
*/
public abstract class Printer {
/**
* The names of the Java Virtual Machine opcodes.
*/
public static final String[] OPCODES;
/**
* The names of the for <code>operand</code> parameter values of the
* {@link org.objectweb.asm.MethodVisitor#visitIntInsn} method when
* <code>opcode</code> is <code>NEWARRAY</code>.
*/
public static final String[] TYPES;
/**
* The names of the <code>tag</code> field values for
* {@link org.objectweb.asm.Handle}.
*/
public static final String[] HANDLE_TAG;
static {
String s = "NOP,ACONST_NULL,ICONST_M1,ICONST_0,ICONST_1,ICONST_2,"
+ "ICONST_3,ICONST_4,ICONST_5,LCONST_0,LCONST_1,FCONST_0,"
+ "FCONST_1,FCONST_2,DCONST_0,DCONST_1,BIPUSH,SIPUSH,LDC,,,"
+ "ILOAD,LLOAD,FLOAD,DLOAD,ALOAD,,,,,,,,,,,,,,,,,,,,,IALOAD,"
+ "LALOAD,FALOAD,DALOAD,AALOAD,BALOAD,CALOAD,SALOAD,ISTORE,"
+ "LSTORE,FSTORE,DSTORE,ASTORE,,,,,,,,,,,,,,,,,,,,,IASTORE,"
+ "LASTORE,FASTORE,DASTORE,AASTORE,BASTORE,CASTORE,SASTORE,POP,"
+ "POP2,DUP,DUP_X1,DUP_X2,DUP2,DUP2_X1,DUP2_X2,SWAP,IADD,LADD,"
+ "FADD,DADD,ISUB,LSUB,FSUB,DSUB,IMUL,LMUL,FMUL,DMUL,IDIV,LDIV,"
+ "FDIV,DDIV,IREM,LREM,FREM,DREM,INEG,LNEG,FNEG,DNEG,ISHL,LSHL,"
+ "ISHR,LSHR,IUSHR,LUSHR,IAND,LAND,IOR,LOR,IXOR,LXOR,IINC,I2L,"
+ "I2F,I2D,L2I,L2F,L2D,F2I,F2L,F2D,D2I,D2L,D2F,I2B,I2C,I2S,LCMP,"
+ "FCMPL,FCMPG,DCMPL,DCMPG,IFEQ,IFNE,IFLT,IFGE,IFGT,IFLE,"
+ "IF_ICMPEQ,IF_ICMPNE,IF_ICMPLT,IF_ICMPGE,IF_ICMPGT,IF_ICMPLE,"
+ "IF_ACMPEQ,IF_ACMPNE,GOTO,JSR,RET,TABLESWITCH,LOOKUPSWITCH,"
+ "IRETURN,LRETURN,FRETURN,DRETURN,ARETURN,RETURN,GETSTATIC,"
+ "PUTSTATIC,GETFIELD,PUTFIELD,INVOKEVIRTUAL,INVOKESPECIAL,"
+ "INVOKESTATIC,INVOKEINTERFACE,INVOKEDYNAMIC,NEW,NEWARRAY,"
+ "ANEWARRAY,ARRAYLENGTH,ATHROW,CHECKCAST,INSTANCEOF,"
+ "MONITORENTER,MONITOREXIT,,MULTIANEWARRAY,IFNULL,IFNONNULL,";
OPCODES = new String[200];
int i = 0;
int j = 0;
int l;
while ((l = s.indexOf(',', j)) > 0) {
OPCODES[i++] = j + 1 == l ? null : s.substring(j, l);
j = l + 1;
}
s = "T_BOOLEAN,T_CHAR,T_FLOAT,T_DOUBLE,T_BYTE,T_SHORT,T_INT,T_LONG,";
TYPES = new String[12];
j = 0;
i = 4;
while ((l = s.indexOf(',', j)) > 0) {
TYPES[i++] = s.substring(j, l);
j = l + 1;
}
s = "H_GETFIELD,H_GETSTATIC,H_PUTFIELD,H_PUTSTATIC,"
+ "H_INVOKEVIRTUAL,H_INVOKESTATIC,H_INVOKESPECIAL,"
+ "H_NEWINVOKESPECIAL,H_INVOKEINTERFACE,";
HANDLE_TAG = new String[10];
j = 0;
i = 1;
while ((l = s.indexOf(',', j)) > 0) {
HANDLE_TAG[i++] = s.substring(j, l);
j = l + 1;
}
}
/**
* The ASM API version implemented by this class. The value of this field
* must be one of {@link org.objectweb.asm.Opcodes#ASM4}.
*/
protected final int api;
/**
* A buffer that can be used to create strings.
*/
protected final StringBuffer buf;
/**
* The text to be printed. Since the code of methods is not necessarily
* visited in sequential order, one method after the other, but can be
* interlaced (some instructions from method one, then some instructions
* from method two, then some instructions from method one again...), it is
* not possible to print the visited instructions directly to a sequential
* stream. A class is therefore printed in a two steps process: a string
* tree is constructed during the visit, and printed to a sequential stream
* at the end of the visit. This string tree is stored in this field, as a
* string list that can contain other string lists, which can themselves
* contain other string lists, and so on.
*/
public final List<Object> text;
/**
* Constructs a new {@link org.objectweb.asm.util.Printer}.
*/
protected Printer(final int api) {
this.api = api;
this.buf = new StringBuffer();
this.text = new ArrayList<Object>();
}
/**
* Class header. See {@link org.objectweb.asm.ClassVisitor#visit}.
*/
public abstract void visit(final int version, final int access,
final String name, final String signature, final String superName,
final String[] interfaces);
/**
* Class source. See {@link org.objectweb.asm.ClassVisitor#visitSource}.
*/
public abstract void visitSource(final String file, final String debug);
/**
* Class outer class. See
* {@link org.objectweb.asm.ClassVisitor#visitOuterClass}.
*/
public abstract void visitOuterClass(final String owner, final String name,
final String desc);
/**
* Class annotation. See
* {@link org.objectweb.asm.ClassVisitor#visitAnnotation}.
*/
public abstract Printer visitClassAnnotation(final String desc,
final boolean visible);
/**
* Class attribute. See
* {@link org.objectweb.asm.ClassVisitor#visitAttribute}.
*/
public abstract void visitClassAttribute(final Attribute attr);
/**
* Class inner name. See
* {@link org.objectweb.asm.ClassVisitor#visitInnerClass}.
*/
public abstract void visitInnerClass(final String name,
final String outerName, final String innerName, final int access);
/**
* Class field. See {@link org.objectweb.asm.ClassVisitor#visitField}.
*/
public abstract Printer visitField(final int access, final String name,
final String desc, final String signature, final Object value);
/**
* Class method. See {@link org.objectweb.asm.ClassVisitor#visitMethod}.
*/
public abstract Printer visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions);
/**
* Class end. See {@link org.objectweb.asm.ClassVisitor#visitEnd}.
*/
public abstract void visitClassEnd();
// ------------------------------------------------------------------------
// Annotations
// ------------------------------------------------------------------------
/**
* Annotation value. See {@link org.objectweb.asm.AnnotationVisitor#visit}.
*/
public abstract void visit(final String name, final Object value);
/**
* Annotation enum value. See
* {@link org.objectweb.asm.AnnotationVisitor#visitEnum}.
*/
public abstract void visitEnum(final String name, final String desc,
final String value);
/**
* Nested annotation value. See
* {@link org.objectweb.asm.AnnotationVisitor#visitAnnotation}.
*/
public abstract Printer visitAnnotation(final String name, final String desc);
/**
* Annotation array value. See
* {@link org.objectweb.asm.AnnotationVisitor#visitArray}.
*/
public abstract Printer visitArray(final String name);
/**
* Annotation end. See {@link org.objectweb.asm.AnnotationVisitor#visitEnd}.
*/
public abstract void visitAnnotationEnd();
// ------------------------------------------------------------------------
// Fields
// ------------------------------------------------------------------------
/**
* Field annotation. See
* {@link org.objectweb.asm.FieldVisitor#visitAnnotation}.
*/
public abstract Printer visitFieldAnnotation(final String desc,
final boolean visible);
/**
* Field attribute. See
* {@link org.objectweb.asm.FieldVisitor#visitAttribute}.
*/
public abstract void visitFieldAttribute(final Attribute attr);
/**
* Field end. See {@link org.objectweb.asm.FieldVisitor#visitEnd}.
*/
public abstract void visitFieldEnd();
// ------------------------------------------------------------------------
// Methods
// ------------------------------------------------------------------------
/**
* Method default annotation. See
* {@link org.objectweb.asm.MethodVisitor#visitAnnotationDefault}.
*/
public abstract Printer visitAnnotationDefault();
/**
* Method annotation. See
* {@link org.objectweb.asm.MethodVisitor#visitAnnotation}.
*/
public abstract Printer visitMethodAnnotation(final String desc,
final boolean visible);
/**
* Method parameter annotation. See
* {@link org.objectweb.asm.MethodVisitor#visitParameterAnnotation}.
*/
public abstract Printer visitParameterAnnotation(final int parameter,
final String desc, final boolean visible);
/**
* Method attribute. See
* {@link org.objectweb.asm.MethodVisitor#visitAttribute}.
*/
public abstract void visitMethodAttribute(final Attribute attr);
/**
* Method start. See {@link org.objectweb.asm.MethodVisitor#visitCode}.
*/
public abstract void visitCode();
/**
* Method stack frame. See
* {@link org.objectweb.asm.MethodVisitor#visitFrame}.
*/
public abstract void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack);
/**
* Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitInsn}
* .
*/
public abstract void visitInsn(final int opcode);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitIntInsn}.
*/
public abstract void visitIntInsn(final int opcode, final int operand);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitVarInsn}.
*/
public abstract void visitVarInsn(final int opcode, final int var);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitTypeInsn}.
*/
public abstract void visitTypeInsn(final int opcode, final String type);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitFieldInsn}.
*/
public abstract void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitMethodInsn}.
*/
public abstract void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitInvokeDynamicInsn}.
*/
public abstract void visitInvokeDynamicInsn(String name, String desc,
Handle bsm, Object... bsmArgs);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitJumpInsn}.
*/
public abstract void visitJumpInsn(final int opcode, final Label label);
/**
* Method label. See {@link org.objectweb.asm.MethodVisitor#visitLabel}.
*/
public abstract void visitLabel(final Label label);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitLdcInsn}.
*/
public abstract void visitLdcInsn(final Object cst);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitIincInsn}.
*/
public abstract void visitIincInsn(final int var, final int increment);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitTableSwitchInsn}.
*/
public abstract void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitLookupSwitchInsn}.
*/
public abstract void visitLookupSwitchInsn(final Label dflt,
final int[] keys, final Label[] labels);
/**
* Method instruction. See
* {@link org.objectweb.asm.MethodVisitor#visitMultiANewArrayInsn}.
*/
public abstract void visitMultiANewArrayInsn(final String desc,
final int dims);
/**
* Method exception handler. See
* {@link org.objectweb.asm.MethodVisitor#visitTryCatchBlock}.
*/
public abstract void visitTryCatchBlock(final Label start, final Label end,
final Label handler, final String type);
/**
* Method debug info. See
* {@link org.objectweb.asm.MethodVisitor#visitLocalVariable}.
*/
public abstract void visitLocalVariable(final String name,
final String desc, final String signature, final Label start,
final Label end, final int index);
/**
* Method debug info. See
* {@link org.objectweb.asm.MethodVisitor#visitLineNumber}.
*/
public abstract void visitLineNumber(final int line, final Label start);
/**
* Method max stack and max locals. See
* {@link org.objectweb.asm.MethodVisitor#visitMaxs}.
*/
public abstract void visitMaxs(final int maxStack, final int maxLocals);
/**
* Method end. See {@link org.objectweb.asm.MethodVisitor#visitEnd}.
*/
public abstract void visitMethodEnd();
/**
* Returns the text constructed by this visitor.
*
* @return the text constructed by this visitor.
*/
public List<Object> getText() {
return text;
}
/**
* Prints the text constructed by this visitor.
*
* @param pw
* the print writer to be used.
*/
public void print(final PrintWriter pw) {
printList(pw, text);
}
/**
* Appends a quoted string to a given buffer.
*
* @param buf
* the buffer where the string must be added.
* @param s
* the string to be added.
*/
public static void appendString(final StringBuffer buf, final String s) {
buf.append('\"');
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
if (c == '\n') {
buf.append("\\n");
} else if (c == '\r') {
buf.append("\\r");
} else if (c == '\\') {
buf.append("\\\\");
} else if (c == '"') {
buf.append("\\\"");
} else if (c < 0x20 || c > 0x7f) {
buf.append("\\u");
if (c < 0x10) {
buf.append("000");
} else if (c < 0x100) {
buf.append("00");
} else if (c < 0x1000) {
buf.append('0');
}
buf.append(Integer.toString(c, 16));
} else {
buf.append(c);
}
}
buf.append('\"');
}
/**
* Prints the given string tree.
*
* @param pw
* the writer to be used to print the tree.
* @param l
* a string tree, i.e., a string list that can contain other
* string lists, and so on recursively.
*/
static void printList(final PrintWriter pw, final List<?> l) {
for (int i = 0; i < l.size(); ++i) {
Object o = l.get(i);
if (o instanceof List) {
printList(pw, (List<?>) o);
} else {
pw.print(o.toString());
}
}
}
}
@@ -0,0 +1,56 @@
/**
* 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.util;
import java.util.Map;
import org.objectweb.asm.Label;
/**
* An {@link org.objectweb.asm.Attribute Attribute} that can print a readable
* representation of itself.
*
* Implementations should construct readable output from an attribute data
* structure. Such representation could be used in unit test assertions.
*
* @author Eugene Kuleshov
*/
public interface Textifiable {
/**
* Build a human readable representation of this attribute.
*
* @param buf
* a buffer used for printing Java code.
* @param labelNames
* map of label instances to their names.
*/
void textify(StringBuffer buf, Map<Label, String> labelNames);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,89 @@
/***
* 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.util;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
/**
* An {@link org.objectweb.asm.AnnotationVisitor} that prints the annotations it visits with a
* {@link org.objectweb.asm.util.Printer}.
*
* @author Eric Bruneton
*/
public final class TraceAnnotationVisitor extends AnnotationVisitor {
private final Printer p;
public TraceAnnotationVisitor(final Printer p) {
this(null, p);
}
public TraceAnnotationVisitor(final AnnotationVisitor av, final Printer p) {
super(Opcodes.ASM4, av);
this.p = p;
}
@Override
public void visit(final String name, final Object value) {
p.visit(name, value);
super.visit(name, value);
}
@Override
public void visitEnum(final String name, final String desc,
final String value) {
p.visitEnum(name, desc, value);
super.visitEnum(name, desc, value);
}
@Override
public AnnotationVisitor visitAnnotation(final String name,
final String desc) {
Printer p = this.p.visitAnnotation(name, desc);
AnnotationVisitor av = this.av == null ? null : this.av
.visitAnnotation(name, desc);
return new TraceAnnotationVisitor(av, p);
}
@Override
public AnnotationVisitor visitArray(final String name) {
Printer p = this.p.visitArray(name);
AnnotationVisitor av = this.av == null ? null : this.av
.visitArray(name);
return new TraceAnnotationVisitor(av, p);
}
@Override
public void visitEnd() {
p.visitAnnotationEnd();
super.visitEnd();
}
}
@@ -0,0 +1,209 @@
/***
* 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.util;
import java.io.PrintWriter;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link org.objectweb.asm.ClassVisitor} that prints the classes it visits with a
* {@link org.objectweb.asm.util.Printer}. This class visitor can be used in the middle of a class
* visitor chain to trace the class that is visited at a given point in this
* chain. This may be useful for debugging purposes.
* <p>
* The trace printed when visiting the <tt>Hello</tt> class is the following:
* <p>
* <blockquote>
*
* <pre>
* // class version 49.0 (49) // access flags 0x21 public class Hello {
*
* // compiled from: Hello.java
*
* // access flags 0x1 public &lt;init&gt; ()V ALOAD 0 INVOKESPECIAL
* java/lang/Object &lt;init&gt; ()V RETURN MAXSTACK = 1 MAXLOCALS = 1
*
* // access flags 0x9 public static main ([Ljava/lang/String;)V GETSTATIC
* java/lang/System out Ljava/io/PrintStream; LDC &quot;hello&quot;
* INVOKEVIRTUAL java/io/PrintStream println (Ljava/lang/String;)V RETURN
* MAXSTACK = 2 MAXLOCALS = 1 }
* </pre>
*
* </blockquote> where <tt>Hello</tt> is defined by:
* <p>
* <blockquote>
*
* <pre>
* public class Hello {
*
* public static void main(String[] args) {
* System.out.println(&quot;hello&quot;);
* }
* }
* </pre>
*
* </blockquote>
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
public final class TraceClassVisitor extends ClassVisitor {
/**
* The print writer to be used to print the class. May be null.
*/
private final PrintWriter pw;
/**
* The object that actually converts visit events into text.
*/
public final Printer p;
/**
* Constructs a new {@link org.objectweb.asm.util.TraceClassVisitor}.
*
* @param pw
* the print writer to be used to print the class.
*/
public TraceClassVisitor(final PrintWriter pw) {
this(null, pw);
}
/**
* Constructs a new {@link org.objectweb.asm.util.TraceClassVisitor}.
*
* @param cv
* the {@link org.objectweb.asm.ClassVisitor} to which this visitor delegates
* calls. May be <tt>null</tt>.
* @param pw
* the print writer to be used to print the class.
*/
public TraceClassVisitor(final ClassVisitor cv, final PrintWriter pw) {
this(cv, new Textifier(), pw);
}
/**
* Constructs a new {@link org.objectweb.asm.util.TraceClassVisitor}.
*
* @param cv
* the {@link org.objectweb.asm.ClassVisitor} to which this visitor delegates
* calls. May be <tt>null</tt>.
* @param p
* the object that actually converts visit events into text.
* @param pw
* the print writer to be used to print the class. May be null if
* you simply want to use the result via
* {@link org.objectweb.asm.util.Printer#getText()}, instead of printing it.
*/
public TraceClassVisitor(final ClassVisitor cv, final Printer p,
final PrintWriter pw) {
super(Opcodes.ASM4, cv);
this.pw = pw;
this.p = p;
}
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName,
final String[] interfaces) {
p.visit(version, access, name, signature, superName, interfaces);
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public void visitSource(final String file, final String debug) {
p.visitSource(file, debug);
super.visitSource(file, debug);
}
@Override
public void visitOuterClass(final String owner, final String name,
final String desc) {
p.visitOuterClass(owner, name, desc);
super.visitOuterClass(owner, name, desc);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
Printer p = this.p.visitClassAnnotation(desc, visible);
AnnotationVisitor av = cv == null ? null : cv.visitAnnotation(desc,
visible);
return new TraceAnnotationVisitor(av, p);
}
@Override
public void visitAttribute(final Attribute attr) {
p.visitClassAttribute(attr);
super.visitAttribute(attr);
}
@Override
public void visitInnerClass(final String name, final String outerName,
final String innerName, final int access) {
p.visitInnerClass(name, outerName, innerName, access);
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public FieldVisitor visitField(final int access, final String name,
final String desc, final String signature, final Object value) {
Printer p = this.p.visitField(access, name, desc, signature, value);
FieldVisitor fv = cv == null ? null : cv.visitField(access, name, desc,
signature, value);
return new TraceFieldVisitor(fv, p);
}
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
Printer p = this.p.visitMethod(access, name, desc, signature,
exceptions);
MethodVisitor mv = cv == null ? null : cv.visitMethod(access, name,
desc, signature, exceptions);
return new TraceMethodVisitor(mv, p);
}
@Override
public void visitEnd() {
p.visitClassEnd();
if (pw != null) {
p.print(pw);
pw.flush();
}
super.visitEnd();
}
}
@@ -0,0 +1,76 @@
/***
* 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.util;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link org.objectweb.asm.FieldVisitor} that prints the fields it visits with a
* {@link org.objectweb.asm.util.Printer}.
*
* @author Eric Bruneton
*/
public final class TraceFieldVisitor extends FieldVisitor {
public final Printer p;
public TraceFieldVisitor(final Printer p) {
this(null, p);
}
public TraceFieldVisitor(final FieldVisitor fv, final Printer p) {
super(Opcodes.ASM4, fv);
this.p = p;
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
Printer p = this.p.visitFieldAnnotation(desc, visible);
AnnotationVisitor av = fv == null ? null : fv.visitAnnotation(desc,
visible);
return new TraceAnnotationVisitor(av, p);
}
@Override
public void visitAttribute(final Attribute attr) {
p.visitFieldAttribute(attr);
super.visitAttribute(attr);
}
@Override
public void visitEnd() {
p.visitFieldEnd();
super.visitEnd();
}
}
@@ -0,0 +1,223 @@
/***
* 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.util;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A {@link org.objectweb.asm.MethodVisitor} that prints the methods it visits with a
* {@link org.objectweb.asm.util.Printer}.
*
* @author Eric Bruneton
*/
public final class TraceMethodVisitor extends MethodVisitor {
public final Printer p;
public TraceMethodVisitor(final Printer p) {
this(null, p);
}
public TraceMethodVisitor(final MethodVisitor mv, final Printer p) {
super(Opcodes.ASM4, mv);
this.p = p;
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
Printer p = this.p.visitMethodAnnotation(desc, visible);
AnnotationVisitor av = mv == null ? null : mv.visitAnnotation(desc,
visible);
return new TraceAnnotationVisitor(av, p);
}
@Override
public void visitAttribute(final Attribute attr) {
p.visitMethodAttribute(attr);
super.visitAttribute(attr);
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
Printer p = this.p.visitAnnotationDefault();
AnnotationVisitor av = mv == null ? null : mv.visitAnnotationDefault();
return new TraceAnnotationVisitor(av, p);
}
@Override
public AnnotationVisitor visitParameterAnnotation(final int parameter,
final String desc, final boolean visible) {
Printer p = this.p.visitParameterAnnotation(parameter, desc, visible);
AnnotationVisitor av = mv == null ? null : mv.visitParameterAnnotation(
parameter, desc, visible);
return new TraceAnnotationVisitor(av, p);
}
@Override
public void visitCode() {
p.visitCode();
super.visitCode();
}
@Override
public void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack) {
p.visitFrame(type, nLocal, local, nStack, stack);
super.visitFrame(type, nLocal, local, nStack, stack);
}
@Override
public void visitInsn(final int opcode) {
p.visitInsn(opcode);
super.visitInsn(opcode);
}
@Override
public void visitIntInsn(final int opcode, final int operand) {
p.visitIntInsn(opcode, operand);
super.visitIntInsn(opcode, operand);
}
@Override
public void visitVarInsn(final int opcode, final int var) {
p.visitVarInsn(opcode, var);
super.visitVarInsn(opcode, var);
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
p.visitTypeInsn(opcode, type);
super.visitTypeInsn(opcode, type);
}
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
p.visitFieldInsn(opcode, owner, name, desc);
super.visitFieldInsn(opcode, owner, name, desc);
}
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
p.visitMethodInsn(opcode, owner, name, desc);
super.visitMethodInsn(opcode, owner, name, desc);
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
p.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
p.visitJumpInsn(opcode, label);
super.visitJumpInsn(opcode, label);
}
@Override
public void visitLabel(final Label label) {
p.visitLabel(label);
super.visitLabel(label);
}
@Override
public void visitLdcInsn(final Object cst) {
p.visitLdcInsn(cst);
super.visitLdcInsn(cst);
}
@Override
public void visitIincInsn(final int var, final int increment) {
p.visitIincInsn(var, increment);
super.visitIincInsn(var, increment);
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
p.visitTableSwitchInsn(min, max, dflt, labels);
super.visitTableSwitchInsn(min, max, dflt, labels);
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
p.visitLookupSwitchInsn(dflt, keys, labels);
super.visitLookupSwitchInsn(dflt, keys, labels);
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
p.visitMultiANewArrayInsn(desc, dims);
super.visitMultiANewArrayInsn(desc, dims);
}
@Override
public void visitTryCatchBlock(final Label start, final Label end,
final Label handler, final String type) {
p.visitTryCatchBlock(start, end, handler, type);
super.visitTryCatchBlock(start, end, handler, type);
}
@Override
public void visitLocalVariable(final String name, final String desc,
final String signature, final Label start, final Label end,
final int index) {
p.visitLocalVariable(name, desc, signature, start, end, index);
super.visitLocalVariable(name, desc, signature, start, end, index);
}
@Override
public void visitLineNumber(final int line, final Label start) {
p.visitLineNumber(line, start);
super.visitLineNumber(line, start);
}
@Override
public void visitMaxs(final int maxStack, final int maxLocals) {
p.visitMaxs(maxStack, maxLocals);
super.visitMaxs(maxStack, maxLocals);
}
@Override
public void visitEnd() {
p.visitMethodEnd();
super.visitEnd();
}
}
@@ -0,0 +1,317 @@
/***
* 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.util;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.signature.SignatureVisitor;
/**
* A {@link org.objectweb.asm.signature.SignatureVisitor} that prints a disassembled view of the signature
* it visits.
*
* @author Eugene Kuleshov
* @author Eric Bruneton
*/
public final class TraceSignatureVisitor extends SignatureVisitor {
private final StringBuffer declaration;
private boolean isInterface;
private boolean seenFormalParameter;
private boolean seenInterfaceBound;
private boolean seenParameter;
private boolean seenInterface;
private StringBuffer returnType;
private StringBuffer exceptions;
/**
* Stack used to keep track of class types that have arguments. Each element
* of this stack is a boolean encoded in one bit. The top of the stack is
* the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
* /2.
*/
private int argumentStack;
/**
* Stack used to keep track of array class types. Each element of this stack
* is a boolean encoded in one bit. The top of the stack is the lowest order
* bit. Pushing false = *2, pushing true = *2+1, popping = /2.
*/
private int arrayStack;
private String separator = "";
public TraceSignatureVisitor(final int access) {
super(Opcodes.ASM4);
isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
this.declaration = new StringBuffer();
}
private TraceSignatureVisitor(final StringBuffer buf) {
super(Opcodes.ASM4);
this.declaration = buf;
}
@Override
public void visitFormalTypeParameter(final String name) {
declaration.append(seenFormalParameter ? ", " : "<").append(name);
seenFormalParameter = true;
seenInterfaceBound = false;
}
@Override
public SignatureVisitor visitClassBound() {
separator = " extends ";
startType();
return this;
}
@Override
public SignatureVisitor visitInterfaceBound() {
separator = seenInterfaceBound ? ", " : " extends ";
seenInterfaceBound = true;
startType();
return this;
}
@Override
public SignatureVisitor visitSuperclass() {
endFormals();
separator = " extends ";
startType();
return this;
}
@Override
public SignatureVisitor visitInterface() {
separator = seenInterface ? ", " : isInterface ? " extends "
: " implements ";
seenInterface = true;
startType();
return this;
}
@Override
public SignatureVisitor visitParameterType() {
endFormals();
if (seenParameter) {
declaration.append(", ");
} else {
seenParameter = true;
declaration.append('(');
}
startType();
return this;
}
@Override
public SignatureVisitor visitReturnType() {
endFormals();
if (seenParameter) {
seenParameter = false;
} else {
declaration.append('(');
}
declaration.append(')');
returnType = new StringBuffer();
return new TraceSignatureVisitor(returnType);
}
@Override
public SignatureVisitor visitExceptionType() {
if (exceptions == null) {
exceptions = new StringBuffer();
} else {
exceptions.append(", ");
}
// startType();
return new TraceSignatureVisitor(exceptions);
}
@Override
public void visitBaseType(final char descriptor) {
switch (descriptor) {
case 'V':
declaration.append("void");
break;
case 'B':
declaration.append("byte");
break;
case 'J':
declaration.append("long");
break;
case 'Z':
declaration.append("boolean");
break;
case 'I':
declaration.append("int");
break;
case 'S':
declaration.append("short");
break;
case 'C':
declaration.append("char");
break;
case 'F':
declaration.append("float");
break;
// case 'D':
default:
declaration.append("double");
break;
}
endType();
}
@Override
public void visitTypeVariable(final String name) {
declaration.append(name);
endType();
}
@Override
public SignatureVisitor visitArrayType() {
startType();
arrayStack |= 1;
return this;
}
@Override
public void visitClassType(final String name) {
if ("java/lang/Object".equals(name)) {
// Map<java.lang.Object,java.util.List>
// or
// abstract public V get(Object key); (seen in Dictionary.class)
// should have Object
// but java.lang.String extends java.lang.Object is unnecessary
boolean needObjectClass = argumentStack % 2 != 0 || seenParameter;
if (needObjectClass) {
declaration.append(separator).append(name.replace('/', '.'));
}
} else {
declaration.append(separator).append(name.replace('/', '.'));
}
separator = "";
argumentStack *= 2;
}
@Override
public void visitInnerClassType(final String name) {
if (argumentStack % 2 != 0) {
declaration.append('>');
}
argumentStack /= 2;
declaration.append('.');
declaration.append(separator).append(name.replace('/', '.'));
separator = "";
argumentStack *= 2;
}
@Override
public void visitTypeArgument() {
if (argumentStack % 2 == 0) {
++argumentStack;
declaration.append('<');
} else {
declaration.append(", ");
}
declaration.append('?');
}
@Override
public SignatureVisitor visitTypeArgument(final char tag) {
if (argumentStack % 2 == 0) {
++argumentStack;
declaration.append('<');
} else {
declaration.append(", ");
}
if (tag == EXTENDS) {
declaration.append("? extends ");
} else if (tag == SUPER) {
declaration.append("? super ");
}
startType();
return this;
}
@Override
public void visitEnd() {
if (argumentStack % 2 != 0) {
declaration.append('>');
}
argumentStack /= 2;
endType();
}
public String getDeclaration() {
return declaration.toString();
}
public String getReturnType() {
return returnType == null ? null : returnType.toString();
}
public String getExceptions() {
return exceptions == null ? null : exceptions.toString();
}
// -----------------------------------------------
private void endFormals() {
if (seenFormalParameter) {
declaration.append('>');
seenFormalParameter = false;
}
}
private void startType() {
arrayStack *= 2;
}
private void endType() {
if (arrayStack % 2 == 0) {
arrayStack /= 2;
} else {
while (arrayStack % 2 != 0) {
arrayStack /= 2;
declaration.append("[]");
}
}
}
}
@@ -0,0 +1,40 @@
<html>
<!--
* 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.
-->
<body>
Provides ASM visitors that can be useful for programming and
debugging purposes. These class visitors are normally not used by applications
at runtime. This is why they are bundled in an optional <tt>asm-util.jar</tt>
library that is separated from (but requires) the <tt>asm.jar</tt> library,
which contains the core ASM framework.
@since ASM 1.3.2
</body>
</html>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,89 @@
/***
* ASM XML Adapter
* Copyright (c) 2004-2011, Eugene Kuleshov
* 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.xml;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
/**
* SAXAdapter
*
* @author Eugene Kuleshov
*/
public class SAXAdapter {
private final ContentHandler h;
protected SAXAdapter(final ContentHandler h) {
this.h = h;
}
protected ContentHandler getContentHandler() {
return h;
}
protected void addDocumentStart() {
try {
h.startDocument();
} catch (SAXException ex) {
throw new RuntimeException(ex.getMessage(), ex.getException());
}
}
protected void addDocumentEnd() {
try {
h.endDocument();
} catch (SAXException ex) {
throw new RuntimeException(ex.getMessage(), ex.getException());
}
}
protected final void addStart(final String name, final Attributes attrs) {
try {
h.startElement("", name, name, attrs);
} catch (SAXException ex) {
throw new RuntimeException(ex.getMessage(), ex.getException());
}
}
protected final void addEnd(final String name) {
try {
h.endElement("", name, name);
} catch (SAXException ex) {
throw new RuntimeException(ex.getMessage(), ex.getException());
}
}
protected final void addElement(final String name, final Attributes attrs) {
addStart(name, attrs);
addEnd(name);
}
}
@@ -0,0 +1,185 @@
/***
* ASM XML Adapter
* Copyright (c) 2004-2011, Eugene Kuleshov
* 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.xml;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.xml.sax.helpers.AttributesImpl;
/**
* SAXAnnotationAdapter
*
* @author Eugene Kuleshov
*/
public final class SAXAnnotationAdapter extends AnnotationVisitor {
SAXAdapter sa;
private final String elementName;
public SAXAnnotationAdapter(final SAXAdapter sa, final String elementName,
final int visible, final String name, final String desc) {
this(Opcodes.ASM4, sa, elementName, visible, desc, name, -1);
}
public SAXAnnotationAdapter(final SAXAdapter sa, final String elementName,
final int visible, final int parameter, final String desc) {
this(Opcodes.ASM4, sa, elementName, visible, desc, null, parameter);
}
protected SAXAnnotationAdapter(final int api, final SAXAdapter sa,
final String elementName, final int visible, final String desc,
final String name, final int parameter) {
super(api);
this.sa = sa;
this.elementName = elementName;
AttributesImpl att = new AttributesImpl();
if (name != null) {
att.addAttribute("", "name", "name", "", name);
}
if (visible != 0) {
att.addAttribute("", "visible", "visible", "", visible > 0 ? "true"
: "false");
}
if (parameter != -1) {
att.addAttribute("", "parameter", "parameter", "",
Integer.toString(parameter));
}
if (desc != null) {
att.addAttribute("", "desc", "desc", "", desc);
}
sa.addStart(elementName, att);
}
@Override
public void visit(final String name, final Object value) {
Class<?> c = value.getClass();
if (c.isArray()) {
AnnotationVisitor av = visitArray(name);
if (value instanceof byte[]) {
byte[] b = (byte[]) value;
for (int i = 0; i < b.length; i++) {
av.visit(null, new Byte(b[i]));
}
} else if (value instanceof char[]) {
char[] b = (char[]) value;
for (int i = 0; i < b.length; i++) {
av.visit(null, new Character(b[i]));
}
} else if (value instanceof short[]) {
short[] b = (short[]) value;
for (int i = 0; i < b.length; i++) {
av.visit(null, new Short(b[i]));
}
} else if (value instanceof boolean[]) {
boolean[] b = (boolean[]) value;
for (int i = 0; i < b.length; i++) {
av.visit(null, Boolean.valueOf(b[i]));
}
} else if (value instanceof int[]) {
int[] b = (int[]) value;
for (int i = 0; i < b.length; i++) {
av.visit(null, new Integer(b[i]));
}
} else if (value instanceof long[]) {
long[] b = (long[]) value;
for (int i = 0; i < b.length; i++) {
av.visit(null, new Long(b[i]));
}
} else if (value instanceof float[]) {
float[] b = (float[]) value;
for (int i = 0; i < b.length; i++) {
av.visit(null, new Float(b[i]));
}
} else if (value instanceof double[]) {
double[] b = (double[]) value;
for (int i = 0; i < b.length; i++) {
av.visit(null, new Double(b[i]));
}
}
av.visitEnd();
} else {
addValueElement("annotationValue", name, Type.getDescriptor(c),
value.toString());
}
}
@Override
public void visitEnum(final String name, final String desc,
final String value) {
addValueElement("annotationValueEnum", name, desc, value);
}
@Override
public AnnotationVisitor visitAnnotation(final String name,
final String desc) {
return new SAXAnnotationAdapter(sa, "annotationValueAnnotation", 0,
name, desc);
}
@Override
public AnnotationVisitor visitArray(final String name) {
return new SAXAnnotationAdapter(sa, "annotationValueArray", 0, name,
null);
}
@Override
public void visitEnd() {
sa.addEnd(elementName);
}
private void addValueElement(final String element, final String name,
final String desc, final String value) {
AttributesImpl att = new AttributesImpl();
if (name != null) {
att.addAttribute("", "name", "name", "", name);
}
if (desc != null) {
att.addAttribute("", "desc", "desc", "", desc);
}
if (value != null) {
att.addAttribute("", "value", "value", "",
SAXClassAdapter.encode(value));
}
sa.addElement(element, att);
}
}
@@ -0,0 +1,324 @@
/***
* ASM XML Adapter
* Copyright (c) 2004-2011, Eugene Kuleshov
* 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.xml;
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;
import org.xml.sax.ContentHandler;
import org.xml.sax.helpers.AttributesImpl;
/**
* A {@link org.objectweb.asm.ClassVisitor ClassVisitor} that generates SAX 2.0
* events from the visited class. It can feed any kind of
* {@link org.xml.sax.ContentHandler ContentHandler}, e.g. XML serializer, XSLT
* or XQuery engines.
*
* @see Processor
* @see ASMContentHandler
*
* @author Eugene Kuleshov
*/
public final class SAXClassAdapter extends ClassVisitor {
SAXAdapter sa;
private final boolean singleDocument;
/**
* Pseudo access flag used to distinguish class access flags.
*/
private static final int ACCESS_CLASS = 262144;
/**
* Pseudo access flag used to distinguish field access flags.
*/
private static final int ACCESS_FIELD = 524288;
/**
* Pseudo access flag used to distinguish inner class flags.
*/
private static final int ACCESS_INNER = 1048576;
/**
* Constructs a new {@link org.objectweb.asm.xml.SAXClassAdapter SAXClassAdapter} object.
*
* @param h
* content handler that will be used to send SAX 2.0 events.
* @param singleDocument
* if <tt>true</tt> adapter will not produce
* {@link org.xml.sax.ContentHandler#startDocument() startDocument()} and
* {@link org.xml.sax.ContentHandler#endDocument() endDocument()} events.
*/
public SAXClassAdapter(final ContentHandler h, boolean singleDocument) {
super(Opcodes.ASM4);
this.sa = new SAXAdapter(h);
this.singleDocument = singleDocument;
if (!singleDocument) {
sa.addDocumentStart();
}
}
@Override
public void visitSource(final String source, final String debug) {
AttributesImpl att = new AttributesImpl();
if (source != null) {
att.addAttribute("", "file", "file", "", encode(source));
}
if (debug != null) {
att.addAttribute("", "debug", "debug", "", encode(debug));
}
sa.addElement("source", att);
}
@Override
public void visitOuterClass(final String owner, final String name,
final String desc) {
AttributesImpl att = new AttributesImpl();
att.addAttribute("", "owner", "owner", "", owner);
if (name != null) {
att.addAttribute("", "name", "name", "", name);
}
if (desc != null) {
att.addAttribute("", "desc", "desc", "", desc);
}
sa.addElement("outerclass", att);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
return new SAXAnnotationAdapter(sa, "annotation", visible ? 1 : -1,
null, desc);
}
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName,
final String[] interfaces) {
StringBuffer sb = new StringBuffer();
appendAccess(access | ACCESS_CLASS, sb);
AttributesImpl att = new AttributesImpl();
att.addAttribute("", "access", "access", "", sb.toString());
if (name != null) {
att.addAttribute("", "name", "name", "", name);
}
if (signature != null) {
att.addAttribute("", "signature", "signature", "",
encode(signature));
}
if (superName != null) {
att.addAttribute("", "parent", "parent", "", superName);
}
att.addAttribute("", "major", "major", "",
Integer.toString(version & 0xFFFF));
att.addAttribute("", "minor", "minor", "",
Integer.toString(version >>> 16));
sa.addStart("class", att);
sa.addStart("interfaces", new AttributesImpl());
if (interfaces != null && interfaces.length > 0) {
for (int i = 0; i < interfaces.length; i++) {
AttributesImpl att2 = new AttributesImpl();
att2.addAttribute("", "name", "name", "", interfaces[i]);
sa.addElement("interface", att2);
}
}
sa.addEnd("interfaces");
}
@Override
public FieldVisitor visitField(final int access, final String name,
final String desc, final String signature, final Object value) {
StringBuffer sb = new StringBuffer();
appendAccess(access | ACCESS_FIELD, sb);
AttributesImpl att = new AttributesImpl();
att.addAttribute("", "access", "access", "", sb.toString());
att.addAttribute("", "name", "name", "", name);
att.addAttribute("", "desc", "desc", "", desc);
if (signature != null) {
att.addAttribute("", "signature", "signature", "",
encode(signature));
}
if (value != null) {
att.addAttribute("", "value", "value", "", encode(value.toString()));
}
return new SAXFieldAdapter(sa, att);
}
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
StringBuffer sb = new StringBuffer();
appendAccess(access, sb);
AttributesImpl att = new AttributesImpl();
att.addAttribute("", "access", "access", "", sb.toString());
att.addAttribute("", "name", "name", "", name);
att.addAttribute("", "desc", "desc", "", desc);
if (signature != null) {
att.addAttribute("", "signature", "signature", "", signature);
}
sa.addStart("method", att);
sa.addStart("exceptions", new AttributesImpl());
if (exceptions != null && exceptions.length > 0) {
for (int i = 0; i < exceptions.length; i++) {
AttributesImpl att2 = new AttributesImpl();
att2.addAttribute("", "name", "name", "", exceptions[i]);
sa.addElement("exception", att2);
}
}
sa.addEnd("exceptions");
return new SAXCodeAdapter(sa, access);
}
@Override
public final void visitInnerClass(final String name,
final String outerName, final String innerName, final int access) {
StringBuffer sb = new StringBuffer();
appendAccess(access | ACCESS_INNER, sb);
AttributesImpl att = new AttributesImpl();
att.addAttribute("", "access", "access", "", sb.toString());
if (name != null) {
att.addAttribute("", "name", "name", "", name);
}
if (outerName != null) {
att.addAttribute("", "outerName", "outerName", "", outerName);
}
if (innerName != null) {
att.addAttribute("", "innerName", "innerName", "", innerName);
}
sa.addElement("innerclass", att);
}
@Override
public final void visitEnd() {
sa.addEnd("class");
if (!singleDocument) {
sa.addDocumentEnd();
}
}
static final String encode(final String s) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '\\') {
sb.append("\\\\");
} else if (c < 0x20 || c > 0x7f) {
sb.append("\\u");
if (c < 0x10) {
sb.append("000");
} else if (c < 0x100) {
sb.append("00");
} else if (c < 0x1000) {
sb.append('0');
}
sb.append(Integer.toString(c, 16));
} else {
sb.append(c);
}
}
return sb.toString();
}
static void appendAccess(final int access, final StringBuffer sb) {
if ((access & Opcodes.ACC_PUBLIC) != 0) {
sb.append("public ");
}
if ((access & Opcodes.ACC_PRIVATE) != 0) {
sb.append("private ");
}
if ((access & Opcodes.ACC_PROTECTED) != 0) {
sb.append("protected ");
}
if ((access & Opcodes.ACC_FINAL) != 0) {
sb.append("final ");
}
if ((access & Opcodes.ACC_STATIC) != 0) {
sb.append("static ");
}
if ((access & Opcodes.ACC_SUPER) != 0) {
if ((access & ACCESS_CLASS) == 0) {
sb.append("synchronized ");
} else {
sb.append("super ");
}
}
if ((access & Opcodes.ACC_VOLATILE) != 0) {
if ((access & ACCESS_FIELD) == 0) {
sb.append("bridge ");
} else {
sb.append("volatile ");
}
}
if ((access & Opcodes.ACC_TRANSIENT) != 0) {
if ((access & ACCESS_FIELD) == 0) {
sb.append("varargs ");
} else {
sb.append("transient ");
}
}
if ((access & Opcodes.ACC_NATIVE) != 0) {
sb.append("native ");
}
if ((access & Opcodes.ACC_STRICT) != 0) {
sb.append("strict ");
}
if ((access & Opcodes.ACC_INTERFACE) != 0) {
sb.append("interface ");
}
if ((access & Opcodes.ACC_ABSTRACT) != 0) {
sb.append("abstract ");
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
sb.append("synthetic ");
}
if ((access & Opcodes.ACC_ANNOTATION) != 0) {
sb.append("annotation ");
}
if ((access & Opcodes.ACC_ENUM) != 0) {
sb.append("enum ");
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
sb.append("deprecated ");
}
}
}
@@ -0,0 +1,362 @@
/***
* ASM XML Adapter
* Copyright (c) 2004-2011, Eugene Kuleshov
* 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.xml;
import java.util.HashMap;
import java.util.Map;
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;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.Printer;
import org.xml.sax.helpers.AttributesImpl;
/**
* A {@link org.objectweb.asm.MethodVisitor} that generates SAX 2.0 events from the visited
* method.
*
* @see SAXClassAdapter
* @see Processor
*
* @author Eugene Kuleshov
*/
public final class SAXCodeAdapter extends MethodVisitor {
static final String[] TYPES = { "top", "int", "float", "double", "long",
"null", "uninitializedThis" };
SAXAdapter sa;
private final Map<Label, String> labelNames;
/**
* Constructs a new {@link org.objectweb.asm.xml.SAXCodeAdapter SAXCodeAdapter} object.
*
* @param sa
* content handler that will be used to send SAX 2.0 events.
*/
public SAXCodeAdapter(final SAXAdapter sa, final int access) {
super(Opcodes.ASM4);
this.sa = sa;
this.labelNames = new HashMap<Label, String>();
if ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE | Opcodes.ACC_NATIVE)) == 0) {
sa.addStart("code", new AttributesImpl());
}
}
@Override
public final void visitCode() {
}
@Override
public void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack) {
AttributesImpl attrs = new AttributesImpl();
switch (type) {
case Opcodes.F_NEW:
case Opcodes.F_FULL:
if (type == Opcodes.F_NEW) {
attrs.addAttribute("", "type", "type", "", "NEW");
} else {
attrs.addAttribute("", "type", "type", "", "FULL");
}
sa.addStart("frame", attrs);
appendFrameTypes(true, nLocal, local);
appendFrameTypes(false, nStack, stack);
break;
case Opcodes.F_APPEND:
attrs.addAttribute("", "type", "type", "", "APPEND");
sa.addStart("frame", attrs);
appendFrameTypes(true, nLocal, local);
break;
case Opcodes.F_CHOP:
attrs.addAttribute("", "type", "type", "", "CHOP");
attrs.addAttribute("", "count", "count", "",
Integer.toString(nLocal));
sa.addStart("frame", attrs);
break;
case Opcodes.F_SAME:
attrs.addAttribute("", "type", "type", "", "SAME");
sa.addStart("frame", attrs);
break;
case Opcodes.F_SAME1:
attrs.addAttribute("", "type", "type", "", "SAME1");
sa.addStart("frame", attrs);
appendFrameTypes(false, 1, stack);
break;
}
sa.addEnd("frame");
}
private void appendFrameTypes(final boolean local, final int n,
final Object[] types) {
for (int i = 0; i < n; ++i) {
Object type = types[i];
AttributesImpl attrs = new AttributesImpl();
if (type instanceof String) {
attrs.addAttribute("", "type", "type", "", (String) type);
} else if (type instanceof Integer) {
attrs.addAttribute("", "type", "type", "",
TYPES[((Integer) type).intValue()]);
} else {
attrs.addAttribute("", "type", "type", "", "uninitialized");
attrs.addAttribute("", "label", "label", "",
getLabel((Label) type));
}
sa.addElement(local ? "local" : "stack", attrs);
}
}
@Override
public final void visitInsn(final int opcode) {
sa.addElement(Printer.OPCODES[opcode], new AttributesImpl());
}
@Override
public final void visitIntInsn(final int opcode, final int operand) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "value", "value", "", Integer.toString(operand));
sa.addElement(Printer.OPCODES[opcode], attrs);
}
@Override
public final void visitVarInsn(final int opcode, final int var) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "var", "var", "", Integer.toString(var));
sa.addElement(Printer.OPCODES[opcode], attrs);
}
@Override
public final void visitTypeInsn(final int opcode, final String type) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "desc", "desc", "", type);
sa.addElement(Printer.OPCODES[opcode], attrs);
}
@Override
public final void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "owner", "owner", "", owner);
attrs.addAttribute("", "name", "name", "", name);
attrs.addAttribute("", "desc", "desc", "", desc);
sa.addElement(Printer.OPCODES[opcode], attrs);
}
@Override
public final void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "owner", "owner", "", owner);
attrs.addAttribute("", "name", "name", "", name);
attrs.addAttribute("", "desc", "desc", "", desc);
sa.addElement(Printer.OPCODES[opcode], attrs);
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
Object... bsmArgs) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "name", "name", "", name);
attrs.addAttribute("", "desc", "desc", "", desc);
attrs.addAttribute("", "bsm", "bsm", "",
SAXClassAdapter.encode(bsm.toString()));
sa.addStart("INVOKEDYNAMIC", attrs);
for (int i = 0; i < bsmArgs.length; i++) {
sa.addElement("bsmArg", getConstantAttribute(bsmArgs[i]));
}
sa.addEnd("INVOKEDYNAMIC");
}
@Override
public final void visitJumpInsn(final int opcode, final Label label) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "label", "label", "", getLabel(label));
sa.addElement(Printer.OPCODES[opcode], attrs);
}
@Override
public final void visitLabel(final Label label) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "name", "name", "", getLabel(label));
sa.addElement("Label", attrs);
}
@Override
public final void visitLdcInsn(final Object cst) {
sa.addElement(Printer.OPCODES[Opcodes.LDC], getConstantAttribute(cst));
}
private static AttributesImpl getConstantAttribute(final Object cst) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "cst", "cst", "",
SAXClassAdapter.encode(cst.toString()));
attrs.addAttribute("", "desc", "desc", "",
Type.getDescriptor(cst.getClass()));
return attrs;
}
@Override
public final void visitIincInsn(final int var, final int increment) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "var", "var", "", Integer.toString(var));
attrs.addAttribute("", "inc", "inc", "", Integer.toString(increment));
sa.addElement(Printer.OPCODES[Opcodes.IINC], attrs);
}
@Override
public final void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "min", "min", "", Integer.toString(min));
attrs.addAttribute("", "max", "max", "", Integer.toString(max));
attrs.addAttribute("", "dflt", "dflt", "", getLabel(dflt));
String o = Printer.OPCODES[Opcodes.TABLESWITCH];
sa.addStart(o, attrs);
for (int i = 0; i < labels.length; i++) {
AttributesImpl att2 = new AttributesImpl();
att2.addAttribute("", "name", "name", "", getLabel(labels[i]));
sa.addElement("label", att2);
}
sa.addEnd(o);
}
@Override
public final void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
AttributesImpl att = new AttributesImpl();
att.addAttribute("", "dflt", "dflt", "", getLabel(dflt));
String o = Printer.OPCODES[Opcodes.LOOKUPSWITCH];
sa.addStart(o, att);
for (int i = 0; i < labels.length; i++) {
AttributesImpl att2 = new AttributesImpl();
att2.addAttribute("", "name", "name", "", getLabel(labels[i]));
att2.addAttribute("", "key", "key", "", Integer.toString(keys[i]));
sa.addElement("label", att2);
}
sa.addEnd(o);
}
@Override
public final void visitMultiANewArrayInsn(final String desc, final int dims) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "desc", "desc", "", desc);
attrs.addAttribute("", "dims", "dims", "", Integer.toString(dims));
sa.addElement(Printer.OPCODES[Opcodes.MULTIANEWARRAY], attrs);
}
@Override
public final void visitTryCatchBlock(final Label start, final Label end,
final Label handler, final String type) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "start", "start", "", getLabel(start));
attrs.addAttribute("", "end", "end", "", getLabel(end));
attrs.addAttribute("", "handler", "handler", "", getLabel(handler));
if (type != null) {
attrs.addAttribute("", "type", "type", "", type);
}
sa.addElement("TryCatch", attrs);
}
@Override
public final void visitMaxs(final int maxStack, final int maxLocals) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "maxStack", "maxStack", "",
Integer.toString(maxStack));
attrs.addAttribute("", "maxLocals", "maxLocals", "",
Integer.toString(maxLocals));
sa.addElement("Max", attrs);
sa.addEnd("code");
}
@Override
public void visitLocalVariable(final String name, final String desc,
final String signature, final Label start, final Label end,
final int index) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "name", "name", "", name);
attrs.addAttribute("", "desc", "desc", "", desc);
if (signature != null) {
attrs.addAttribute("", "signature", "signature", "",
SAXClassAdapter.encode(signature));
}
attrs.addAttribute("", "start", "start", "", getLabel(start));
attrs.addAttribute("", "end", "end", "", getLabel(end));
attrs.addAttribute("", "var", "var", "", Integer.toString(index));
sa.addElement("LocalVar", attrs);
}
@Override
public final void visitLineNumber(final int line, final Label start) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "line", "line", "", Integer.toString(line));
attrs.addAttribute("", "start", "start", "", getLabel(start));
sa.addElement("LineNumber", attrs);
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
return new SAXAnnotationAdapter(sa, "annotationDefault", 0, null, null);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
return new SAXAnnotationAdapter(sa, "annotation", visible ? 1 : -1,
null, desc);
}
@Override
public AnnotationVisitor visitParameterAnnotation(final int parameter,
final String desc, final boolean visible) {
return new SAXAnnotationAdapter(sa, "parameterAnnotation", visible ? 1
: -1, parameter, desc);
}
@Override
public void visitEnd() {
sa.addEnd("method");
}
private final String getLabel(final Label label) {
String name = labelNames.get(label);
if (name == null) {
name = Integer.toString(labelNames.size());
labelNames.put(label, name);
}
return name;
}
}
@@ -0,0 +1,63 @@
/***
* ASM XML Adapter
* Copyright (c) 2004-2011, Eugene Kuleshov
* 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.xml;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;
import org.xml.sax.Attributes;
/**
* SAXFieldAdapter
*
* @author Eugene Kuleshov
*/
public final class SAXFieldAdapter extends FieldVisitor {
SAXAdapter sa;
public SAXFieldAdapter(final SAXAdapter sa, final Attributes att) {
super(Opcodes.ASM4);
this.sa = sa;
sa.addStart("field", att);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
return new SAXAnnotationAdapter(sa, "annotation", visible ? 1 : -1,
null, desc);
}
@Override
public void visitEnd() {
sa.addEnd("field");
}
}
@@ -0,0 +1,349 @@
<!--
ASM XML Adapter
Copyright (c) 2004-2011, Eugene Kuleshov
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.
-->
<!--
This DTD must be used to create XML documents to be processed by
org.objectweb.asm.xml.ASMContentHandler
-->
<!--
Root element used to aggregate multiple classes into single document.
-->
<!ELEMENT classes ( class+ )>
<!--
Root element for a single class.
-->
<!ELEMENT class ( interfaces, ( field | innerclass | method )*)>
<!ATTLIST class access CDATA #REQUIRED>
<!ATTLIST class name CDATA #REQUIRED>
<!ATTLIST class parent CDATA #REQUIRED>
<!ATTLIST class major CDATA #REQUIRED>
<!ATTLIST class minor CDATA #REQUIRED>
<!ATTLIST class source CDATA #IMPLIED>
<!ELEMENT interfaces ( interface* )>
<!ELEMENT interface EMPTY>
<!ATTLIST interface name CDATA #REQUIRED>
<!ELEMENT field EMPTY>
<!ATTLIST field access CDATA #REQUIRED>
<!ATTLIST field desc CDATA #REQUIRED>
<!ATTLIST field name CDATA #REQUIRED>
<!--
All characters out of interval 0x20 to 0x7f (inclusive) must
be encoded (\uXXXX) and character '\' must be replaced by "\\"
-->
<!ATTLIST field value CDATA #IMPLIED>
<!ELEMENT innerclass EMPTY>
<!ATTLIST innerclass access CDATA #REQUIRED>
<!ATTLIST innerclass innerName CDATA #IMPLIED>
<!ATTLIST innerclass name CDATA #REQUIRED>
<!ATTLIST innerclass outerName CDATA #IMPLIED>
<!--
Root element for method definition.
-->
<!ELEMENT method ( exceptions, code? )>
<!ATTLIST method access CDATA #REQUIRED>
<!ATTLIST method desc CDATA #REQUIRED>
<!ATTLIST method name CDATA #REQUIRED>
<!ELEMENT exceptions ( exception* )>
<!ELEMENT exception EMPTY>
<!ATTLIST exception name CDATA #REQUIRED>
<!--
code element contains bytecode instructions and definitions for labels, line numbers, try/catch and max
-->
<!ELEMENT code (( AALOAD | AASTORE | ACONST_NULL | ALOAD | ANEWARRAY | ARETURN | ARRAYLENGTH | ASTORE | ATHROW | BALOAD | BASTORE | BIPUSH | CALOAD | CASTORE | CHECKCAST | D2F | D2I | D2L | DADD | DALOAD | DASTORE | DCMPG | DCMPL | DCONST_0 | DCONST_1 | DDIV | DLOAD | DMUL | DNEG | DREM | DRETURN | DSTORE | DSUB | DUP | DUP2 | DUP2_X1 | DUP2_X2 | DUP_X1 | DUP_X2 | SWAP | F2D | F2I | F2L | FADD | FALOAD | FASTORE | FCMPG | FCMPL | FCONST_0 | FCONST_1 | FCONST_2 | FDIV | FLOAD | FMUL | FNEG | FREM | FRETURN | FSTORE | FSUB | GETFIELD | GETSTATIC | GOTO | I2B | I2C | I2D | I2F | I2L | I2S | IADD | IALOAD | IAND | IASTORE | ICONST_0 | ICONST_1 | ICONST_2 | ICONST_3 | ICONST_4 | ICONST_5 | ICONST_M1 | IDIV | IFEQ | IFGE | IFGT | IFLE | IFLT | IFNE | IFNONNULL | IFNULL | IF_ACMPEQ | IF_ACMPNE | IF_ICMPEQ | IF_ICMPGE | IF_ICMPGT | IF_ICMPLE | IF_ICMPLT | IF_ICMPNE | IINC | ILOAD | IMUL | INEG | INSTANCEOF | INVOKEINTERFACE | INVOKESPECIAL | INVOKESTATIC | INVOKEVIRTUAL | IOR | IREM | IRETURN | ISHL | ISHR | ISTORE | ISUB | IUSHR | IXOR | JSR | L2D | L2F | L2I | LADD | LALOAD | LAND | LASTORE | LCMP | LCONST_0 | LCONST_1 | LDC | LDIV | LLOAD | LMUL | LNEG | LOOKUPSWITCH | LOR | LREM | LRETURN | LSHL | LSHR | LSTORE | LSUB | LUSHR | LXOR | MONITORENTER | MONITOREXIT | MULTIANEWARRAY | NEW | NEWARRAY | NOP | POP | POP2 | PUTFIELD | PUTSTATIC | RET | RETURN | SALOAD | SASTORE | SIPUSH | TABLESWITCH | Label | LineNumber | TryCatch )*, Max)>
<!ELEMENT Label EMPTY>
<!ATTLIST Label name CDATA #REQUIRED>
<!ELEMENT TryCatch EMPTY>
<!ATTLIST TryCatch end CDATA #REQUIRED>
<!ATTLIST TryCatch handler CDATA #REQUIRED>
<!ATTLIST TryCatch start CDATA #REQUIRED>
<!ATTLIST TryCatch type CDATA #IMPLIED>
<!ELEMENT LineNumber EMPTY>
<!ATTLIST LineNumber line CDATA #REQUIRED>
<!ATTLIST LineNumber start CDATA #REQUIRED>
<!ELEMENT Max EMPTY>
<!ATTLIST Max maxLocals CDATA #REQUIRED>
<!ATTLIST Max maxStack CDATA #REQUIRED>
<!ELEMENT AALOAD EMPTY>
<!ELEMENT AASTORE EMPTY>
<!ELEMENT ACONST_NULL EMPTY>
<!ELEMENT ALOAD EMPTY>
<!ATTLIST ALOAD var CDATA #REQUIRED>
<!ELEMENT ANEWARRAY EMPTY>
<!ATTLIST ANEWARRAY desc CDATA #REQUIRED>
<!ELEMENT ARETURN EMPTY>
<!ELEMENT ARRAYLENGTH EMPTY>
<!ELEMENT ASTORE EMPTY>
<!ATTLIST ASTORE var CDATA #REQUIRED>
<!ELEMENT ATHROW EMPTY>
<!ELEMENT BALOAD EMPTY>
<!ELEMENT BASTORE EMPTY>
<!ELEMENT BIPUSH EMPTY>
<!ATTLIST BIPUSH value CDATA #REQUIRED>
<!ELEMENT CALOAD EMPTY>
<!ELEMENT CASTORE EMPTY>
<!ELEMENT CHECKCAST EMPTY>
<!ATTLIST CHECKCAST desc CDATA #REQUIRED>
<!ELEMENT D2F EMPTY>
<!ELEMENT D2I EMPTY>
<!ELEMENT D2L EMPTY>
<!ELEMENT DADD EMPTY>
<!ELEMENT DALOAD EMPTY>
<!ELEMENT DASTORE EMPTY>
<!ELEMENT DCMPG EMPTY>
<!ELEMENT DCMPL EMPTY>
<!ELEMENT DCONST_0 EMPTY>
<!ELEMENT DCONST_1 EMPTY>
<!ELEMENT DDIV EMPTY>
<!ELEMENT DLOAD EMPTY>
<!ATTLIST DLOAD var CDATA #REQUIRED>
<!ELEMENT DMUL EMPTY>
<!ELEMENT DNEG EMPTY>
<!ELEMENT DREM EMPTY>
<!ELEMENT DRETURN EMPTY>
<!ELEMENT DSTORE EMPTY>
<!ATTLIST DSTORE var CDATA #REQUIRED>
<!ELEMENT DSUB EMPTY>
<!ELEMENT DUP EMPTY>
<!ELEMENT DUP2 EMPTY>
<!ELEMENT DUP2_X1 EMPTY>
<!ELEMENT DUP2_X2 EMPTY>
<!ELEMENT DUP_X1 EMPTY>
<!ELEMENT DUP_X2 EMPTY>
<!ELEMENT SWAP EMPTY>
<!ELEMENT F2D EMPTY>
<!ELEMENT F2I EMPTY>
<!ELEMENT F2L EMPTY>
<!ELEMENT FADD EMPTY>
<!ELEMENT FALOAD EMPTY>
<!ELEMENT FASTORE EMPTY>
<!ELEMENT FCMPG EMPTY>
<!ELEMENT FCMPL EMPTY>
<!ELEMENT FCONST_0 EMPTY>
<!ELEMENT FCONST_1 EMPTY>
<!ELEMENT FCONST_2 EMPTY>
<!ELEMENT FDIV EMPTY>
<!ELEMENT FLOAD EMPTY>
<!ATTLIST FLOAD var CDATA #REQUIRED>
<!ELEMENT FMUL EMPTY>
<!ELEMENT FNEG EMPTY>
<!ELEMENT FREM EMPTY>
<!ELEMENT FRETURN EMPTY>
<!ELEMENT FSTORE EMPTY>
<!ATTLIST FSTORE var CDATA #REQUIRED>
<!ELEMENT FSUB EMPTY>
<!ELEMENT GETFIELD EMPTY>
<!ATTLIST GETFIELD desc CDATA #REQUIRED>
<!ATTLIST GETFIELD name CDATA #REQUIRED>
<!ATTLIST GETFIELD owner CDATA #REQUIRED>
<!ELEMENT GETSTATIC EMPTY>
<!ATTLIST GETSTATIC desc CDATA #REQUIRED>
<!ATTLIST GETSTATIC name CDATA #REQUIRED>
<!ATTLIST GETSTATIC owner CDATA #REQUIRED>
<!ELEMENT GOTO EMPTY>
<!ATTLIST GOTO label CDATA #REQUIRED>
<!ELEMENT I2B EMPTY>
<!ELEMENT I2C EMPTY>
<!ELEMENT I2D EMPTY>
<!ELEMENT I2F EMPTY>
<!ELEMENT I2L EMPTY>
<!ELEMENT I2S EMPTY>
<!ELEMENT IADD EMPTY>
<!ELEMENT IALOAD EMPTY>
<!ELEMENT IAND EMPTY>
<!ELEMENT IASTORE EMPTY>
<!ELEMENT ICONST_0 EMPTY>
<!ELEMENT ICONST_1 EMPTY>
<!ELEMENT ICONST_2 EMPTY>
<!ELEMENT ICONST_3 EMPTY>
<!ELEMENT ICONST_4 EMPTY>
<!ELEMENT ICONST_5 EMPTY>
<!ELEMENT ICONST_M1 EMPTY>
<!ELEMENT IDIV EMPTY>
<!ELEMENT IFEQ EMPTY>
<!ATTLIST IFEQ label CDATA #REQUIRED>
<!ELEMENT IFGE EMPTY>
<!ATTLIST IFGE label CDATA #REQUIRED>
<!ELEMENT IFGT EMPTY>
<!ATTLIST IFGT label CDATA #REQUIRED>
<!ELEMENT IFLE EMPTY>
<!ATTLIST IFLE label CDATA #REQUIRED>
<!ELEMENT IFLT EMPTY>
<!ATTLIST IFLT label CDATA #REQUIRED>
<!ELEMENT IFNE EMPTY>
<!ATTLIST IFNE label CDATA #REQUIRED>
<!ELEMENT IFNONNULL EMPTY>
<!ATTLIST IFNONNULL label CDATA #REQUIRED>
<!ELEMENT IFNULL EMPTY>
<!ATTLIST IFNULL label CDATA #REQUIRED>
<!ELEMENT IF_ACMPEQ EMPTY>
<!ATTLIST IF_ACMPEQ label CDATA #REQUIRED>
<!ELEMENT IF_ACMPNE EMPTY>
<!ATTLIST IF_ACMPNE label CDATA #REQUIRED>
<!ELEMENT IF_ICMPEQ EMPTY>
<!ATTLIST IF_ICMPEQ label CDATA #REQUIRED>
<!ELEMENT IF_ICMPGE EMPTY>
<!ATTLIST IF_ICMPGE label CDATA #REQUIRED>
<!ELEMENT IF_ICMPGT EMPTY>
<!ATTLIST IF_ICMPGT label CDATA #REQUIRED>
<!ELEMENT IF_ICMPLE EMPTY>
<!ATTLIST IF_ICMPLE label CDATA #REQUIRED>
<!ELEMENT IF_ICMPLT EMPTY>
<!ATTLIST IF_ICMPLT label CDATA #REQUIRED>
<!ELEMENT IF_ICMPNE EMPTY>
<!ATTLIST IF_ICMPNE label CDATA #REQUIRED>
<!ELEMENT IINC EMPTY>
<!ATTLIST IINC inc CDATA #REQUIRED>
<!ATTLIST IINC var CDATA #REQUIRED>
<!ELEMENT ILOAD EMPTY>
<!ATTLIST ILOAD var CDATA #REQUIRED>
<!ELEMENT IMUL EMPTY>
<!ELEMENT INEG EMPTY>
<!ELEMENT INSTANCEOF EMPTY>
<!ATTLIST INSTANCEOF desc CDATA #REQUIRED>
<!ELEMENT INVOKEINTERFACE EMPTY>
<!ATTLIST INVOKEINTERFACE desc CDATA #REQUIRED>
<!ATTLIST INVOKEINTERFACE name CDATA #REQUIRED>
<!ATTLIST INVOKEINTERFACE owner CDATA #REQUIRED>
<!ELEMENT INVOKESPECIAL EMPTY>
<!ATTLIST INVOKESPECIAL desc CDATA #REQUIRED>
<!ATTLIST INVOKESPECIAL name CDATA #REQUIRED>
<!ATTLIST INVOKESPECIAL owner CDATA #REQUIRED>
<!ELEMENT INVOKESTATIC EMPTY>
<!ATTLIST INVOKESTATIC desc CDATA #REQUIRED>
<!ATTLIST INVOKESTATIC name CDATA #REQUIRED>
<!ATTLIST INVOKESTATIC owner CDATA #REQUIRED>
<!ELEMENT INVOKEVIRTUAL EMPTY>
<!ATTLIST INVOKEVIRTUAL desc CDATA #REQUIRED>
<!ATTLIST INVOKEVIRTUAL name CDATA #REQUIRED>
<!ATTLIST INVOKEVIRTUAL owner CDATA #REQUIRED>
<!ELEMENT INVOKEDYNAMIC ( bsmArgs+ )>
<!ATTLIST INVOKEDYNAMIC desc CDATA #REQUIRED>
<!ATTLIST INVOKEDYNAMIC name CDATA #REQUIRED>
<!ATTLIST INVOKEDYNAMIC bsm CDATA #REQUIRED>
<!ELEMENT bsmArgs EMPTY>
<!ATTLIST bsmArgs cst CDATA #REQUIRED>
<!ATTLIST bsmArgs desc CDATA #REQUIRED>
<!ELEMENT IOR EMPTY>
<!ELEMENT IREM EMPTY>
<!ELEMENT IRETURN EMPTY>
<!ELEMENT ISHL EMPTY>
<!ELEMENT ISHR EMPTY>
<!ELEMENT ISTORE EMPTY>
<!ATTLIST ISTORE var CDATA #REQUIRED>
<!ELEMENT ISUB EMPTY>
<!ELEMENT IUSHR EMPTY>
<!ELEMENT IXOR EMPTY>
<!ELEMENT JSR EMPTY>
<!ATTLIST JSR label CDATA #REQUIRED>
<!ELEMENT L2D EMPTY>
<!ELEMENT L2F EMPTY>
<!ELEMENT L2I EMPTY>
<!ELEMENT LADD EMPTY>
<!ELEMENT LALOAD EMPTY>
<!ELEMENT LAND EMPTY>
<!ELEMENT LASTORE EMPTY>
<!ELEMENT LCMP EMPTY>
<!ELEMENT LCONST_0 EMPTY>
<!ELEMENT LCONST_1 EMPTY>
<!ELEMENT LDC EMPTY>
<!--
All characters out of interval 0x20 to 0x7f (inclusive) must
be encoded (\uXXXX) and character '\' must be replaced by "\\"
-->
<!ATTLIST LDC cst CDATA #REQUIRED>
<!ATTLIST LDC desc CDATA #REQUIRED>
<!ELEMENT LDIV EMPTY>
<!ELEMENT LLOAD EMPTY>
<!ATTLIST LLOAD var CDATA #REQUIRED>
<!ELEMENT LMUL EMPTY>
<!ELEMENT LNEG EMPTY>
<!ELEMENT LOR EMPTY>
<!ELEMENT LREM EMPTY>
<!ELEMENT LRETURN EMPTY>
<!ELEMENT LSHL EMPTY>
<!ELEMENT LSHR EMPTY>
<!ELEMENT LSTORE EMPTY>
<!ATTLIST LSTORE var CDATA #REQUIRED>
<!ELEMENT LSUB EMPTY>
<!ELEMENT LUSHR EMPTY>
<!ELEMENT LXOR EMPTY>
<!ELEMENT MONITORENTER EMPTY>
<!ELEMENT MONITOREXIT EMPTY>
<!ELEMENT MULTIANEWARRAY EMPTY>
<!ATTLIST MULTIANEWARRAY desc CDATA #REQUIRED>
<!ATTLIST MULTIANEWARRAY dims CDATA #REQUIRED>
<!ELEMENT NEW EMPTY>
<!ATTLIST NEW desc CDATA #REQUIRED>
<!ELEMENT NEWARRAY EMPTY>
<!ATTLIST NEWARRAY value CDATA #REQUIRED>
<!ELEMENT NOP EMPTY>
<!ELEMENT POP EMPTY>
<!ELEMENT POP2 EMPTY>
<!ELEMENT PUTFIELD EMPTY>
<!ATTLIST PUTFIELD desc CDATA #REQUIRED>
<!ATTLIST PUTFIELD name CDATA #REQUIRED>
<!ATTLIST PUTFIELD owner CDATA #REQUIRED>
<!ELEMENT PUTSTATIC EMPTY>
<!ATTLIST PUTSTATIC desc CDATA #REQUIRED>
<!ATTLIST PUTSTATIC name CDATA #REQUIRED>
<!ATTLIST PUTSTATIC owner CDATA #REQUIRED>
<!ELEMENT RET EMPTY>
<!ATTLIST RET var CDATA #REQUIRED>
<!ELEMENT RETURN EMPTY>
<!ELEMENT SALOAD EMPTY>
<!ELEMENT SASTORE EMPTY>
<!ELEMENT SIPUSH EMPTY>
<!ATTLIST SIPUSH value CDATA #REQUIRED>
<!ELEMENT LOOKUPSWITCH ( label+ )>
<!ATTLIST LOOKUPSWITCH dflt CDATA #REQUIRED>
<!ELEMENT TABLESWITCH ( label+ )>
<!ATTLIST TABLESWITCH dflt CDATA #REQUIRED>
<!ATTLIST TABLESWITCH max CDATA #REQUIRED>
<!ATTLIST TABLESWITCH min CDATA #REQUIRED>
<!ELEMENT label EMPTY>
<!ATTLIST label key CDATA #IMPLIED>
<!ATTLIST label name CDATA #REQUIRED>
@@ -0,0 +1,96 @@
<html>
<!--
* ASM XML Adapter
* Copyright (c) 2004-2011, Eugene Kuleshov
* 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.
-->
<body>
Provides <a href="http://sax.sourceforge.net/">SAX 2.0</a> adapters for ASM
visitors to convert classes to and from XML.
These adapters can be chained with other SAX compliant content handlers and
filters, eg. XSLT or XQuery engines. This package is bundled as
a separate <tt>asm-xml.jar</tt> library and requires <tt>asm.jar</tt>.
<p>
<tt>ASMContentHandler</tt> and <tt>SAXClassAdapter/SAXCodeAdapter</tt>
are using <a href="asm-xml.dtd">asm-xml.dtd</a>.
Here is the example of bytecode to bytecode XSLT transformation.
<pre>
SAXTransformerFactory saxtf = ( SAXTransformerFactory) TransformerFactory.newInstance();
Templates templates = saxtf.newTemplates( xsltSource);
TransformerHandler handler = saxtf.newTransformerHandler( templates);
handler.setResult( new SAXResult( new ASMContentHandler( outputStream, computeMax)));
ClassReader cr = new ClassReader( bytecode);
cr.accept( new SAXClassAdapter( handler, cr.getVersion(), false), false);
</pre>
See JAXP and SAX documentation for more detils.
<p>
There are few illustrations of the bytecode transformation with XSLT in
examples directory. The following XSLT procesors has been tested.
<blockquote>
<table border="1" cellspacing="0" cellpadding="3">
<tr>
<th>Engine</td>
<th>javax.xml.transform.TransformerFactory property</td>
</tr>
<tr>
<td>jd.xslt</td>
<td>jd.xml.xslt.trax.TransformerFactoryImpl</td>
</tr>
<tr>
<td>Saxon</td>
<td>net.sf.saxon.TransformerFactoryImpl</td>
</tr>
<tr>
<td>Caucho</td>
<td>com.caucho.xsl.Xsl</td>
</tr>
<tr>
<td>Xalan interpeter</td>
<td>org.apache.xalan.processor.TransformerFactory</td>
</tr>
<tr>
<td>Xalan xsltc</td>
<td>org.apache.xalan.xsltc.trax.TransformerFactoryImpl</td>
</tr>
</table>
</blockquote>
@since ASM 1.4.3
</body>
</html>