Skip to content

Commit 4de6f8e

Browse files
committed
Added ClassTransformers back!
1 parent 67a1ec4 commit 4de6f8e

File tree

7 files changed

+164
-4
lines changed

7 files changed

+164
-4
lines changed

src/main/java/module-info.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@
22
requires jdk.unsupported;
33
requires java.scripting;
44

5-
exports org.mangorage.bootstrap;
5+
opens org.mangorage.bootstrap;
6+
exports org.mangorage.bootstrap.api;
7+
8+
uses org.mangorage.bootstrap.api.IClassTransformer;
69
}

src/main/java/org/mangorage/bootstrap/Bootstrap.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ public static void main(String[] args) throws IOException {
4646
final var moduleLayerController = ModuleLayer.defineModules(moduleCfg, List.of(ModuleLayer.boot()), s -> moduleCl);
4747
final var moduleLayer = moduleLayerController.layer();
4848

49-
50-
System.out.println("Imagiine we worked!");
5149
Thread.currentThread().setContextClassLoader(moduleCl);
5250

51+
moduleCl.loadTransformers();
52+
5353
callMain("org.mangorage.entrypoint.MangoBotCore", args, moduleLayer.findModule("org.mangorage.mangobotcore").get());
5454
}
5555
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.mangorage.bootstrap.api;
2+
3+
public interface IClassTransformer {
4+
TransformResult transform(byte[] classData);
5+
6+
String getName();
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.mangorage.bootstrap.api;
2+
3+
public record TransformResult(byte[] classData, TransformerFlag flag) {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.mangorage.bootstrap.api;
2+
3+
public enum TransformerFlag {
4+
NO_REWRITE,
5+
SIMPLE_REWRITE,
6+
FULL_REWRITE;
7+
8+
public TransformResult of(byte[] classData) {
9+
return new TransformResult(classData, this);
10+
}
11+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package org.mangorage.bootstrap.internal;
2+
3+
import org.mangorage.bootstrap.api.IClassTransformer;
4+
import org.mangorage.bootstrap.api.TransformResult;
5+
import org.mangorage.bootstrap.api.TransformerFlag;
6+
7+
import java.io.IOException;
8+
import java.util.ArrayList;
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
12+
import java.util.concurrent.atomic.AtomicReference;
13+
14+
public final class ClassTransformers {
15+
private final Map<String, Class<?>> classes = new HashMap<>(); // Transformed Class's
16+
private final List<IClassTransformer> transformers = new ArrayList<>(); // Transformer's
17+
private final ClassLoader loader;
18+
19+
public ClassTransformers(ClassLoader loader) {
20+
this.loader = loader;
21+
}
22+
23+
public void add(String name, Class<?> clz) {
24+
classes.put(name, clz);
25+
}
26+
27+
public void add(IClassTransformer transformer) {
28+
transformers.add(transformer);
29+
}
30+
31+
public boolean isEmpty() {
32+
return transformers.isEmpty();
33+
}
34+
35+
private byte[] getClassBytes(String clazz) {
36+
try {
37+
String className = clazz.replace('.', '/');
38+
String classFileName = className + ".class";
39+
40+
try (var is = loader.getResourceAsStream(classFileName)) {
41+
if (is != null) return is.readAllBytes();
42+
}
43+
} catch (IOException e) {
44+
return null;
45+
}
46+
return null;
47+
}
48+
49+
public byte[] transform(String name) {
50+
byte[] originalClassData = getClassBytes(name);
51+
52+
AtomicReference<TransformResult> result = new AtomicReference<>(TransformerFlag.NO_REWRITE.of(originalClassData));
53+
AtomicReference<IClassTransformer> _transformer = new AtomicReference<>();
54+
55+
for (IClassTransformer transformer : transformers) {
56+
result.set(transformer.transform(originalClassData));
57+
if (result.get().flag() != TransformerFlag.NO_REWRITE) {
58+
_transformer.set(transformer);
59+
break;
60+
}
61+
}
62+
63+
if (result.get().flag() != TransformerFlag.NO_REWRITE && _transformer.get() != null) {
64+
System.out.println("%s Transformed %s".formatted(_transformer.get().getName(), name));
65+
return result.get().classData();
66+
}
67+
68+
return null;
69+
}
70+
71+
public boolean containsClass(String name) {
72+
return classes.containsKey(name);
73+
}
74+
75+
public Class<?> getClazz(String string) {
76+
return classes.get(string);
77+
}
78+
}

src/main/java/org/mangorage/bootstrap/internal/MangoLoader.java

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.mangorage.bootstrap.internal;
22

3+
import org.mangorage.bootstrap.api.IClassTransformer;
34

45
import java.io.IOException;
56
import java.lang.module.ModuleReader;
@@ -9,6 +10,7 @@
910
import java.nio.ByteBuffer;
1011
import java.util.HashMap;
1112
import java.util.Map;
13+
import java.util.ServiceLoader;
1214
import java.util.Set;
1315

1416
public final class MangoLoader extends URLClassLoader {
@@ -17,6 +19,7 @@ public final class MangoLoader extends URLClassLoader {
1719
ClassLoader.registerAsParallelCapable();
1820
}
1921

22+
private ClassTransformers transformers = new ClassTransformers(this);
2023
private final Map<String, LoadedModule> moduleMap = new HashMap<>();
2124
private final Map<String, LoadedModule> localPackageToModule = new HashMap<>();
2225

@@ -41,6 +44,58 @@ public MangoLoader(URL[] urls, Set<ResolvedModule> modules, ClassLoader parent)
4144
});
4245
}
4346

47+
public void loadTransformers() {
48+
ServiceLoader.load(IClassTransformer.class)
49+
.stream()
50+
.forEach(provider -> {
51+
transformers.add(provider.get());
52+
});
53+
}
54+
55+
@Override
56+
protected Class<?> findClass(String name) throws ClassNotFoundException {
57+
if (transformers == null || transformers.isEmpty())
58+
return super.findClass(name);
59+
60+
if (transformers.containsClass(name))
61+
return transformers.getClazz(name);
62+
63+
byte[] originalBytes = getClassBytes(name);
64+
65+
if (originalBytes == null) {
66+
throw new ClassNotFoundException("Failed to load original class bytes for " + name);
67+
}
68+
69+
byte[] arr = transformers.transform(name);
70+
if (arr != null) {
71+
Class<?> clz = defineClass(name, arr);
72+
transformers.add(name, clz);
73+
return clz;
74+
}
75+
76+
return super.findClass(name);
77+
}
78+
79+
private byte[] getClassBytes(String clazz) {
80+
try {
81+
String className = clazz.replace('.', '/');
82+
String classFileName = className + ".class";
83+
84+
try (var is = getResourceAsStream(classFileName)) {
85+
if (is != null) return is.readAllBytes();
86+
}
87+
} catch (IOException e) {
88+
return null;
89+
}
90+
return null;
91+
}
92+
93+
private Class<?> defineClass(String name, byte[] bytes) {
94+
return super.defineClass(name, bytes, 0, bytes.length);
95+
}
96+
97+
98+
4499
@Override
45100
protected URL findResource(String moduleName, String name) throws IOException {
46101
final var loadedModule = moduleMap.get(moduleName);
@@ -58,8 +113,11 @@ protected URL findResource(String moduleName, String name) throws IOException {
58113
protected Class<?> findClass(String moduleName, String name) {
59114
Class<?> c = null;
60115
LoadedModule loadedModule = findLoadedModule(name);
61-
if (loadedModule != null && loadedModule.getModuleReference().descriptor().name().equals(moduleName))
116+
if (loadedModule != null && loadedModule.getModuleReference().descriptor().name().equals(moduleName)) {
62117
c = defineClass(name, loadedModule);
118+
} else if (loadedModule != null) {
119+
throw new IllegalArgumentException("Expected Class '%s' in module '%s', instead was in '%s'".formatted(name, moduleName, loadedModule.getModuleReference().descriptor().name()));
120+
}
63121
return c;
64122
}
65123

0 commit comments

Comments
 (0)