Skip to content

Commit 010d03b

Browse files
authored
Merge pull request #1 from andreaTP/fix-memory-leak
Fix the memory leak
2 parents 8e1aed5 + 1e17c1b commit 010d03b

4 files changed

Lines changed: 83 additions & 70 deletions

File tree

java-wasm/src/main/java/org/prism/Prism.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,9 @@ public ParseResult serializeParse(byte[] packedOptions, String source) {
3333
}
3434

3535
@Override
36-
public abstract void close();
36+
public void close() {
37+
if (wasi != null) {
38+
wasi.close();
39+
}
40+
}
3741
}

java-wasm/src/main/java/org/prism/PrismAOT.java

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,39 @@
99

1010
import java.nio.charset.StandardCharsets;
1111

12-
@WasmModuleInterface(WasmResource.absoluteFile)
1312
public class PrismAOT extends Prism {
14-
private final Instance instance;
1513
private final Prism_ModuleExports exports;
14+
private final Instance instance;
1615

1716
public PrismAOT() {
1817
super();
19-
WasmModule module = PrismParser.load();
20-
PrismParser parser = new PrismParser();
21-
instance = Instance.builder(module)
22-
.withMemoryFactory(limits -> new ByteArrayMemory(new MemoryLimits(10, MemoryLimits.MAX_PAGES)))
23-
.withMachineFactory(parser.machineFactory())
18+
instance = Instance.builder(PrismParser.load())
19+
.withMemoryFactory(ByteArrayMemory::new)
20+
.withMachineFactory(PrismParser::create)
2421
.withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build())
2522
.build();
2623
exports = new Prism_ModuleExports(instance);
2724
}
2825

29-
public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) {
26+
@Override
27+
public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLength) {
3028
int sourcePointer = 0;
3129
int optionsPointer = 0;
3230
int bufferPointer = 0;
3331
byte[] result;
3432
try {
35-
sourcePointer = exports.calloc(sourceLength, 1);
36-
exports.memory().write(sourcePointer, source);
33+
sourcePointer = exports.calloc(1, sourceLength + 1);
34+
instance.memory().write(sourcePointer, sourceBytes, 0, sourceLength);
35+
instance.memory().writeByte(sourcePointer + sourceLength, (byte) 0);
3736

3837
optionsPointer = exports.calloc(1, packedOptions.length);
39-
exports.memory().write(optionsPointer, packedOptions);
38+
instance.memory().write(optionsPointer, packedOptions);
4039

4140
bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1);
4241
exports.pmBufferInit(bufferPointer);
4342

44-
exports.pmSerializeParse(bufferPointer, sourcePointer, sourceLength, optionsPointer);
43+
exports.pmSerializeParse(
44+
bufferPointer, sourcePointer, sourceLength, optionsPointer);
4545

4646
result = instance.memory().readBytes(
4747
exports.pmBufferValue(bufferPointer),
@@ -62,18 +62,8 @@ public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) {
6262
return result;
6363
}
6464

65-
public ParseResult serializeParse(byte[] packedOptions, String source) {
66-
var sourceBytes = source.getBytes(StandardCharsets.US_ASCII);
67-
68-
byte[] result = serialize(packedOptions, sourceBytes, sourceBytes.length);
69-
70-
return Loader.load(result, sourceBytes);
71-
}
72-
73-
@Override
74-
public void close() {
75-
if (wasi != null) {
76-
wasi.close();
77-
}
65+
// DEBUG CHECK
66+
public int memorySize() {
67+
return instance.memory().pages();
7868
}
7969
}
Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,69 @@
11
package org.prism;
22

3-
import com.dylibso.chicory.annotations.WasmModuleInterface;
43
import com.dylibso.chicory.runtime.ByteArrayMemory;
5-
import com.dylibso.chicory.runtime.ExportFunction;
64
import com.dylibso.chicory.runtime.ImportValues;
75
import com.dylibso.chicory.runtime.Instance;
8-
import com.dylibso.chicory.runtime.WasmRuntimeException;
9-
import com.dylibso.chicory.wasi.WasiOptions;
10-
import com.dylibso.chicory.wasi.WasiPreview1;
6+
import com.dylibso.chicory.runtime.InterpreterMachine;
7+
import com.dylibso.chicory.wasm.Parser;
118
import com.dylibso.chicory.wasm.WasmModule;
12-
import com.dylibso.chicory.wasm.types.MemoryLimits;
139

14-
import java.nio.charset.StandardCharsets;
15-
16-
@WasmModuleInterface(WasmResource.absoluteFile)
1710
public class PrismWASM extends Prism {
18-
private final ExportFunction calloc;
19-
private final ExportFunction pmSerializeParse;
20-
private final ExportFunction pmBufferInit;
21-
private final ExportFunction pmBufferSizeof;
22-
private final ExportFunction pmBufferValue;
23-
private final ExportFunction pmBufferLength;
24-
11+
private final Prism_ModuleExports exports;
2512
private final Instance instance;
2613

2714
public PrismWASM() {
2815
super();
29-
instance = Instance.builder(PrismParser.load())
16+
WasmModule module = Parser.parse(
17+
PrismWASM.class.getResourceAsStream("/prism.wasm"));
18+
instance = Instance.builder(module)
3019
.withMemoryFactory(ByteArrayMemory::new)
31-
.withMachineFactory(PrismParser::create)
20+
.withMachineFactory(InterpreterMachine::new)
3221
.withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build())
3322
.build();
34-
35-
calloc = instance.exports().function("calloc");
36-
pmSerializeParse = instance.exports().function("pm_serialize_parse");
37-
pmBufferInit = instance.exports().function("pm_buffer_init");
38-
pmBufferSizeof = instance.exports().function("pm_buffer_sizeof");
39-
pmBufferValue = instance.exports().function("pm_buffer_value");
40-
pmBufferLength = instance.exports().function("pm_buffer_length");
23+
exports = new Prism_ModuleExports(instance);
4124
}
4225

4326
@Override
4427
public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLength) {
45-
var sourcePointer = calloc.apply(1, sourceLength);
46-
instance.memory().write((int) sourcePointer[0], sourceBytes, 0, sourceLength);
28+
int sourcePointer = 0;
29+
int optionsPointer = 0;
30+
int bufferPointer = 0;
31+
byte[] result;
32+
try {
33+
sourcePointer = exports.calloc(1, sourceLength + 1);
34+
instance.memory().write(sourcePointer, sourceBytes, 0, sourceLength);
35+
instance.memory().writeByte(sourcePointer + sourceLength, (byte) 0);
4736

48-
var optionsPointer = calloc.apply(1, packedOptions.length);
49-
instance.memory().write((int) optionsPointer[0], packedOptions);
37+
optionsPointer = exports.calloc(1, packedOptions.length);
38+
instance.memory().write(optionsPointer, packedOptions);
5039

51-
var bufferPointer = calloc.apply(pmBufferSizeof.apply()[0], 1);
52-
pmBufferInit.apply(bufferPointer);
40+
bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1);
41+
exports.pmBufferInit(bufferPointer);
5342

54-
pmSerializeParse.apply(
55-
bufferPointer[0], sourcePointer[0], sourceLength, optionsPointer[0]);
43+
exports.pmSerializeParse(
44+
bufferPointer, sourcePointer, sourceLength, optionsPointer);
5645

57-
var result = instance.memory().readBytes(
58-
(int) pmBufferValue.apply(bufferPointer[0])[0],
59-
(int) pmBufferLength.apply(bufferPointer[0])[0]);
46+
result = instance.memory().readBytes(
47+
exports.pmBufferValue(bufferPointer),
48+
exports.pmBufferLength(bufferPointer));
49+
} finally {
50+
if (sourcePointer != 0) {
51+
exports.free(sourcePointer);
52+
}
53+
if (optionsPointer != 0) {
54+
exports.free(optionsPointer);
55+
}
56+
if (bufferPointer != 0) {
57+
exports.pmBufferFree(bufferPointer);
58+
exports.free(bufferPointer);
59+
}
60+
}
6061

