下面列出了怎么用org.objectweb.asm.commons.GeneratorAdapter的API类实例代码及写法,或者点击链接到github查看源代码。
@SuppressWarnings("unchecked")
public void visitEnd() {
if (!done && found) {
done = true;
try {
GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, new Method(LOG_INTERNAL_METHOD, LOG_METHOD_SIGNATURE), LOG_METHOD_SIGNATURE, new Type[] {}, this);
Label start = mg.mark();
mg.invokeStatic(LOG_INTERNAL_TYPE, Method.getMethod(LOG_INTERNAL_CLASS.getMethod(LOG_METHOD_NAME)));
mg.returnValue();
Label end = mg.mark();
mg.catchException(start, end, JAVA_LANG_THROWABLE_TYPE);
mg.returnValue();
mg.endMethod();
} catch (NoSuchMethodException nsme) {
System.err.println("Unable to find Agent.rlogCallChain method");
System.err.println("M:"+nsme);
nsme.printStackTrace();
}
}
super.visitEnd();
}
/**
* Given an array with values at the top of the stack, the values are unboxed and stored
* on the given variables. The array is popped from the stack.
*/
static void restoreVariables(
@NonNull GeneratorAdapter mv,
@NonNull List<LocalVariable> variables) {
for (int i = 0; i < variables.size(); i++) {
LocalVariable variable = variables.get(i);
// Duplicates the array on the stack;
mv.dup();
// Sets up the index
mv.push(i);
// Gets the Object value
mv.arrayLoad(Type.getType(Object.class));
// Unboxes to the type of the local variable
mv.unbox(variable.type);
// Restores the local variable
mv.visitVarInsn(variable.type.getOpcode(Opcodes.ISTORE), variable.var);
}
// Pops the array from the stack.
mv.pop();
}
/**
* Adds the instructions to do a generic redirection.
* <p>
* Note that the generated bytecode does not have a direct translation to code, but as an
* example, the following code block gets inserted.
* <code>
* if ($starkChange != null) {
* $starkChange.access$dispatch($name, new object[] { arg0, ... argsN })
* $anyCodeInsertedbyRestore
* }
* $originalMethodBody
*</code>
* @param mv the method visitor to add the instructions to.
* @param change the local variable containing the alternate implementation.
*/
void redirect(GeneratorAdapter mv, int change) {
// code to check if a new implementation of the current class is available.
Label l0 = new Label();
mv.loadLocal(change);
mv.visitJumpInsn(Opcodes.IFNULL, l0);
doRedirect(mv, change);
// Return
if (type == Type.VOID_TYPE) {
mv.pop();
} else {
ByteCodeUtils.unbox(mv, type);
}
mv.returnValue();
// jump label for classes without any new implementation, just invoke the original
// method implementation.
mv.visitLabel(l0);
}
/**
* Emit code for a string if-else block.
*
* if (s.equals("collided_method1")) {
* visit(s);
* } else if (s.equals("collided_method2")) {
* visit(s);
* }
*
* In the most common case of just one string, this degenerates to:
*
* visit(s)
*
*/
private void visitx(GeneratorAdapter mv, List<String> strings) {
if (strings.size() == 1) {
visitCase(strings.get(0));
return;
}
for (String string : strings) {
Label label = new Label();
visitString();
mv.visitLdcInsn(string);
mv.invokeVirtual(STRING_TYPE, Method.getMethod("boolean equals(Object)"));
mv.visitJumpInsn(Opcodes.IFEQ, label);
visitCase(string);
mv.visitLabel(label);
}
visitDefault();
}
private static void createClassArray(GeneratorAdapter mv, List<Type> args) {
// create an array of objects capable of containing all the parameters and optionally the "this"
createLocals(mv, args);
// we need to maintain the stack index when loading parameters from, as for long and double
// values, it uses 2 stack elements, all others use only 1 stack element.
int stackIndex = 0;
for (int arrayIndex = 0; arrayIndex < args.size(); arrayIndex++) {
Type arg = args.get(arrayIndex);
// duplicate the array of objects reference, it will be used to store the value in.
mv.dup();
// index in the array of objects to store the boxed parameter.
mv.push(arrayIndex);
// Pushes the appropriate local variable on the stack
redirectLocal(mv, arg);
// mv.visitLdcInsn(Type.getType(arg.getDescriptor()));
// potentially box up intrinsic types.
// mv.box(arg);
mv.arrayStore(Type.getType(Class.class));
// stack index must progress according to the parameter type we just processed.
// stackIndex += arg.getSize();
}
}
public void visitEnd() {
if (! doneAddField) {
doneAddField = true;
super.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, CLASS_FIELD, Type.getDescriptor(Agent.class), null, null);
}
if (! doneAddMethod) {
doneAddMethod = true;
GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, new Method(LOG_CLASS_METHOD, LOG_CLASS_SIGNATURE), LOG_CLASS_SIGNATURE, new Type[] {}, this);
Label target = mg.newLabel();
mg.getStatic(JAVA_LANG_SYSTEM_TYPE, CLASS_FIELD, JAVA_LANG_CLASS_TYPE);
mg.ifNull(target);
mg.push(LOG_INTERNAL_TYPE);
mg.putStatic(JAVA_LANG_SYSTEM_TYPE, CLASS_FIELD, JAVA_LANG_CLASS_TYPE);
mg.mark(target);
mg.getStatic(JAVA_LANG_SYSTEM_TYPE, CLASS_FIELD, JAVA_LANG_CLASS_TYPE);
mg.returnValue();
}
super.visitEnd();
}
private void visitx(GeneratorAdapter mv, List<String> strings) {
if (strings.size() == 1) {
visitCase(strings.get(0));
return;
}
for (String string : strings) {
Label label = new Label();
visitString();
mv.visitLdcInsn(string);
mv.invokeVirtual(STRING_TYPE, Method.getMethod("boolean equals(Object)"));
mv.visitJumpInsn(Opcodes.IFEQ, label);
visitCase(string);
mv.visitLabel(label);
}
visitDefault();
}
@Override
public Type load(Context ctx) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
Label labelNotNull = new Label();
Label labelExit = new Label();
expression.load(ctx);
g.ifNonNull(labelNotNull);
g.push(false);
g.goTo(labelExit);
g.mark(labelNotNull);
g.push(true);
g.mark(labelExit);
return Type.BOOLEAN_TYPE;
}
/**
* Given an array on the stack, it loads it with the values of the given variables stating at
* offset.
*/
static void loadVariableArray(
GeneratorAdapter mv,
List<LocalVariable> variables, int offset) {
// we need to maintain the stack index when loading parameters from, as for long and double
// values, it uses 2 stack elements, all others use only 1 stack element.
for (int i = offset; i < variables.size(); i++) {
LocalVariable variable = variables.get(i);
// duplicate the array of objects reference, it will be used to store the value in.
mv.dup();
// index in the array of objects to store the boxed parameter.
mv.push(i);
// Pushes the appropriate local variable on the stack
mv.visitVarInsn(variable.type.getOpcode(Opcodes.ILOAD), variable.var);
// potentially box up intrinsic types.
mv.box(variable.type);
// store it in the array
mv.arrayStore(Type.getType(Object.class));
}
}
/**
* Adds the instructions to do a generic redirection.
*/
protected void redirect(GeneratorAdapter mv, int change) {
// code to check if a new implementation of the current class is available.
Label l0 = new Label();
mv.loadLocal(change);
mv.visitJumpInsn(Opcodes.IFNULL, l0);
doRedirect(mv, change);
// Return
if (type == Type.VOID_TYPE) {
mv.pop();
} else {
ByteCodeUtils.unbox(mv, type);
}
mv.returnValue();
// jump label for classes without any new implementation, just invoke the original
// method implementation.
mv.visitLabel(l0);
}
/**
* Generates:
* <pre>
* Method method =
* method.getDeclaringClass().getMethod("methodName", new Class[] { args... });
* </pre>
* @param mg
* @param method
* @param methodType
*/
private static void loadCurrentMethod(GeneratorAdapter mg, java.lang.reflect.Method method, Type methodType)
{
mg.push(Type.getType(method.getDeclaringClass()));
mg.push(method.getName());
// create the Class[]
mg.push(methodType.getArgumentTypes().length);
mg.newArray(TYPE_CLASS);
// push parameters into array
for (int i = 0; i < methodType.getArgumentTypes().length; i++)
{
// keep copy of array on stack
mg.dup();
// push index onto stack
mg.push(i);
mg.push(methodType.getArgumentTypes()[i]);
mg.arrayStore(TYPE_CLASS);
}
// invoke getMethod() with the method name and the array of types
mg.invokeVirtual(TYPE_CLASS, Method.getMethod("java.lang.reflect.Method getDeclaredMethod(String, Class[])"));
}
/**
* Emit code for a string if-else block.
*
* if (s.equals("collided_method1")) {
* visit(s);
* } else if (s.equals("collided_method2")) {
* visit(s);
* }
*
* In the most common case of just one string, this degenerates to:
*
* visit(s)
*
*/
private void visitx(GeneratorAdapter mv, List<String> strings) {
if (strings.size() == 1) {
visitCase(strings.get(0));
return;
}
for (int i = 0; i < strings.size(); ++i) {
String string = strings.get(i);
Label label = new Label();
visitString();
mv.visitLdcInsn(string);
mv.invokeVirtual(STRING_TYPE, Method.getMethod("boolean equals(Object)"));
mv.visitJumpInsn(Opcodes.IFEQ, label);
visitCase(string);
mv.visitLabel(label);
}
visitDefault();
}
@Override
public Type load(Context ctx) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
Type accumulatorType = accumulator.load(ctx);
Type valueType = value.load(ctx);
String methodName;
Type methodParameterType;
if (valueType == LONG_TYPE || valueType.getClassName().equals(Long.class.getName())) {
methodName = "addLong";
methodParameterType = LONG_TYPE;
} else if (valueType == INT_TYPE || valueType.getClassName().equals(Integer.class.getName())) {
methodName = "addInt";
methodParameterType = INT_TYPE;
} else {
methodName = "addObject";
methodParameterType = getType(Object.class);
}
if (isWrapperType(valueType)) {
g.unbox(methodParameterType);
}
ctx.invoke(accumulatorType, methodName, methodParameterType);
return VOID_TYPE;
}
public Class<?> createFakeClass() {
String className = TestCustomFunctions.class.getName() + "$Foo";
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
classWriter.visit(Opcodes.V1_5, ACC_PUBLIC | ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC,
className.replace('.', '/'), null, Type.getInternalName(Object.class), null);
org.objectweb.asm.commons.Method m = org.objectweb.asm.commons.Method.getMethod("void <init>()");
GeneratorAdapter constructor = new GeneratorAdapter(ACC_PRIVATE | ACC_SYNTHETIC, m, null, null, classWriter);
constructor.loadThis();
constructor.loadArgs();
constructor.invokeConstructor(Type.getType(Object.class), m);
constructor.returnValue();
constructor.endMethod();
GeneratorAdapter gen = new GeneratorAdapter(ACC_STATIC | ACC_PUBLIC | ACC_SYNTHETIC,
org.objectweb.asm.commons.Method.getMethod("double bar()"), null, null, classWriter);
gen.push(2.0);
gen.returnValue();
gen.endMethod();
byte[] bc = classWriter.toByteArray();
return defineClass(className, bc, 0, bc.length);
}
@Override
public Type load(Context ctx) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
Label exit = new Label();
Label labelTrue = new Label();
for (Expression predicate : expressions) {
Type type = predicate.load(ctx);
checkArgument(type == BOOLEAN_TYPE);
g.ifZCmp(GeneratorAdapter.NE, labelTrue);
}
g.push(false);
g.goTo(exit);
g.mark(labelTrue);
g.push(true);
g.mark(exit);
return BOOLEAN_TYPE;
}
public Context(DefiningClassLoader classLoader,
GeneratorAdapter g,
Type selfType,
Class<?> superclass,
List<Class<?>> interfaces,
Map<String, Class<?>> fields,
Map<Method, Expression> methods,
Map<Method, Expression> staticMethods,
Method method,
Map<String, Object> staticConstants) {
this.classLoader = classLoader;
this.g = g;
this.selfType = selfType;
this.superclass = superclass;
this.interfaces = interfaces;
this.fields = fields;
this.methods = methods;
this.staticMethods = staticMethods;
this.method = method;
this.staticConstants = staticConstants;
}
@Override
public Type load(Context ctx) {
Label labelTrue = new Label();
Label labelExit = new Label();
GeneratorAdapter g = ctx.getGeneratorAdapter();
Type conditionType = condition.load(ctx);
g.push(true);
g.ifCmp(conditionType, GeneratorAdapter.EQ, labelTrue);
right.load(ctx);
g.goTo(labelExit);
g.mark(labelTrue);
Type leftType = left.load(ctx);
g.mark(labelExit);
return leftType;
}
@Override
public Type load(Context ctx) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
Label exit = new Label();
Label labelFalse = new Label();
for (Expression predicate : expressions) {
Type type = predicate.load(ctx);
checkArgument(type == BOOLEAN_TYPE);
g.ifZCmp(GeneratorAdapter.EQ, labelFalse);
}
g.push(true);
g.goTo(exit);
g.mark(labelFalse);
g.push(false);
g.mark(exit);
return BOOLEAN_TYPE;
}
@Override
public void store(Context ctx, Object storeContext, Type type) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
try {
Field javaField = owner.getField(name);
if (Modifier.isPublic(javaField.getModifiers()) && Modifier.isStatic(javaField.getModifiers())) {
Type fieldType = getType(javaField.getType());
g.putStatic((Type) storeContext, name, fieldType);
return;
}
} catch (NoSuchFieldException ignored) {
}
throw new RuntimeException(format("No static field or setter for class %s for field \"%s\". %s ",
((Type) storeContext).getClassName(),
name,
exceptionInGeneratedClass(ctx))
);
}
/**
* Generates unboxing bytecode for the passed type. An {@link Object} is expected to be on the
* stack when these bytecodes are inserted.
*
* ASM takes a short cut when dealing with short/byte types and convert them into int rather
* than short/byte types. This is not an issue on the jvm nor Android's ART but it is an issue
* on Dalvik.
*
* @param mv the {@link GeneratorAdapter} generating a method implementation.
* @param type the expected un-boxed type.
*/
public static void unbox(GeneratorAdapter mv, Type type) {
if (type.equals(Type.SHORT_TYPE)) {
mv.checkCast(NUMBER_TYPE);
mv.invokeVirtual(NUMBER_TYPE, SHORT_VALUE);
} else if (type.equals(Type.BYTE_TYPE)) {
mv.checkCast(NUMBER_TYPE);
mv.invokeVirtual(NUMBER_TYPE, BYTE_VALUE);
} else {
mv.unbox(type);
}
}
/**
* Pushes an array on the stack that contains the value of all the given variables.
*/
static void newVariableArray(
@NonNull GeneratorAdapter mv,
@NonNull List<LocalVariable> variables) {
mv.push(variables.size());
mv.newArray(Type.getType(Object.class));
loadVariableArray(mv, variables, 0);
}
@Override
public Type load(Context ctx) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
Type argType = arg.load(ctx);
int argSort = argType.getSort();
if (argSort == Type.DOUBLE || argSort == Type.FLOAT || argSort == Type.LONG || argSort == Type.INT) {
g.math(GeneratorAdapter.NEG, argType);
return argType;
}
if (argSort == Type.BYTE || argSort == Type.SHORT || argSort == Type.CHAR) {
// g.cast(argType, INT_TYPE);
g.math(GeneratorAdapter.NEG, INT_TYPE);
return argType;
}
if (argSort == Type.BOOLEAN) {
Label labelTrue = new Label();
Label labelExit = new Label();
g.push(true);
g.ifCmp(BOOLEAN_TYPE, GeneratorAdapter.EQ, labelTrue);
g.push(true);
g.goTo(labelExit);
g.mark(labelTrue);
g.push(false);
g.mark(labelExit);
return INT_TYPE;
}
throw new RuntimeException(format("%s is not primitive. %s",
ctx.toJavaType(argType),
exceptionInGeneratedClass(ctx))
);
}
@Override
public Type load(Context ctx) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
Label labelFalse = g.newLabel();
Label labelExit = g.newLabel();
expression.load(ctx);
g.ifZCmp(GeneratorAdapter.EQ, labelFalse);
g.push(false);
g.goTo(labelExit);
g.visitLabel(labelFalse);
g.push(true);
g.visitLabel(labelExit);
return Type.BOOLEAN_TYPE;
}
@Override
protected void doRedirect(@NonNull GeneratorAdapter mv, int change) {
// Push the three arguments
mv.loadLocal(change);
mv.push(name);
ByteCodeUtils.newVariableArray(mv, ByteCodeUtils.toLocalVariables(types));
// now invoke the generic dispatch method.
mv.invokeInterface(MonitorVisitor.CHANGE_TYPE, Method.getMethod("Object access$dispatch(String, Object[])"));
}
/**
* Generates a standard error exception with message similar to:
*
* String switch could not find 'equals.(Ljava/lang/Object;)Z' with hashcode 0
* in com/example/basic/GrandChild
*
* @param mv The generator adaptor used to emit the lookup switch code.
* @param visitedClassName The abstract string trie structure.
*/
void writeMissingMessageWithHash(GeneratorAdapter mv, String visitedClassName) {
mv.newInstance(STARK_RELOAD_EXCEPTION_TYPE);
mv.dup();
mv.push("String switch could not find '%s' with hashcode %s in %s");
mv.push(3);
mv.newArray(OBJECT_TYPE);
mv.dup();
mv.push(0);
visitString();
mv.arrayStore(OBJECT_TYPE);
mv.dup();
mv.push(1);
visitString();
visitHashMethod(mv);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"java/lang/Integer",
"valueOf",
"(I)Ljava/lang/Integer;", false);
mv.arrayStore(OBJECT_TYPE);
mv.dup();
mv.push(2);
mv.push(visitedClassName);
mv.arrayStore(OBJECT_TYPE);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
"java/lang/String",
"format",
"(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;", false);
mv.invokeConstructor(STARK_RELOAD_EXCEPTION_TYPE,
Method.getMethod("void <init> (String)"));
mv.throwException();
}
@Override
public Type load(Context ctx) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
length.load(ctx);
g.newArray(getType(getType(type).getDescriptor().substring(1)));
return getType(type);
}
@Override
protected void doRedirect(GeneratorAdapter mv, int change) {
// Push the three arguments
mv.loadLocal(change);
mv.push(name);
ByteCodeUtils.newVariableArray(mv, ByteCodeUtils.toLocalVariables(types));
// now invoke the generic dispatch method.
mv.invokeInterface(TBIncrementalSupportVisitor.ALI_CHANGE_TYPE, Method.getMethod("Object ipc$dispatch(String, Object[])"));
}
private static void prepareMethodParameters(GeneratorAdapter mv, String className, List<Type> args, Type returnType, boolean isStatic, int methodId) {
//第一个参数:new Object[]{...};,如果方法没有参数直接传入new Object[0]
if (args.size() == 0) {
mv.visitInsn(Opcodes.ICONST_0);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
} else {
createObjectArray(mv, args, isStatic);
}
//第二个参数:this,如果方法是static的话就直接传入null
if (isStatic) {
mv.visitInsn(Opcodes.ACONST_NULL);
} else {
mv.visitVarInsn(Opcodes.ALOAD, 0);
}
//第三个参数:changeQuickRedirect
mv.visitFieldInsn(Opcodes.GETSTATIC,
className,
REDIRECTFIELD_NAME,
REDIRECTCLASSNAME);
//第四个参数:false,标志是否为static
mv.visitInsn(isStatic ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
//第五个参数:
mv.push(methodId);
//第六个参数:参数class数组
createClassArray(mv, args);
//第七个参数:返回值类型class
createReturnClass(mv, returnType);
}
@Override
public void visitEnd() {
if (onBeforeMethodVisitor != null) {
int access = onBeforeMethodVisitor.access;
String name = onBeforeMethodVisitor.name;
String descriptor = "(Z" + onBeforeMethodVisitor.descriptor.substring(1);
String signature = onBeforeMethodVisitor.signature;
String[] exceptions = onBeforeMethodVisitor.exceptions;
GeneratorAdapter mv = new GeneratorAdapter(
cw.visitMethod(access, name, descriptor, signature, exceptions), access, name,
descriptor);
mv.visitCode();
mv.visitVarInsn(ILOAD, 0);
Label endWithDefaultLabel = new Label();
mv.visitJumpInsn(IFEQ, endWithDefaultLabel);
mv.loadArgs(1, mv.getArgumentTypes().length - 1);
mv.visitMethodInsn(INVOKESTATIC, checkNotNull(className), name,
onBeforeMethodVisitor.descriptor, false);
mv.returnValue();
mv.visitLabel(endWithDefaultLabel);
mv.visitFrame(F_NEW, 0, new Object[0], 0, new Object[0]);
if (mv.getReturnType().getSort() != Type.VOID) {
// return value will be ignored when !enabled, but need to return something
WeavingMethodVisitor.pushDefault(mv, mv.getReturnType());
}
mv.returnValue();
mv.endMethod();
}
super.visitEnd();
}
/**
* Pushes in the stack the value that should be redirected for the given local.
*/
protected static void redirectLocal(GeneratorAdapter mv, Type arg) {
switch (arg.getDescriptor()) {
case "Z":
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;");
break;
case "B":
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;");
break;
case "C":
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;");
break;
case "S":
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;");
break;
case "I":
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
break;
case "F":
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;");
break;
case "D":
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;");
break;
case "J":
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;");
break;
case "V":
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
break;
default:
mv.visitLdcInsn(Type.getType(arg.getDescriptor()));
}
}