下面列出了怎么用org.objectweb.asm.tree.MethodInsnNode的API类实例代码及写法,或者点击链接到github查看源代码。
private boolean applyBrandingPatch(ClassNode classNode) {
boolean applied = false;
for (MethodNode node : classNode.methods) {
if (node.name.equals("getClientModName") || node.name.equals("getServerModName") && node.desc.endsWith(")Ljava/lang/String;")) {
debug("Applying brand name hook to " + classNode.name + "::" + node.name);
ListIterator<AbstractInsnNode> it = node.instructions.iterator();
while (it.hasNext()) {
if (it.next().getOpcode() == Opcodes.ARETURN) {
it.previous();
it.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "net/fabricmc/loader/entrypoint/minecraft/hooks/EntrypointBranding", "brand", "(Ljava/lang/String;)Ljava/lang/String;", false));
it.next();
}
}
applied = true;
}
}
return applied;
}
/**
* Generates instructions for generating marker instructions. These marker instructions are meant to be is useful for debugging
* instrumented code. For example, you can spot a specific portion of instrumented code by looking for specific markers in the assembly
* output.
* @param markerType marker type (determines what kind of instructions are generated)
* @param text text to print out
* @return instructions to call System.out.println with a string constant
* @throws NullPointerException if any argument is {@code null}
*/
public static InsnList debugMarker(MarkerType markerType, String text) {
Validate.notNull(markerType);
Validate.notNull(text);
InsnList ret = new InsnList();
switch (markerType) {
case NONE:
break;
case CONSTANT:
ret.add(new LdcInsnNode(text));
ret.add(new InsnNode(Opcodes.POP));
break;
case STDOUT:
ret.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
ret.add(new LdcInsnNode(text));
ret.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false));
break;
default:
throw new IllegalStateException();
}
return ret;
}
public void populateClassGraph() {
String className = Type.getObjectType(classNode.name).getClassName();
logger.debug("Creating graph for class {}" , className);
for (MethodNode methodNode : classNode.methods) {
String methodName = methodNode.name;
MethodGraph caller = new MethodGraph(className, methodName);
InsnList instructions = methodNode.instructions;
for (int i = 0; i < instructions.size(); i++) {
AbstractInsnNode insnNode = instructions.get(i);
if (insnNode.getType() == AbstractInsnNode.METHOD_INSN) {
MethodInsnNode methodInsnNode = (MethodInsnNode)insnNode;
String calledOwner = Type.getObjectType(methodInsnNode.owner).getClassName();
String calledName = methodInsnNode.name;
MethodGraph called = new MethodGraph(calledOwner, calledName);
Call call = new Call(caller, called);
if (!called.getOwner().equals("java.lang.Object") && !called.getName().equals("<init>")) {
logger.trace("Adding call graph: {}", call.toString());
GraphHolder.addCallGraph(call);
}
}
}
}
}
public static void processMethodHandleInvoke(ClassNode classNode, String newMethodDesc, MethodInsnNode invoke) {
String newMethodName = String.format("methodhandle$%s$%s", invoke.name, String.valueOf(invoke.desc.hashCode()).replace("-", ""));
MethodNode invokeWrapper = new MethodNode(Opcodes.ASM7,
Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC,
newMethodName, newMethodDesc, null, new String[0]);
int localVarsPosition = 0;
for (Type arg : Type.getArgumentTypes(newMethodDesc)) {
invokeWrapper.instructions.add(new VarInsnNode(arg.getOpcode(Opcodes.ILOAD), localVarsPosition));
localVarsPosition += arg.getSize();
}
invokeWrapper.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, invoke.owner, invoke.name, invoke.desc, false));
invokeWrapper.instructions.add(new InsnNode(Type.getReturnType(newMethodDesc).getOpcode(Opcodes.IRETURN)));
classNode.methods.add(invokeWrapper);
}
@Override
public void postProcess(MethodContext context) {
context.method.instructions.clear();
if (Util.getFlag(context.clazz.access, Opcodes.ACC_INTERFACE)) {
InsnList list = new InsnList();
for (int i = 0; i <= context.invokeSpecialId; i++) {
list.add(context.method.instructions.get(i));
}
int localVarsPosition = 0;
for (Type arg : context.argTypes) {
list.add(new VarInsnNode(arg.getOpcode(Opcodes.ILOAD), localVarsPosition));
localVarsPosition += arg.getSize();
}
if (context.nativeMethod == null) {
throw new RuntimeException("Native method not created?!");
}
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
context.obfuscator.getStaticClassProvider().getCurrentClassName(),
context.nativeMethod.name, context.nativeMethod.desc, false));
list.add(new InsnNode(Type.getReturnType(context.method.desc).getOpcode(Opcodes.IRETURN)));
context.method.instructions = list;
}
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
super.visitMethodInsn(opcode, owner, name, desc);
// The only reason for adding to pausableMethods is to create a BB for pausable
// method call sites. If the class is already woven, we don't need this
// functionality.
if (!classFlow.isWoven) {
boolean invokedCallIsStackPausable = detector.invokedCallIsStackPausable(owner, name, desc);
//if (methodStatus == Detector.PAUSABLE_METHOD_FOUND) {
if (invokedCallIsStackPausable) {
MethodInsnNode min = (MethodInsnNode)instructions.get(instructions.size()-1);
pausableMethods.add(min);
}
if(!classToNestingLevels.containsKey(owner)){
classToNestingLevels.put(owner, detector.getNestingLevels(owner));
}
}
}
/**
* The total number consumed by the call, including its object reference
*/
int getNumArgs() {
if (numArgs == -1) {
MethodInsnNode m = getMethodInsn();
String desc = m.desc;
//String desc0 = this.bb.flow.desc;
int natype = TypeDesc.getNumArgumentTypes(desc);
int frStatic = (isStaticCall() ? 0 : 1);
//int fibfa = (!fiberFromArguments?-1:0);
//System.err.println(String.format("natype: %s, frStatic: %s desc: %s->%s", natype , frStatic, m.name, desc));//desc has been ammended
numArgs = natype + frStatic ;//+ fibfa;//add extra arg if fiber must be obtained from stack
}
return numArgs;
}
@Test
public void methodOfNioBufferWithCovariantTypes_beforeDesugar(
@AsmNode(className = "NioBufferInvocations", memberName = "getByteBufferPosition", round = 0)
MethodNode before) {
ImmutableList<AbstractInsnNode> methodInvocations =
Arrays.stream(before.instructions.toArray())
.filter(insnNode -> insnNode.getType() == METHOD_INSN)
.collect(toImmutableList());
assertThat(methodInvocations).hasSize(1);
MethodInsnNode methodInsnNode = (MethodInsnNode) Iterables.getOnlyElement(methodInvocations);
assertThat(methodInsnNode.owner).isEqualTo("java/nio/ByteBuffer");
assertThat(methodInsnNode.name).isEqualTo("position");
assertThat(methodInsnNode.desc).isEqualTo("(I)Ljava/nio/ByteBuffer;");
assertThat(methodInsnNode.getNext().getOpcode()).isEqualTo(Opcodes.ARETURN);
}
/**
* API that returns the list of parameter types
* @param min
* @return
*/
public static List<String> getParamType(MethodInsnNode min) {
List<String> paramTypeList = new LinkedList<String>();
String desc = min.desc;
desc = desc.substring(1, desc.indexOf(')'));
String[] type = desc.split(";");
for (String t : type) {
// Removing the L that starts the class name and then adding type in
// a list
paramTypeList.add(t.substring(1));
}
return paramTypeList;
}
@SuppressWarnings("unchecked") // ASM API
public static void checkSetOnTouchListenerCall(
@NonNull ClassContext context,
@NonNull MethodNode method,
@NonNull MethodInsnNode call) {
String owner = call.owner;
// Ignore the call if it was called on a non-view.
ClassNode ownerClass = context.getDriver().findClass(context, owner, 0);
if(ownerClass == null
|| !context.getDriver().isSubclassOf(ownerClass, ANDROID_VIEW_VIEW)) {
return;
}
MethodNode performClick = findMethod(ownerClass.methods, PERFORM_CLICK, PERFORM_CLICK_SIG);
//noinspection VariableNotUsedInsideIf
if (performClick == null) {
String message = String.format(
"Custom view `%1$s` has `setOnTouchListener` called on it but does not "
+ "override `performClick`", ownerClass.name);
context.report(ISSUE, method, call, context.getLocation(call), message);
}
}
private static void checkValidSetSeed(ClassContext context, MethodInsnNode call) {
assert call.name.equals(SET_SEED);
// Make sure the argument passed is not a literal
AbstractInsnNode prev = LintUtils.getPrevInstruction(call);
if (prev == null) {
return;
}
int opcode = prev.getOpcode();
if (opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 || opcode == Opcodes.LDC) {
context.report(ISSUE, context.getLocation(call),
"Do not call `setSeed()` on a `SecureRandom` with a fixed seed: " +
"it is not secure. Use `getSeed()`.");
} else if (opcode == Opcodes.INVOKESTATIC) {
String methodName = ((MethodInsnNode) prev).name;
if (methodName.equals("currentTimeMillis") || methodName.equals("nanoTime")) {
context.report(ISSUE, context.getLocation(call),
"It is dangerous to seed `SecureRandom` with the current time because " +
"that value is more predictable to an attacker than the default seed.");
}
}
}
Constructor(@NonNull String owner,
@NonNull List<AbstractInsnNode> prelude,
@NonNull VarInsnNode loadThis,
int lineForLoad,
@NonNull MethodNode args,
@NonNull MethodInsnNode delegation,
@NonNull MethodNode body,
@NonNull List<LocalVariable> variables,
int localsAtLoadThis) {
this.owner = owner;
this.prelude = prelude;
this.loadThis = loadThis;
this.lineForLoad = lineForLoad;
this.args = args;
this.delegation = delegation;
this.body = body;
this.variables = variables;
this.localsAtLoadThis = localsAtLoadThis;
}
/**
* <p>
* This method provides instruction to log method entry/exit information
* </p>
*
* @param className
* Class which has called the logger
* @param methodName
* Method in which the logger has been called
* @param logMsg
* log message
* @return InsnList Instructions
*/
public static InsnList addLogMessage(Object... objects) {
String method = "addLogMsg";
InsnList il = getBasicInstructions(objects);
Type[] types = new Type[objects.length];
for (int i = 0; i < objects.length; i++) {
types[i] = TYPE_OBJECT;
}
String methodDesc = Type.getMethodDescriptor(Type.VOID_TYPE, types);
il.add(new MethodInsnNode(Opcodes.INVOKESTATIC, CLASSNAME_LOGUTIL,
method, methodDesc));
return il;
}
@Test
public void test_getWrapperMethod() {
AbstractInsnNode wrapperMethod = NodeUtils.getWrapperMethod(Type.INT_TYPE);
if (wrapperMethod instanceof MethodInsnNode) {
MethodInsnNode method = (MethodInsnNode) wrapperMethod;
assertEquals("valueOf", method.name);
assertEquals(method.desc, "(I)Ljava/lang/Integer;");
assertEquals(method.owner, "java/lang/Integer");
} else {
fail();
}
assertEquals(NodeUtils.getWrapperMethod(Type.VOID_TYPE).getOpcode(), Opcodes.NOP);
assertEquals(NodeUtils.getWrapperMethod(Type.getType("Ljava/lang/Object;")).getOpcode(), Opcodes.NOP);
}
private InsnList getThrowTraceInstructions() {
InsnList il = new InsnList();
int exceptionVariablePosition = getFistAvailablePosition();
il.add(new VarInsnNode(Opcodes.ASTORE, exceptionVariablePosition));
this.methodOffset++; // Actualizamos el offset
addGetCallback(il);
il.add(new VarInsnNode(Opcodes.ALOAD, this.methodVarIndex));
il.add(new VarInsnNode(Opcodes.ALOAD, exceptionVariablePosition));
il.add(new VarInsnNode(Opcodes.ALOAD, this.executionIdIndex));
il.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
"org/brutusin/instrumentation/Callback", "onThrowableThrown",
"(Ljava/lang/Object;Ljava/lang/Throwable;Ljava/lang/String;)V", false));
il.add(new VarInsnNode(Opcodes.ALOAD, exceptionVariablePosition));
return il;
}
@Override
public void transform(ClassNode cnode) {
Game.logger().info("Transforming IForgeRegistryEntry class for correct NOVA mod id mapping.");
ObfMapping mapping = new ObfMapping("net/minecraftforge/fml/common/registry/IForgeRegistryEntry$Impl", "setRegistryName", "(Ljava/lang/String;)Lnet/minecraftforge/fml/common/registry/IForgeRegistryEntry;");
MethodNode method = ASMHelper.findMethod(mapping, cnode);
if (method == null) {
throw new IllegalStateException("[NOVA] Lookup " + mapping + " failed!");
}
Game.logger().info("Transforming method {}", method.name);
InsnList list = new InsnList();
list.add(new VarInsnNode(ALOAD, 5));
list.add(new MethodInsnNode(INVOKESTATIC, "nova/core/wrapper/mc/forge/v1_11_2/asm/StaticForwarder", "isNovaPrefix", "(Ljava/lang/String;)Z", false));
list.add(new JumpInsnNode(IFNE, (LabelNode) method.instructions.get(120)));
method.instructions.insert(method.instructions.get(101), list);
Game.logger().info("Injected instruction to method: {}", method.name);
}
/**
* Invokes the method through reflection. Other methods and fields are removed to prevent accidental execution.
*
* @param owner
* Decryption classnode
* @param min
* Decryption method
* @param args
* Decryption method args
* @return
*/
public static Object getIsolatedReturn(ClassNode owner, MethodInsnNode min, Object[] args) {
if (owner == null) {
return null;
}
ClassNode isolated = new ClassNode();
isolated.version = 52;
isolated.name = owner.name;
isolated.superName = "java/lang/Object";
int i = 0;
for (MethodNode mn : owner.methods) {
if (mn.name.equals(min.name) && mn.desc.equals(min.desc)) {
isolated.methods.add(owner.methods.get(i));
}
i++;
}
return get(isolated, min.name, min.desc, args);
}
private static AbstractInsnNode getInitializationInstructionsSet(AbstractInsnNode node) {
AbstractInsnNode paramStartNode = null;
AbstractInsnNode traversalInsnNode = node;
if (node instanceof MethodInsnNode) {
MethodInsnNode min = (MethodInsnNode) node;
String initializedObjectType = min.owner;
traversalInsnNode = node.getPrevious();
while (!isInitializationInstructionStartReached(traversalInsnNode, initializedObjectType)) {
traversalInsnNode = traversalInsnNode.getPrevious();
}
paramStartNode = traversalInsnNode;
}
return paramStartNode;
}
public InsnValue onMethod(AbstractInsnNode insn, List<InsnValue> values) {
String desc = "V";
if (insn.getOpcode() == Opcodes.INVOKEDYNAMIC){
desc = ((InvokeDynamicInsnNode) insn).desc;
}else{
desc = ((MethodInsnNode) insn).desc;
}
// Until I'm ready to simulate method calls the opcode here really
// doesn't matter.
/*
* switch (insn.getOpcode()) { case Opcodes.INVOKEDYNAMIC: case
* Opcodes.INVOKESPECIAL: case Opcodes.INVOKEINTERFACE: case
* Opcodes.INVOKESTATIC: case Opcodes.INVOKEVIRTUAL: }
*/
if (desc.endsWith("V")) {
return null;
}
return new InsnValue(Type.getReturnType(desc));
}
private AbstractInsnNode getNumberPush(MethodInsnNode min, int var) {
// TODO find out by invoking if it can't be identified by patterns in future versions
AbstractInsnNode ain = min;
while (ain != null) {
if (ain instanceof VarInsnNode) {
if (((VarInsnNode) ain).var == var) {
int nextOp = ain.getNext().getOpcode();
// jump should never happen
if (nextOp == IFEQ) {
return new InsnNode(ICONST_1);
} else if (nextOp == IFNE) {
return new InsnNode(ICONST_0);
}
}
}
ain = ain.getNext();
}
return null;
}
private ArrayList<String> findNeededContents(ClassNode cn, MethodNode mn) {
ArrayList<String> neededContents = new ArrayList<>();
for (AbstractInsnNode ain : mn.instructions.toArray()) {
if (ain instanceof MethodInsnNode) {
MethodInsnNode min = (MethodInsnNode) ain;
if (min.owner.equals(cn.name) && !neededContents.contains(min.name + min.desc)) {
neededContents.add(min.name + min.desc);
neededContents.addAll(findNeededContents(cn, ClassUtils.getMethod(cn, min.name, min.desc)));
}
}
if (ain instanceof FieldInsnNode) {
FieldInsnNode fin = (FieldInsnNode) ain;
if (fin.owner.equals(cn.name) && !neededContents.contains(fin.name + fin.desc)) {
neededContents.add(fin.name + fin.desc);
}
}
}
return neededContents;
}
private void findBelongingClasses(ArrayList<MethodNode> scanned, ArrayList<ClassNode> decryptionClasses,
JarArchive ja, ClassNode cn, ClassNode proxy, MethodNode node) {
if (scanned.contains(node)) {
return;
}
scanned.add(node);
for (AbstractInsnNode ain : node.instructions.toArray()) {
if (ain instanceof MethodInsnNode) {
MethodInsnNode min = (MethodInsnNode) ain;
if (!min.owner.startsWith("java/") && !min.owner.startsWith("javax/")) {
ClassNode decryptionClass = ja.getClasses().get(min.owner);
if (decryptionClass != null && !decryptionClasses.contains(decryptionClass)) {
decryptionClasses.add(decryptionClass);
for (MethodNode mn : decryptionClass.methods) {
findBelongingClasses(scanned, decryptionClasses, ja, decryptionClass, proxy, mn);
}
}
}
}
}
}
public static void invokestatic(Frame frame) {
MethodInsnNode methodInsnNode = frame.methodInsnNode();
String className = methodInsnNode.owner;
String methodName = methodInsnNode.name;
String methodDesc = methodInsnNode.desc;
MethodCode methodCode = frame.methodArea.loadMethod(className, methodName, methodDesc);
MethodArgs methodArgs = new MethodArgs(methodCode.argsVariableType, frame.operandStack, true);
//Log.opcode(frame.getCurrentOpCode(), className, methodName, methodDesc);
Result result = NativeMethod.run(methodCode, methodArgs, frame);
if (result != null) {
return;
}
frame.vm.run(methodCode, methodArgs.frameArgs, true);
}
@Test
public void methodOfNioBufferWithCovariantTypes_afterDesugar(
@AsmNode(className = "NioBufferInvocations", memberName = "getByteBufferPosition", round = 1)
MethodNode after) {
ImmutableList<AbstractInsnNode> methodInvocations =
Arrays.stream(after.instructions.toArray())
.filter(insnNode -> insnNode.getType() == METHOD_INSN)
.collect(toImmutableList());
assertThat(methodInvocations).hasSize(1);
MethodInsnNode methodInsnNode = (MethodInsnNode) Iterables.getOnlyElement(methodInvocations);
assertThat(methodInsnNode.owner).isEqualTo("java/nio/ByteBuffer");
assertThat(methodInsnNode.name).isEqualTo("position");
assertThat(methodInsnNode.desc).isEqualTo("(I)Ljava/nio/Buffer;");
TypeInsnNode typeInsnNode = (TypeInsnNode) methodInsnNode.getNext();
assertThat(typeInsnNode.getOpcode()).isEqualTo(Opcodes.CHECKCAST);
assertThat(typeInsnNode.desc).isEqualTo("java/nio/ByteBuffer");
assertThat(typeInsnNode.getNext().getOpcode()).isEqualTo(Opcodes.ARETURN);
}
@Override
public ConstValue naryOperation(AbstractInsnNode insn, List<? extends ConstValue> values) {
int opcode = insn.getOpcode();
if (opcode == MULTIANEWARRAY) {
return ConstValue.ONE_SLOT;
}
var desc = (opcode == INVOKEDYNAMIC) ? ((InvokeDynamicInsnNode) insn).desc
: ((MethodInsnNode) insn).desc;
return ConstValue.slotForSize(Type.getReturnType(desc).getSize());
}
private static boolean isSkippedEnumSwitch(ClassContext context, ClassNode classNode,
MethodNode method, FieldInsnNode node, String owner, int api) {
// Enum-style switches are handled in a different way: it generates
// an innerclass where the class initializer creates a mapping from
// the ordinals to the corresponding values.
// Here we need to check to see if the call site which *used* the
// table switch had a suppress node on it (or up that node's parent
// chain
AbstractInsnNode next = getNextInstruction(node);
if (next != null && next.getOpcode() == Opcodes.INVOKEVIRTUAL
&& CLASS_CONSTRUCTOR.equals(method.name)
&& ORDINAL_METHOD.equals(((MethodInsnNode) next).name)
&& classNode.outerClass != null
&& isEnumSwitchInitializer(classNode)) {
LintDriver driver = context.getDriver();
ClassNode outer = driver.getOuterClassNode(classNode);
if (outer != null) {
MethodNode switchUser = findEnumSwitchUsage(outer, owner);
if (switchUser != null) {
// Is the API check suppressed at the call site?
if (driver.isSuppressed(UNSUPPORTED, outer, switchUser,
null)) {
return true;
}
// Is there a @TargetAPI annotation on the method or
// class referencing this switch map class?
if (getLocalMinSdk(switchUser.invisibleAnnotations) >= api
|| getLocalMinSdk(outer.invisibleAnnotations) >= api) {
return true;
}
}
}
}
return false;
}
/**
* <p>
* This method provides instructions to add Pattern.compile() call for regex
* values
* </p>
*
* @return Instructions
*/
private InsnList addPatternCompiler() {
JobConfig jobConfig = (JobConfig)getConfig();
String keyRegex = jobConfig.getMapReduceKeyRegex(getClassName());
String valueRegex = jobConfig.getMapReduceValueRegex(getClassName());
InsnList il = new InsnList();
LOGGER.debug(MessageFormat.format(InstrumentationMessageLoader
.getMessage(MessageConstants.LOG_ADD_PATTERN_COMPILE),
getClassName()));
// regex values
String[] regexes = new String[] { keyRegex, valueRegex };
// class fields
String[] filedNames = new String[] { KEY_PATTERN, VALUE_PATTERN };
for (int i = 0; i < regexes.length; i++) {
il.add(new LabelNode());
il.add(new VarInsnNode(Opcodes.ALOAD, 0));
// if regex is not null
if (regexes[i] != null) {
il.add(new LdcInsnNode(regexes[i]));
il.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
CLASSNAME_PATTERN, "compile", Type.getMethodDescriptor(
Type.getType(Pattern.class), TYPE_STRING)));
il.add(new FieldInsnNode(
Opcodes.PUTFIELD,
ConfigurationUtil.convertQualifiedClassNameToInternalName(getClassName()),
filedNames[i], DESCRIPTOR_PATTERN));
}
}
return il;
}
public static AbstractInsnNode continueLoop() {
return new MethodInsnNode(
INVOKESTATIC,
Type.getInternalName(Dispatch.class),
"signed_le",
Type.getMethodDescriptor(
Type.BOOLEAN_TYPE,
Type.getType(Number.class),
Type.getType(Number.class),
Type.getType(Number.class)),
false);
}
@MethodPatch(
mcpName = "handleChunkData",
notchName = "a",
mcpDesc = "(Lnet/minecraft/network/play/server/SPacketChunkData;)V",
notchDesc = "(Lje;)V")
public void handleChunkData(MethodNode methodNode, PatchManager.Environment env) {
final InsnList insnList = new InsnList();
insnList.add(new VarInsnNode(ALOAD, 1));
insnList.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(this.getClass()), "handleChunkDataHook", env == PatchManager.Environment.IDE ? "(Lnet/minecraft/network/play/server/SPacketChunkData;)V" : "(Lje;)V", false));
methodNode.instructions.insertBefore(ASMUtil.bottom(methodNode), insnList);
}
@Override
public BasicValue naryOperation(final AbstractInsnNode insn, final List<? extends BasicValue> values) throws AnalyzerException {
int opcode = insn.getOpcode();
if (opcode == MULTIANEWARRAY) {
return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc));
} else if (opcode == INVOKEDYNAMIC) {
return newValue(Type.getReturnType(((InvokeDynamicInsnNode) insn).desc));
} else {
return newValue(Type.getReturnType(((MethodInsnNode) insn).desc));
}
}