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
......@@ -9,7 +9,10 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
......@@ -20,11 +23,6 @@ import ch.usi.dag.disl.classparser.DislClasses;
import ch.usi.dag.disl.exception.DiSLException;
import ch.usi.dag.disl.exception.DiSLIOException;
import ch.usi.dag.disl.exception.DiSLInMethodException;
import ch.usi.dag.disl.exception.InvalidContextUsageException;
import ch.usi.dag.disl.exception.MarkerException;
import ch.usi.dag.disl.exception.ProcessorException;
import ch.usi.dag.disl.exception.ReflectionException;
import ch.usi.dag.disl.exception.StaticContextGenException;
import ch.usi.dag.disl.exclusion.ExclusionSet;
import ch.usi.dag.disl.guard.GuardHelper;
import ch.usi.dag.disl.localvar.SyntheticLocalVar;
......@@ -38,28 +36,27 @@ import ch.usi.dag.disl.snippet.Shadow;
import ch.usi.dag.disl.snippet.Snippet;
import ch.usi.dag.disl.staticcontext.generator.SCGenerator;
import ch.usi.dag.disl.util.ClassNodeHelper;
import ch.usi.dag.disl.util.Logging;
import ch.usi.dag.disl.weaver.Weaver;
import ch.usi.dag.util.logging.Logger;
// TODO javadoc comment all
/**
* Main DiSL class providing interface for an instrumentation framework
* (normally DiSL Server).
* Provides an entry-point and a simple interface to the DiSL instrumentation
* framework. This interface is primarily used by the DiSL instrumentation
* server, but is generally intended for any instrumentation tool wishing to use
* DiSL.
*/
public final class DiSL {
public static final boolean trace = Boolean.getBoolean ("trace");
public static final boolean debug = trace || Boolean.getBoolean ("debug");
// default is that exception handler is inserted
// this is the reason for "double" negation in assignment
private final boolean useExceptHandler = !Boolean.getBoolean("disl.noexcepthandler");
private final boolean splitLongMethods = Boolean.getBoolean ("disl.splitmethods");
private final Logger __log = Logging.getPackageInstance ();
private final boolean useDynamicBypass;
private final boolean debug = Boolean.getBoolean ("debug");
//
private final Set <CodeOption> __codeOptions;
private final Transformers __transformers;
private final Set <Scope> __excludedScopes;
......@@ -67,20 +64,80 @@ public final class DiSL {
private final DislClasses __dislClasses;
/**
* DiSL initialization.
* Initializes a DiSL instance by loading transformers, exclusion lists, and
* DiSL classes.
* <p>
* <b>Note:</b> This constructor is deprecated and will be removed in later
* releases. Use the {@link #init()} static factory method to obtain a
* {@link DiSL} instance.
*
* @param useDynamicBypass
* enable or disable dynamic bypass instrumentation
* determines whether to generate bypass code and whether to control
* the bypass dynamically.
*/
// this method should be called only once
@Deprecated
public DiSL (final boolean useDynamicBypass) throws DiSLException {
this.useDynamicBypass = useDynamicBypass;
__codeOptions = __codeOptionsFrom (System.getProperties ());
// Add the necessary bypass options for backwards compatibility.
if (useDynamicBypass) {
__codeOptions.add (CodeOption.CREATE_BYPASS);
__codeOptions.add (CodeOption.DYNAMIC_BYPASS);
}
__transformers = Transformers.load ();
__excludedScopes = ExclusionSet.prepare();
__dislClasses = DislClasses.load (useDynamicBypass, useExceptHandler);
__dislClasses = DislClasses.load (__codeOptions);
}
/**
* Initializes a DiSL instance.
*/
private DiSL (
final Set <CodeOption> codeOptions, final Transformers transformers,
final Set <Scope> excludedScopes, final DislClasses dislClasses
) {
__codeOptions = codeOptions;
__transformers = transformers;
__excludedScopes = excludedScopes;
__dislClasses = dislClasses;
}
/**
* Loads transformers, exclusion lists, and DiSL classes with snippets, and
* creates an instance of the {@link DiSL} class.
*
* @return A {@link DiSL} instance.
* @throws DiSLException
* if the initialization failed.
*/
public static DiSL init () throws DiSLException {
return init (System.getProperties ());
}
/**
* Loads transformers, exclusion lists, and DiSL classes with snippets, and
* creates an instance of the {@link DiSL} class.
*
* @param properties
* the properties to use in place of system properties, may not be
* {@code null}
* @return A {@link DiSL} instance.
* @throws DiSLException
* if the initialization failed.
*/
private static DiSL init (final Properties properties) throws DiSLException {
final Set <CodeOption> codeOptions = __codeOptionsFrom (
Objects.requireNonNull (properties)
);
final Transformers transformers = Transformers.load ();
final Set <Scope> excludedScopes = ExclusionSet.prepare();
final DislClasses dislClasses = DislClasses.load (codeOptions);
// TODO put checker here
// like After should catch normal and abnormal execution
......@@ -89,25 +146,59 @@ public final class DiSL {
// probably something, you don't want - so just warn the user
// also it can warn about unknown opcodes if you let user to
// specify this for InstructionMarker
return new DiSL (codeOptions, transformers, excludedScopes, dislClasses);
}
/**
* Instruments a method in a class.
*
* NOTE: This method changes the classNode argument
* Derives code options from global properties. This is a transitional
* compatibility method for the transition to per-request code options.
*/
private static Set <CodeOption> __codeOptionsFrom (
final Properties properties
) {
final Set <CodeOption> result = EnumSet.noneOf (CodeOption.class);
final boolean useExceptHandler = !__getBoolean ("disl.noexcepthandler", properties);
if (useExceptHandler) {
result.add (CodeOption.CATCH_EXCEPTIONS);
}
final boolean disableBypass = __getBoolean ("disl.disablebypass", properties);
if (!disableBypass) {
result.add (CodeOption.CREATE_BYPASS);
result.add (CodeOption.DYNAMIC_BYPASS);
}
final boolean splitLongMethods = __getBoolean ("disl.splitmethods", properties);
if (splitLongMethods) {
result.add (CodeOption.SPLIT_METHODS);
}
return result;
}
private static boolean __getBoolean (
final String name, final Properties properties
) {
return Boolean.parseBoolean (properties.getProperty(name));
}
/**
* Instruments a method in a class. <b>Note:</b> This method changes the
* {@code classNode} arugment.
*
* @param classNode
* class that will be instrumented
* class that will be instrumented
* @param methodNode
* method in the classNode argument, that will be instrumented
* @return
* {@code true} if the methods was changed, {@code false} otherwise.
* method in the classNode argument, that will be instrumented
* @return {@code true} if the methods was changed, {@code false} otherwise.
*/
private boolean instrumentMethod (
final ClassNode classNode, final MethodNode methodNode
) throws ReflectionException, StaticContextGenException,
ProcessorException, InvalidContextUsageException, MarkerException {
) throws DiSLException {
// skip abstract methods
if ((methodNode.access & Opcodes.ACC_ABSTRACT) != 0) {
......@@ -124,9 +215,10 @@ public final class DiSL {
final String methodDesc = methodNode.desc;
// evaluate exclusions
// TODO LB: Add support for inclusion
for (final Scope exclScope : __excludedScopes) {
if (exclScope.matches (className, methodName, methodDesc)) {
__debug ("DiSL: excluding method: %s.%s(%s)\n",
__log.debug ("excluding method: %s.%s(%s)",
className, methodName, methodDesc);
return false;
}
......@@ -144,22 +236,18 @@ public final class DiSL {
// if there is nothing to instrument -> quit
// just to be faster out
if (matchedSnippets.isEmpty ()) {
__debug ("DiSL: skipping unaffected method: %s.%s(%s)\n",
__log.debug ("skipping unaffected method: %s.%s(%s)",
className, methodName, methodDesc);
return false;
}
// *** create shadows ***
__trace ("DiSL: processing method: %s.%s(%s)\n",
className, methodName, methodDesc);
// shadows mapped to snippets - for weaving
final Map<Snippet, List<Shadow>> snippetMarkings =
new HashMap<Snippet, List<Shadow>>();
final Map<Snippet, List<Shadow>> snippetMarkings = new HashMap <> ();
for (final Snippet snippet : matchedSnippets) {
__trace ("DiSL: snippet: %s.%s()\n",
__log.trace ("\tsnippet: %s.%s()",
snippet.getOriginClassName (), snippet.getOriginMethodName ());
// marking
......@@ -172,8 +260,7 @@ public final class DiSL {
snippet.getGuard (), shadows
);
__trace ("DiSL: selected shadows: %d\n",
selectedShadows.size ());
__log.trace ("\tselected shadows: %d", selectedShadows.size ());
// add to map
if (!selectedShadows.isEmpty ()) {
......@@ -183,11 +270,17 @@ public final class DiSL {
// *** compute static info ***
__log.trace ("calculating static information for method: %s.%s(%s)",
className, methodName, methodDesc);
// prepares SCGenerator class (computes static context)
final SCGenerator staticInfo = new SCGenerator (snippetMarkings);
final SCGenerator staticInfo = SCGenerator.computeStaticInfo (snippetMarkings);
// *** used synthetic local vars in snippets ***
__log.trace ("finding synthetic locals used by method: %s.%s(%s)",
className, methodName, methodDesc);
// weaver needs list of synthetic locals that are actively used in
// selected (matched) snippets
......@@ -198,6 +291,9 @@ public final class DiSL {
// *** prepare processors ***
__log.trace ("preparing argument processors for method: %s.%s(%s)",
className, methodName, methodDesc);
final PIResolver piResolver = new ProcGenerator ().compute (snippetMarkings);
// *** used synthetic local vars in processors ***
......@@ -211,10 +307,9 @@ public final class DiSL {
// *** weaving ***
__trace ("DiSL: snippet markings: %d\n", snippetMarkings.size ());
if (snippetMarkings.size () > 0) {
__debug ("DiSL: instrumenting method: %s.%s(%s)\n",
className, methodName, methodDesc);
__log.debug ("found %d snippet marking(s), weaving method: %s.%s(%s)",
snippetMarkings.size (), className, methodName, methodDesc);
Weaver.instrument (
classNode, methodNode, snippetMarkings,
new LinkedList <SyntheticLocalVar> (usedSLVs),
......@@ -224,8 +319,8 @@ public final class DiSL {
return true;
} else {
__debug ("DiSL: skipping unaffected method: %s.%s(%s)\n",
className, methodName, methodDesc);
__log.debug ("found %d snippet marking(s), skipping method: %s.%s(%s)",
snippetMarkings.size (), className, methodName, methodDesc);
return false;
}
......@@ -233,31 +328,25 @@ public final class DiSL {
/**
* Selects only shadows matching the passed guard.
* Selects only shadows passing the given guard.
*
* @param guard
* guard, on witch conditions are the shadows selected
* @param marking
* the list of shadows from where the gurads selects
* @return selected shadows
* the guard to use for filtering the {@link Shadow} instances.
* @param shadows
* the list of {@link Shadow} instances to filter.
* @return A list of {@link Shadow} instances passing the guard.
*/
private List <Shadow> selectShadowsWithGuard (
final Method guard, final List <Shadow> marking
final Method guard, final List <Shadow> shadows
) {
if (guard == null) {
return marking;
return shadows;
}
final List <Shadow> selectedMarking = new LinkedList <Shadow> ();
// check guard for each shadow
for (final Shadow shadow : marking) {
if (GuardHelper.guardApplicable (guard, shadow)) {
selectedMarking.add (shadow);
}
}
return selectedMarking;
return shadows.stream ()
// potentially .parallel(), needs thread-safe static context
.filter (shadow -> GuardHelper.guardApplicable (guard, shadow))
.collect (Collectors.toList ());
}
......@@ -265,25 +354,15 @@ public final class DiSL {
* Data holder for an instrumented class
*/
private static class InstrumentedClass {
private final ClassNode __classNode;
private final Set <String> __changedMethods;
final ClassNode classNode;
final Set <String> changedMethods;
public InstrumentedClass (
final ClassNode classNode, final Set <String> changedMethods
) {
__classNode = classNode;
__changedMethods = changedMethods;
}
public ClassNode classNode () {
return __classNode;
}
public Set <String> changedMethods () {
return __changedMethods;
this.classNode = classNode;
this.changedMethods = changedMethods;
}
}
......@@ -314,6 +393,8 @@ public final class DiSL {
// intercept all exceptions and add a method name
try {
__log.trace ("processing method: %s.%s(%s)",
classNode.name, methodNode.name, methodNode.desc);
methodChanged = instrumentMethod (classNode, methodNode);
} catch (final DiSLException e) {
......@@ -333,7 +414,7 @@ public final class DiSL {
final Set <ThreadLocalVar> insertTLVs = new HashSet <ThreadLocalVar> ();
// dynamic bypass
if (useDynamicBypass) {
if (__codeOptions.contains (CodeOption.DYNAMIC_BYPASS)) {
// prepare dynamic bypass thread local variable
final ThreadLocalVar tlv = new ThreadLocalVar (
null, "bypass", Type.getType (boolean.class), false
......@@ -408,13 +489,14 @@ public final class DiSL {
// with the instrumented method code and create code to switch between
// the two versions based on the result of a bypass check.
//
final ClassNode instCN = instResult.classNode();
if (useDynamicBypass) {
final ClassNode instCN = instResult.classNode;
if (__codeOptions.contains (CodeOption.CREATE_BYPASS)) {
CodeMerger.mergeOriginalCode (
instResult.changedMethods (), origCN, instCN
instResult.changedMethods, origCN, instCN
);
}
//
// Fix-up methods that have become too long due to instrumentation.
// To split out the instrumented version of the method, we will need
......@@ -423,7 +505,7 @@ public final class DiSL {
// XXX LB: This will not help long methods produced by the transformers.
//
CodeMerger.fixupLongMethods (
splitLongMethods, origCN, instCN
__codeOptions.contains (CodeOption.SPLIT_METHODS), origCN, instCN
);
return ClassNodeHelper.marshal (instCN);
......@@ -536,18 +618,4 @@ public final class DiSL {
}
}
//
private void __debug (final String format, final Object ... args) {
if (debug) {
System.out.printf (format, args);
}
}
private void __trace (final String format, final Object ... args) {
if (trace) {
System.out.printf (format, args);
}
}
}
......@@ -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.FrameHelper;
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.util.logging.Logger;
/**
* Parses DiSL class with local variables.
*/
abstract class AbstractParser {
private final Logger __log = Logging.getPackageInstance ();
//
protected LocalVars allLocalVars = new LocalVars();
public LocalVars getAllLocalVars() {
......@@ -142,7 +148,6 @@ abstract class AbstractParser {
final String className, final FieldNode field,
final AnnotationNode annotation
) throws ParserException {
// check if field is static
if ((field.access & Opcodes.ACC_STATIC) == 0) {
throw new ParserException("Field " + className + "." + field.name
......@@ -171,7 +176,6 @@ abstract class AbstractParser {
final String className, final FieldNode field,
final AnnotationNode annotation
) throws ParserException {
// check if field is static
if ((field.access & Opcodes.ACC_STATIC) == 0) {
throw new ParserException("Field " + field.name + className
......@@ -251,8 +255,8 @@ abstract class AbstractParser {
// which marks the end of the initialization code.
//
if (slv.hasInitCode ()) {
System.out.printf (
"DiSL: warning, replacing initialization code "+
__log.warn (
"replacing initialization code "+
"for synthetic local variable %s\n", slv.getID ()
);
}
......@@ -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 (
final MethodNode method
) throws ParserException {
......@@ -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
static <T> void parseAnnotation (
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);