Commit ec5b7a4a authored by Lubomir Bulej's avatar Lubomir Bulej

DiSL: moved transformer related code into Transformers class.

DiSL: moved DiSL class initialization into DislClasses class.
DiSL: moved ClassNode unmarshalling/marshaling code into ClassNodeHelper.
ClassNodeHelper: encapsulate different loading strategies for snippets and instrumented classes.
ClassNodeHelper: provide ClassNode duplication code.
ClassNodeHelper: provide code for marshaling ClassNode into class-files.
Transformer: moved to disl package.
Transformer: removed propagateUninstrumentedClasses() method, its purpose is unclear.
TransformerException: moved to disl package, to be used for exceptions resulting from applying transformers to code.
TransformerInitializationException: created as a class to be used for exceptions occurring during transformer loading and initialization.
Transformers: encapsulates transformer loading, initialization, and application code.
InitException: renamed to DiSLInitializationException to serve as top-level exception for initialization exceptions.
LocalVars: added put() methods for thread-local and synthetic-local variables.
LocalVars: getThreadLocals() and getSyntheticLocals() now return unmodifiable maps.
AbstractParser: use the LocalVars.put() methods to insert local variables into LocalVars.
DislClassParser: renamed to DislClasses, as it represents the collected snippets.
DislClasses: moved snippet and argument processor initialization code from DiSL class.
DislClasses: removed the getProcessors() and getAllLocalVars() methods that are no long necessary.
ClassByteLoader, DiSL: cleaned up formatting.
testtools.agent.Transformer: initialize DiSL statically.
testtools.agent.Transformer: partially cleaned up formatting.
 initialization/loading code 
