package org.botnode.asm;
/***
* ASM Guide
* Copyright (c) 2007 Eric Bruneton
* 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.
*/
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.F_SAME;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.IADD;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.IFLT;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_5;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.util.TraceClassVisitor;
/**
* ASM example class.
*/
public class Operations extends ClassLoader {
public void buildConstantCalc(final MethodVisitor mv) {
mv.visitCode();
// visitVarInsn,
// Visits a local variable instruction: ILOAD, LLOAD, FLOAD, DLOAD, ALOAD,
// ISTORE, LSTORE, FSTORE, DSTORE, ASTORE, or RET.
/*
* From the asm guide:
*
* The first instruction pushes this on the operand stack, as before. The second
* instruction pushes the local variable 1, which was initialized with the f argument
* value during the creation of the frame for this method call. The third
* instruction pops these two values and stores the int value in the f field of
* the referenced object, i.e. in this.f. The last instruction, which is implicit
* in the source code but which is mandatory in the compiled code, destroys the
* current execution frame and returns to the caller.
*
* Example Setter:
*
* public void setF(int f) {
* this.f = f;
* }
*
* ALOAD 0
* ILOAD 1
* PUTFIELD pkg/Bean f I
* RETURN
*/
// Push 'this' onto the stack
mv.visitVarInsn(ALOAD, 0);
// Add 1 + whatever F is on the stack
mv.visitInsn(ICONST_1);
mv.visitInsn(ICONST_1);
mv.visitInsn(IADD);
mv.visitFieldInsn(PUTFIELD, "pkg/Bean", "f", "I");
// Return void
mv.visitInsn(RETURN);
// Visits the maximum stack size and the maximum number of local variables of the method.
mv.visitMaxs(3, 1);
mv.visitEnd();
}
/*
--------------------------------------
public testCalc_2b()V
ALOAD 0
DUP
GETFIELD org/botnode/asm/SimpleClassDecompile.forSimpleCalc2b : I
ALOAD 0
GETFIELD org/botnode/asm/SimpleClassDecompile.forSimpleCalc2b : I
IADD
PUTFIELD org/botnode/asm/SimpleClassDecompile.forSimpleCalc2b : I
MAXSTACK = 3
MAXLOCALS = 1
--------------------------------------
// access flags 1
// Another example:
public testCalc_2c()V
L0
LINENUMBER 80 L0
ALOAD 0
DUP
GETFIELD org/botnode/asm/SimpleClassDecompile.forSimpleCalc2b : I
ICONST_1
IADD
PUTFIELD org/botnode/asm/SimpleClassDecompile.forSimpleCalc2b : I
L1
LINENUMBER 81 L1
RETURN
L2
LOCALVARIABLE this Lorg/botnode/asm/SimpleClassDecompile; L0 L2 0
MAXSTACK = 3
MAXLOCALS = 1
--------------------------------------
*/
public void buildFieldCalc(final MethodVisitor mv) {
mv.visitCode();
// Push 'this' onto the stack
mv.visitVarInsn(ALOAD, 0);
// Push 'this' onto the stack
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "pkg/Bean", "f", "I");
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "pkg/Bean", "f", "I");
mv.visitInsn(IADD);
// 'this' is still on the stack
mv.visitFieldInsn(PUTFIELD, "pkg/Bean", "f", "I");
// Return void
mv.visitInsn(RETURN);
// Visits the maximum stack size and the maximum number of local variables of the method.
mv.visitMaxs(3, 1);
mv.visitEnd();
}
public byte[] generate(PrintWriter printWriter) {
ClassWriter cw = new ClassWriter(0);
ClassVisitor tcv = new TraceClassVisitor(cw, printWriter);
ClassAdapter cv = new ClassAdapter(tcv);
cv.visit(V1_5, ACC_PUBLIC, "pkg/Bean", null, "java/lang/Object", null);
cv.visitSource("Bean.java", null);
// Define a private field
FieldVisitor fv = cv.visitField(ACC_PRIVATE, "f", "I", null, null);
if (fv != null) {
fv.visitEnd();
}
MethodVisitor mv;
//*********************************
// Build the object
//*********************************
mv = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
if (mv != null) {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>",
"()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
//*********************************
// Define the method 'get'
//*********************************
mv = cv.visitMethod(ACC_PUBLIC, "getF", "()I", null, null);
if (mv != null) {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "pkg/Bean", "f", "I");
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
//*********************************
// Define the method 'set'
//*********************************
mv = cv.visitMethod(ACC_PUBLIC, "setF", "(I)V", null, null);
if (mv != null) {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 1);
mv.visitFieldInsn(PUTFIELD, "pkg/Bean", "f", "I");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
mv = cv.visitMethod(ACC_PUBLIC, "checkAndSetF", "(I)V", null, null);
if (mv != null) {
mv.visitCode();
mv.visitVarInsn(ILOAD, 1);
Label label = new Label();
mv.visitJumpInsn(IFLT, label);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 1);
mv.visitFieldInsn(PUTFIELD, "pkg/Bean", "f", "I");
Label end = new Label();
mv.visitJumpInsn(GOTO, end);
mv.visitLabel(label);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL,
"java/lang/IllegalArgumentException", "<init>", "()V");
mv.visitInsn(ATHROW);
mv.visitLabel(end);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
//*******************************************
// Create an add method
//*******************************************
mv = cv.visitMethod(ACC_PUBLIC, "calcConstant", "()V", null, null);
if (mv != null) {
buildConstantCalc(mv);
}
mv = cv.visitMethod(ACC_PUBLIC, "calcField", "()V", null, null);
if (mv != null) {
buildFieldCalc(mv);
}
// End of create class.
cv.visitEnd();
//*******************************************
// Convert the data to a byte array
//*******************************************
return cw.toByteArray();
}
public static void main(final String[] args) throws Exception {
System.out.println("Running");
Operations cg = new Operations();
PrintWriter pw = new PrintWriter(System.out, true);
byte[] b = cg.generate(pw);
Class c = cg.defineClass("pkg.Bean", b, 0, b.length);
Object bean = c.newInstance();
final Class c2[] = {};
final Method getF = c.getMethod("getF", c2);
final Method calc = c.getMethod("calcField", c2);
Class[] classes1 = { int.class };
Method setF = c.getMethod("setF", classes1);
Object[] args1 = { 999 };
setF.invoke(bean, args1);
// Invoke the getter.
final int val = ((Integer) getF.invoke(bean)).intValue();
System.out.println("Running =>" + val);
// Invoke the getter.
final int val2 = ((Integer) getF.invoke(bean)).intValue();
System.out.println("Running (2) =>" + val2);
calc.invoke(bean);
final int val3 = ((Integer) getF.invoke(bean)).intValue();
System.out.println("Running (3) =>" + val3);
}
}