6162
return result;
6263
}
6364

64-
@Override
65-
public void close() {
66-
if (wasi != null) {
67-
wasi.close();
68-
}
65+
// DEBUG
66+
public int memorySize() {
67+
return instance.memory().pages();
6968
}
7069
}

java-wasm/src/test/java/org/prism/JRubyTest.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import java.nio.charset.StandardCharsets;
99
import java.util.EnumSet;
1010

11+
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
1113
public class JRubyTest {
1214
final static String[] JRUBY_BOOT_FILES = {
1315
"jruby/java.rb",
@@ -55,9 +57,27 @@ public class JRubyTest {
5557
};
5658

5759
@Test
58-
public void basicJRubyTest() throws Throwable{
59-
Prism prism = new PrismWASM();
60-
byte[] src = new byte[1024*1024];
60+
public void jrubyAOT() throws Exception {
61+
var prismAot = new PrismAOT();
62+
63+
for (int i = 0; i < 3; i++) {
64+
basicJRubyTest(prismAot);
65+
}
66+
var memoryPagesBefore = prismAot.memorySize();
67+
for (int i = 0; i < 100; i++) {
68+
basicJRubyTest(prismAot);
69+
}
70+
var memoryPagesAfter = prismAot.memorySize();
71+
assertEquals(memoryPagesBefore, memoryPagesAfter);
72+
}
73+
74+
@Test
75+
public void jrubyWASM() throws Exception {
76+
basicJRubyTest(new PrismWASM());
77+
}
78+
79+
private static void basicJRubyTest(Prism prism) throws Exception {
80+
byte[] src = new byte[1024 * 1024];
6181

6282
for (var file : JRUBY_BOOT_FILES) {
6383
byte[] options = ParsingOptions.serialize(

0 commit comments

Comments
 (0)