Commit 4abd9a50 authored by Lubomir Bulej's avatar Lubomir Bulej

SCResolver, GuardHelper, SCGenerator: don't associate static context data in

the resolver, do it where the static context is being used, i.e., in the
GuardHelper and SCGenerator.

SCGenerator, StaticContextMethod: move invocation of the static context
method into the StaticContextMethod class to make the class pull its weight
a bit.

AbstractParser, SnippetParser: source code cleanups related to annotation
processing.
parent 5ebf90b1
package ch.usi.dag.disl.classparser;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
......@@ -23,8 +22,10 @@ import org.objectweb.asm.tree.VarInsnNode;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.tree.analysis.SourceValue;
import ch.usi.dag.disl.InitializationException;
import ch.usi.dag.disl.annotation.SyntheticLocal;
import ch.usi.dag.disl.exception.DiSLFatalException;
import ch.usi.dag.disl.annotation.SyntheticLocal.Initialize;
import ch.usi.dag.disl.annotation.ThreadLocal;
import ch.usi.dag.disl.exception.ParserException;
import ch.usi.dag.disl.exception.ReflectionException;
import ch.usi.dag.disl.localvar.LocalVars;
......@@ -82,8 +83,8 @@ abstract class AbstractParser {
// parse init code for local vars and assigns them accordingly
if (cinit != null && cinit.instructions != null) {
parseInitCodeForSLV(cinit.instructions, localVars.getSyntheticLocals());
parseInitCodeForTLV(dislClass.name, cinit, localVars.getThreadLocals());
parseInitCodeForSLV (cinit.instructions, localVars.getSyntheticLocals ());
parseInitCodeForTLV (dislClass.name, cinit, localVars.getThreadLocals ());
}
}
......@@ -140,7 +141,10 @@ abstract class AbstractParser {
private static class TLAnnotationData {
public boolean inheritable = false; // default
/**
* The default for the {@link ThreadLocal#inheritable} attribute.
*/
boolean inheritable = false;
}
......@@ -148,27 +152,37 @@ abstract class AbstractParser {
final String className, final FieldNode field,
final AnnotationNode annotation
) throws ParserException {
// check if field is static
//
// Ensure that the thread local field is declared static
// and parse the annotation data.
//
if ((field.access & Opcodes.ACC_STATIC) == 0) {
throw new ParserException("Field " + className + "." + field.name
+ " declared as ThreadLocal but is not static");
}
// parse annotation
final TLAnnotationData tlad = new TLAnnotationData();
AbstractParser.parseAnnotation(annotation, tlad);
final Type fieldType = Type.getType (field.desc);
final TLAnnotationData tlad = parseAnnotation (
annotation, new TLAnnotationData ()
);
// default value will be set later on
return new ThreadLocalVar(className, field.name, fieldType,
tlad.inheritable);
return new ThreadLocalVar (
className, field.name, Type.getType (field.desc), tlad.inheritable
);
}
private static class SLAnnotationData {
// see code below for default
public String[] initialize = null;
/**
* The default for the {@link SyntheticLocal#initialize} attribute.
* <p>
* <b>Note:</b> The {@link AnnotationNode} represents an enum as a
* two-value String array containing a type descriptor and the value.
* See {@link AnnotationNode#values} and the implementation of
* {@link AnnotationNode#visitEnum(String, String, String)} for details.
*/
String [] initialize = {
Type.getDescriptor (Initialize.class), Initialize.ALWAYS.name ()
};
}
......@@ -176,31 +190,25 @@ abstract class AbstractParser {
final String className, final FieldNode field,
final AnnotationNode annotation
) throws ParserException {
// check if field is static
//
// Ensure that the synthetic local field is declared static, parse
// annotation data and determine the initialization mode for the
// variable.
//
if ((field.access & Opcodes.ACC_STATIC) == 0) {
throw new ParserException("Field " + field.name + className
+ "." + " declared as SyntheticLocal but is not static");
}
// parse annotation data
final SLAnnotationData slad = new SLAnnotationData();
AbstractParser.parseAnnotation(annotation, slad);
// default val for init
SyntheticLocal.Initialize slvInit = SyntheticLocal.Initialize.ALWAYS;
final SLAnnotationData slad = parseAnnotation (
annotation, new SLAnnotationData ()
);
if(slad.initialize != null) {
final Initialize initMode = Initialize.valueOf (slad.initialize [1]);
// enum is converted to array
// - first value is class name
// - second value is value name
slvInit = SyntheticLocal.Initialize.valueOf(slad.initialize[1]);
}
// field type
final Type fieldType = Type.getType (field.desc);
return new SyntheticLocalVar(className, field.name, fieldType, slvInit);
return new SyntheticLocalVar (
className, field.name, Type.getType (field.desc), initMode
);
}
......@@ -430,7 +438,7 @@ abstract class AbstractParser {
//
public static void ensureMethodReturnsVoid (
static void ensureMethodReturnsVoid (
final MethodNode method
) throws ParserException {
final Type returnType = Type.getReturnType (method.desc);
......@@ -440,7 +448,7 @@ abstract class AbstractParser {
}
public static void ensureMethodIsStatic (
static void ensureMethodIsStatic (
final MethodNode method
) throws ParserException {
if ((method.access & Opcodes.ACC_STATIC) == 0) {
......@@ -449,7 +457,7 @@ abstract class AbstractParser {
}
public static void ensureMethodUsesContextProperly (
static void ensureMethodUsesContextProperly (
final MethodNode method
) throws ParserException {
//
......@@ -498,7 +506,7 @@ abstract class AbstractParser {
}
public static void ensureMethodHasOnlyContextArguments (
static void ensureMethodHasOnlyContextArguments (
final MethodNode method
) throws ParserException {
//
......@@ -527,7 +535,7 @@ abstract class AbstractParser {
* @param method the method to check
* @throws ParserException if the method is empty
*/
public static void ensureMethodIsNotEmpty (
static void ensureMethodIsNotEmpty (
final MethodNode method
) throws ParserException {
final AbstractInsnNode head = method.instructions.getFirst ();
......@@ -538,7 +546,7 @@ abstract class AbstractParser {
}
public static void ensureMethodThrowsNoExceptions (
static void ensureMethodThrowsNoExceptions (
final MethodNode method
) throws ParserException {
if (! method.exceptions.isEmpty ()) {
......@@ -547,13 +555,23 @@ abstract class AbstractParser {
}
// NOTE: second parameter is modified by this function
static <T> void parseAnnotation (
final AnnotationNode annotation, final T parsedDataObject
/**
* Parses an annotation node and sets the fields of the given object to the
* values found in the annotation. All fields must be declared in the class
* of which the object is an instance.
*
* @param annotation
* the annotation to process
* @param result
* the result object in which to modify field values
* @return the modified result object
*/
static <T> T parseAnnotation (
final AnnotationNode annotation, final T result
) {
// nothing to do
if (annotation.values == null) {
return;
return result;
}
try {
......@@ -561,33 +579,40 @@ abstract class AbstractParser {
while (it.hasNext ()) {
//
// Name-value pairs are stored as two consecutive elements.
// Find the right field and set its value.
// Set the value of the field with the corresponding name.
//
final String name = (String) it.next ();
final Object value = it.next ();
final Field attr = parsedDataObject.getClass ().getField (name);
if (attr != null) {
attr.set (parsedDataObject, value);
} else {
throw new DiSLFatalException (
"Unknown attribute "+ name +" in annotation "+
Type.getType (annotation.desc).toString () +
". This may happen if annotation class is changed"+
" but parser class is not."
);
}
__setFieldValue (name, value, result);
}
return result;
} catch (final Exception e) {
throw new DiSLFatalException (
"Reflection error while parsing annotation", e);
throw new InitializationException (
e, "failed to parse the %s annotation",
Type.getType (annotation.desc).getClassName ()
);
}
}
private static <T> void __setFieldValue (
final String name, final Object value, final T target
) {
try {
target.getClass ().getDeclaredField (name).set (target, value);
} catch (final NoSuchFieldException | IllegalAccessException e) {
throw new InitializationException (
e, "failed to store annotation attribute '%s'", name
);
}
}
public static Class <?> getGuard (final Type guardType)
static Class <?> getGuard (final Type guardType)
throws ReflectionException {
if (guardType == null) {
return null;
......
......@@ -26,8 +26,8 @@ import ch.usi.dag.disl.exception.SnippetParserException;
import ch.usi.dag.disl.guard.GuardHelper;
import ch.usi.dag.disl.marker.Marker;
import ch.usi.dag.disl.marker.Parameter;
import ch.usi.dag.disl.scope.ScopeMatcher;
import ch.usi.dag.disl.scope.Scope;
import ch.usi.dag.disl.scope.ScopeMatcher;
import ch.usi.dag.disl.snippet.Snippet;
import ch.usi.dag.disl.snippet.SnippetUnprocessedCode;
import ch.usi.dag.disl.util.AsmHelper;
......@@ -216,18 +216,20 @@ class SnippetParser extends AbstractParser {
// data holder for AnnotationParser
private static class SnippetAnnotationData {
final Class <?> type;
public Class<?> type;
Type marker;
// annotation values
public Type marker = null;
public String args = null; // default
public String scope = "*"; // default
public Type guard = null; // default
public int order = 100; // default
public boolean dynamicBypass = true; // default
//
// Default values of annotation attributes.
//
String args = null;
String scope = "*";
Type guard = null;
int order = 100;
boolean dynamicBypass = true;
public SnippetAnnotationData(final Class<?> type) {
SnippetAnnotationData (final Class <?> type) {
this.type = type;
}
}
......@@ -236,12 +238,13 @@ class SnippetParser extends AbstractParser {
private SnippetAnnotationData __parseSnippetAnnotationFields (
final AnnotationNode annotation, final Class <?> type
) {
final SnippetAnnotationData result = new SnippetAnnotationData (type);
final SnippetAnnotationData result = parseAnnotation (
annotation, new SnippetAnnotationData (type)
);
parseAnnotation (annotation, result);
if (result.marker == null) {
throw new DiSLFatalException (
"Missing [marker] attribute in annotation "+ type.toString ()
"Missing [marker] attribute in annotation "+ type.getName ()
+ ". This may happen if annotation class is changed but"
+ " data holder class is not."
);
......
......@@ -2,6 +2,9 @@ package ch.usi.dag.disl.coderep;
import java.lang.reflect.Method;
import ch.usi.dag.disl.exception.StaticContextGenException;
import ch.usi.dag.disl.staticcontext.StaticContext;
public class StaticContextMethod {
......@@ -52,4 +55,29 @@ public class StaticContextMethod {
return __referencedClass;
}
/**
* Invokes the static context method on the given static context instance.
*
* @param staticContext
* the target of the static context method invocation
* @return static context data
* @throws StaticContextGenException
* if the static context method invocation fails for some reason
*/
public Object invoke (
final StaticContext staticContext
) throws StaticContextGenException {
try {
__method.setAccessible (true);
return __method.invoke (staticContext);
} catch (final Exception e) {
throw new StaticContextGenException (
e, "Invocation of static context method %s.%s failed",
__method.getDeclaringClass ().getName (), __method.getName ()
);
}
}
}
......@@ -227,12 +227,14 @@ public abstract class GuardHelper {
} else {
//
// The guard method passed validation, so here it can only
// require static context. Get a static context instance
// for the shadow location.
// require static context. Get a static context instance and
// associate it with the shadow location.
//
try {
arguments [argIndex] = SCResolver.getInstance ().
getStaticContextInstance (parameterType, shadow);
final StaticContext staticContext = SCResolver.getInstance ().
getStaticContextInstance (parameterType);
staticContext.staticContextData (shadow);
arguments [argIndex] = staticContext;
} catch (final ReflectionException re) {
final String message = String.format (
......
......@@ -4,7 +4,6 @@ import java.util.HashMap;
import java.util.Map;
import ch.usi.dag.disl.exception.ReflectionException;
import ch.usi.dag.disl.snippet.Shadow;
import ch.usi.dag.disl.staticcontext.StaticContext;
import ch.usi.dag.disl.util.ReflectionHelper;
......@@ -19,12 +18,12 @@ public class SCResolver {
// list of static context instances
// validity of an instance is for whole instrumentation run
// instances are created lazily when needed
private Map <Class <?>, Object>
private final Map <Class <?>, Object>
staticContextInstances = new HashMap <Class <?>, Object> ();
public synchronized StaticContext getStaticContextInstance (
final Class <?> staticContextClass, final Shadow shadow
final Class <?> staticContextClass
) throws ReflectionException {
//
// Get a static context instance from cache, or create a new one and
......@@ -38,7 +37,6 @@ public class SCResolver {
}
final StaticContext result = (StaticContext) sc;
result.staticContextData (shadow);
return result;
}
......
package ch.usi.dag.disl.staticcontext.generator;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -58,12 +57,10 @@ public class SCGenerator {
final StaticContextKey that = (StaticContextKey) object;
//
// Shadows and methods must either be null in both
// objects, or equal.
// Shadows and methods must be either both null or equal.
//
final boolean shadowsEqual = __nullOrEqual (this.shadow, that.shadow);
if (shadowsEqual) {
return __nullOrEqual (this.methodId, that.methodId);
if (__bothNullOrEqual (this.shadow, that.shadow)) {
return __bothNullOrEqual (this.methodId, that.methodId);
}
}
......@@ -71,7 +68,7 @@ public class SCGenerator {
}
}
private static boolean __nullOrEqual (final Object obj1, final Object obj2) {
private static boolean __bothNullOrEqual (final Object obj1, final Object obj2) {
if (obj1 == null) {
return obj2 == null;
} else {
......@@ -101,47 +98,29 @@ public class SCGenerator {
// context data for each static context method for each snippet
// instance (shadow).
//
final Map <StaticContextKey, Object> staticInfoData = new HashMap <> ();
final Map <StaticContextKey, Object> result = new HashMap <> ();
for (final Snippet snippet : snippetMarkings.keySet ()) {
for (final StaticContextMethod scm : snippet.getCode ().getReferencedSCMs ()) {
for (final Shadow shadow : snippetMarkings.get (snippet)) {
final StaticContext staticContext =
SCResolver.getInstance().getStaticContextInstance (
scm.getReferencedClass (), shadow
);
final Object result = getStaticContextData (
staticContext, scm.getMethod ()
);
// store the result
staticInfoData.put (
new StaticContextKey (shadow, scm.getId ()), result
//
// Get SC instance, associate it with the shadow location, and
// cache it along with the method.
//
final StaticContext staticContext = SCResolver.getInstance()
.getStaticContextInstance (scm.getReferencedClass ());
staticContext.staticContextData (shadow);
result.put (
new StaticContextKey (shadow, scm.getId ()),
scm.invoke (staticContext)
);
}
}
}
return new SCGenerator (staticInfoData);
}
// resolves static context data - uses static context data caching
private static Object getStaticContextData (
final StaticContext staticContext, final Method method
) throws StaticContextGenException, ReflectionException {
try {
// get static data by invoking static context method
method.setAccessible (true);
return method.invoke (staticContext);
} catch (final Exception e) {
throw new StaticContextGenException (
e, "Invocation of static context method %s.%s failed",
method.getDeclaringClass ().getName (), method.getName ()
);
}
return new SCGenerator (result);
}
//
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment