The jefnet-0_5-rc2 release delivers:
-
1. great enhancements in the API provided for manipulating Java class files data-structures
-
2. reengineering of the classes implementing the “method-chain disconnection” functionality
-
3. first implementation of the “method-chain injection” functionality
-
4. introduction of new attributes implementations
We have been able to solve some issues in how to handle inner-classes references found while extracting and injecting a “method-chain”. We have then added new attributes defined by the JSR-202 (others are on their way to be implemented), and prepared an interesting test that should show you how effectively we can move auto-consistent logic from one class to another, and even spawn brand new classes as needed: we call this the “disconnectionTest”.
This release is anyway a preview release: we do hope to be able to have a 0.5 final release that should even deliver all those updates required by JSR-202.
Follow some details throwing light on the major improvements provided by this release.
API enhancements
We have worked hardly on a really fundamental subject in this release. The Java class file format, as defined by the official Java Class File Spec., is made up by a number of data-structures that store information about the class iteself. Some of these data structures, when manipulated, require the manipulator to pay attention to a myriad of details. For sake of clarity I am going to provide you an interesting example
Let’s suppose we want to manipulate a Code attribute defined as follows:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
What we need to do is, for example, modifying the bytecode stored in the code array. Let’s recall how this attribute is represented in the JEF-Dna sub-project:
. . .
private int maxStack;
private int maxLocals;
private int codeLength;
private List<Instruction> objectiveCode;
private List<ExceptionTableEntry> exceptionTable;
private List<Attribute> attributes;
. . .
As you can see, we have a typed list called objectiveCode used to store internally the content of the code array in an object oriented way.
Well, whenever you modify the code array you have to keep in your mind that you always have to keep in synch other information too: i.e. you have to update both the code_length and the attribute_length accordingly. If you do not do that the verifier surely will, and your manipulated class won’t be accepted at all.
Since we always have to perform a bunch of similar operations, we have devised a solution to the problem above that let us concentrate on just the main manipulations we need to do, i.e. adding a new set of bytecode instruction to a Code attribute. The burden of keeping in synch all of the different parts of the Java Class File data-structures is taken by the JEF-Dna framework.
The following example, taken from the implementation of our disconnection functionality, shows how simple it is to remove a couple of bytecode instructions from a Code attribute:
Collection<Attribute> atts = clonedMethodInfo.getAttributes();
for(Attribute a : atts) {
if(a instanceof CodeAttribute) {
((CodeAttribute)a).getObjectiveCode().remove(0);
((CodeAttribute)a).getObjectiveCode().remove(0);
}
}
The same enhancements obviously apply to all the other data-structures exposing similarities with the provided example.
Disconnection and Injection of “methods-chain”
Finally we have reached one of the fundamental milestones of the JEF Project. This preview release shows how it is possible to extract or deep-copy an auto-consistent “methods-chain” from a class, for injecting it into another class, thus generating new classes at runtime, or altering a class’ behavior at runtime.
This release comes with a test that shows you how this is possible.
After having downloaded the jefnet-0.5.0-rc2 archive of your choice, decompress it wherever you want, and type:
%> ant disconnectionTest
You’ll se an output like the following one:
As you can see, the test starts loading a class called SimpleClassD, and a class called SimpleClassE. What it does next is:
-
1. extracting a “method-chain” rooted in method “void foo()” of class SimpleClassD
-
2. injecting the previously extracted “method-chain” into SimpleClassE
Then the test presents the time spent in performing the two operations above, and finally the modified class SimpleClassE is instantiated and tested invoking the method at the root of the injected “method-chain”.
Obviously the test is interesting since the “SimpleClassD.foo():void” method requires other methods both defined in SimpleClassD and in other SimpleClassD’s inner-classes. This test shows how:
-
1. the JEF runtime is able to find out all these “requirements”
-
2. uses all the self-discovered requirements above to construct an auto-consistent “method-chain”
-
3. is able to produce brand new inner-classes if needed, as in the proof of concept we propose
For sake of clarity we now propose you the sources of the two class files above
/*
* SimpleClassD.java
*
* Created on February 26, 2006, 6:24 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package jef.test.dummies;
import java.util.Date;
/**
*
* @author frusso
*/
public class SimpleClassD {
/** Creates a new instance of SimpleClassD */
public SimpleClassD() {
}
//******************************************* methods
public void foo() {
System.out.println("*** TEST METHOD CHAIN & NESTED CLASSES ***");
System.out.println("Beginning of the 'public void foo()' method taken from class SimpleClassD\nToday is: "+date);
System.out.println(bar());
NestedClass nestedClass = new NestedClass();
nestedClass.nop();
testInnerClasses();
System.out.println("End of the 'public void foo()' method taken from class SimpleClassD");
}
private String bar() {
System.out.println("Beginning of the 'public String bar()' method taken from class SimpleClassD");
try {
// silly but necessary try/catch block for
// testing ExceptionsAttributes
increment();
} catch (Throwable ex) {
ex.printStackTrace();
}
System.out.println("New value of 'value' inner property is: "+value);
System.out.println("End of the 'public String bar()' method taken from class SimpleClassD");
return "This is the 'private String bar()' method's return value";
}
protected void increment() throws Throwable {
System.out.println("Beginning of the 'public void increment()' method taken from class SimpleClassD");
System.out.println("Old value of 'value' inner property is: "+value);
value++;
System.out.println("End of the 'public void increment()' method taken from class SimpleClassD");
}
private void testInnerClasses() {
System.out.println("*** TEST INNER CLASSES ***");
FirstInnerClass firstInnerClass = new FirstInnerClass();
firstInnerClass.firstInnerClassMethod();
secondInnerClass.secondInnerClassMethod();
thirdInnerClass.thirdInnerClassMethod();
FourthInnerClass fourthInnerClass = new FourthInnerClass();
fourthInnerClass.fourthInnerClassMethod();
}
//******************************************* properties
private int value = 10;
private SecondInnerClass secondInnerClass = new SecondInnerClass();
private ThirdInnerClass thirdInnerClass = new ThirdInnerClass();
private Date date = new Date(System.currentTimeMillis());
//******************************************* Inner classes
public class FirstInnerClass {
public void firstInnerClassMethod() {
System.out.println("This is the 'public void firstInnerClassMethod()' method taken from inner 'public class SimpleClassD$FirstInnerClass'");
}
}
protected class SecondInnerClass {
protected void secondInnerClassMethod() {
System.out.println("This is the 'protected void secondInnerClassMethod()' method taken from inner 'protected class SimpleClassD$SecondInnerClass'");
}
}
class ThirdInnerClass {
void thirdInnerClassMethod() {
System.out.println("This is the 'void thirdInnerClassMethod()' method taken from inner 'class SimpleClassD$ThirdInnerClass'");
}
}
private class FourthInnerClass {
private void fourthInnerClassMethod() {
System.out.println("This is the 'private void fourthInnerClassMethod()' method taken from inner 'private class SimpleClassD$FourthInnerClass'");
}
}
}
//******************************************* Nested class
class NestedClass {
public void nop() {
System.out.println("This is the 'public void nop()' method taken from nested class SimpleClassD.NestedClass");
}
}
/*
* SimpleClassE.java
*
* Created on February 26, 2006, 7:11 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package jef.test.dummies;
/**
*
* @author frusso
*/
public class SimpleClassE {
/** Creates a new instance of SimpleClassE */
public SimpleClassE() {
}
}
As you can see SimpleClassE only exposes its constructor before injection: all the other logic has been added at runtime. The most interesting and remarkable thing is that this is accomplished just telling the JTR runtime to “extract” the method “void foo()” from SimpleClassD, without telling/knowing anything about all the other methods actually required by “void foo()”.
Support for new Java Class File attributes (JSR-202)
At the moment we support the following attribute types:
-
1. Code
-
2. ConstantValue
-
3. Deprecated
-
4. EnclosingMethod
-
5. Exceptions
-
6. InnerClasses
-
7. LineNumberTable
-
8. LocalVariableTable
-
9. Signature
-
10. SourceDebugExtension
-
11. SourceFile
-
12. Synthetic
In order to be compliant with JSR-202 other attributes must be supported. We plan to ship them with the jefnet-0.5.0-alpha final release.