Commit 55b410df authored by Lubomir Bulej's avatar Lubomir Bulej

DiSL: use the logging infrastructure instead of standard output/error to produce any messages.

DiSL: mark the constructor as deprecated because of the amount of work done in it, and provide an init() static factory method.
DiSL: selectShadowsWithGuards() converted to the streams API.
DiSL: dropped getters from InstrumentedClass, as it is private class that only serves to return a multi-value result.
DiSL: introduce the usage of the CodeOption enums to determine how to instrument code, so far in backwards compatibility mode.

AbstractParser: use logger instead of stdout to print out warning.
AbstractParser: added ensure*() methods to check for expected properties in snippets.
AnnotationMapper: new class for processing Java annotations.
ArgProcessorParser: cleaned up the parsing code, switched several loops to the streams API.
ContextKind: introduced to represent the various kinds of contexts.
DiSLClasses: use a set of CodeOption enums instead of boolean flags when loading DiSL classes.
SnippetParser: extracted the Contexts class into ContextUsage class.
SnippetParser: cleaned up snippet validation and parsing.
ParserRuntimeException: introduced as an internal exception for class parser classes.

Code, UnprocessedCode: use MethodNode as a reference to code, instead of separate instruction and try-catch-block lists.
Code, UnprocessedCode: drop context kind usage information, because it is not used by any code.
Code: only expose information on local variables and static context methods as unmodifiable sets.
Code, UnprocessedCode, SnippetCode, SnippetUnprocessedCode: introduce a copy contructor for use in the clone() method.
ContextUsage: extracted from SnippetParser and slightly refactored.
InvalidStaticContextInvocationException: introduced as an internal exception for code representation classes.
UnprocessedCode: added className(), methodName(), and location(AbstractInsn) methods.
UnprocessedCode: cleaned up translateThreadLocalVars() method and renamed it to __rewriteThreadLocalVarAccesses().
UnprocessedCode: converted many loops in helper methods to the streams API, sometimes even using parallel streams.

ArgumentContextImpl: just wrap ProcMethodInstance instead of duplicating the information.
GuardHelper: use ProcMethodInstance in guardApplicable() method instead of passing argument context parameters explicitly.

ArgProcessorParserException: repurposed to ParserRuntimeException.
ParserException, ParserRuntimeException, ProcessorException, StaticContextGenException: added constructor(s) for formatted messages and cleaned up formatting.

ArgProcessor: added getReferencedSCMs() method to collected static context methods referenced in argument processor methods.
ArgProcessorKind: removed valueOf(ProcessAlso.Type) because it is not used.
ArgProcessorMethod: use Code and UnprocessedCode instead of ProcCode and ProcUnprocessedCode.
ArgProcessorMethod: replaced getTypes() with handlesType(ArgProcessorKind) to make the class do more work.
ArgProcessorMethod, Snippet: make sure getCode() can be only called after the template code has been expanded.
ArgProcessorMethod, Snippet, SnippetUnprocessedCode: use the template code as a source of the snippet origin class and method names.
ProcCode, ProcUnprocessedCode: removed, as their function is subsumed by Code and UnprocessedCode classes.
PIResolver: cleaned up and simplified the equals() implementation.
ProcGenerator: use handlesType() instead of getTypes().contains() on ArgProcessorMethod.
ProcGenerator: pass ProcMethodInstance into guardApplicable() invocations, eliminated isPMGuardApplicable() private method.
ProcMethodInstance: use Code instead of ProcCode.
ProcMethodInstance: renamed getArgPos() to getArgIndex().
ProcMethodInstance: renamed getArgType() to getKind() because that is what it does.
ProcMethodInstance: changed getArgTypeDesc() to getArgType() and made it to return Type, instead of string type descriptor to keep the abstraction higher.

