Skip to content

Commit a258a3d

Browse files
committed
Merge branch 'memory-leak' into main
2 parents d80b37b + 2e2384d commit a258a3d

File tree

3 files changed

+66
-4
lines changed

3 files changed

+66
-4
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ In the long run, we'll use the usual semantic versioning.
182182

183183
For 0.x.y releases, all bets are off, and no backward compatibility is guaranteed.
184184

185+
Releases with four numbers, like a.b.c.d, are "tail" releases containing
186+
important fixes to old releases.
187+
185188
### Logo
186189

187190
Logo was derived from this public-domain image: https://openclipart.org/detail/44023/small-trees-bushes

bosk-core/src/main/java/works/bosk/bytecode/ClassBuilder.java

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
import java.io.FileOutputStream;
44
import java.io.IOException;
55
import java.lang.invoke.CallSite;
6-
import java.lang.invoke.ConstantCallSite;
76
import java.lang.invoke.MethodHandles;
87
import java.lang.invoke.MethodType;
98
import java.lang.reflect.Constructor;
109
import java.lang.reflect.InvocationTargetException;
1110
import java.lang.reflect.Method;
11+
import java.util.ArrayList;
12+
import java.util.List;
1213
import java.util.Map;
1314
import java.util.Objects;
1415
import java.util.concurrent.ConcurrentHashMap;
@@ -27,13 +28,16 @@
2728
import static java.lang.System.identityHashCode;
2829
import static java.lang.reflect.Modifier.isStatic;
2930
import static java.util.Objects.requireNonNull;
31+
import static java.util.stream.Collectors.joining;
3032
import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
3133
import static org.objectweb.asm.Opcodes.ACC_FINAL;
34+
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
3235
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
3336
import static org.objectweb.asm.Opcodes.ACC_SUPER;
3437
import static org.objectweb.asm.Opcodes.ALOAD;
3538
import static org.objectweb.asm.Opcodes.CHECKCAST;
3639
import static org.objectweb.asm.Opcodes.DUP;
40+
import static org.objectweb.asm.Opcodes.GETFIELD;
3741
import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
3842
import static org.objectweb.asm.Opcodes.IFEQ;
3943
import static org.objectweb.asm.Opcodes.IFNE;
@@ -45,6 +49,7 @@
4549
import static org.objectweb.asm.Opcodes.ISTORE;
4650
import static org.objectweb.asm.Opcodes.NEW;
4751
import static org.objectweb.asm.Opcodes.POP;
52+
import static org.objectweb.asm.Opcodes.PUTFIELD;
4853
import static org.objectweb.asm.Opcodes.RETURN;
4954
import static org.objectweb.asm.Opcodes.SWAP;
5055
import static org.objectweb.asm.Opcodes.V21;
@@ -70,6 +75,8 @@ public final class ClassBuilder<T> {
7075
private MethodBuilder currentMethod = null;
7176
private int currentLineNumber = -1;
7277

78+
private final List<CurriedField> curriedFields = new ArrayList<>();
79+
7380
/**
7481
* @param className The simple name of the generated class;
7582
* the actual name will be given the prefix <code>GENERATED_</code> to identify it as not corresponding to any source file
@@ -108,13 +115,21 @@ public void beginClass() {
108115
}
109116

110117
private void generateConstructor(StackWalker.StackFrame sourceFileOrigin) {
111-
MethodVisitor ctor = classVisitor.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
118+
String ctorParameterDescriptor = curriedFields.stream()
119+
.map(CurriedField::typeDescriptor)
120+
.collect(joining());
121+
MethodVisitor ctor = classVisitor.visitMethod(ACC_PUBLIC, "<init>", "(" + ctorParameterDescriptor + ")V", null, null);
112122
ctor.visitCode();
113123
Label label = new Label();
114124
ctor.visitLabel(label);
115125
ctor.visitLineNumber(sourceFileOrigin.getLineNumber(), label);
116126
ctor.visitVarInsn(ALOAD, 0);
117127
ctor.visitMethodInsn(INVOKESPECIAL, superClassName, "<init>", "()V", false);
128+
for (CurriedField field: curriedFields) {
129+
ctor.visitVarInsn(ALOAD, 0);
130+
ctor.visitVarInsn(ALOAD, field.slot());
131+
ctor.visitFieldInsn(PUTFIELD, slashyName, field.name(), field.typeDescriptor());
132+
}
118133
ctor.visitInsn(RETURN);
119134
ctor.visitMaxs(0, 0); // Computed automatically
120135
ctor.visitEnd();
@@ -196,8 +211,36 @@ public LocalVariable popToLocal(Type type) {
196211
* be accessible from the generated class)
197212
*/
198213
public void pushObject(String name, Object object, Class<?> type) {
214+
CurriedField field = curry(name, object, type);
215+
beginPush();
216+
methodVisitor().visitVarInsn(ALOAD, 0);
217+
methodVisitor().visitFieldInsn(GETFIELD, slashyName, field.name(), field.typeDescriptor());
218+
}
219+
220+
private CurriedField curry(String name, Object object, Class<?> type) {
199221
type.cast(object);
200-
invokeDynamic(name, new ConstantCallSite(MethodHandles.constant(type, object)));
222+
for (CurriedField candidate: curriedFields) {
223+
if (candidate.value() == object) {
224+
return candidate;
225+
}
226+
}
227+
228+
int ctorParameterSlot = 1 + curriedFields.size();
229+
CurriedField result = new CurriedField(
230+
ctorParameterSlot,
231+
"CURRIED" + ctorParameterSlot + "_" + name,
232+
Type.getDescriptor(type),
233+
object);
234+
curriedFields.add(result);
235+
236+
classVisitor.visitField(
237+
ACC_PRIVATE | ACC_FINAL,
238+
result.name(),
239+
result.typeDescriptor(),
240+
null, null
241+
).visitEnd();
242+
243+
return result;
201244
}
202245

203246
public void invokeDynamic(String name, CallSite callSite) {
@@ -367,8 +410,9 @@ public T buildInstance() {
367410
Constructor<?> ctor = new CustomClassLoader()
368411
.loadThemBytes(dottyName, bytes)
369412
.getConstructors()[0];
413+
Object[] args = curriedFields.stream().map(CurriedField::value).toArray();
370414
try {
371-
return supertype.cast(ctor.newInstance());
415+
return supertype.cast(ctor.newInstance(args));
372416
} catch (InstantiationException | IllegalAccessException | VerifyError | InvocationTargetException e) {
373417
throw new AssertionError("Should be able to instantiate the generated class", e);
374418
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package works.bosk.bytecode;
2+
3+
/**
4+
* An object reference that should be passed to the constructor of a generated
5+
* class so that it can be referenced from method bodies via
6+
* {@link org.objectweb.asm.Opcodes#GETFIELD GETFIELD}.
7+
*
8+
* @param slot The parameter slot in which this will arrive in the constructor
9+
*/
10+
public record CurriedField(
11+
int slot,
12+
String name,
13+
String typeDescriptor,
14+
Object value
15+
) { }

0 commit comments

Comments
 (0)