[index]
[prev] [next]

1. Jiapi Reflection

Jiapi reflection API is a foundation where rest of the Jiapi is built on. It provides a Java reflection like API to modify Java classes. The differences between Java reflection and Jiapi reflection are:

The prerequisite to understand Jiapi reflection, is to be comfortable with Java reflection. Some knowledge about Java bytecode is also needed. Feel free to skip this section if you are more interested to utilize Jiapi in a higher level of abstraction.

1.1 Basic Concepts

Java reflection API provides an abstraction for a Class, Method and Field. These abstractions can be used to query reflective information about classes and interfaces. Jiapi reflection extends this concept so that it is possible to alter the classes and interfaces too.

Using JiapiClass it is possible to query and modify the class it represents. All bytecode modifications are done through InstructionList which represents the bytecode Instructions of a class.

1.2 Examples

Hello world 1

This example adds a hello world functionality to a target class. The target class will be Test.java:
       package samples.reflect.hello1;
        
       import java.lang.reflect.Method;
        
       public class Test {
            public static void main(String args[]) throws Exception {
               Class cl = Test.class;
               Method m = cl.getMethod("helloWorld", new Class[] {});
               HelloWorld hw = (HelloWorld) cl.newInstance();
               m.invoke(hw, new Object[] {});
           }
       }
Please notice how Test gets a reference to its method helloWorld. It also creates an instance of itself and casts it to HelloWorld. If you try to run this class you would naturally get NoSuchMethodException and ClassCastException. This example shows how you can add these missing features to the class.

The HelloWorld interface looks like:

       package samples.reflect.hello1;
        
       public interface HelloWorld {
           public void helloWorld();
       }
The class modification is done in class HelloWorldBuilder:
       1 package samples.reflect.hello1;
       2 
       3 import java.io.FileOutputStream;
       4 import java.lang.reflect.Modifier;
       5 import alt.jiapi.reflect.*;
       6 
       7 public class HelloWorldBuilder {
       8     public HelloWorldBuilder() throws Exception {
       9         // Load the target class:
      10         Loader loader = new Loader();
      11         JiapiClass clazz = loader.loadClass("samples.reflect.hello1.Test");
      12 
      13         // Add an interface to a clazz:
      14         clazz.addInterface("samples.reflect.hello1.HelloWorld");
      15 
      16         // Add an empty method for a clazz. The method signature must
      17         // match the method from HelloWorld interface, the only difference
      18         // is that we want to implement the method now so it can't be abstract.
      19         JiapiMethod method = clazz.addMethod(Modifier.PUBLIC, "void",
      20                                              "helloWorld", new String[] {});
      21         
      22         // Then create the method body. The body will make a call to
      23         // System.out.println and then just return.
      24         InstructionList il = method.getInstructionList();
      25         InstructionFactory iFactory = il.getInstructionFactory();
      26         il.add(iFactory.getField(loader.loadClass("java.lang.System").getField("out")));
      27         il.add(iFactory.constant("hello world!"));
      28         il.add(iFactory.invoke(System.out.getClass().getMethod("println", new Class[] { String.class })));
      29         il.add(iFactory.returnMethod(method));
      30 
      31         // Finalize the modified class and dump it to the file:
      32         clazz.doPreDefine();
      33         clazz.dump(new FileOutputStream("Test.class"));
      34     }
      35 
      36     public static void main(String args[]) throws Exception {
      37         new HelloWorldBuilder();
      38     }
      39 }
To run this example set your CLASSPATH as specified in README file. Then say:
java samples.reflect.hello1.HelloWorldBuilder

You should now see a Test.class in your working directory. To verify if the Test.class works you can try to run it. First make a required package hierarchy and move Test.class there so that Java vitual machine finds it:
mkdir -p samples/reflect/hello1
mv Test.class samples/reflect/hello1/

Then add . as a first element in your CLASSPATH and launch Test:
export CLASSPATH=.:$CLASSPATH
java samples.reflect.hello1.Test

You should see a line "hello world!" in your console instead of NoSuchMethodException or ClassCastException.

[prev] [next]