Snippet: use Integer.compare() on snippet order in the compareTo() method.
Snippet: cleaned up code and javadoc formatting.
SnippetCode: decorate Code instead of extending it, besides other things it makes cloning much simpler.
SnippetCode: build the set of reference static context methods lazily based on the SCMs referenced in the template code and the associated argument processor methods.
SnippetUnprocessedCode: decorate UnprocessedCode instead of extending it, makes cloning much simpler.
SnippetUnprocessedCode: dropped context kind usage information, it is not used anywhere.
SnippetUnprocessedCode, UnprocessedCode: cleaned up and added comments to the process() method.
SnippetUnprocessedCode: dropped getters from the privater ProcessorInfo class.
SnippetUnprocessedCode: cleaned up the insnInvokesProcessor() method.
SnippetUnprocessedCode: cleaned up the insertExceptionHandler() method and renamed it to __insertExceptionHandler().
SnippetUnprocessedCode: cleaned up the insertDynamicBypass() method and renamed it to __insertDynamicBypassControl().

SCGenerator: simplified computeStaticInfo() and cleaned up the formatting.

AsmHelper: removed the deprecated helper methods, they now reside closer to where they are used.

WeavingCode: reflect the changes in ProcMethodInstance method names.

RequestProcessor: use DiSL.init() instead of new DiSL(), and convert "dislserver.disablebypass" property to "disl.disablebypass" to preserve compatibility.

