Skip to content

Commit 2319a02

Browse files
committed
remove shouldTransform(className) from transformer.
1 parent e98cd24 commit 2319a02

7 files changed

Lines changed: 143 additions & 94 deletions

File tree

src/main/java/me/nallar/javatransformer/api/JavaTransformer.java

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,6 @@ public void addTransformer(@NonNull Transformer.TargetedTransformer t) {
202202
}
203203

204204
public void addTransformer(@NonNull String s, @NonNull Transformer t) {
205-
if (!t.shouldTransform(s)) {
206-
throw new IllegalArgumentException("Transformer " + t + " must transform class of name " + s);
207-
}
208205
if (classTransformers.get(s).contains(t)) {
209206
throw new IllegalArgumentException("Transformer " + t + " has already been added for class " + s);
210207
}
@@ -227,64 +224,73 @@ public Supplier<byte[]> transformJava(@NonNull Supplier<byte[]> data, @NonNull S
227224
if (!shouldTransform(name))
228225
return data;
229226

230-
CompilationUnit cu;
231-
try {
232-
cu = JavaParser.parse(new ByteArrayInputStream(data.get()));
233-
} catch (ParseException e) {
234-
throw new RuntimeException(e);
235-
}
236-
237-
String packageName = cu.getPackage().getName().getName();
238-
for (TypeDeclaration typeDeclaration : cu.getTypes()) {
239-
if (!(typeDeclaration instanceof ClassOrInterfaceDeclaration)) {
240-
continue;
227+
CachingSupplier<ClassOrInterfaceDeclaration> supplier = CachingSupplier.of(() -> {
228+
CompilationUnit cu;
229+
try {
230+
cu = JavaParser.parse(new ByteArrayInputStream(data.get()));
231+
} catch (ParseException e) {
232+
throw new RuntimeException(e);
241233
}
242-
val classDeclaration = (ClassOrInterfaceDeclaration) typeDeclaration;
243234

244-
String shortClassName = classDeclaration.getName();
245-
if ((packageName + '.' + shortClassName).equalsIgnoreCase(name)) {
246-
transformJar(new SourceInfo(classDeclaration));
235+
String packageName = cu.getPackage().getName().getName();
236+
for (TypeDeclaration typeDeclaration : cu.getTypes()) {
237+
if (!(typeDeclaration instanceof ClassOrInterfaceDeclaration)) {
238+
continue;
239+
}
240+
ClassOrInterfaceDeclaration classDeclaration = (ClassOrInterfaceDeclaration) typeDeclaration;
241+
242+
String shortClassName = classDeclaration.getName();
243+
if ((packageName + '.' + shortClassName).equalsIgnoreCase(name)) {
244+
return classDeclaration;
245+
}
247246
}
248-
}
249247

250-
return () -> cu.toString().getBytes(Charset.forName("UTF-8"));
248+
throw new Error("Couldn't find any class or interface declaration matching expected name " + name);
249+
});
250+
251+
transformClassInfo(new SourceInfo(supplier, name));
252+
253+
return supplier.isCached() ? () -> supplier.get().toString().getBytes(Charset.forName("UTF-8")) : data;
254+
251255
}
252256

253257
public Supplier<byte[]> transformClass(@NonNull Supplier<byte[]> data, @NonNull String name) {
258+
System.out.println("Transforming " + name);
254259
if (!shouldTransform(name))
255260
return data;
256261

257-
ClassNode node = new ClassNode();
258-
ClassReader reader = new ClassReader(data.get());
259-
reader.accept(node, ClassReader.EXPAND_FRAMES);
262+
Holder<ClassReader> readerHolder = new Holder<>();
263+
CachingSupplier<ClassNode> supplier = CachingSupplier.of(() -> {
264+
ClassNode node = new ClassNode();
265+
ClassReader reader = new ClassReader(data.get());
266+
reader.accept(node, ClassReader.EXPAND_FRAMES);
260267

261-
transformJar(new ByteCodeInfo(node));
268+
readerHolder.value = reader;
269+
270+
return node;
271+
});
262272

263-
ClassWriter classWriter = new ClassWriter(reader, 0);
264-
node.accept(classWriter);
265-
return classWriter::toByteArray;
273+
transformClassInfo(new ByteCodeInfo(supplier, name));
274+
275+
if (!supplier.isCached())
276+
return data;
277+
278+
return () -> {
279+
ClassWriter classWriter = new ClassWriter(readerHolder.value, 0);
280+
supplier.get().accept(classWriter);
281+
return classWriter.toByteArray();
282+
};
266283
}
267284

268-
private void transformJar(ClassInfo editor) {
269-
val name = editor.getName();
285+
private void transformClassInfo(ClassInfo editor) {
270286
transformers.forEach((x) -> {
271-
if (x.shouldTransform(name)) {
272-
x.transform(editor);
273-
}
287+
x.transform(editor);
274288
});
275-
classTransformers.get(name).forEach((it) -> it.transform(editor));
289+
classTransformers.get(editor.getName()).forEach((it) -> it.transform(editor));
276290
}
277291

278292
private boolean shouldTransform(String className) {
279-
if (!classTransformers.get(className).isEmpty())
280-
return true;
281-
282-
for (Transformer transformer : transformers) {
283-
if (transformer.shouldTransform(className))
284-
return true;
285-
}
286-
287-
return false;
293+
return !transformers.isEmpty() || !classTransformers.get(className).isEmpty();
288294
}
289295

290296
private Supplier<byte[]> transformBytes(String relativeName, Supplier<byte[]> dataSupplier) {
@@ -342,4 +348,41 @@ public String toString() {
342348
return map.toString();
343349
}
344350
}
351+
352+
@Data
353+
private static class CachingSupplier<T> implements Supplier<T> {
354+
@NonNull
355+
private final Supplier<T> wrapped;
356+
private transient T value;
357+
358+
protected CachingSupplier(Supplier<T> wrapped) {
359+
this.wrapped = wrapped;
360+
}
361+
362+
public static <T> CachingSupplier<T> of(Supplier<T> wrapped) {
363+
return new CachingSupplier<>(wrapped);
364+
}
365+
366+
@Override
367+
public T get() {
368+
T value = this.value;
369+
370+
if (value == null) {
371+
synchronized (this) {
372+
if (this.value == null)
373+
this.value = value = Objects.requireNonNull(wrapped.get());
374+
}
375+
}
376+
377+
return value;
378+
}
379+
380+
public boolean isCached() {
381+
return value != null;
382+
}
383+
}
384+
385+
private static class Holder<T> {
386+
public T value;
387+
}
345388
}

src/main/java/me/nallar/javatransformer/api/Transformer.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@
33
import java.util.*;
44

55
public interface Transformer {
6-
/**
7-
* Determines whether a class should be transformed
8-
*
9-
* @param className Full class name, eg. java.lang.String
10-
* @return Whether the given class should be transformed
11-
*/
12-
default boolean shouldTransform(String className) {
13-
return true;
14-
}
15-
166
/**
177
* @param editor editor instance associated with a class
188
*/
@@ -23,9 +13,5 @@ interface TargetedTransformer extends Transformer {
2313
* @return List of classes which this transformer will run on
2414
*/
2515
Collection<String> getTargetClasses();
26-
27-
default boolean shouldTransform(String className) {
28-
return getTargetClasses().contains(className);
29-
}
3016
}
3117
}

src/main/java/me/nallar/javatransformer/internal/ByteCodeInfo.java

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.nallar.javatransformer.internal;
22

3+
import lombok.AllArgsConstructor;
34
import lombok.Data;
45
import lombok.Getter;
56
import lombok.val;
@@ -11,33 +12,37 @@
1112
import org.objectweb.asm.tree.MethodNode;
1213

1314
import java.util.*;
15+
import java.util.function.*;
1416
import java.util.stream.*;
1517

1618
@Data
19+
@AllArgsConstructor
1720
@SuppressWarnings("unchecked")
1821
public class ByteCodeInfo implements ClassInfoStreams {
19-
private final ClassNode node;
22+
private final Supplier<ClassNode> node;
2023
@Getter(lazy = true)
2124
private final List<Annotation> annotations = getAnnotationsInternal();
25+
private String className;
2226

2327
@Override
2428
public String getName() {
25-
return node.name.replace('/', '.');
29+
return className;
2630
}
2731

2832
@Override
2933
public void setName(String name) {
30-
node.name = name.replace('.', '/');
34+
className = name;
35+
node.get().name = name.replace('.', '/');
3136
}
3237

3338
@Override
3439
public AccessFlags getAccessFlags() {
35-
return new AccessFlags(node.access);
40+
return new AccessFlags(node.get().access);
3641
}
3742

3843
@Override
3944
public void setAccessFlags(AccessFlags accessFlags) {
40-
node.access = accessFlags.access;
45+
node.get().access = accessFlags.access;
4146
}
4247

4348
public void add(MethodInfo method) {
@@ -66,7 +71,7 @@ public void add(MethodInfo method) {
6671
MethodInfo info = new MethodNodeInfo(node);
6772
info.setAll(method);
6873
}
69-
this.node.methods.add(node);
74+
this.node.get().methods.add(node);
7075
}
7176

7277
public void add(FieldInfo field) {
@@ -84,7 +89,7 @@ public void add(FieldInfo field) {
8489
val nodeInfo = new FieldNodeInfo(node);
8590
nodeInfo.setAll(field);
8691
}
87-
this.node.fields.add(node);
92+
this.node.get().fields.add(node);
8893
}
8994

9095
@Override
@@ -94,7 +99,7 @@ public void remove(MethodInfo method) {
9499
if (methodNodeInfo == null)
95100
throw new RuntimeException("Method " + method + " can not be removed as it is not present");
96101

97-
node.methods.remove(methodNodeInfo.node);
102+
node.get().methods.remove(methodNodeInfo.node);
98103
}
99104

100105
@Override
@@ -104,29 +109,29 @@ public void remove(FieldInfo field) {
104109
if (fieldNodeInfo == null)
105110
throw new RuntimeException("Field " + field + " can not be removed as it is not present");
106111

107-
node.fields.remove(fieldNodeInfo.node);
112+
node.get().fields.remove(fieldNodeInfo.node);
108113
}
109114

110115
@Override
111116
public Type getSuperType() {
112-
return new Type("L" + node.superName + ";");
117+
return new Type("L" + node.get().superName + ";");
113118
}
114119

115120
@Override
116121
public List<Type> getInterfaceTypes() {
117-
return node.interfaces.stream().map((it) -> new Type("L" + it + ";")).collect(Collectors.toList());
122+
return node.get().interfaces.stream().map((it) -> new Type("L" + it + ";")).collect(Collectors.toList());
118123
}
119124

120125
public Stream<MethodInfo> getMethodStream() {
121-
return node.methods.stream().map(MethodNodeInfo::new);
126+
return node.get().methods.stream().map(MethodNodeInfo::new);
122127
}
123128

124129
public Stream<FieldInfo> getFieldStream() {
125-
return node.fields.stream().map(FieldNodeInfo::new);
130+
return node.get().fields.stream().map(FieldNodeInfo::new);
126131
}
127132

128133
private List<Annotation> getAnnotationsInternal() {
129-
return CollectionUtil.union(node.invisibleAnnotations, node.visibleAnnotations).map(AnnotationParser::annotationFromAnnotationNode).collect(Collectors.toList());
134+
return CollectionUtil.union(node.get().invisibleAnnotations, node.get().visibleAnnotations).map(AnnotationParser::annotationFromAnnotationNode).collect(Collectors.toList());
130135
}
131136

132137
@Override

0 commit comments

Comments
 (0)