下面列出了org.objectweb.asm.Opcodes# INVOKEVIRTUAL 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private void buildRecorderFromObject(
int opcode, String owner, String name, String signature, boolean itf) {
super.visitMethodInsn(opcode, owner, name, signature, itf);
// -> stack: ... newobj
super.visitInsn(Opcodes.DUP);
// -> stack: ... newobj newobj
super.visitInsn(Opcodes.DUP);
// -> stack: ... newobj newobj newobj
// We could be instantiating this class or a subclass, so we
// have to get the class the hard way.
super.visitMethodInsn(
Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
// -> stack: ... newobj newobj Class
super.visitInsn(Opcodes.SWAP);
// -> stack: ... newobj Class newobj
super.visitMethodInsn(
Opcodes.INVOKESTATIC, recorderClass, recorderMethod, CLASS_RECORDER_SIG, false);
// -> stack: ... newobj
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor,
boolean isInterface) {
if (!owner.equals(expectedOwner) || opcode != Opcodes.INVOKEVIRTUAL) {
this.mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
return;
}
Replacement replacement = REPLACEMENTS.get(name);
if (replacement != null && replacement.descriptor.equals(descriptor)) {
MutationIdentifier identifier = context.registerMutation(factory, replacement.toString());
if (context.shouldMutate(identifier)) {
this.mv.visitMethodInsn(
opcode,
owner,
replacement.destinationName,
replacement.descriptor,
false);
return;
}
}
this.mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
static String normalizeInterfaceMethodName(String name, boolean isLambda, int opcode) {
if (isLambda) {
// Rename lambda method to reflect the new owner. Not doing so confuses LambdaDesugaring
// if it's run over this class again. LambdaDesugaring has already renamed the method from
// its original name to include the interface name at this point.
return name + DependencyCollector.INTERFACE_COMPANION_SUFFIX;
}
switch (opcode) {
case Opcodes.INVOKESPECIAL:
// Rename static methods holding default method implementations since their descriptor
// differs from the original method (due to explicit receiver parameter). This avoids
// possible clashes with static interface methods or generated stubs for default methods
// that could otherwise have the same name and descriptor by coincidence.
return name + DEFAULT_COMPANION_METHOD_SUFFIX;
case Opcodes.INVOKESTATIC: // moved but with same name
return name + "$$STATIC$$"; // TODO(b/117453106): Stop renaming static interface methods
case Opcodes.INVOKEINTERFACE: // not moved
case Opcodes.INVOKEVIRTUAL: // tolerate being called for non-interface methods
return name;
default:
throw new IllegalArgumentException("Unexpected opcode calling " + name + ": " + opcode);
}
}
@Override
public void transform(ClassNode classNode, String name) {
for (MethodNode methodNode : classNode.methods) { // Loop through all methods inside of the class.
String methodName = methodNode.name;
if (nameMatches(methodName, "showDurabilityBar")) { // always deobfuscated
// Objective:
// Find: return stack.isItemDamaged();
// Replace With: return ItemHook.isItemDamaged(stack);
Iterator<AbstractInsnNode> iterator = methodNode.instructions.iterator();
while (iterator.hasNext()) {
AbstractInsnNode abstractNode = iterator.next();
if (abstractNode instanceof MethodInsnNode && abstractNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
MethodInsnNode methodInsnNode = (MethodInsnNode)abstractNode;
if (methodInsnNode.owner.equals(TransformerClass.ItemStack.getNameRaw()) && TransformerMethod.isItemDamaged.matches(methodInsnNode)) {
methodNode.instructions.insertBefore(abstractNode, new MethodInsnNode(Opcodes.INVOKESTATIC, "codes/biscuit/skyblockaddons/asm/hooks/ItemHook",
"isItemDamaged", "("+ TransformerClass.ItemStack.getName()+")Z", false)); // ItemHook.isItemDamaged(stack);
iterator.remove(); // Remove the old line.
break;
}
}
}
}
if (nameMatches(methodName, "getDurabilityForDisplay")) { // always deobfuscated
// Objective:
// Find: Method head.
// Insert: ReturnValue returnValue = new ReturnValue();
// ItemHook.getDurabilityForDisplay(stack, returnValue);
// if (returnValue.isCancelled()) {
// return returnValue.getValue();
// }
methodNode.instructions.insertBefore(methodNode.instructions.getFirst(), insertDurabilityHook());
}
}
}
@Override
public GambitCreator.Invocable createInvocable() {
int op = Opcodes.INVOKEVIRTUAL;
if (Modifier.isStatic(modifiers)) {
op = Opcodes.INVOKESTATIC;
}
return ExactInvocation.exactInvoke(op, name, unit.getType(), getReturnType(), getArgumentTypes());
}
/**
* Returns true if the mirror contains a method that mirrors
* <code>methodName</code>. The mirror method should be static, have the
* same name, and have the exact same arguments excepting an argument of
* type
*/
public boolean hasMethod(final String owner, final String methodName, final String methodDescriptor, final int opcode) {
Type[] types = Type.getArgumentTypes(methodDescriptor);
if (opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKEINTERFACE) {
Type[] newTypes = new Type[types.length + 1];
newTypes[0] = Type.getType("L" + owner + ";");
System.arraycopy(types, 0, newTypes, 1, types.length);
types = newTypes;
}
outer_loop:
for (Method m : class_.getDeclaredMethods()) {
final Type[] methodTypes = Type.getArgumentTypes(m);
if (!m.getName().equals(methodName)
|| methodTypes.length != types.length) {
continue;
}
for (int i = 0; i < types.length; ++i) {
if (!types[i].equals(methodTypes[i])) {
continue outer_loop;
}
}
return true;
}
return false;
}
@Override
public void visitMethodInsn(
final int opcodeAndSource,
final String owner,
final String name,
final String descriptor,
final boolean isInterface) {
if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
// Redirect the call to the deprecated version of this method.
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
return;
}
int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
checkVisitCodeCalled();
checkVisitMaxsNotCalled();
checkOpcodeMethod(opcode, Method.VISIT_METHOD_INSN);
if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) {
checkMethodIdentifier(version, name, "name");
}
checkInternalName(version, owner, "owner");
checkMethodDescriptor(version, descriptor);
if (opcode == Opcodes.INVOKEVIRTUAL && isInterface) {
throw new IllegalArgumentException("INVOKEVIRTUAL can't be used with interfaces");
}
if (opcode == Opcodes.INVOKEINTERFACE && !isInterface) {
throw new IllegalArgumentException("INVOKEINTERFACE can't be used with classes");
}
if (opcode == Opcodes.INVOKESPECIAL && isInterface && (version & 0xFFFF) < Opcodes.V1_8) {
throw new IllegalArgumentException(
"INVOKESPECIAL can't be used with interfaces prior to Java 8");
}
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
++insnCount;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (opcode == Opcodes.INVOKESPECIAL && name.startsWith("lambda$")) {
opcode = itf ? Opcodes.INVOKEINTERFACE : Opcodes.INVOKEVIRTUAL;
}
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
private void pushClassNameOnStack() {
super.visitInsn(Opcodes.DUP);
// -> stack: ... class class
super.visitMethodInsn(
Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
// -> stack: ... class classNameDotted
}
private boolean needsFrameGuard(int opcode, String owner, String name, String desc) {
if (owner.startsWith("java/") || owner.startsWith("javax/")) {
//System.out.println("SKIP:: " + owner + "." + name + desc);
return false;
}
// Always create save-point before Continuation methods (like suspend)
if (CONTINUATION_CLASS_INTERNAL_NAME.equals(owner)) {
return CONTINUATION_CLASS_CONTINUABLE_METHODS.contains(name);
}
// No need to create save-point before constructors -- it's forbidden to suspend in constructors anyway
if (opcode == Opcodes.INVOKESPECIAL && "<init>".equals(name)) {
return false;
}
if (opcode == Opcodes.INVOKEINTERFACE ||
opcode == Opcodes.INVOKESPECIAL ||
opcode == Opcodes.INVOKESTATIC ||
opcode == Opcodes.INVOKEVIRTUAL) {
ContinuableClassInfo classInfo;
try {
classInfo = cciResolver.resolve(owner);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return null != classInfo && classInfo.isContinuableMethod(opcode, name, desc, desc);
}
return false;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (opcode == Opcodes.INVOKESPECIAL && UPDATED_METHODS.contains(name)) {
opcode = Opcodes.INVOKEVIRTUAL;
}
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
public static boolean isCallOnContextAware(final AbstractInsnNode insn)
{
Objects.requireNonNull(insn, "insn");
if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL
&& insn.getOpcode() != Opcodes.INVOKEINTERFACE)
return false;
final MethodInsnNode mi = (MethodInsnNode) insn;
return isAssignableTo(mi.owner, ContextAware.class);
}
private void doVisitMethodInsn(
final int opcode,
final String owner,
final String name,
final String descriptor,
final boolean isInterface) {
checkVisitCodeCalled();
checkVisitMaxsNotCalled();
checkOpcodeMethod(opcode, Method.VISIT_METHOD_INSN);
if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) {
checkMethodIdentifier(version, name, "name");
}
checkInternalName(version, owner, "owner");
checkMethodDescriptor(version, descriptor);
if (opcode == Opcodes.INVOKEVIRTUAL && isInterface) {
throw new IllegalArgumentException("INVOKEVIRTUAL can't be used with interfaces");
}
if (opcode == Opcodes.INVOKEINTERFACE && !isInterface) {
throw new IllegalArgumentException("INVOKEINTERFACE can't be used with classes");
}
if (opcode == Opcodes.INVOKESPECIAL && isInterface && (version & 0xFFFF) < Opcodes.V1_8) {
throw new IllegalArgumentException(
"INVOKESPECIAL can't be used with interfaces prior to Java 8");
}
// Calling super.visitMethodInsn requires to call the correct version depending on this.api
// (otherwise infinite loops can occur). To simplify and to make it easier to automatically
// remove the backward compatibility code, we inline the code of the overridden method here.
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
++insnCount;
}
protected void _call(int op, String owner, String name, String desc) {
save_stack(false);
int argLen = Type.getArgumentTypes(desc).length + (op == INVOKESTATIC ? 0 : 1);
Expr[] args = new Expr[argLen];
for (int i = args.length - 1; i >= 0; i--) {
args[i] = pop();
}
InvocationExpr callExpr;
switch(op) {
case Opcodes.INVOKEVIRTUAL:
case Opcodes.INVOKEINTERFACE:
case Opcodes.INVOKESPECIAL:
callExpr = new VirtualInvocationExpr(VirtualInvocationExpr.resolveCallType(op), args, owner, name, desc);
break;
case Opcodes.INVOKESTATIC:
callExpr = new StaticInvocationExpr(args, owner, name, desc);
break;
default:
throw new IllegalArgumentException("invalid call opcode " + op);
}
if(callExpr.getType() == Type.VOID_TYPE) {
addStmt(new PopStmt(callExpr));
} else {
int index = currentStack.height();
Type type = assign_stack(index, callExpr);
push(load_stack(index, type));
}
}
private static int resolveASMOpcode(CallType t) {
switch (t) {
case SPECIAL:
return Opcodes.INVOKESPECIAL;
case VIRTUAL:
return Opcodes.INVOKEVIRTUAL;
case INTERFACE:
return Opcodes.INVOKEINTERFACE;
default:
throw new IllegalArgumentException(t.toString());
}
}
@Override
public MethodNode generate() {
int size = Bytecode.getArgsSize(this.argTypes) + this.returnType.getSize() + (this.targetIsStatic ? 0 : 1);
MethodNode method = this.createMethod(size, size);
if (!this.targetIsStatic) {
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
}
Bytecode.loadArgs(this.argTypes, method.instructions, this.targetIsStatic ? 0 : 1);
boolean isPrivate = Bytecode.hasFlag(this.targetMethod, Opcodes.ACC_PRIVATE);
int opcode = this.targetIsStatic ? Opcodes.INVOKESTATIC : (isPrivate ? Opcodes.INVOKESPECIAL : Opcodes.INVOKEVIRTUAL);
method.instructions.add(new MethodInsnNode(opcode, this.info.getClassNode().name, this.targetMethod.name, this.targetMethod.desc, false));
method.instructions.add(new InsnNode(this.returnType.getOpcode(Opcodes.IRETURN)));
return method;
}
/**
* Convert code from using Class.X methods to our remapped versions
*/
public static byte[] transform(byte[] code) {
ClassReader reader = new ClassReader(code); // Turn from bytes into visitor
ClassNode node = new ClassNode();
reader.accept(node, 0); // Visit using ClassNode
for (MethodNode method : node.methods) { // Taken from SpecialSource
ListIterator<AbstractInsnNode> insnIterator = method.instructions.iterator();
while (insnIterator.hasNext()) {
AbstractInsnNode insn = insnIterator.next();
if (!(insn instanceof MethodInsnNode)) {
continue;
}
MethodInsnNode mi = (MethodInsnNode) insn;
switch (mi.getOpcode()) {
case Opcodes.INVOKEVIRTUAL:
remapVirtual(mi);
break;
case Opcodes.INVOKESTATIC:
remapForName(mi);
break;
}
}
}
ClassWriter writer = new ClassWriter(0);
node.accept(writer);
return writer.toByteArray();
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
identityState = IdentityMethodState.NOT;
methodCallCount++;
if (isAccessMethod && this.accessOwner == null) {
this.accessOwner = owner;
this.accessName = name;
this.accessDesc = desc;
this.accessIsStatic = opcode == Opcodes.INVOKESTATIC;
this.accessForField = false;
}
if (stubState == StubState.LOADED_STUB && opcode == Opcodes.INVOKESPECIAL
&& "java/lang/RuntimeException".equals(owner) && "<init>".equals(name)) {
stubState = StubState.INITIALIZE_RUNTIME;
} else {
stubState = StubState.INITIAL;
}
if (owner.startsWith("java/util/concurrent")) {
mBuilder.setUsesConcurrency();
}
if (opcode == Opcodes.INVOKEINTERFACE) {
return;
}
if (owner.charAt(0) == '[' && owner.charAt(owner.length() - 1) != ';') {
// primitive array
return;
}
if (opcode == Opcodes.INVOKESTATIC && "java/lang/System".equals(owner) && "exit".equals(name)
&& !sawReturn) {
sawSystemExit = true;
}
justSawInitializationOfUnsupportedOperationException = opcode == Opcodes.INVOKESPECIAL
&& "java/lang/UnsupportedOperationException".equals(owner) && "<init>".equals(name);
if (isBridge && bridgedMethodSignature == null) {
switch (opcode) {
case Opcodes.INVOKEVIRTUAL:
case Opcodes.INVOKESPECIAL:
case Opcodes.INVOKESTATIC:
case Opcodes.INVOKEINTERFACE:
if (desc != null && name.equals(methodName)) {
bridgedMethodSignature = desc;
}
break;
default:
break;
}
}
// System.out.println("Call from " +
// ClassParserUsingASM.this.slashedClassName +
// " to " + owner + " : " + desc);
if (desc == null || desc.indexOf('[') == -1 && desc.indexOf('L') == -1) {
return;
}
if (ClassParserUsingASM.this.slashedClassName.equals(owner)) {
return;
}
ClassDescriptor classDescriptor = DescriptorFactory.instance().getClassDescriptor(owner);
calledClassSet.add(classDescriptor);
}
@Override
public void transform(ClassNode classNode, String name) {
for (MethodNode methodNode : classNode.methods) {
if (TransformerMethod.renderTileEntityAt.matches(methodNode)) {
// Objective:
// Find: this.bindTexture(ENDER_CHEST_TEXTURE);
// Replacement: TileEntityEnderChestRendererHook.bindTexture(this, (ResourceLocation)ENDER_CHEST_TEXTURE);
// Objective:
// Find: this.field_147521_c.renderAll();
// Insert 2 lines before: TileEntityEnderChestRendererHook.setEnderchestColor();
int bindTextureCount = 0;
Iterator<AbstractInsnNode> iterator = methodNode.instructions.iterator();
while (iterator.hasNext()) {
AbstractInsnNode abstractNode = iterator.next();
if (abstractNode instanceof MethodInsnNode && abstractNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
MethodInsnNode methodInsnNode = (MethodInsnNode)abstractNode;
if (methodInsnNode.owner.equals(TransformerClass.TileEntityEnderChestRenderer.getNameRaw())
&& TransformerMethod.bindTexture.matches(methodInsnNode)) { // TileEntityEnderChestRendererHook.bindTexture(ENDER_CHEST_TEXTURE);
if (bindTextureCount == 1) { // Find the second statement, not the first one.
methodNode.instructions.insertBefore(abstractNode, new MethodInsnNode(Opcodes.INVOKESTATIC, "codes/biscuit/skyblockaddons/asm/hooks/TileEntityEnderChestRendererHook",
// Add TileEntityEnderChestRendererHook.bindTexture(this, (ResourceLocation)ENDER_CHEST_TEXTURE);
"bindTexture", "("+TransformerClass.TileEntityEnderChestRenderer.getName()+TransformerClass.ResourceLocation.getName()+")V", false));
iterator.remove(); // Remove the old method call.
}
bindTextureCount++;
} else if (methodInsnNode.owner.equals(TransformerClass.ModelChest.getNameRaw())
&& TransformerMethod.renderAll.matches(methodInsnNode)) { // The two lines are to make sure its before the "this" & the "field_147521_c".
methodNode.instructions.insertBefore(methodNode.instructions.get(methodNode.instructions.indexOf(abstractNode)-2), insertChangeEnderchestColor());
}
}
}
break;
}
}
}
public boolean appendExpression(StringBuilder b) {
// special case for clone on an array which isn't a real method invocation
if(name.equals("clone") && owner.indexOf('[') > -1) {
if (targetObjectLiteral != null) {
b.append("cloneArray(").append(targetObjectLiteral).append(")");
} else {
b.append("cloneArray(POP_OBJ(1))");
}
return true;
}
StringBuilder bld = new StringBuilder();
if(origOpcode == Opcodes.INVOKEINTERFACE || origOpcode == Opcodes.INVOKEVIRTUAL) {
//b.append(" ");
bld.append("virtual_");
}
if(origOpcode == Opcodes.INVOKESTATIC) {
// find the actual class of the static method to workaround javac not defining it correctly
ByteCodeClass bc = Parser.getClassObject(owner.replace('/', '_').replace('$', '_'));
owner = findActualOwner(bc);
}
bld.append(owner.replace('/', '_').replace('$', '_'));
bld.append("_");
if(name.equals("<init>")) {
bld.append("__INIT__");
} else {
if(name.equals("<clinit>")) {
bld.append("__CLINIT__");
} else {
bld.append(name);
}
}
bld.append("__");
ArrayList<String> args = new ArrayList<String>();
String returnVal = BytecodeMethod.appendMethodSignatureSuffixFromDesc(desc, bld, args);
int numLiteralArgs = this.getNumLiteralArgs();
if (numLiteralArgs > 0) {
b.append("/* CustomInvoke */");
}
boolean noPop = false;
b.append(bld);
b.append("(threadStateData");
if(origOpcode != Opcodes.INVOKESTATIC) {
if (targetObjectLiteral == null) {
//b.append(", SP[-");
//b.append(args.size() + 1 - numLiteralArgs);
//b.append("].data.o");
return false;
} else {
b.append(", ").append(targetObjectLiteral);
numLiteralArgs++;
}
}
//int offset = args.size();
//int numArgs = offset;
int argIndex=0;
for(String a : args) {
b.append(", ");
if (literalArgs != null && literalArgs[argIndex] != null) {
b.append(literalArgs[argIndex]);
} else {
return false;
//b.append("SP[-");
//b.append(offset);
//b.append("].data.");
//b.append(a);
//offset--;
}
argIndex++;
}
if (returnVal == null) {
return false;
}
b.append(")");
return true;
}