Skip to content

Commit

Permalink
Add Javassist
Browse files Browse the repository at this point in the history
  • Loading branch information
T5750 committed Jan 30, 2020
1 parent 8587d3d commit e0d62ef
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 2 deletions.
3 changes: 2 additions & 1 deletion doc/source/jdk8/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ Java™ Platform Standard Ed. 8
:numbered: 0

jdkJre
Lambda
Lambda
javassistTutorial
136 changes: 136 additions & 0 deletions doc/source/jdk8/javassistTutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Javassist Tutorial

## 1. Reading and writing bytecode
The class `Javassist.CtClass` is an abstract representation of a class file. A `CtClass` (compile-time class) object is a handle for dealing with a class file.
```
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Rectangle");
cc.setSuperclass(pool.get("test.Point"));
cc.writeFile();
```

### Defining a new class
To define a new class from scratch, `makeClass()` must be called on a `ClassPool`.
```
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Point");
```

### Frozen classes
If a `CtClass` object is converted into a class file by `writeFile()`, `toClass()`, or `toBytecode()`, Javassist freezes that `CtClass` object. Further modifications of that `CtClass` object are not permitted. This is for warning the developers when they attempt to modify a class file that has been already loaded since the JVM does not allow reloading a class.

To disallow pruning a particular `CtClass`, `stopPruning()` must be called on that object in advance

### Class search path
```
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath("/usr/local/javalib");
```

## 2. ClassPool
A `ClassPool` object is a container of `CtClass` objects. Once a `CtClass` object is created, it is recorded in a `ClassPool` for ever. This is because a compiler may need to access the `CtClass` object later when it compiles source code that refers to the class represented by that `CtClass`.

### Avoid out of memory
```
CtClass cc = ... ;
cc.writeFile();
cc.detach();
```
```
ClassPool cp = new ClassPool(true);
// if needed, append an extra search path by appendClassPath()
ClassPool cp = new ClassPool();
cp.appendSystemPath(); // or append another path by appendClassPath()
```

### Cascaded ClassPools
Multiple `ClassPool` objects can be cascaded like `java.lang.ClassLoader`.
```
ClassPool parent = ClassPool.getDefault();
ClassPool child = new ClassPool(parent);
child.insertClassPath("./classes");
```
```
ClassPool parent = ClassPool.getDefault();
ClassPool child = new ClassPool(parent);
child.appendSystemPath(); // the same class path as the default one.
child.childFirstLookup = true; // changes the behavior of the child.
```

### Changing a class name for defining a new class
```
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.setName("Pair");
```
```
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
CtClass cc1 = pool.get("Point"); // cc1 is identical to cc.
cc.setName("Pair");
CtClass cc2 = pool.get("Pair"); // cc2 is identical to cc.
CtClass cc3 = pool.get("Point"); // cc3 is not identical to cc.
```

### Renaming a frozen class for defining a new class
Once a `CtClass` object is converted into a class file by `writeFile()` or `toBytecode()`, Javassist rejects further modifications of that `CtClass` object.
```
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.writeFile();
CtClass cc2 = pool.getAndRename("Point", "Pair");
```
If `getAndRename()` is called, the `ClassPool` first reads `Point.class` for creating a new `CtClass` object representing `Point` class. However, it renames that `CtClass` object from `Point` to `Pair` before it records that `CtClass` object in a hash table. Thus `getAndRename()` can be executed after `writeFile()` or `toBytecode()` is called on the the `CtClass` object representing `Point` class.

## 3. Class loader
1. Get a `CtClass` object by calling `ClassPool.get()`,
2. Modify it, and
3. Call `writeFile()` or `toBytecode()` on that `CtClass` object to obtain a modified class file.

### 3.1 The toClass method in CtClass


### 3.2 Class loading in Java


### 3.3 Using javassist.Loader


### 3.4 Writing a class loader


### 3.5 Modifying a system class


### 3.6 Reloading a class at runtime


## 4. Introspection and customization



## 5. Bytecode level API



## 6. Generics



## 7. Varargs



## 8. J2ME



## 9. Boxing/Unboxing



## 10. Debug