parent a982baf8
This diff is collapsed.
package ch.usi.dag.disl;
import java.lang.instrument.ClassFileTransformer;
/**
* Represents a bytecode-level class transformer. A {@link Transfomer} is
* expected to consume an array of bytes representing the contents of a class
* file, process it, and return an array of bytes representing the class file
* contents of the the modified class, or {@code null} if the class was not
* modified in any way. This mimics the convention established by the
* {@link ClassFileTransformer} interface.
* <p>
* Transformers are applied to classes before being processed by DiSL, and need
* to be specified using the {@code DiSL-Transformers} attribute in the manifest
* of a {@code .jar} file containing the instrumentation classes. The attribute
* lists the names of classes implementing the {@link Transformer} interface,
* separated by a comma. The transformer are applied in the listed order.
* <p>
* <b>Note:</b> The implementation of a {@link Transformer} implementation
* <b>MUST</b> be thread-safe, and the implementing class <b>MUST</b> provide a
* default (parameterless) constructor.
*/
public interface Transformer {
/**
* Transforms the given class bytecode and returns the bytecode of the
* transformed class.
*
* @param classFileBytes
* the class to be transformed
* @return the bytecode of the modified class or {@code null} if the class
* was not modified in any way.
*/
byte [] transform (byte [] classFileBytes) throws Exception;
}
package ch.usi.dag.disl;
import ch.usi.dag.disl.exception.DiSLException;
@SuppressWarnings ("serial")
class TransformerException extends DiSLException {
public TransformerException (
final Throwable cause, final String format, final Object ... args
) {
super (cause, format, args);
}
}
package ch.usi.dag.disl;
import ch.usi.dag.disl.exception.DiSLInitializationException;
@SuppressWarnings ("serial")
class TransformerInitializationException extends DiSLInitializationException {
public TransformerInitializationException (
final String format, final Object ... args
) {
super (format, args);
}
public TransformerInitializationException (
final Throwable cause, final String format, final Object ... args
) {
super (cause, format, args);
}
}
package ch.usi.dag.disl;
import java.util.List;
import ch.usi.dag.disl.cbloader.ManifestHelper;
import ch.usi.dag.disl.cbloader.ManifestHelper.ManifestInfo;
import ch.usi.dag.disl.exception.ManifestInfoException;
import ch.usi.dag.util.Lists;
class Transformers {
private final List <Transformer> __transformers;
//
private Transformers (final List <Transformer> transformers) {
// not to be instantiated from outside
__transformers = transformers;
}
public byte [] apply (
final byte [] originalBytes
) throws TransformerException {
byte [] result = originalBytes;
for (final Transformer transformer : __transformers) {
try {
final byte [] bytes = transformer.transform (result);
if (bytes != null) {
result = bytes;
}
} catch (final Exception e) {
throw new TransformerException (
e, "transformation failed in %s", transformer
);
}
}
return result;
}
//
/**
* Loads and instantiates {@link Transformer} classes.
*/
public static Transformers load () throws TransformerInitializationException {
try {
return new Transformers (__loadTransformers ());
} catch (final ManifestInfoException e) {
throw new TransformerInitializationException (
e, "failed to load transformers"
);
}
}
private static List <Transformer> __loadTransformers ()
throws ManifestInfoException, TransformerInitializationException {
final List <Transformer> result = Lists.newLinkedList ();
final ManifestInfo mi = ManifestHelper.getDiSLManifestInfo();
if (mi != null) {
final String xfClassName = mi.getDislTransformer ();
if (xfClassName != null) {
result.add (__createTransformer (xfClassName));
}
}
return result;
}
private static Transformer __createTransformer (
final String className
) throws TransformerInitializationException {
final Class <?> resolvedClass = __resolveTransformer (className);
if (Transformer.class.isAssignableFrom (resolvedClass)) {
return __instantiateTransformer (resolvedClass);
} else {
throw new TransformerInitializationException (
"invalid transformer %s: class does not implement %s",
className, Transformer.class.getName ()
);
}
}
private static Class <?> __resolveTransformer (final String className)
throws TransformerInitializationException {
try {
return Class.forName (className);
} catch (final Exception e) {
throw new TransformerInitializationException (
e, "failed to resolve transformer %s", className
);
}
}
private static Transformer __instantiateTransformer (
final Class <?> transformerClass
) throws TransformerInitializationException {
try {
return (Transformer) transformerClass.newInstance ();
} catch (final Exception e) {
throw new TransformerInitializationException (
e, "failed to instantiate transformer %s",
transformerClass.getName ()
);
}
}
}
......@@ -8,18 +8,18 @@ import java.util.LinkedList;
import java.util.List;
import ch.usi.dag.disl.cbloader.ManifestHelper.ManifestInfo;
import ch.usi.dag.disl.exception.InitException;
import ch.usi.dag.disl.exception.DiSLInitializationException;
import ch.usi.dag.disl.exception.ManifestInfoException;
public abstract class ClassByteLoader {
public static final String PROP_DISL_CLASSES = "disl.classes";
public static final String DISL_CLASSES_DELIM = ",";
public static final String DISL_CLASSES_EXT = ".class";
public static final char CLASS_DELIM = '.';
public static final char FILE_DELIM = '/';
// How to use jar support
// 1) Create jar with a name specified in build.properties (instr.jar.name)
// 2) Include manifest file that contains names of all used DiSL classes
......@@ -32,86 +32,67 @@ public abstract class ClassByteLoader {
// and call "ant package -Dtest.name=processor"
// To run the test case with the instrumentation located in jar call
// "./run-pkg.sh processor"
public static List<InputStream> loadDiSLClasses()
throws InitException {
try {
List<InputStream> result = loadClassesFromProperty();
if(result == null) {
result = loadClassesFromManifest();
}
return result;
}
catch (IOException e) {
throw new InitException(e);
}
catch (ManifestInfoException e) {
throw new InitException(e);
}
}
private static List<InputStream> loadClassesFromProperty()
throws IOException {
String classesList = System.getProperty(PROP_DISL_CLASSES);
// no classes found
if ( (classesList == null) || classesList.isEmpty() ) {
return null;
}
// get streams from class names
List<InputStream> dislClasses = new LinkedList<InputStream>();
for (String fileName : classesList.split(DISL_CLASSES_DELIM)) {
File file = new File(fileName);
dislClasses.add(new FileInputStream(file));
}
return dislClasses;
}
private static List<InputStream> loadClassesFromManifest()
throws IOException, ManifestInfoException {
// get DiSL manifest info
ManifestInfo mi = ManifestHelper.getDiSLManifestInfo();
// no manifest found
if(mi == null) {
return null;
}
String classesList = mi.getDislClasses();
// empty class list in manifest
if(classesList.isEmpty()) {
return null;
}
// get streams from class names
List<InputStream> dislClasses = new LinkedList<InputStream>();
for (String className : classesList.split(DISL_CLASSES_DELIM)) {
// create file name from class name
String fileName = className.replace(CLASS_DELIM, FILE_DELIM)
+ DISL_CLASSES_EXT;
ClassLoader cl = ClassByteLoader.class.getClassLoader();
dislClasses.add(cl.getResourceAsStream(fileName));
}
return dislClasses;
}
public static List <InputStream> loadDiSLClasses () throws DiSLInitializationException {
try {
List <InputStream> result = loadClassesFromProperty ();
if (result == null) {
result = loadClassesFromManifest ();
}
return result;
} catch (final IOException e) {
throw new DiSLInitializationException (e);
} catch (final ManifestInfoException e) {
throw new DiSLInitializationException (e);
}
}
private static List <InputStream> loadClassesFromProperty () throws IOException {
final String classesList = System.getProperty (PROP_DISL_CLASSES);
// no classes found
if ((classesList == null) || classesList.isEmpty ()) {
return null;
}
// get streams from class names
final List <InputStream> dislClasses = new LinkedList <InputStream> ();
for (final String fileName : classesList.split (DISL_CLASSES_DELIM)) {
final File file = new File (fileName);
dislClasses.add (new FileInputStream (file));
}
return dislClasses;
}
private static List <InputStream> loadClassesFromManifest ()
throws IOException, ManifestInfoException {
// get DiSL manifest info
final ManifestInfo mi = ManifestHelper.getDiSLManifestInfo ();
if (mi == null) {
return null;
}
// empty class list in manifest
final String classesList = mi.getDislClasses ();
if (classesList.isEmpty ()) {
return null;
}
// get streams from class names
final List <InputStream> dislClasses = new LinkedList <InputStream> ();
for (final String className : classesList.split (DISL_CLASSES_DELIM)) {
// create file name from class name
final String fileName = className.replace (CLASS_DELIM, FILE_DELIM) + DISL_CLASSES_EXT;
final ClassLoader cl = ClassByteLoader.class.getClassLoader ();
dislClasses.add (cl.getResourceAsStream (fileName));
}
return dislClasses;
}
}
......@@ -102,21 +102,19 @@ abstract class AbstractParser {
Type annotationType = Type.getType(annotation.desc);
// thread local
if (annotationType.equals(Type.getType(
ch.usi.dag.disl.annotation.ThreadLocal.class))) {
ThreadLocalVar tlv = parseThreadLocal(className, field,
annotation);
result.getThreadLocals().put(tlv.getID(), tlv);
Type tlvAnnotation = Type.getType(
ch.usi.dag.disl.annotation.ThreadLocal.class);
if (annotationType.equals(tlvAnnotation)) {
ThreadLocalVar tlv = parseThreadLocal (className, field, annotation);
result.put(tlv);
continue;
}
// synthetic local
if (annotationType.equals(Type.getType(SyntheticLocal.class))) {
Type slvAnnotation = Type.getType(SyntheticLocal.class);
if (annotationType.equals(slvAnnotation)) {
SyntheticLocalVar slv = parseSyntheticLocal(className, field, annotation);
result.getSyntheticLocals().put(slv.getID(), slv);
result.put(slv);
continue;
}
......
......@@ -3,55 +3,115 @@ package ch.usi.dag.disl.classparser;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import ch.usi.dag.disl.annotation.ArgumentProcessor;
import ch.usi.dag.disl.cbloader.ClassByteLoader;
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.ProcessorException;
import ch.usi.dag.disl.exception.ReflectionException;
import ch.usi.dag.disl.exception.StaticContextGenException;
import ch.usi.dag.disl.localvar.LocalVars;
import ch.usi.dag.disl.processor.ArgProcessor;
import ch.usi.dag.disl.snippet.Snippet;
import ch.usi.dag.disl.util.ClassNodeHelper;
/**
* Parser for DiSL classes containing either snippets or method argument
* processors.
*/
public class DislClassParser {
public class DislClasses {
private final SnippetParser __snippetParser = new SnippetParser ();
private final SnippetParser __snippetParser;
private final ArgProcessorParser __argProcParser = new ArgProcessorParser ();
//
private DislClasses (final SnippetParser snippetParser) {
// not to be instantiated from outside
__snippetParser = snippetParser;
}
//
public void parse (final InputStream classBytes)
throws ParserException, ReflectionException, StaticContextGenException,
MarkerException, GuardException {
public static DislClasses load (
final boolean useDynamicBypass, final boolean useExceptHandler
) throws DiSLInitializationException, ParserException, ReflectionException,
StaticContextGenException, MarkerException, GuardException, ProcessorException {
final List <InputStream> classStreams = ClassByteLoader.loadDiSLClasses ();
if (classStreams == null) {
throw new DiSLInitializationException (
"Cannot load DiSL classes. Please set the property "+
ClassByteLoader.PROP_DISL_CLASSES +
" or supply jar with DiSL classes and proper manifest"
);
}
//
final SnippetParser sp = new SnippetParser ();
final ArgProcessorParser app = new ArgProcessorParser ();
for (final InputStream is : classStreams) {
//
// Get an ASM tree representation of the DiSL class first, then
// parse it as a snippet or an argument processor depending on the
// annotations associated with the class.
//
final ClassNode classNode = __createClassNode (is);
if (__isArgumentProcessor (classNode)) {
app.parse (classNode);
} else {
sp.parse (classNode);
}
}
//
// Get an ASM representation of the DiSL class first, then parse it
// as a snippet or an argument processor depending on the annotations
// associated with the class.
// Collect all local variables and initialize the argument processor
// and snippets.
//
final ClassNode classNode = __createClassNode (classBytes);
if (__isArgumentProcessor (classNode)) {
__argProcParser.parse (classNode);
} else {
__snippetParser.parse (classNode);
final LocalVars localVars = __collectLocals (sp, app);
// TODO LB: Move the loop to the ArgProcessorParser class
for (final ArgProcessor processor : app.getProcessors ().values()) {
processor.init (localVars);
}
// TODO LB: Move the loop to the SnippetParser class
for (final Snippet snippet : sp.getSnippets ()) {
snippet.init (
localVars, app.getProcessors (),
useExceptHandler, useDynamicBypass
);
}
return new DislClasses (sp);
}
private boolean __isArgumentProcessor (final ClassNode classNode) {
private static ClassNode __createClassNode (final InputStream is)
throws ParserException {
//
// Parse input stream into a class node. Include debug information so
// that we can report line numbers in case of problems in DiSL classes.
// Re-throw any exceptions as DiSL exceptions.
//
try {
return ClassNodeHelper.SNIPPET.unmarshal (is);
} catch (final IOException ioe) {
throw new ParserException (ioe);
}
}
private static boolean __isArgumentProcessor (final ClassNode classNode) {
//
// An argument processor must have an @ArgumentProcessor annotation
// associated with the class. DiSL instrumentation classes may have
......@@ -73,35 +133,17 @@ public class DislClassParser {
return false;
}
private ClassNode __createClassNode (final InputStream is)
throws ParserException {
//
// Parse input stream into a class node. Include debug information so
// that we can report line numbers in case of problems in DiSL classes.
// Re-throw any exceptions as DiSL exceptions.
//
try {
final ClassReader classReader = new ClassReader (is);
final ClassNode classNode = new ClassNode ();
classReader.accept (classNode, ClassReader.SKIP_FRAMES);
return classNode;
} catch (final IOException ioe) {
throw new ParserException (ioe);
}
}
//
public LocalVars getAllLocalVars () {
private static LocalVars __collectLocals (
final SnippetParser sp, final ArgProcessorParser app
) {
//
// Merge all local variables from snippets and argument processors.
//
final LocalVars result = new LocalVars ();
result.putAll (__snippetParser.getAllLocalVars ());
result.putAll (__argProcParser.getAllLocalVars ());
result.putAll (sp.getAllLocalVars ());
result.putAll (app.getAllLocalVars ());
return result;
}
......@@ -110,9 +152,4 @@ public class DislClassParser {
return __snippetParser.getSnippets ();
}
public Map <Type, ArgProcessor> getProcessors () {
return __argProcParser.getProcessors ();
}
}
package ch.usi.dag.disl.exception;
@SuppressWarnings ("serial")
public abstract class DiSLException extends Exception {
private static final long serialVersionUID = 6916051574250648195L;
public DiSLException() {
super();
public DiSLException () {
super ();
}
public DiSLException(String message, Throwable cause) {
super(message, cause);
public DiSLException (final String message, final Throwable cause) {
super (message, cause);
}
public DiSLException(String message) {
super(message);
public DiSLException (final String message) {
super (message);
}
public DiSLException(Throwable cause) {
super(cause);
public DiSLException (final Throwable cause) {
super (cause);
}
public DiSLException (final String format, final Object ... args) {
public DiSLException (final String format, final Object... args) {
super (String.format (format, args));
}
public DiSLException (
final Throwable cause, final String format, final Object... args
) {
super (String.format (format, args), cause);
}
}
package ch.usi.dag.disl.exception;
@SuppressWarnings ("serial")
public class DiSLInitializationException extends DiSLException {
public DiSLInitializationException (final String message) {
super (message);
}
public DiSLInitializationException (final Throwable cause) {
super (cause);
}
public DiSLInitializationException (
final String format, final Object ... args
) {
super (format, args);
}
public DiSLInitializationException (
final Throwable cause, final String format, final Object ... args
) {
super (cause, format, args);
}
}
package ch.usi.dag.disl.exception;
public class InitException extends DiSLException {
private static final long serialVersionUID = 7146998896782333764L;
public InitException() {
super();
}
public InitException(String message, Throwable cause) {
super(message, cause);
}
public InitException(String message) {
super(message);
}
public InitException(Throwable cause) {
super(cause);
}
}
package ch.usi.dag.disl.exception;
public class TransformerException extends DiSLException {
private static final long serialVersionUID = 8899006117334791742L;
public TransformerException() {
}
public TransformerException(String message, Throwable cause) {
super(message, cause);
}
public TransformerException(String message) {
super(message);
}
public TransformerException(Throwable cause) {
super(cause);
}
}
package ch.usi.dag.disl.localvar;
import java.util.Collections;
import java.util.HashMap;