use of org.objectweb.asm.tree.MethodInsnNode in project quasar by puniverse.
the class InstrumentMethod method collectCodeBlocks.
private void collectCodeBlocks() {
final int numIns = mn.instructions.size();
codeBlocks[0] = FrameInfo.FIRST;
for (int i = 0; i < numIns; i++) {
final Frame f = frames[i];
if (f != null) {
// reachable ?
final AbstractInsnNode in = mn.instructions.get(i);
if (in.getType() == AbstractInsnNode.METHOD_INSN || in.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN) {
boolean susp = true;
if (in.getType() == AbstractInsnNode.METHOD_INSN) {
final MethodInsnNode min = (MethodInsnNode) in;
int opcode = min.getOpcode();
if (isSyntheticAccess(min.owner, min.name))
db.log(LogLevel.DEBUG, "Synthetic accessor method call at instruction %d is assumed suspendable", i);
else if (isReflectInvocation(min.owner, min.name))
db.log(LogLevel.DEBUG, "Reflective method call at instruction %d is assumed suspendable", i);
else if (isMethodHandleInvocation(min.owner, min.name))
db.log(LogLevel.DEBUG, "MethodHandle invocation at instruction %d is assumed suspendable", i);
else if (isInvocationHandlerInvocation(min.owner, min.name))
db.log(LogLevel.DEBUG, "InvocationHandler invocation at instruction %d is assumed suspendable", i);
else {
SuspendableType st = db.isMethodSuspendable(min.owner, min.name, min.desc, opcode);
if (st == SuspendableType.NON_SUSPENDABLE) {
susp = false;
} else if (st == null) {
db.log(LogLevel.WARNING, "Method not found in class - assuming suspendable: %s#%s%s (at %s:%s#%s)", min.owner, min.name, min.desc, sourceName, className, mn.name);
susp = true;
} else if (st != SuspendableType.SUSPENDABLE_SUPER) {
db.log(LogLevel.DEBUG, "Method call at instruction %d to %s#%s%s is suspendable", i, min.owner, min.name, min.desc);
}
if (st == SuspendableType.SUSPENDABLE_SUPER) {
db.log(LogLevel.DEBUG, "Method call at instruction %d to %s#%s%s to suspendable-super (instrumentation for proxy support will be enabled)", i, min.owner, min.name, min.desc);
this.callsSuspendableSupers = true;
}
}
} else if (in.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN) {
// invoke dynamic
final InvokeDynamicInsnNode idin = (InvokeDynamicInsnNode) in;
if (idin.bsm.getOwner().equals("java/lang/invoke/LambdaMetafactory")) {
// lambda
db.log(LogLevel.DEBUG, "Lambda at instruction %d", i);
susp = false;
} else
db.log(LogLevel.DEBUG, "InvokeDynamic Method call at instruction %d to is assumed suspendable", i);
}
if (susp) {
FrameInfo fi = addCodeBlock(f, i);
splitTryCatch(fi);
} else if (in.getType() == AbstractInsnNode.METHOD_INSN) {
// not invokedynamic
// noinspection ConstantConditions
final MethodInsnNode min = (MethodInsnNode) in;
db.log(LogLevel.DEBUG, "Method call at instruction %d to %s#%s%s is not suspendable", i, min.owner, min.name, min.desc);
possiblyWarnAboutBlocking(min);
}
}
}
}
addCodeBlock(null, numIns);
}
use of org.objectweb.asm.tree.MethodInsnNode in project quasar by puniverse.
the class InstrumentMethod method accept.
public void accept(MethodVisitor mv, boolean hasAnnotation) {
db.log(LogLevel.INFO, "Instrumenting method %s:%s#%s%s", sourceName, className, mn.name, mn.desc);
if (mn.name.charAt(0) == '<')
throw new UnableToInstrumentException("special method", className, mn.name, mn.desc);
collectCallsites();
final boolean skipInstrumentation = canInstrumentationBeSkipped(suspCallsBcis);
emitInstrumentedAnn(db, mv, mn, sourceName, className, skipInstrumentation, startSourceLine, endSourceLine, suspCallsSourceLines, suspCallsNames, null);
if (skipInstrumentation) {
db.log(LogLevel.INFO, "[OPTIMIZE] Skipping instrumentation for method %s:%s#%s%s", sourceName, className, mn.name, mn.desc);
// Dump
mn.accept(mv);
return;
}
// Else instrument
// Must be called first, sets flags & state used below
collectCodeBlocks();
// noinspection ConstantConditions
final boolean handleProxyInvocations = HANDLE_PROXY_INVOCATIONS && callsSuspendableSupers;
mv.visitCode();
Label lMethodStart = new Label();
Label lMethodStart2 = new Label();
Label lMethodEnd = new Label();
Label lCatchSEE = new Label();
Label lCatchUTE = new Label();
Label lCatchAll = new Label();
Label[] lMethodCalls = new Label[numCodeBlocks - 1];
Label[][] refInvokeTryCatch;
for (int i = 1; i < numCodeBlocks; i++) lMethodCalls[i - 1] = new Label();
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitVarInsn(Opcodes.ASTORE, lvarInvocationReturnValue);
// if (verifyInstrumentation) {
// mv.visitInsn(Opcodes.ICONST_0);
// mv.visitVarInsn(Opcodes.ISTORE, lvarSuspendableCalled);
// }
mv.visitTryCatchBlock(lMethodStart, lMethodEnd, lCatchSEE, SUSPEND_EXECUTION_NAME);
mv.visitTryCatchBlock(lMethodStart, lMethodEnd, lCatchSEE, RUNTIME_SUSPEND_EXECUTION_NAME);
if (handleProxyInvocations)
mv.visitTryCatchBlock(lMethodStart, lMethodEnd, lCatchUTE, UNDECLARED_THROWABLE_NAME);
// Prepare visitTryCatchBlocks for InvocationTargetException.
// With reflective invocations, the SuspendExecution exception will be wrapped in InvocationTargetException. We need to catch it and unwrap it.
// Note that the InvocationTargetException will be regenrated on every park, adding further overhead on top of the reflective call.
// This must be done here, before all other visitTryCatchBlock, because the exception's handler
// will be matched according to the order of in which visitTryCatchBlock has been called. Earlier calls take precedence.
refInvokeTryCatch = new Label[numCodeBlocks - 1][];
for (int i = 1; i < numCodeBlocks; i++) {
final FrameInfo fi = codeBlocks[i];
final AbstractInsnNode in = mn.instructions.get(fi.endInstruction);
if (mn.instructions.get(fi.endInstruction) instanceof MethodInsnNode) {
MethodInsnNode min = (MethodInsnNode) in;
if (isReflectInvocation(min.owner, min.name)) {
Label[] ls = new Label[3];
for (int k = 0; k < 3; k++) ls[k] = new Label();
refInvokeTryCatch[i - 1] = ls;
mv.visitTryCatchBlock(ls[0], ls[1], ls[2], "java/lang/reflect/InvocationTargetException");
}
}
}
// Output try-catch blocks
for (final Object o : mn.tryCatchBlocks) {
final TryCatchBlockNode tcb = (TryCatchBlockNode) o;
if (// we allow catch of SuspendExecution only in methods annotated with @Suspendable and in lambda-generated ones.
SUSPEND_EXECUTION_NAME.equals(tcb.type) && !hasAnnotation && !mn.name.startsWith(Classes.LAMBDA_METHOD_PREFIX))
throw new UnableToInstrumentException("catch for SuspendExecution", className, mn.name, mn.desc);
if (// we allow catch of SuspendExecution in method annotated with @Suspendable.
handleProxyInvocations && UNDECLARED_THROWABLE_NAME.equals(tcb.type))
throw new UnableToInstrumentException("catch for UndeclaredThrowableException", className, mn.name, mn.desc);
// if (INTERRUPTED_EXCEPTION_NAME.equals(tcb.type))
// throw new UnableToInstrumentException("catch for " + InterruptedException.class.getSimpleName(), className, mn.name, mn.desc);
tcb.accept(mv);
}
// Output parameter annotations
if (mn.visibleParameterAnnotations != null)
dumpParameterAnnotations(mv, mn.visibleParameterAnnotations, true);
if (mn.invisibleParameterAnnotations != null)
dumpParameterAnnotations(mv, mn.invisibleParameterAnnotations, false);
// Output method annotations
if (mn.visibleAnnotations != null) {
for (Object o : mn.visibleAnnotations) {
AnnotationNode an = (AnnotationNode) o;
an.accept(mv.visitAnnotation(an.desc, true));
}
}
mv.visitTryCatchBlock(lMethodStart, lMethodEnd, lCatchAll, null);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, STACK_NAME, "getStack", "()L" + STACK_NAME + ";", false);
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ASTORE, lvarStack);
// println(mv, "STACK: ", lvarStack);
// dumpStack(mv);
// DUAL
mv.visitJumpInsn(Opcodes.IFNULL, lMethodStart);
mv.visitVarInsn(Opcodes.ALOAD, lvarStack);
// we'll assume we have been resumed
emitStoreResumed(mv, true);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "nextMethodEntry", "()I", false);
mv.visitTableSwitchInsn(1, numCodeBlocks - 1, lMethodStart2, lMethodCalls);
mv.visitLabel(lMethodStart2);
// the following code handles the case of an instrumented method called not as part of a suspendable code path
// isFirstInStack will return false in that case.
mv.visitVarInsn(Opcodes.ALOAD, lvarStack);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "isFirstInStackOrPushed", "()Z", false);
// if true
mv.visitJumpInsn(Opcodes.IFNE, lMethodStart);
// This will reset the fiber stack local if isFirstStack returns false.
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitVarInsn(Opcodes.ASTORE, lvarStack);
mv.visitLabel(lMethodStart);
// no, we have not been resumed
emitStoreResumed(mv, false);
dumpCodeBlock(mv, 0, 0);
// Blocks leading to suspendable calls
for (int i = 1; i < numCodeBlocks; i++) {
final FrameInfo fi = codeBlocks[i];
// Emit instrumented call
final AbstractInsnNode min = mn.instructions.get(fi.endInstruction);
final String owner = getMethodOwner(min), name = getMethodName(min), desc = getMethodDesc(min);
if (isYieldMethod(owner, name)) {
// special case - call to yield
if (min.getOpcode() != Opcodes.INVOKESTATIC)
throw new UnableToInstrumentException("invalid call to suspending method.", className, mn.name, mn.desc);
final int numYieldArgs = TypeAnalyzer.getNumArguments(desc);
final boolean yieldReturnsValue = (Type.getReturnType(desc) != Type.VOID_TYPE);
// we preserve the arguments for the call to yield on the operand stack
emitStoreState(mv, i, fi, numYieldArgs);
// we have not been resumed
emitStoreResumed(mv, false);
// emitSuspendableCalled(mv);
// we call the yield method
min.accept(mv);
if (yieldReturnsValue)
// we ignore the returned value...
mv.visitInsn(Opcodes.POP);
// we resume AFTER the call
mv.visitLabel(lMethodCalls[i - 1]);
final Label afterPostRestore = new Label();
mv.visitVarInsn(Opcodes.ILOAD, lvarResumed);
mv.visitJumpInsn(Opcodes.IFEQ, afterPostRestore);
emitPostRestore(mv);
mv.visitLabel(afterPostRestore);
emitRestoreState(mv, i, fi, numYieldArgs);
if (yieldReturnsValue)
// ... and replace the returned value with the value of resumed
mv.visitVarInsn(Opcodes.ILOAD, lvarResumed);
// See #211: if Fiber.park() is the last call before catch, ASM generates
// empty handlers (start_pc = end_pc) that won't pass ASM's nor JVM's bytecode checker because of
// exception_table's spec here: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.3
mv.visitInsn(Opcodes.NOP);
dumpCodeBlock(mv, i, 1);
} else {
final Label lbl = new Label();
// DUAL
mv.visitVarInsn(Opcodes.ALOAD, lvarStack);
mv.visitJumpInsn(Opcodes.IFNULL, lbl);
// normal case - call to a suspendable method - resume before the call
emitStoreState(mv, i, fi, 0);
// we have not been resumed
emitStoreResumed(mv, false);
// emitPreemptionPoint(mv, PREEMPTION_CALL);
mv.visitLabel(lMethodCalls[i - 1]);
emitRestoreState(mv, i, fi, 0);
// DUAL
mv.visitLabel(lbl);
if (isReflectInvocation(owner, name)) {
// We catch the InvocationTargetException and unwrap it if it wraps a SuspendExecution exception.
Label[] ls = refInvokeTryCatch[i - 1];
final Label startTry = ls[0];
final Label endTry = ls[1];
final Label startCatch = ls[2];
final Label endCatch = new Label();
final Label notSuspendExecution = new Label();
// mv.visitTryCatchBlock(startTry, endTry, startCatch, "java/lang/reflect/InvocationTargetException");
// try {
mv.visitLabel(startTry);
// method.invoke()
min.accept(mv);
// save return value
mv.visitVarInsn(Opcodes.ASTORE, lvarInvocationReturnValue);
// }
mv.visitLabel(endTry);
mv.visitJumpInsn(Opcodes.GOTO, endCatch);
// catch(InvocationTargetException ex) {
mv.visitLabel(startCatch);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "getCause", "()Ljava/lang/Throwable;", false);
mv.visitTypeInsn(Opcodes.INSTANCEOF, SUSPEND_EXECUTION_NAME);
mv.visitJumpInsn(Opcodes.IFEQ, notSuspendExecution);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "getCause", "()Ljava/lang/Throwable;", false);
mv.visitLabel(notSuspendExecution);
mv.visitInsn(Opcodes.ATHROW);
mv.visitLabel(endCatch);
// restore return value
mv.visitVarInsn(Opcodes.ALOAD, lvarInvocationReturnValue);
dumpCodeBlock(mv, i, 1);
} else {
// emitSuspendableCalled(mv);
dumpCodeBlock(mv, i, 0);
}
}
}
// Emit catchall's catch section
mv.visitLabel(lMethodEnd);
if (handleProxyInvocations) {
mv.visitLabel(lCatchUTE);
mv.visitInsn(Opcodes.DUP);
// println(mv, "CTCH: ");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "getCause", "()Ljava/lang/Throwable;", false);
// println(mv, "CAUSE: ");
mv.visitTypeInsn(Opcodes.INSTANCEOF, SUSPEND_EXECUTION_NAME);
mv.visitJumpInsn(Opcodes.IFEQ, lCatchAll);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "getCause", "()Ljava/lang/Throwable;", false);
mv.visitJumpInsn(Opcodes.GOTO, lCatchSEE);
}
mv.visitLabel(lCatchAll);
emitPopMethod(mv);
mv.visitLabel(lCatchSEE);
// println(mv, "THROW: ");
// rethrow shared between catchAll and catchSSE
mv.visitInsn(Opcodes.ATHROW);
if (mn.localVariables != null) {
for (Object o : mn.localVariables) ((LocalVariableNode) o).accept(mv);
}
// Needed by ASM analysis
mv.visitMaxs(mn.maxStack + ADD_OPERANDS, mn.maxLocals + NUM_LOCALS + additionalLocals);
mv.visitEnd();
}
use of org.objectweb.asm.tree.MethodInsnNode in project Minechem by iopleke.
the class MinechemTransformer method findInstructionNode.
private static AbstractInsnNode findInstructionNode(InstructionNode instructionNode, MethodNode methodNode) {
boolean close = false;
AbstractInsnNode result = null;
for (Iterator<AbstractInsnNode> itr = methodNode.instructions.iterator(); itr.hasNext(); ) {
AbstractInsnNode node = itr.next();
if (node instanceof MethodInsnNode) {
if (close) {
if (((MethodInsnNode) node).name.equals(instructionNode.getBefore())) {
return instructionNode.replace ? node : result;
} else {
close = false;
}
}
if (((MethodInsnNode) node).name.equals(instructionNode.getAfter())) {
close = true;
result = node;
}
}
}
return result == null ? methodNode.instructions.getLast().getPrevious() : result;
}
use of org.objectweb.asm.tree.MethodInsnNode in project flink by apache.
the class NestedMethodAnalyzer method naryOperation.
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public BasicValue naryOperation(AbstractInsnNode insn, List rawValues) throws AnalyzerException {
final List<BasicValue> values = (List<BasicValue>) rawValues;
boolean isStatic = false;
switch(insn.getOpcode()) {
case INVOKESTATIC:
isStatic = true;
case INVOKESPECIAL:
case INVOKEVIRTUAL:
case INVOKEINTERFACE:
final MethodInsnNode method = (MethodInsnNode) insn;
String methodOwner = method.owner;
// that contains the actual implementation to determine the tuple size
if (method.name.equals("getField") || method.name.equals("setField")) {
try {
final String newMethodOwner = (String) findMethodNode(methodOwner, method.name, method.desc)[1];
if (newMethodOwner.startsWith("org/apache/flink/api/java/tuple/Tuple")) {
methodOwner = newMethodOwner;
}
} catch (IllegalStateException e) {
// proceed with the known method owner
}
}
// special case: collect method of Collector
if (method.name.equals("collect") && methodOwner.equals("org/apache/flink/util/Collector") && isTagged(values.get(0)) && tagged(values.get(0)).isCollector()) {
// check for invalid return value
if (isTagged(values.get(1)) && tagged(values.get(1)).isNull()) {
analyzer.handleNullReturn();
} else // valid return value with input dependencies
if (hasImportantDependencies(values.get(1))) {
// add a copy and a reference
// to capture the current state and future changes in alternative paths
analyzer.getCollectorValues().add(tagged(values.get(1)));
analyzer.getCollectorValues().add(tagged(values.get(1)).copy());
} else // valid return value without input dependencies
{
analyzer.getCollectorValues().add(null);
}
} else // special case: iterator method of Iterable
if (method.name.equals("iterator") && methodOwner.equals("java/lang/Iterable") && isTagged(values.get(0)) && tagged(values.get(0)).isInputIterable()) {
return new TaggedValue(Type.getObjectType("java/util/Iterator"), (tagged(values.get(0)).isInput1Iterable()) ? Tag.INPUT_1_ITERATOR : Tag.INPUT_2_ITERATOR);
} else // special case: hasNext method of Iterator
if (method.name.equals("hasNext") && methodOwner.equals("java/util/Iterator") && isTagged(values.get(0)) && tagged(values.get(0)).isInputIterator() && !analyzer.isUdfBinary() && !analyzer.isIteratorTrueAssumptionApplied()) {
return new TaggedValue(Type.BOOLEAN_TYPE, Tag.ITERATOR_TRUE_ASSUMPTION);
} else // special case: next method of Iterator
if (method.name.equals("next") && methodOwner.equals("java/util/Iterator") && isTagged(values.get(0)) && tagged(values.get(0)).isInputIterator()) {
// after this call it is not possible to assume "TRUE" of "hasNext()" again
analyzer.applyIteratorTrueAssumption();
if (tagged(values.get(0)).isInput1Iterator()) {
return analyzer.getInput1AsTaggedValue();
} else {
return analyzer.getInput2AsTaggedValue();
}
} else // special case: do not follow the getRuntimeContext method of RichFunctions
if (!isStatic && isTagged(values.get(0)) && tagged(values.get(0)).isThis() && hasImportantDependencies(values.get(0)) && !isGetRuntimeContext(method)) {
TaggedValue tv = invokeNestedMethod(values, method);
if (tv != null) {
return tv;
}
} else // we can assume that method has at least one argument
if ((!isStatic && isTagged(values.get(0)) && tagged(values.get(0)).isThis() && hasImportantDependencies(values, true)) || (!isStatic && (!isTagged(values.get(0)) || !tagged(values.get(0)).isThis()) && hasImportantDependencies(values, false)) || (isStatic && hasImportantDependencies(values, false))) {
// special case: Java unboxing/boxing methods on input
Type newType;
if (isTagged(values.get(0)) && tagged(values.get(0)).isInput() && (!isStatic && (newType = checkForUnboxing(method.name, methodOwner)) != null || (isStatic && (newType = checkForBoxing(method.name, method.desc, methodOwner)) != null))) {
return tagged(values.get(0)).copy(newType);
} else // special case: setField method of TupleXX
if (method.name.equals("setField") && methodOwner.startsWith("org/apache/flink/api/java/tuple/Tuple") && isTagged(values.get(0))) {
final TaggedValue tuple = tagged(values.get(0));
tuple.setTag(Tag.CONTAINER);
// if not, we can not determine a state for the tuple
if (!isTagged(values.get(2)) || !tagged(values.get(2)).isIntConstant()) {
tuple.makeRegular();
} else {
final int constant = tagged(values.get(2)).getIntConstant();
if (constant < 0 || Integer.parseInt(methodOwner.split("Tuple")[1]) <= constant) {
analyzer.handleInvalidTupleAccess();
}
// if it is at least tagged, add it anyways
if (isTagged(values.get(1))) {
tuple.addContainerMapping("f" + constant, tagged(values.get(1)), currentFrame);
} else // mark the field as it has an undefined state
{
tuple.addContainerMapping("f" + constant, null, currentFrame);
}
}
} else // special case: getField method of TupleXX
if (method.name.equals("getField") && methodOwner.startsWith("org/apache/flink/api/java/tuple/Tuple")) {
// we can assume that 0 is an input dependent tuple
final TaggedValue tuple = tagged(values.get(0));
// constant field index
if (// constant
isTagged(values.get(1)) && tagged(values.get(1)).isIntConstant()) {
final int constant = tagged(values.get(1)).getIntConstant();
if (constant < 0 || Integer.valueOf(methodOwner.split("Tuple")[1]) <= constant) {
analyzer.handleInvalidTupleAccess();
}
if (tuple.containerContains("f" + constant)) {
final TaggedValue tupleField = tuple.getContainerMapping().get("f" + constant);
if (tupleField != null) {
return tupleField;
}
}
} else // unknown field index
{
// we need to make the tuple regular as we cannot track modifications of fields
tuple.makeRegular();
return new TaggedValue(Type.getObjectType("java/lang/Object"));
}
} else // nested method invocation
{
TaggedValue tv = invokeNestedMethod(values, method);
if (tv != null) {
return tv;
}
}
}
return super.naryOperation(insn, values);
default:
// TODO support for INVOKEDYNAMIC instructions
return super.naryOperation(insn, values);
}
}
use of org.objectweb.asm.tree.MethodInsnNode in project MinecraftForge by MinecraftForge.
the class ItemStackTransformer method transform.
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (!"net.minecraft.item.ItemStack".equals(name))
return basicClass;
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(basicClass);
classReader.accept(classNode, 0);
FieldNode itemField = null;
for (FieldNode f : classNode.fields) {
if (ITEM_TYPE.equals(f.desc) && itemField == null) {
itemField = f;
} else if (ITEM_TYPE.equals(f.desc)) {
throw new RuntimeException("Error processing ItemStack - found a duplicate Item field");
}
}
if (itemField == null) {
throw new RuntimeException("Error processing ItemStack - no Item field declared (is the code somehow obfuscated?)");
}
MethodNode getItemMethod = null;
for (MethodNode m : classNode.methods) {
if (m.name.equals("getItemRaw"))
continue;
if (GETITEM_DESC.equals(m.desc) && getItemMethod == null) {
getItemMethod = m;
} else if (GETITEM_DESC.equals(m.desc)) {
throw new RuntimeException("Error processing ItemStack - duplicate getItem method found");
}
}
if (getItemMethod == null) {
throw new RuntimeException("Error processing ItemStack - no getItem method found (is the code somehow obfuscated?)");
}
for (MethodNode m : classNode.methods) {
if (m.name.equals("getItemRaw"))
continue;
for (ListIterator<AbstractInsnNode> it = m.instructions.iterator(); it.hasNext(); ) {
AbstractInsnNode insnNode = it.next();
if (insnNode.getType() == AbstractInsnNode.FIELD_INSN) {
FieldInsnNode fi = (FieldInsnNode) insnNode;
if (itemField.name.equals(fi.name) && fi.getOpcode() == Opcodes.GETFIELD) {
it.remove();
MethodInsnNode replace = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "net/minecraft/item/ItemStack", getItemMethod.name, getItemMethod.desc, false);
it.add(replace);
}
}
}
}
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classNode.accept(writer);
return writer.toByteArray();
}
Aggregations