## References
- [Getting Started with Javassist](http://www.javassist.org/tutorial/tutorial.html)
5 changes: 4 additions & 1 deletion jdk8/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Contents
- [Lambda](../doc/source/jdk8/Lambda.md)
- [JDK, JRE, JVM, JSE, JEE, JME](../doc/source/jdk8/jdkJre.md)
- [Javassist Tutorial](../doc/source/jdk8/javassistTutorial.md)

## Runtime Environment
- [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)
- [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)
- [Javassist 3.26.x](https://github.com/jboss-javassist/javassist)
16 changes: 16 additions & 0 deletions jdk8/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,24 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<javassist.version>3.26.0-GA</javassist.version>
<junit.version>4.11</junit.version>
</properties>

<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>${javassist.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>${project.artifactId}</finalName>
<plugins>
Expand Down
46 changes: 46 additions & 0 deletions jdk8/src/test/java/t5750/javassist/ClassPoolTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package t5750.javassist;

import org.junit.Before;
import org.junit.Test;

import javassist.ClassPool;
import javassist.CtClass;
import t5750.javassist.util.JavassistUtil;

public class ClassPoolTest {
private ClassPool pool;

@Before
public void setup() {
pool = ClassPool.getDefault();
}

@Test
public void main() throws Exception {
CtClass cc = pool.get(JavassistUtil.DOMAIN + "Rectangle");
cc.setSuperclass(pool.get(JavassistUtil.POINT));
cc.writeFile();
// Avoid out of memory
cc.detach();
// Cascaded ClassPools
ClassPool child = new ClassPool(pool);
child.insertClassPath("./classes");
// Changing a class name for defining a new class
cc = pool.get(JavassistUtil.POINT);
CtClass cc1 = pool.get(JavassistUtil.POINT); // cc1 is identical to cc.
cc.setName(JavassistUtil.DOMAIN + "Pair");
// cc2 is identical to cc.
CtClass cc2 = pool.get(JavassistUtil.DOMAIN + "Pair");
// cc3 is not identical to cc.
CtClass cc3 = pool.get(JavassistUtil.POINT);
cc.writeFile();
System.out.println("cc cc1: " + cc.equals(cc1));
System.out.println("cc cc2: " + cc.equals(cc2));
System.out.println("cc2 cc3: " + cc2.equals(cc3));
// Renaming a frozen class for defining a new class
// cc.setName("Pair"); // wrong since writeFile() has been called.
CtClass ccPointPair = pool.getAndRename(JavassistUtil.POINT,
JavassistUtil.DOMAIN + "PointPair");
ccPointPair.writeFile();
}
}
51 changes: 51 additions & 0 deletions jdk8/src/test/java/t5750/javassist/ReadWriteBytecodeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package t5750.javassist;

import org.junit.Before;
import org.junit.Test;

import javassist.ClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.URLClassPath;
import t5750.javassist.util.JavassistUtil;

public class ReadWriteBytecodeTest {
private ClassPool pool;
private ClassPool poolNew;

@Before
public void setup() {
pool = ClassPool.getDefault();
poolNew = ClassPool.getDefault();
}

@Test
public void main() throws Exception {
CtClass cc = pool.get(JavassistUtil.DOMAIN + "Rectangle");
cc.setSuperclass(pool.get("java.awt.Point"));
cc.writeFile();
byte[] b = cc.toBytecode();
Class clazz = cc.toClass();
System.out.println(clazz.getSuperclass());
// Defining a new class
CtClass ccNew = poolNew.makeClass(JavassistUtil.DOMAIN + "Point");
ccNew.writeFile();
// Frozen classes
ccNew.defrost();
ccNew.setSuperclass(poolNew.get("java.awt.geom.Point2D"));
ccNew.stopPruning(true);
ccNew.writeFile();
}

@Test
public void classSearchPath() throws Exception {
// Class search path
// http://localhost:8000/test2/IncOp.class
ClassPath cp = new URLClassPath("localhost", 8000, "/", null);
poolNew.insertClassPath(cp);
CtClass ccPath = poolNew
.makeClass(JavassistUtil.DOMAIN + "ClassSearchPath");
ccPath.setSuperclass(poolNew.get("test2.IncOp"));
ccPath.writeFile();
}
}
6 changes: 6 additions & 0 deletions jdk8/src/test/java/t5750/javassist/domain/Rectangle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package t5750.javassist.domain;

public class Rectangle {
private int width;
private int height;
}
6 changes: 6 additions & 0 deletions jdk8/src/test/java/t5750/javassist/util/JavassistUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package t5750.javassist.util;

public final class JavassistUtil {
public static final String DOMAIN = "t5750.javassist.domain.";
public static final String POINT = "java.awt.Point";
}

0 comments on commit e0d62ef

Please sign in to comment.