OnPlaceTransformer, Transformer: use DiSL.init() instead of new DiSL()
parent 0c01ffb4
This diff is collapsed.
...@@ -35,13 +35,19 @@ import ch.usi.dag.disl.util.AsmHelper.Insns; ...@@ -35,13 +35,19 @@ import ch.usi.dag.disl.util.AsmHelper.Insns;
import ch.usi.dag.disl.util.Constants; import ch.usi.dag.disl.util.Constants;
import ch.usi.dag.disl.util.FrameHelper; import ch.usi.dag.disl.util.FrameHelper;
import ch.usi.dag.disl.util.Insn; import ch.usi.dag.disl.util.Insn;
import ch.usi.dag.disl.util.Logging;
import ch.usi.dag.disl.util.ReflectionHelper; import ch.usi.dag.disl.util.ReflectionHelper;
import ch.usi.dag.util.logging.Logger;
/** /**
* Parses DiSL class with local variables. * Parses DiSL class with local variables.
*/ */
abstract class AbstractParser { abstract class AbstractParser {
private final Logger __log = Logging.getPackageInstance ();
//
protected LocalVars allLocalVars = new LocalVars(); protected LocalVars allLocalVars = new LocalVars();
public LocalVars getAllLocalVars() { public LocalVars getAllLocalVars() {
...@@ -142,7 +148,6 @@ abstract class AbstractParser { ...@@ -142,7 +148,6 @@ abstract class AbstractParser {
final String className, final FieldNode field, final String className, final FieldNode field,
final AnnotationNode annotation final AnnotationNode annotation
) throws ParserException { ) throws ParserException {
// check if field is static // check if field is static
if ((field.access & Opcodes.ACC_STATIC) == 0) { if ((field.access & Opcodes.ACC_STATIC) == 0) {
throw new ParserException("Field " + className + "." + field.name throw new ParserException("Field " + className + "." + field.name
...@@ -171,7 +176,6 @@ abstract class AbstractParser { ...@@ -171,7 +176,6 @@ abstract class AbstractParser {
final String className, final FieldNode field, final String className, final FieldNode field,
final AnnotationNode annotation final AnnotationNode annotation
) throws ParserException { ) throws ParserException {
// check if field is static // check if field is static
if ((field.access & Opcodes.ACC_STATIC) == 0) { if ((field.access & Opcodes.ACC_STATIC) == 0) {
throw new ParserException("Field " + field.name + className throw new ParserException("Field " + field.name + className
...@@ -251,8 +255,8 @@ abstract class AbstractParser { ...@@ -251,8 +255,8 @@ abstract class AbstractParser {
// which marks the end of the initialization code. // which marks the end of the initialization code.
// //
if (slv.hasInitCode ()) { if (slv.hasInitCode ()) {
System.out.printf ( __log.warn (
"DiSL: warning, replacing initialization code "+ "replacing initialization code "+
"for synthetic local variable %s\n", slv.getID () "for synthetic local variable %s\n", slv.getID ()
); );
} }
...@@ -416,6 +420,25 @@ abstract class AbstractParser { ...@@ -416,6 +420,25 @@ abstract class AbstractParser {
// //
public static void ensureMethodReturnsVoid (
final MethodNode method
) throws ParserException {
final Type returnType = Type.getReturnType (method.desc);
if (! Type.VOID_TYPE.equals (returnType)) {
throw new ParserException ("method may not return any value!");
}
}
public static void ensureMethodIsStatic (
final MethodNode method
) throws ParserException {
if ((method.access & Opcodes.ACC_STATIC) == 0) {
throw new ParserException ("method must be declared static!");
}
}
public static void ensureMethodUsesContextProperly ( public static void ensureMethodUsesContextProperly (
final MethodNode method final MethodNode method
) throws ParserException { ) throws ParserException {
...@@ -465,6 +488,55 @@ abstract class AbstractParser { ...@@ -465,6 +488,55 @@ abstract class AbstractParser {
} }
public static void ensureMethodHasOnlyContextArguments (
final MethodNode method
) throws ParserException {
//
// The type of each method argument must be a context of some kind.
//
final Type [] argTypes = Type.getArgumentTypes (method.desc);
for (int argIndex = 0; argIndex < argTypes.length; argIndex++) {
final Type argType = argTypes [argIndex];
final ContextKind contextType = ContextKind.forType (argType);
if (contextType == null) {
throw new ParserException (
"argument #%d has invalid type, %s does not "+
"implement any context interface!",
(argIndex + 1), argType.getClassName ()
);
}
}
}
/**
* Ensures that a given method is not empty, i.e., it does not start with a
* return instruction.
*
* @param method the method to check
* @throws ParserException if the method is empty
*/
public static void ensureMethodIsNotEmpty (
final MethodNode method
) throws ParserException {
final AbstractInsnNode head = method.instructions.getFirst ();
final AbstractInsnNode firstInsn = Insns.FORWARD.firstRealInsn (head);
if (AsmHelper.isReturn (firstInsn)) {
throw new ParserException ("method does not contain any code!");
}
}
public static void ensureMethodThrowsNoExceptions (
final MethodNode method
) throws ParserException {
if (! method.exceptions.isEmpty ()) {
throw new ParserException ("method may not throw any exceptions!");
}
}
// NOTE: second parameter is modified by this function // NOTE: second parameter is modified by this function
static <T> void parseAnnotation ( static <T> void parseAnnotation (
final AnnotationNode annotation, final T parsedDataObject final AnnotationNode annotation, final T parsedDataObject
......
package ch.usi.dag.disl.classparser;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.MethodNode;
final class AnnotationMapper {
private final Map <
Class <?>, Map <Predicate <String>, BiConsumer <String, Object>>
> __consumers = new LinkedHashMap <> ();
//
public AnnotationMapper register (
final Class <?> annotationClass,
final String nameRegex, final BiConsumer <String, Object> consumer
) {
final Predicate <String> predicate = Pattern.compile (nameRegex).asPredicate ();
__getConsumers (annotationClass).put (predicate, consumer);
return this;
}
private Map <Predicate <String>, BiConsumer <String, Object>> __getConsumers (
final Class <?> ac
) {
Map <Predicate <String>, BiConsumer <String, Object>> result = __consumers.get (ac);
if (result == null) {
result = new HashMap <> ();
__consumers.put (ac, result);
}
return result;
}
private Map <Predicate <String>, BiConsumer <String, Object>> __findConsumers (
final Class <?> ac
) {
return __consumers.entrySet ().stream ()
.filter (e -> e.getKey ().isAssignableFrom (ac))
.findFirst ()
.orElseThrow (() -> new ParserRuntimeException (
"unsupported annotation type: %s", ac.getName ()
)).getValue ();
}
public AnnotationMapper processDefaults () {
__consumers.keySet ().stream ()
.filter (ac -> ac.isAnnotation ())
.forEach (ac -> __accept (ac));
return this;
}
private void __accept (final Class <?> ac) {
final Map <
Predicate <String>, BiConsumer <String, Object>
> consumers = __findConsumers (ac);
Arrays.stream (ac.getDeclaredMethods ()).forEach (m -> {
final String name = m.getName ();
__getConsumer (consumers, name).accept (name, m.getDefaultValue ());
});
}
private BiConsumer <String, Object> __getConsumer (
final Map <Predicate <String>, BiConsumer <String, Object>> consumers,
final String name
) {
return consumers.entrySet ().stream ()
.filter (e -> e.getKey ().test (name))
.findFirst ()
.orElseThrow (() -> new ParserRuntimeException (
"no consumer for annotation attribute %s", name
)).getValue ();
}
public AnnotationMapper accept (final MethodNode mn) {
Arrays.asList (mn.visibleAnnotations, mn.invisibleAnnotations).stream ()
.filter (l -> l != null)
.flatMap (l -> l.stream ())
.forEach (an -> accept (an));
return this;
}
public AnnotationMapper accept (final AnnotationNode an) {
final Class <?> ac = __resolveClass (Type.getType (an.desc));
final Map <
Predicate <String>, BiConsumer <String, Object>
> consumers = __findConsumers (ac);
an.accept (new AnnotationVisitor (Opcodes.ASM5) {
@Override
public void visit (final String name, final Object value) {
__getConsumer (consumers, name).accept (name, value);
}
@Override
public void visitEnum (
final String name, final String desc, final String value
) {
final Object enumValue = __instantiateEnum (desc, value);
__getConsumer (consumers, name).accept (name, enumValue);
}
@Override
public AnnotationVisitor visitArray (final String name) {
final BiConsumer <String, Object> consumer = __getConsumer (consumers, name);
return new ListCollector (name, consumer);
}
});
return this;
}
/**
* Collects individual values into a list and submits the result to the
* given consumer when the {@link #visitEnd()} method is called.
* <p>
* <b>Note:</b>This collector does not currently support nested arrays or
* annotation values.
*/
private static class ListCollector extends AnnotationVisitor {
final List <Object> __values = new ArrayList <> ();
final String __name;
final BiConsumer <String, Object> __consumer;
ListCollector (final String name, final BiConsumer <String, Object> consumer) {
super (Opcodes.ASM5);
__name = name;
__consumer = consumer;
}
@Override
public void visit (final String name, final Object value) {
__values.add (value);
}
@Override
public void visitEnum (final String name, final String desc, final String value) {
__values.add (__instantiateEnum (desc, value));
}
@Override
public void visitEnd () {
__consumer.accept (__name, __values);
}
};
private static Class <?> __resolveClass (final Type type) {
try {
return Class.forName (type.getClassName ());
} catch (final ClassNotFoundException e) {
throw new ParserRuntimeException (e);
}
}
private static Object __instantiateEnum (
final String desc, final String value
) {
final String className = Type.getType (desc).getClassName ();
try {
final Class <?> enumClass = Class.forName (className);
final Method valueMethod = enumClass.getMethod ("valueOf", String.class );
final Object result = valueMethod.invoke (null, value);
if (result != null) {
return result;
}
// Throw the exception outside this try-catch block.
} catch (final Exception e) {
throw new ParserRuntimeException (
e, "failed to instantiate enum value %s.%s", className, value
);
}
throw new ParserRuntimeException (
"failed to instantiate enum value %s.%s", className, value
);
}
}
package ch.usi.dag.disl.classparser;
import java.util.HashMap;
import java.util.Map;
import org.objectweb.asm.Type;
import ch.usi.dag.disl.classcontext.ClassContext;
import ch.usi.dag.disl.dynamiccontext.DynamicContext;
import ch.usi.dag.disl.processorcontext.ArgumentContext;
import ch.usi.dag.disl.processorcontext.ArgumentProcessorContext;
import ch.usi.dag.disl.staticcontext.StaticContext;
import ch.usi.dag.disl.util.ReflectionHelper;
public enum ContextKind {
STATIC (StaticContext.class) {
private final Map <Type, Boolean> __cache = new HashMap <Type, Boolean> ();
@Override
public boolean matches (final Type type) {
//
// Static context has to implement the StaticContext interface.
//
final Boolean cachedResult = __cache.get (type);
if (cachedResult != null) {
return cachedResult;
} else {
final boolean result = __implementsStaticContext (type);
__cache.put (type, result);
return result;
}
}
private boolean __implementsStaticContext (final Type type) {
final Class <?> typeClass = ReflectionHelper.tryResolveClass (type);
if (typeClass != null) {
return ReflectionHelper.implementsInterface (typeClass, _itfClass);
} else {
return false;
}
}
},
DYNAMIC (DynamicContext.class),
CLASS (ClassContext.class),
ARGUMENT (ArgumentContext.class),
ARGUMENT_PROCESSOR (ArgumentProcessorContext.class);
//
protected final Class <?> _itfClass;
protected final Type _itfType;
//
private ContextKind (final Class <?> itfClass) {
_itfClass = itfClass;
_itfType = Type.getType (_itfClass);
}
public boolean matches (final Type type) {
return _itfType.equals (type);
}
public static ContextKind forType (final Type type) {
//
// Find the context that matches the type
//
for (final ContextKind context : ContextKind.values ()) {
if (context.matches (type)) {
return context;
}
}
return null;
}
}
...@@ -3,16 +3,16 @@ package ch.usi.dag.disl.classparser; ...@@ -3,16 +3,16 @@ package ch.usi.dag.disl.classparser;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Set;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import ch.usi.dag.disl.DiSL.CodeOption;
import ch.usi.dag.disl.annotation.ArgumentProcessor; import ch.usi.dag.disl.annotation.ArgumentProcessor;
import ch.usi.dag.disl.cbloader.ClassByteLoader; import ch.usi.dag.disl.cbloader.ClassByteLoader;
import ch.usi.dag.disl.exception.DiSLInitializationException; import ch.usi.dag.disl.exception.DiSLInitializationException;
import ch.usi.dag.disl.exception.GuardException;
import ch.usi.dag.disl.exception.MarkerException;
import ch.usi.dag.disl.exception.ParserException; import ch.usi.dag.disl.exception.ParserException;
import ch.usi.dag.disl.exception.ProcessorException; import ch.usi.dag.disl.exception.ProcessorException;
import ch.usi.dag.disl.exception.ReflectionException; import ch.usi.dag.disl.exception.ReflectionException;
...@@ -41,9 +41,9 @@ public class DislClasses { ...@@ -41,9 +41,9 @@ public class DislClasses {
// //
public static DislClasses load ( public static DislClasses load (
final boolean useDynamicBypass, final boolean useExceptHandler final Set <CodeOption> options
) throws DiSLInitializationException, ParserException, ReflectionException, ) throws DiSLInitializationException, ParserException,
StaticContextGenException, MarkerException, GuardException, ProcessorException { StaticContextGenException, ReflectionException, ProcessorException {
final List <InputStream> classStreams = ClassByteLoader.loadDiSLClasses (); final List <InputStream> classStreams = ClassByteLoader.loadDiSLClasses ();
if (classStreams == null) { if (classStreams == null) {
throw new DiSLInitializationException ( throw new DiSLInitializationException (
...@@ -83,12 +83,13 @@ public class DislClasses { ...@@ -83,12 +83,13 @@ public class DislClasses {
processor.init (localVars); processor.init (localVars);
} }
// TODO LB: Consider whether we need to create the argument processor
// invocation map now -- we basically discard the argument processors
// and keep an invocation map keyed to instruction indices! :-(
// TODO LB: Move the loop to the SnippetParser class // TODO LB: Move the loop to the SnippetParser class
for (final Snippet snippet : sp.getSnippets ()) { for (final Snippet snippet : sp.getSnippets ()) {
snippet.init ( snippet.init (localVars, app.getProcessors (), options);
localVars, app.getProcessors (),
useExceptHandler, useDynamicBypass
);
} }
return new DislClasses (sp); return new DislClasses (sp);
......
package ch.usi.dag.disl.classparser;
@SuppressWarnings ("serial")
class ParserRuntimeException extends RuntimeException {
public ParserRuntimeException (final String message) {
super (message);
}
public ParserRuntimeException (final Throwable cause) {
super (cause);
}
public ParserRuntimeException (
final String format, final Object... args
) {
super (String.format (format, args));
}
public ParserRuntimeException (
final Throwable cause, final String format, final Object... args
) {
super (String.format (format, args), cause);
}
}
package ch.usi.dag.disl.coderep; package ch.usi.dag.disl.coderep;
import java.util.HashSet; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode; import org.objectweb.asm.tree.TryCatchBlockNode;
import ch.usi.dag.disl.localvar.LocalVars;
import ch.usi.dag.disl.localvar.SyntheticLocalVar; import ch.usi.dag.disl.localvar.SyntheticLocalVar;
import ch.usi.dag.disl.localvar.ThreadLocalVar; import ch.usi.dag.disl.localvar.ThreadLocalVar;
import ch.usi.dag.disl.util.AsmHelper.ClonedCode; import ch.usi.dag.disl.util.AsmHelper;
/** /**
* Stores various information about a piece of java bytecode. * Represents an analyzed and partially expanded code template. Instances of
* {@link Code} are obtained from {@link UnprocessedCode} instances as a result
* of calling the {@link UnprocessedCode#process(LocalVars) process()} method on
* them.
*/ */
public class Code { public class Code {
private InsnList instructions; /** A method representing the code template. */
private List<TryCatchBlockNode> tryCatchBlocks; private final MethodNode __method;
private Set<SyntheticLocalVar> referencedSLVs;
private Set<ThreadLocalVar> referencedTLVs; /** Synthetic-local variables referenced by the code template. */
private Set<StaticContextMethod> staticContexts; private final Set <SyntheticLocalVar> __syntheticLocals;
private boolean usesDynamicContext;
private boolean usesClassContext; /** Thread-local variables referenced by the code template. */
// the code contains handler that handles exception and doesn't propagate private final Set <ThreadLocalVar> __threadLocals;
// it further - can cause stack inconsistency that has to be handled
private boolean containsHandledException; /** Static context methods invoked by the code template. */
private final Set <StaticContextMethod> __staticContextMethods;
/** /**
* Constructs the Code structure. * Determines whether the code contains an exception handler that handles an
* exception and does not propagate it. This may cause stack inconsistency
* which needs to be handled.
*/ */
public Code(InsnList instructions, List<TryCatchBlockNode> tryCatchBlocks, private final boolean _containsHandledException;
Set<SyntheticLocalVar> referencedSLVs,
Set<ThreadLocalVar> referencedTLVs, //
Set<StaticContextMethod> staticContexts,
boolean usesDynamicContext, public Code (
boolean usesClassContext, final MethodNode method,
boolean containsHandledException) { final Set <SyntheticLocalVar> syntheticLocals,
super(); final Set <ThreadLocalVar> threadLocals,
this.instructions = instructions; final Set <StaticContextMethod> staticContextMethods,
this.tryCatchBlocks = tryCatchBlocks; final boolean containsHandledException
this.referencedSLVs = referencedSLVs; ) {
this.referencedTLVs = referencedTLVs; __method = method; // caller responsible for giving us a clone
this.staticContexts = staticContexts;
this.usesDynamicContext = usesDynamicContext; __syntheticLocals = Collections.unmodifiableSet (syntheticLocals);
this.usesClassContext = usesClassContext; __threadLocals = Collections.unmodifiableSet (threadLocals);
this.containsHandledException = containsHandledException; __staticContextMethods = Collections.unmodifiableSet (staticContextMethods);
_containsHandledException = containsHandledException;
} }
/**
* Returns an ASM instruction list. private Code (final Code that) {
*/ __method = AsmHelper.cloneMethod (that.__method);
public InsnList getInstructions() {
return instructions; // The following immutables can be shared.
__syntheticLocals = that.__syntheticLocals;
__threadLocals = that.__threadLocals;
__staticContextMethods = that.__staticContextMethods;
_containsHandledException = that._containsHandledException;
} }
/** /**
* Returns list of exceptions (as represented in ASM). * @return An ASM instruction list.
*/ */
public List<TryCatchBlockNode> getTryCatchBlocks() { public InsnList getInstructions () {
return tryCatchBlocks; return __method.instructions;
} }