下面列出了怎么用org.objectweb.asm.commons.Method的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();
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc,
boolean itf) {
Type receiver = Type.getObjectType(owner);
if (!incompatibleChange.isPresent()) {
if (opcode == Opcodes.INVOKEVIRTUAL && blackListedMethods.containsKey(receiver)) {
for (Method method : blackListedMethods.get(receiver)) {
if (method.getName().equals(name) && method.getDescriptor().equals(desc)) {
incompatibleChange = Optional.of(StarkVerifierStatus.REFLECTION_USED);
}
}
}
}
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
private static void defineDefaultConstructor(ClassWriter cw, Type proxyType, Type superType)
{
GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC,
new Method("<init>", Type.VOID_TYPE, new Type[]{ }),
null,
null,
cw);
mg.visitCode();
// invoke super constructor
mg.loadThis();
mg.invokeConstructor(superType, Method.getMethod("void <init> ()"));
mg.returnValue();
mg.endMethod();
mg.visitEnd();
}
/**
* 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();
}
/**
* Defines a new Object[] and push all method argmuments into the array.
*
* @param mg
* @param method
* @param methodType
*/
private static void loadArguments(GeneratorAdapter mg, java.lang.reflect.Method method, Type methodType)
{
// create the Object[]
mg.push(methodType.getArgumentTypes().length);
mg.newArray(TYPE_OBJECT);
// 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.loadArg(i);
mg.valueOf(methodType.getArgumentTypes()[i]);
mg.arrayStore(TYPE_OBJECT);
}
}
/**
* For calls to constructors in the same package, calls are rewritten to use reflection
* to create the instance (see above, the NEW and DUP instructions are also removed) using
* the following pseudo code.
* <p>
* before:
* <code>
* $value = new $type(arg1, arg2);
* </code>
* after:
* <code>
* $value = ($type)$package/AndroidInstantRuntime.newForClass(new Object[] {arg1, arg2 },
* new Class[]{ String.class, Integer.class }, $type.class);
* </code>
*
*/
private boolean handleConstructor(String owner, String name, String desc) {
if (isInSamePackage(owner)) {
Type expectedType = Type.getType("L" + owner + ";");
pushMethodRedirectArgumentsOnStack(name, desc);
// pop the name, we don't need it.
pop();
visitLdcInsn(expectedType);
invokeStatic(RUNTIME_TYPE, Method.getMethod(
"Object newForClass(Object[], Class[], Class)"));
checkCast(expectedType);
ByteCodeUtils.unbox(this, expectedType);
return true;
} else {
return false;
}
}
@Override
public void visitCode(){
super.visitCode();
// Push the current stack size to operand stack and invoke AVMStackWatcher.enterMethod(int)
Method m1 = Method.getMethod("void enterMethod(int)");
visitLdcInsn(this.maxLocals + this.maxStack);
invokeStatic(typeHelper, m1);
// If current method has at least one try catch block, we need to generate a StackWacher stamp.
if (this.tryCatchBlockCount > 0){
//invoke AVMStackWatcher.getCurStackDepth() and put the result into local variable
Method m2 = Method.getMethod("int getCurStackDepth()");
invokeStatic(typeHelper, m2);
this.stackDepthLocalVariableIndex = newLocal(typeInt);
storeLocal(this.stackDepthLocalVariableIndex, typeInt);
//invoke AVMStackWatcher.getCurStackSize() and put the result into local variable
Method m3 = Method.getMethod("int getCurStackSize()");
invokeStatic(typeHelper, m3);
this.stackSizeLocalVariableIndex = newLocal(typeInt);
storeLocal(this.stackSizeLocalVariableIndex, typeInt);
}
}
private void initOnThrowAdvice(PointcutClass adviceClass, PointcutMethod adviceMethod)
throws AdviceConstructionException {
checkState(!hasOnThrowAdvice, "@Pointcut '" + adviceClass.type().getClassName()
+ "' has more than one @OnThrow method");
Method asmMethod = adviceMethod.toAsmMethod();
List<AdviceParameter> parameters =
getAdviceParameters(adviceMethod.parameterAnnotationTypes(),
asmMethod.getArgumentTypes(), onThrowBindAnnotationTypes, OnThrowType);
for (int i = 1; i < parameters.size(); i++) {
checkState(parameters.get(i).kind() != ParameterKind.THROWABLE,
"@BindThrowable must be the first argument to @OnThrow");
}
checkState(asmMethod.getReturnType().getSort() == Type.VOID,
"@OnThrow method must return void (for now)");
builder.onThrowAdvice(asmMethod);
builder.addAllOnThrowParameters(parameters);
checkForBindThreadContext(parameters);
checkForBindOptionalThreadContext(parameters);
hasOnThrowAdvice = true;
}
/**
* Inject a callback to our servlet handler.
*
* @param method
*/
private void injectServletCallback(String method) {
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchStart = new Label();
Label catchEnd = new Label();
visitTryCatchBlock(tryStart, tryEnd, catchStart, "java/lang/NoClassDefFoundError");
mark(tryStart); // try {
loadArgs();
invokeStatic(Type.getType(SERVLET_CALLBACK_TYPE), Method.getMethod(method));
mark(tryEnd); // }
visitJumpInsn(GOTO, catchEnd);
mark(catchStart); // catch() {
pop();
mark(catchEnd); // }
}
public void addExposedMethodBypass(Method method, Type sourceInterface) {
MethodVisitor mv = writer.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, method.getName(), method.getDescriptor(), null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, clsName, TARGET_FIELD_NAME, targetType.getDescriptor());
Type[] args = method.getArgumentTypes();
for (int i = 0; i < args.length; i++)
mv.visitVarInsn(args[i].getOpcode(Opcodes.ILOAD), i + 1);
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, sourceInterface.getInternalName(), method.getName(), method.getDescriptor(), true);
Type returnType = method.getReturnType();
mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
mv.visitMaxs(0, 0);
mv.visitEnd();
}
public static Method toPrimitive(Type type) {
if (type.getSort() == BOOLEAN || type.equals(WRAPPED_BOOLEAN_TYPE))
return getMethod("boolean booleanValue()");
if (type.getSort() == CHAR || type.equals(WRAPPED_CHAR_TYPE))
return getMethod("char charValue()");
if (type.getSort() == BYTE || type.equals(WRAPPED_BYTE_TYPE))
return getMethod("byte byteValue()");
if (type.getSort() == SHORT || type.equals(WRAPPED_SHORT_TYPE))
return getMethod("short shortValue()");
if (type.getSort() == INT || type.equals(WRAPPED_INT_TYPE))
return getMethod("int intValue()");
if (type.getSort() == FLOAT || type.equals(WRAPPED_FLOAT_TYPE))
return getMethod("float floatValue()");
if (type.getSort() == LONG || type.equals(WRAPPED_LONG_TYPE))
return getMethod("long longValue()");
if (type.getSort() == DOUBLE || type.equals(WRAPPED_DOUBLE_TYPE))
return getMethod("double doubleValue()");
throw new IllegalArgumentException(format("No primitive value method for %s ", type.getClassName()));
}
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();
}
@SuppressWarnings("unchecked")
public void visitEnd() {
if (!foundClinit && instrument()) {
// didn't find <clinit> so lets make one
try {
GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, new Method(CLINIT_NAME, CLINIT_SIGNATURE), CLINIT_SIGNATURE, new Type[] {}, this);
Label start = mg.mark();
mg.push(className);
mg.invokeStatic(LOG_INTERNAL_TYPE, Method.getMethod(LOG_INTERNAL_CLASS.getMethod(LOG_METHOD_NAME, String.class)));
Label end = mg.mark();
mg.returnValue();
mg.catchException(start, end, JAVA_LANG_THROWABLE_TYPE);
mg.returnValue();
mg.endMethod();
} catch (NoSuchMethodException nsme) {
System.out.println("Unable to find Agent.reportClass method");
}
}
super.visitEnd();
}
private void initOnBeforeAdvice(PointcutClass adviceClass, PointcutMethod adviceMethod)
throws AdviceConstructionException {
checkState(!hasOnBeforeAdvice, "@Pointcut '" + adviceClass.type().getClassName()
+ "' has more than one @OnBefore method");
Method asmMethod = adviceMethod.toAsmMethod();
builder.onBeforeAdvice(asmMethod);
List<AdviceParameter> parameters =
getAdviceParameters(adviceMethod.parameterAnnotationTypes(),
asmMethod.getArgumentTypes(), onBeforeBindAnnotationTypes, OnBeforeType);
builder.addAllOnBeforeParameters(parameters);
if (asmMethod.getReturnType().getSort() != Type.VOID) {
builder.travelerType(asmMethod.getReturnType());
}
checkForBindThreadContext(parameters);
checkForBindOptionalThreadContext(parameters);
hasOnBeforeAdvice = true;
}
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 void visitCode() {
if (!disableRedirection) {
// Labels cannot be used directly as they are volatile between different visits,
// so we must use LabelNode and resolve before visiting for better performance.
for (Redirection redirection : redirections) {
resolvedRedirections.put(redirection.getPosition().getLabel(), redirection);
}
super.visitLabel(start);
change = newLocal(MTD_MAP_TYPE);
push(new Integer(AcesoProguardMap.instance().getClassIndex(visitedClassName)));
push(new Integer(AcesoProguardMap.instance().getMtdIndex(visitedClassName, IncrementalTool.getMtdSig(name, desc))));
invokeStatic(IncrementalVisitor.MTD_MAP_TYPE, Method.getMethod("com.android.tools.fd.runtime.IncrementalChange get(int,int)"));
storeLocal(change);
redirectAt(start);
}
super.visitCode();
}
@RequiresNonNull("type")
private void addShim(ShimType shimType) {
for (java.lang.reflect.Method reflectMethod : shimType.shimMethods()) {
Method method = Method.getMethod(reflectMethod);
Shim shim = reflectMethod.getAnnotation(Shim.class);
checkNotNull(shim);
if (shim.value().length != 1) {
throw new IllegalStateException(
"@Shim annotation must have exactly one value when used on methods");
}
Method targetMethod = Method.getMethod(shim.value()[0]);
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, method.getName(), method.getDescriptor(),
null, null);
mv.visitCode();
int i = 0;
mv.visitVarInsn(ALOAD, i++);
for (Type argumentType : method.getArgumentTypes()) {
mv.visitVarInsn(argumentType.getOpcode(ILOAD), i++);
}
mv.visitMethodInsn(INVOKEVIRTUAL, type.getInternalName(), targetMethod.getName(),
targetMethod.getDescriptor(), false);
mv.visitInsn(method.getReturnType().getOpcode(IRETURN));
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
private static void defineSuperAccessorMethod(ClassWriter cw, java.lang.reflect.Method method, Type superType,
String superAccessorMethodSuffix)
{
Method originalAsmMethod = Method.getMethod(method);
Method newAsmMethod = new Method(method.getName() + superAccessorMethodSuffix,
originalAsmMethod.getReturnType(),
originalAsmMethod.getArgumentTypes());
GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, newAsmMethod, null, null, cw);
mg.visitCode();
// call super method
mg.loadThis();
mg.loadArgs();
mg.visitMethodInsn(Opcodes.INVOKESPECIAL,
superType.getInternalName(),
method.getName(),
Type.getMethodDescriptor(method),
false);
mg.returnValue();
// finish the method
mg.endMethod();
mg.visitMaxs(10, 10);
mg.visitEnd();
}
private void initOnReturnAdvice(PointcutClass adviceClass, PointcutMethod adviceMethod)
throws AdviceConstructionException {
checkState(!hasOnReturnAdvice, "@Pointcut '" + adviceClass.type().getClassName()
+ "' has more than one @OnReturn method");
Method asmMethod = adviceMethod.toAsmMethod();
List<AdviceParameter> parameters =
getAdviceParameters(adviceMethod.parameterAnnotationTypes(),
asmMethod.getArgumentTypes(), onReturnBindAnnotationTypes, OnReturnType);
for (int i = 1; i < parameters.size(); i++) {
checkState(parameters.get(i).kind() != ParameterKind.RETURN,
"@BindReturn must be the first argument to @OnReturn");
checkState(parameters.get(i).kind() != ParameterKind.OPTIONAL_RETURN,
"@BindOptionalReturn must be the first argument to @OnReturn");
}
builder.onReturnAdvice(asmMethod);
builder.addAllOnReturnParameters(parameters);
checkForBindThreadContext(parameters);
checkForBindOptionalThreadContext(parameters);
hasOnReturnAdvice = true;
}
/**
* 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 <T> Class<T> generateProxyClass(ClassLoader classLoader,
Class<T> targetClass,
String suffix,
String superAccessorMethodSuffix,
Class<?>[] additionalInterfaces,
java.lang.reflect.Method[] delegateMethods,
java.lang.reflect.Method[] interceptMethods)
{
String proxyName = targetClass.getName() + suffix;
String classFileName = proxyName.replace('.', '/');
byte[] proxyBytes = generateProxyClassBytes(targetClass,
classFileName, superAccessorMethodSuffix, additionalInterfaces, delegateMethods, interceptMethods);
Class<T> proxyClass = (Class<T>) loadClass(classLoader, proxyName, proxyBytes,
targetClass.getProtectionDomain());
return proxyClass;
}
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;
}
public Type invoke(Type ownerType, String methodName, Type... argumentTypes) {
Class<?>[] arguments = Stream.of(argumentTypes).map(this::toJavaType).toArray(Class[]::new);
Method foundMethod;
if (ownerType.equals(getSelfType())) {
foundMethod = findMethod(
getMethods().keySet().stream(),
methodName,
arguments);
g.invokeVirtual(ownerType, foundMethod);
} else {
Class<?> javaOwnerType = toJavaType(ownerType);
foundMethod = findMethod(
Arrays.stream(javaOwnerType.getMethods())
.filter(m -> !isStatic(m.getModifiers()))
.map(Method::getMethod),
methodName,
arguments);
if (javaOwnerType.isInterface()) {
g.invokeInterface(ownerType, foundMethod);
} else {
g.invokeVirtual(ownerType, foundMethod);
}
}
return foundMethod.getReturnType();
}
public Type invokeStatic(Type ownerType, String methodName, Type... argumentTypes) {
Class<?>[] arguments = Stream.of(argumentTypes).map(this::toJavaType).toArray(Class[]::new);
Method foundMethod;
if (ownerType.equals(getSelfType())) {
foundMethod = findMethod(
getStaticMethods().keySet().stream(),
methodName,
arguments);
} else {
foundMethod = findMethod(
Arrays.stream(toJavaType(ownerType).getMethods())
.filter(m -> isStatic(m.getModifiers()))
.map(Method::getMethod),
methodName,
arguments);
}
g.invokeStatic(ownerType, foundMethod);
return foundMethod.getReturnType();
}
void box(final InsnList instructions, Type type) {
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return;
}
if (type == Type.VOID_TYPE) {
// push null
instructions.add(new InsnNode(Opcodes.ACONST_NULL));
} else {
Type boxed = getBoxedType(type);
// new instance.
newInstance(instructions, boxed);
if (type.getSize() == 2) {
// Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
// dupX2
dupX2(instructions);
// dupX2
dupX2(instructions);
// pop
pop(instructions);
} else {
// p -> po -> opo -> oop -> o
// dupX1
dupX1(instructions);
// swap
swap(instructions);
}
invokeConstructor(instructions, boxed, new Method("<init>", Type.VOID_TYPE, new Type[]{type}));
}
}
/**
* 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();
}
/**
* Rewrites INVOKESTATIC method calls.
* <p>
* Static calls to non-public methods are rewritten according to the following pseudo code:
* before:
* <code>
* $value = $type.protectedStatic(arg1, arg2);
* </code>
* after:
* <code>
* $value = (unbox)$package/AndroidInstantRuntime.invokeProtectedStaticMethod(
* new object[] {arg1, arg2}, new Class[] { String.class, Integer.class },
* "protectedStatic", $type.class);
* </code>
*/
private boolean handleStaticOpcode(String owner, String name, String desc, boolean itf) {
if (DEBUG) {
System.out.println(
"Static Method : " + name + ":" + desc + ":" + itf + ":" + isStatic);
}
AccessRight accessRight = getMethodAccessRight(owner, name, desc);
if (accessRight == AccessRight.PUBLIC) {
return false;
}
// for anything else, private, protected and package private, we must go through
// reflection.
// stack: <param_1>
// <param_2>
// ...
// <param_n>
pushMethodRedirectArgumentsOnStack(name, desc);
// push the class implementing the original static method
visitLdcInsn(Type.getType("L" + owner + ";"));
// stack: <boxed method parameter>
// <target parameter types>
// <target method name>
// <target class name>
invokeStatic(RUNTIME_TYPE, Method.getMethod(
"Object invokeProtectedStaticMethod(Object[], Class[], String, Class)"));
// stack : method return value or null if the method was VOID.
handleReturnType(desc);
return true;
}
@Override
protected void onMethodExit(int opcode){
// Push the current stack size to operand stack and invoke AVMStackWatcher.exitMethod(int)
Method m1 = Method.getMethod("void exitMethod(int)");
visitLdcInsn(this.maxLocals + this.maxStack);
invokeStatic(typeHelper, m1);
}
@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[])"));
}
/**
* Rewrites INVOKESTATIC method calls.
* <p>
* Static calls to non-public methods are rewritten according to the following pseudo code:
* before:
* <code>
* $value = $type.protectedStatic(arg1, arg2);
* </code>
* after:
* <code>
* $value = (unbox)$package/AndroidInstantRuntime.invokeProtectedStaticMethod(
* new object[] {arg1, arg2}, new Class[] { String.class, Integer.class },
* "protectedStatic", $type.class);
* </code>
*/
private boolean handleStaticOpcode(String owner, String name, String desc, boolean itf) {
if (DEBUG) {
System.out.println(
"Static Method : " + name + ":" + desc + ":" + itf + ":" + isStatic);
}
AccessRight accessRight = getMethodAccessRight(owner, name, desc);
if (accessRight == AccessRight.PUBLIC) {
return false;
}
// for anything else, private, protected and package private, we must go through
// reflection.
// stack: <param_1>
// <param_2>
// ...
// <param_n>
pushMethodRedirectArgumentsOnStack(name, desc);
// push the class implementing the original static method
visitLdcInsn(Type.getType("L" + owner + ";"));
// stack: <boxed method parameter>
// <target parameter types>
// <target method name>
// <target class name>
invokeStatic(ALI_RUNTIME_TYPE, Method.getMethod(
"Object invokeProtectedStaticMethod(Object[], Class[], String, Class)"));
// stack : method return value or null if the method was VOID.
handleReturnType(desc);
return true;
}