Using the ASM Framework to Implement Common Java Bytecode Transformation Patterns

Using the ASM Framework to Implement Common Java Bytecode Transformation Patterns

Using the ASM framework to implement common Java bytecode transformation patterns Eugene Kuleshov, [email protected] ABSTRACT the visited tree with objects. Visitors can change call chains and Most AOP frameworks targeting the Java platform use a bytecode therefore transform the visited code. Using the Adapter design weaving approach as it is currently considered the most practical pattern [7] visitors can be chained in order to implement complex solution. It allows applying cross-cutting concerns to Java transformation from smaller building blocks. A similar approach applications when source code is not available, is portable and is also used in the SAX API for XML processing [8]. works on existing JVMs, in comparison to VM-level AOP ASM hides all the complexity of the serialization and implementations. deserialization of the class bytecode, using the following techniques: Load-time bytecode weaving (LTW), which happens right before the application code is loaded into the Java VM, significantly • Automatic management of the class constant pool, simplifies development environment, but also raises the bar for therefore the user does not have to manipulate indexes the performance and memory requirements for these of these constants. transformations. Such requirements directly apply to the toolkit • Automatic management of the class structure, including that will be used to perform these transformations. In this paper, annotations, fields, methods, method code and other we examine how Java bytecode transformations, typical for AOP standard bytecode attributes. implementations can be done efficiently, using the ASM1 bytecode manipulation framework [1]. • Labels are used to manage instruction addresses, so it is easy to insert new code in between existing instructions Transformations used by general-use AOP frameworks and similar applications can be categorized, and the common patterns • Computation of maximum stack and local variables, as can be reused to implement specific transformations. These well as StackMapFrames patterns can also be combined to implement more complex The event-based interaction between event producers and event transformations. consumers is defined by several interfaces: ClassVisitor, FieldVisitor, MethodVisitor, and AnnotationVisitor. Event General Terms producers, like ClassReader fire visit*() calls to those interfaces. Languages, Design, Performance, Experimentation, On the other hand, event receivers like writers (ClassWriter, Standardization FieldWriter, MethodWriter, and AnnotationWriter), adapters (ClassAdapter and MethodAdapter) or classes from the tree package (ClassNode, MethodNode, etc) implementing those Keywords interfaces. Aspect-Oriented Programming, Java, bytecode, weaving, ASM The following code demonstrates how this looks from the 1. INTRODUCTION developer’s point of view: The ASM bytecode framework was designed at France Telecom ClassReader cr = new ClasReader(bytecode); ClassWriter cw = new ClassWiter(cr, R&D by Eric Bruneton, Romain Lenglet and Thierry Coupaye ClassWriter.COMPUTE_MAXS | [2]. After evaluating several existing frameworks, including ClassWriter.COMPUTE_FRAMES); BCEL [3], Serp [4] and JOIE [5], they designed a more efficient FooClassAdapter cv = new FooClassAdapter(cw); approach, providing better performance and memory foot print. cr.accept(cv, 0); Today ASM is used in many applications and has become the de- Here ClassReader reads the bytecode. On accept() method call facto standard for bytecode processing. ClassReader fires all visiting events corresponding to the The main idea of the ASM API [6] is not to use an object bytecode structure. FooClassAdapter will receive those events representation of the bytecode. This made it possible expressing and can change the event flow before passing them to the the same transformations using only a few classes comparing to ClassWriter. Once ClassWriter receive all the events it will have approximately 80 classes in Serp and 270 in BCEL API. Those transformed bytecode. You may notice that the ClassReader frameworks create lots of objects during class deserialization, instance is passed to the ClassWriter that allows performance which takes a lot of time and memory. ASM avoids this overhead optimizations based on the assumption that the transformations to keep transformation fast and to use very little memory. This is mostly add new code. done by using the Visitor design pattern [7], without representing The following sections will show a number of practical examples that should help you to better understanding of the ASM 1 The ASM name does not mean anything: it is just a reference to framework. the keyword in C which allows some functions to be implemented in assembly language. 2. Accessing class data used to analyze selected methods in isolation. The result of such The visitor-based approach allows capturing class data analysis can be also used for inter-method analysis. incrementally, collecting only the information required for Combining those features it is possible to retrieve required class specific use case without creating and destroying lots of short- information with very controlled memory overhead by making lived objects. Incremental processing allows decisions such as decisions on the required transformations at load time. whether or not part of the class needs to be transformed, skipping In the next sections we will see concrete examples of the bytecode parsing of class parts that don’t need to be transformed. transformations typical for AOP. ASM provide very simple API to support this. First of all there are several bit-mask flags in the ClassReader.accept() method: 3. COMMON TRANSFORMATIONS AOP frameworks that are using load time transformations usually • SKIP_DEBUG – Used to ignore debug info, such as build their own high-level abstractions about how application source file, line number and variable info. code needs to be transformed. Those abstractions can be • SKIP_FRAMES – Used to ignore StackMapTable decomposed into smaller building blocks that can be chained information used for Java 6 split bytecode verifier. together to achieve the required transformation. Here are most • EXPAND_FRAMES – Expand the StackMapTable common use cases: data, allowing visitor to have information on types of all • Class Transformations local variables and current stack slots. • Introduce Interface • SKIP_CODE – Exclude code of all methods from • Add a New Field visiting, while still passing trough method’s and parameter’s attributes and annotations. • Add a New Method Additionally, the visitor can decide to skip the corresponding • Replace Method Body bytecode section it is not interested in. In order to do that, • Merge Two Classes into One visitField(), visitMethod() and visitAnnotation() methods that returns nested visitor can return null. That will indicate to the • Method Transformations bytecode producer to skip the corresponding class element. • Insert Code before Method, Constructor or Static When there is no need to read class data, but just the class Initializer Execution hierarchy, ClassReader provides shortcut methods • Insert Code before Method Exit getSuperName() and getInterfaces() to read super class and implemented interfaces, respectively. • Replace Field Access It is also possible use the tree package of ASM framework to read • Replace Method Call the entire class or selected method in memory and change its • Inline Method structures using DOM-like API. For example: ClassReader cr = new ClassReader(source); 3.1 Class Transformations ClassWriter cw = new ClassWriter(); ClassAdapter ca = new ClassAdapter(cw) { 3.1.1 Introducing Interface public MethodVisitor visitMethod(int access, This transformation only changes the class information about the String name, String desc, implemented interfaces. We can use simple ClassAdapter for this: String signature, String[] exceptions) { final MethodVisitor mv = public class InterfaceAdder extends ClassAdapter { super.visitMethod(access, private Set newInterfaces; name, desc, signature, exceptions); MethodNode mn = new MethodNode(access, public InterfacesAdder(ClassVisitor cv, name, desc, signature, exceptions) { Set newInterfaces) { public void visitEnd() { super(cv); // transform/analyze this method DOM this.newInterfaces = newInterfaces; accept(mv); } } public void visit(int version, int access, }; String name, String signature, return mn; String superName, String[] interfaces) { } Set ints = new HashSet(newInterfaces); }; ints.addAll(Arrays.asList(interfaces)); cr.accept(ca, 0); cv.visit(version, access, name, signature, The above code will basically suspend passing method events to superName, (String[]) ints.toArray()); the next visitor in the chain (i.e. ClassWriter in this case) until the } visitEnd() event. When visitEnd() is passed, the MethodNode } instance will contain the entire method data. At this point the Note, the actual methods required to implement the introduced method data can be transformed and only after that, finally passed interfaces must be added with a separate transformation, which to the next visitor using the MethodNode.accept() method. will be discussed shortly. It is worth mentioning that ASM provides several basic Data Flow Analysis algorithms that work on the tree package and can be 3.1.2 Adding a New Field 3.1.4 Replace Method Body Adding new fields to an existing class is a common This transformation is a variation of the transformation for adding transformation. Usually it is used to enrich class state with a new method. In this case, the

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    7 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us