diff options
Diffstat (limited to 'src/main/java/ftbsc/lll/utils')
8 files changed, 762 insertions, 0 deletions
diff --git a/src/main/java/ftbsc/lll/utils/DescriptorBuilder.java b/src/main/java/ftbsc/lll/utils/DescriptorBuilder.java new file mode 100644 index 0000000..541497b --- /dev/null +++ b/src/main/java/ftbsc/lll/utils/DescriptorBuilder.java @@ -0,0 +1,141 @@ +package ftbsc.lll.utils; + +import org.objectweb.asm.Type; + +import java.util.ArrayList; + +/** + * Builds a method descriptor for you. + * See the <a href="https://asm.ow2.io/asm4-guide.pdf">documentation</a> to better understand what this is. + * Parameters must be given in a specific order. + * Return type should always be specified for clarity, but defaults to void. + */ +public class DescriptorBuilder { + /** + * The descriptor of the return type. + */ + private String returnType; + + /** + * The descriptors of the parameters. + */ + private final ArrayList<String> params; + + /** + * Public constructor. + * Initialises default values. + */ + public DescriptorBuilder() { + this.returnType = Type.getDescriptor(void.class); + this.params = new ArrayList<>(); + } + + /** + * Sets the return type to the given type. + * Passing a {@link Class} may cause problems if used with objects outside the Java + * SDK. Pass the fully qualified name as a {@link String} rather than the {@link Class} + * object for non-standard types. + * @param returnType the Class object corresponding to the return type + * @return the builder's state after the change + */ + public DescriptorBuilder setReturnType(Class<?> returnType) { + this.returnType = Type.getDescriptor(returnType); + return this; + } + + /** + * Sets the return type to the Object specified here as a fully + * qualified name. Example: java.lang.String. + * No validity checks are performed: it's up to the user to ensure the name is correct. + * @param returnType the fully qualified name of the desired Object. + * @return the builder's state after the change + */ + public DescriptorBuilder setReturnType(String returnType) { + return this.setReturnType(returnType, 0); + } + + /** + * Sets the return type to the Object specified here as a fully + * qualified name (example: java.lang.String), with the specified array level. + * No validity checks are performed: it's up to the user to ensure the name is correct. + * @param returnType the fully qualified name of the desired Object. + * @param arrayLevel how many levels of array are there + * (example: String is 0, String[] is 1, String[][] is 2, etc.) + * @return the builder's state after the change + */ + public DescriptorBuilder setReturnType(String returnType, int arrayLevel) { + this.returnType = nameToDescriptor(returnType, arrayLevel); + return this; + } + + /** + * Adds a parameter of the given class type to the method. + * Parameter order matters. + * Passing a {@link Class} may cause problems if used with objects outside the Java + * SDK. Pass the fully qualified name as a {@link String} rather than the {@link Class} + * object for non-standard types. + * @param param the Class object corresponding to the parameter + * @return the builder's state after the change + */ + public DescriptorBuilder addParameter(Class<?> param) { + this.params.add(Type.getDescriptor(param)); + return this; + } + + /** + * Adds a parameter with the type specified by the given fully + * qualified name to the method. Example: java.lang.String. + * Parameter order matters. + * No validity checks are performed: it's up to the user to ensure the name is correct. + * @param param the fully qualified name of the parameter type + * @return the builder's state after the change + */ + public DescriptorBuilder addParameter(String param) { + return this.addParameter(param, 0); + } + + /** + * Adds a parameter with the type specified by the given fully + * qualified name (example: java.lang.String) to the method, with + * the specified array level. + * Parameter order matters. + * No validity checks are performed: it's up to the user to ensure the name is correct. + * @param param the fully qualified name of the parameter type + * @param arrayLevel how many levels of array are there + * (example: String is 0, String[] is 1, String[][] is 2, etc.) + * @return the builder's state after the change + */ + public DescriptorBuilder addParameter(String param, int arrayLevel) { + this.params.add(nameToDescriptor(param, arrayLevel)); + return this; + } + + /** + * Builds the descriptor into a string. + * Example result: {@code int m(Object[] o)} becomes {@code ([Ljava/lang/Object;)I} + * @return the resulting descriptor + */ + public String build() { + StringBuilder sb = new StringBuilder(); + sb.append('('); + for(String p : params) + sb.append(p); + sb.append(')').append(returnType); + return sb.toString(); + } + + /** + * Converts a fully qualified name and array level to a descriptor. + * @param name the fully qualified name of the object type + * @param arrayLevel how many levels of array are there + * (example: String is 0, String[] is 1, String[][] is 2, etc.) + * @return object descriptor + */ + public static String nameToDescriptor(String name, int arrayLevel) { + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < arrayLevel; i++) + sb.append('['); + sb.append('L').append(name.replace('.', '/')).append(';'); + return sb.toString(); + } +} diff --git a/src/main/java/ftbsc/lll/utils/InsnSequence.java b/src/main/java/ftbsc/lll/utils/InsnSequence.java new file mode 100644 index 0000000..4734505 --- /dev/null +++ b/src/main/java/ftbsc/lll/utils/InsnSequence.java @@ -0,0 +1,138 @@ +package ftbsc.lll.utils; + +import ftbsc.lll.exceptions.InstructionMismatchException; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.InsnList; + +import java.util.Objects; + +/** + * Represents a sequence of instructions contained within two given nodes. + * Extends {@link InsnList}, but provides additional flexibility and features. + */ +public class InsnSequence extends InsnList { + /** + * Public constructor. + * This creates an empty sequence. + */ + public InsnSequence() { + super(); + } + + /** + * Public constructor for list with single item. + * Must be given a single non-null node. + * @param node the node in question + */ + public InsnSequence(AbstractInsnNode node) { + super(); + this.add(node); + } + + /** + * Public constructor. + * Must be given two non-null, connected nodes. + * @param startNode the starting node of the pattern + * @param endNode the first node of the pattern + */ + public InsnSequence(AbstractInsnNode startNode, AbstractInsnNode endNode) { + Objects.requireNonNull(startNode); + Objects.requireNonNull(endNode); + for(; startNode != null; startNode = startNode.getNext()) { + this.add(startNode); + if(startNode == endNode) break; + } + if(startNode == null) + throw new InstructionMismatchException("Nodes" + getFirst() + " and " + getLast() + " are not connected."); + } + + /** + * Extends the existing get function from InsnList to allow for negative indexes. + * @param index the index of the instruction that must be returned + * @return the instruction whose index is given + */ + @Override + public AbstractInsnNode get(int index) { + if(index >= 0) + return super.get(index); + index = Math.abs(index); + if(index > size()) + throw new IndexOutOfBoundsException(); + return super.get(size() - index); + } + + /** + * Adds an array of nodes to the list. + * @param nodes the nodes to add + */ + public void add(AbstractInsnNode... nodes) { + for(AbstractInsnNode node : nodes) + this.add(node); + } + + /** + * Wraps InsnList's add() to throw an exception + * when given null values. + * @param node to add + */ + @Override + public void add(AbstractInsnNode node) { + Objects.requireNonNull(node); + super.add(node); + } + + /** + * Replaces a node with another one. Mostly used internally. + * @param oldNode node to replace + * @param newNode new node + */ + public void replaceNode(AbstractInsnNode oldNode, AbstractInsnNode newNode) { + super.insert(oldNode, newNode); + super.remove(oldNode); + } + + /** + * Replaces n occurrences of said opcode with the given node. + * @param opcode the opcode to replace + * @param newNode the replacement node + * @param amount how many occurrences to replace, set to 0 to replace all + * @return true if anything was changed, false otherwise + */ + public boolean replace(int opcode, AbstractInsnNode newNode, int amount) { + return replace(opcode, newNode, amount, false); + } + + /** + * Replaces n occurrences of said opcode with the given node. + * @param opcode the opcode to replace + * @param newNode the replacement node + * @param reverse whether the search should be done from the end + * @param amount how many occurrences to replace, set to 0 to replace all + * @return true if anything was changed, false otherwise + */ + public boolean replace(int opcode, AbstractInsnNode newNode, int amount, boolean reverse) { + boolean changed = false; + for(AbstractInsnNode cur = this.getFirst(); + cur != null && cur.getPrevious() != this.getLast() && cur.getNext() != this.getFirst(); + cur = reverse ? cur.getPrevious() : cur.getNext()) { + if(cur.getOpcode() == opcode) { + this.replaceNode(cur, newNode); + changed = true; + amount--; // will go to negative if it was already 0, causing it to go on until for loop finishes + if(amount == 0) + return changed; + } + } + return changed; + } + + /** + * Cut a number of nodes from the list. + * @param amount how many nodes to cut + * @param reverse true if should cut from the end, false otherwise + */ + public void cut(int amount, boolean reverse) { + for(int i = 0; i < amount; i++) + this.remove(reverse ? getLast() : getFirst()); + } +} diff --git a/src/main/java/ftbsc/lll/utils/PatternMatcher.java b/src/main/java/ftbsc/lll/utils/PatternMatcher.java new file mode 100644 index 0000000..9518749 --- /dev/null +++ b/src/main/java/ftbsc/lll/utils/PatternMatcher.java @@ -0,0 +1,250 @@ +package ftbsc.lll.utils; + +import ftbsc.lll.exceptions.PatternNotFoundException; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +/** + * Describes a pattern to match on a list of ASM instructions. + */ +public class PatternMatcher { + /** + * The list of predicates to match. + */ + private final List<Predicate<AbstractInsnNode>> predicates; + + /** + * Whether pattern search should be done from the end. + */ + private final boolean reverse; + + /** + * Patterns flagged with this ignore labels. + */ + private final boolean ignoreLabels; + + /** + * Patterns flagged with this ignore FRAME instructions. + */ + private final boolean ignoreFrames; + + /** + * Patterns flagged with this ignore LINENUMBER instructions. + */ + private final boolean ignoreLineNumbers; + + /** + * Private constructor because a PatternMatcher should only ever be initialized + * through the builder. + * @param predicates the list of predicates to match + * @param reverse search direction + * @param ignoreLabels whether LABEL instructions should be ignored + * @param ignoreFrames whether FRAME instructions should be ignored + * @param ignoreLineNumbers whether LINENUMBER instructions should be ignored + */ + private PatternMatcher(List<Predicate<AbstractInsnNode>> predicates, boolean reverse, + boolean ignoreLabels, boolean ignoreFrames, boolean ignoreLineNumbers) { + this.predicates = predicates; + this.reverse = reverse; + this.ignoreLabels = ignoreLabels; + this.ignoreFrames = ignoreFrames; + this.ignoreLineNumbers = ignoreLineNumbers; + } + + /** + * @return the Builder object for this {@link PatternMatcher} + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Tries to match the given pattern on a given {@link MethodNode}. + * @param node the {@link MethodNode} to search + * @return the InsnSequence object representing the matched pattern + */ + public InsnSequence find(MethodNode node) { + return find(reverse ? node.instructions.getLast() : node.instructions.getFirst()); + } + + /** + * Tries to match the given pattern starting from a given node. + * @param node the node to start the search on + * @return the {@link InsnSequence} object representing the matched pattern + */ + public InsnSequence find(AbstractInsnNode node) { + if(node != null) { + AbstractInsnNode first, last; + for(AbstractInsnNode cur = node; cur != null; cur = reverse ? cur.getPrevious() : cur.getNext()) { + if(predicates.size() == 0) return new InsnSequence(cur); //match whatever + first = cur; + last = cur; + for(int match = 0; last != null && match < predicates.size(); last = reverse ? last.getPrevious() : last.getNext()) { + if(match != 0) { + if(ignoreLabels && last.getType() == AbstractInsnNode.LABEL) continue; + if(ignoreFrames && last.getType() == AbstractInsnNode.FRAME) continue; + if(ignoreLineNumbers && last.getType() == AbstractInsnNode.LINE) continue; + } + if(!predicates.get(match).test(last)) break; + if(match == predicates.size() - 1) { + if(reverse) return new InsnSequence(last, first); //we are matching backwards + else return new InsnSequence(first, last); + } else match++; + } + } + } + throw new PatternNotFoundException("Failed to find pattern!"); + } + + /** + * The Builder object for {@link PatternMatcher}. + */ + public static class Builder { + + /** + * List of predicates the pattern has to match. + */ + private final List<Predicate<AbstractInsnNode>> predicates = new ArrayList<>(); + + /** + * Whether the pattern matching should proceed in reversed order. + */ + private boolean reverse = false; + + /** + * Patterns flagged with this ignore labels. + */ + private boolean ignoreLabels = false; + + /** + * Patterns flagged with this ignore FRAME instructions. + */ + private boolean ignoreFrames = false; + + /** + * Patterns flagged with this ignore LINENUMBER instructions. + */ + private boolean ignoreLineNumbers = false; + + /** + * Builds the pattern defined so far. + * @return the built {@link PatternMatcher} + */ + public PatternMatcher build() { + return new PatternMatcher(predicates, reverse, ignoreLabels, ignoreFrames, ignoreLineNumbers); + } + + /** + * Sets the pattern to match starting from the end. + * @return the builder's state after the operation + */ + public Builder reverse() { + this.reverse = true; + return this; + } + + /** + * Adds a custom predicate to the list. Also used internally. + * @param predicate the predicate to add + * @return the builder's state after the operation + */ + public Builder check(Predicate<AbstractInsnNode> predicate) { + predicates.add(predicate); + return this; + } + + /** + * Wildcard, matches any kind of node. + * @return the builder's state after the operation + */ + public Builder any() { + return check(i -> true); + } + + /** + * Matches a specific opcode. + * @param opcode opcode to match + * @return the builder's state after the operation + */ + public Builder opcode(int opcode) { + return check(i -> i.getOpcode() == opcode); + } + + /** + * Matches a list of opcodes. + * @param opcodes list of opcodes to match + * @return the builder's state after the operation + */ + public Builder opcodes(int... opcodes) { + Builder res = this; + for(int o : opcodes) + res = opcode(o); + return res; + } + + /** + * Matches a method invokation of any kind: one of INVOKEVIRTUAL, + * INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE. + * @return the builder's state after the operation + */ + public Builder method() { + return check(i -> i.getType() == AbstractInsnNode.METHOD_INSN); + } + + /** + * Matches a field invokation of any kind: one of GETSTATIC, PUTSTATIC, + * GETFIELD or PUTFIELD. + * @return the builder's state after the operation + */ + public Builder field() { + return check(i -> i.getType() == AbstractInsnNode.FIELD_INSN); + } + + /** + * Matches any kind of jump instruction. + * @return the builder's state after the operation + */ + public Builder jump() { + return check(i -> i.getType() == AbstractInsnNode.JUMP_INSN); + } + + /** + * Matches any kind of label. + * @return the builder's state after the operation + */ + public Builder label() { + return check(i -> i.getType() == AbstractInsnNode.LABEL); + } + + /** + * Tells the pattern matcher to ignore LABEL instructions. + * @return the builder's state after the operation + */ + public Builder ignoreLabels() { + this.ignoreLabels = true; + return this; + } + + /** + * Tells the pattern matcher to ignore FRAME instructions. + * @return the builder's state after the operation + */ + public Builder ignoreFrames() { + this.ignoreFrames = true; + return this; + } + + /** + * Tells the pattern matcher to ignore LINENUMBER instructions. + * @return the builder's state after the operation + */ + public Builder ignoreLineNumbers() { + this.ignoreLineNumbers = true; + return this; + } + } +} diff --git a/src/main/java/ftbsc/lll/utils/StackUtils.java b/src/main/java/ftbsc/lll/utils/StackUtils.java new file mode 100644 index 0000000..225b1ee --- /dev/null +++ b/src/main/java/ftbsc/lll/utils/StackUtils.java @@ -0,0 +1,86 @@ +package ftbsc.lll.utils; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; + +import java.util.Comparator; + +/** + * Various methods for manipulating the stack. + * Includes anything from instantiation to variable manipulation - just about + * anything that loads stuff on or from the stack. + */ +public class StackUtils implements Opcodes { + /** + * Creates a new instance of an object, given its internal name, constructor descriptor and instructions to load + * the parameters. + * The created object is loaded on the stack. + * @param name the internal name of the object to initialize, where + * the internal name of a class is its fully qualified name, where '.' are replaced by '/' + * @param desc the descriptor of the constructor to call + * @param args nodes containing instructions to load the constructor arguments, in the right order + * @return an instruction list containing the opcodes needed to create the new object and load it on the stack. + */ + public static InsnList instantiate(String name, String desc, AbstractInsnNode... args) { + InsnSequence is = new InsnSequence(); + is.add(args); + return instantiate(name, desc, is); + } + + /** + * Creates a new instance of an object, given its internal name, constructor descriptor and instructions to load + * the parameters. + * The created object is loaded on the stack. + * @param name the internal name of the object to initialize, where + * the internal name of a class is its fully qualified name, where '.' are replaced by '/' + * @param desc the descriptor of the constructor to call + * @param args a list of instructions loading the constructor arguments onto the stack in the correct order + * @return an instruction list containing the opcodes needed to create the new object and load it on the stack. + */ + public static InsnList instantiate(String name, String desc, InsnList args) { + InsnSequence list = new InsnSequence(); + list.add(new TypeInsnNode(NEW, name), new InsnNode(DUP)); + if(args != null) list.add(args); + list.add(new MethodInsnNode(INVOKESPECIAL, name, "<init>", desc, false)); + return list; + } + + + /** + * Creates a new local variable, lasting in scope from the first to the last label of the given method. + * @param method the method for which to declare the local variable + * @param name the variable's name + * @param desc the type descriptor for the new variable + * @return the index value of the new local variable + */ + public static int addLocalVariable(MethodNode method, String name, String desc) { + return addLocalVariable( + method, name, desc, + (LabelNode) PatternMatcher.builder().label().build().find(method).getFirst(), + (LabelNode) PatternMatcher.builder().label().reverse().build().find(method).getFirst() + ); + } + + /** + * Creates a new local variable, lasting in scope between two given {@link LabelNode}s. + * @param method the method for which to declare the local variable + * @param name the variable's name + * @param desc the type descriptor for the new variable + * @param start the label at which the variable should enter scope + * @param end the label at which the variable should go out of scope + * @return the index value of the new local variable + */ + public static int addLocalVariable(MethodNode method, String name, String desc, LabelNode start, LabelNode end) { + final int targetIndex = + method.localVariables + .stream() + .max(Comparator.comparingInt(v -> v.index)) + .map(var -> var.desc.equals("J") || var.desc.equals("D") + ? var.index + 2 //skip two if long or double - major java moment + : var.index + 1) + .orElse(0); + LocalVariableNode variable = new LocalVariableNode(name, desc, null, start, end, targetIndex); + method.localVariables.add(variable); + return targetIndex; + } +} diff --git a/src/main/java/ftbsc/lll/utils/debug/BytecodePrinter.java b/src/main/java/ftbsc/lll/utils/debug/BytecodePrinter.java new file mode 100644 index 0000000..c58fba4 --- /dev/null +++ b/src/main/java/ftbsc/lll/utils/debug/BytecodePrinter.java @@ -0,0 +1,79 @@ +package ftbsc.lll.utils.debug; + +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.util.Printer; +import org.objectweb.asm.util.Textifier; +import org.objectweb.asm.util.TraceMethodVisitor; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Paths; + +/** + * A collection of static methods for debugging by printing the ASM bytecode. + * These methods are only for debug, so most of the time they should stay unused. + */ +public class BytecodePrinter { + + /** + * Used for converting visit events to text, acts pretty much like a buffer in our case. + */ + private static final Printer PRINTER = new Textifier(); + + /** + * MethodVisitor that visits the method and prints it to a given printer. + */ + private static final TraceMethodVisitor MP = new TraceMethodVisitor(PRINTER); + + /** + * Prints the bytecode of a method using System.out.print(). + * @param main the method to print + */ + public static void printMethod(final MethodNode main) { + for (AbstractInsnNode i : main.instructions.toArray()) + System.out.print(insnToString(i)); + } + + /** + * Logs the bytecode of a method using the ASM logger. + * @param main the method to print + * @param logger the Log4j {@link Logger} to print it with + */ + public static void logMethod(final MethodNode main, final Logger logger) { + for (AbstractInsnNode i : main.instructions.toArray()) + logger.debug(insnToString(i)); + } + + /** + * Logs the bytecode of a method to a file. + * @param main the method to print + * @param path the file to log it to + */ + public static void logMethod(final MethodNode main, String path) { + StringBuilder out = new StringBuilder(); + for (AbstractInsnNode i : main.instructions.toArray()) + out.append(insnToString(i)); + try { + Files.write(Paths.get(path), out.toString().getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Converts an instruction node to a String. + * @param insn the node to convert + * @return the converted string + */ + public static String insnToString(AbstractInsnNode insn) { + insn.accept(MP); + StringWriter sw = new StringWriter(); + PRINTER.print(new PrintWriter(sw)); + PRINTER.getText().clear(); + return sw.toString(); + } +} diff --git a/src/main/java/ftbsc/lll/utils/nodes/FieldProxyInsnNode.java b/src/main/java/ftbsc/lll/utils/nodes/FieldProxyInsnNode.java new file mode 100644 index 0000000..e809ccd --- /dev/null +++ b/src/main/java/ftbsc/lll/utils/nodes/FieldProxyInsnNode.java @@ -0,0 +1,22 @@ +package ftbsc.lll.utils.nodes; + +import ftbsc.lll.proxies.impl.FieldProxy; +import org.objectweb.asm.tree.FieldInsnNode; + +/** + * Overrides the {@link FieldInsnNode} to add a constructor + * taking in a {@link FieldProxy}. + * @since 0.3.0 + */ +public class FieldProxyInsnNode extends FieldInsnNode { + /** + * Constructs a new {@link FieldInsnNode} starting + * from a {@link FieldProxy}. + * @param opcode the opcode, must be one of GETSTATIC, PUTSTATIC, + * GETFIELD or PUTFIELD + * @param f a {@link FieldProxy} representing the field to call + */ + public FieldProxyInsnNode(int opcode, FieldProxy f) { + super(opcode, f.parent.internalName, f.name, f.descriptor); + } +} diff --git a/src/main/java/ftbsc/lll/utils/nodes/MethodProxyInsnNode.java b/src/main/java/ftbsc/lll/utils/nodes/MethodProxyInsnNode.java new file mode 100644 index 0000000..2e062f4 --- /dev/null +++ b/src/main/java/ftbsc/lll/utils/nodes/MethodProxyInsnNode.java @@ -0,0 +1,23 @@ +package ftbsc.lll.utils.nodes; + +import ftbsc.lll.proxies.impl.MethodProxy; +import org.objectweb.asm.tree.MethodInsnNode; + +/** + * Overrides the {@link MethodInsnNode} to add a constructor + * taking in a {@link MethodProxy}. + * @since 0.3.0 + */ +public class MethodProxyInsnNode extends MethodInsnNode { + + /** + * Constructs a new {@link MethodInsnNode} starting + * from a {@link MethodProxy}. + * @param opcode the opcode, must be one of INVOKEVIRTUAL, + * INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE + * @param m a {@link MethodProxy} representing the method to call + */ + public MethodProxyInsnNode(int opcode, MethodProxy m) { + super(opcode, m.parent.internalName, m.name, m.descriptor); + } +} diff --git a/src/main/java/ftbsc/lll/utils/nodes/TypeProxyInsnNode.java b/src/main/java/ftbsc/lll/utils/nodes/TypeProxyInsnNode.java new file mode 100644 index 0000000..3648278 --- /dev/null +++ b/src/main/java/ftbsc/lll/utils/nodes/TypeProxyInsnNode.java @@ -0,0 +1,23 @@ +package ftbsc.lll.utils.nodes; + +import ftbsc.lll.proxies.impl.TypeProxy; +import org.objectweb.asm.tree.TypeInsnNode; + +/** + * Overrides the {@link TypeInsnNode} to add a constructor + * taking in a {@link TypeProxy}. + * @since 0.4.0 + */ +public class TypeProxyInsnNode extends TypeInsnNode { + /** + * Constructs a new {@link TypeInsnNode} starting from a + * {@link TypeProxy}. The user should ensure that the TypeInsnNode + * represents a declared type before calling this. + * @param opcode the opcode, must be one of NEW, ANEWARRAY, + * CHECKCAST or INSTANCEOF + * @param t a {@link TypeProxy} representing the type to call + */ + public TypeProxyInsnNode(int opcode, TypeProxy t) { + super(opcode, t.internalName); + } +} |