From e0af0eff86bcd8ba13837a973a4ebb31422e0c88 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 21 Jan 2026 17:00:41 -0600 Subject: [PATCH 01/14] Update group and artifact for publishing org.jruby:chicory-prism --- java-wasm/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java-wasm/pom.xml b/java-wasm/pom.xml index ba0b66da9c..592989a921 100644 --- a/java-wasm/pom.xml +++ b/java-wasm/pom.xml @@ -2,11 +2,11 @@ 4.0.0 - com.prism - java-prism - 999-SNAPSHOT + org.jruby + chicory-prism + 0.0.1-SNAPSHOT Java Prism - Pure Java Prism interpreting WASM + Pure Java Prism using Chicory WASM runtime From 0d3a2f26943c8ecfe4a8a6212a7ca7044d0ac566 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 21 Jan 2026 17:01:28 -0600 Subject: [PATCH 02/14] Add JRuby dependency for building parser API Nodes need access to RubySymbol and support APIs at least. --- java-wasm/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java-wasm/pom.xml b/java-wasm/pom.xml index 592989a921..ebe08728f2 100644 --- a/java-wasm/pom.xml +++ b/java-wasm/pom.xml @@ -50,6 +50,11 @@ host-module-annotations-experimental provided + + org.jruby + jruby-base + 10.0.2.0 + From 239e5ee418346e393cf5243b06c0b1a0d2bf89a7 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 21 Jan 2026 19:24:17 -0600 Subject: [PATCH 03/14] Move java_wasm output to src/main/resources * It is a resource used as source code (in wasm) that is compiled for the project artifact. * Added to Rake CLOBBER --- .github/workflows/java-wasm-bindings.yml | 2 +- Makefile | 5 +++-- Rakefile | 1 + java-wasm/pom.xml | 2 +- .../src/main/java-templates/org/prism/WasmResource.java | 2 +- java-wasm/src/{test => main}/resources/.gitignore | 0 6 files changed, 7 insertions(+), 5 deletions(-) rename java-wasm/src/{test => main}/resources/.gitignore (100%) diff --git a/.github/workflows/java-wasm-bindings.yml b/.github/workflows/java-wasm-bindings.yml index b3e427add0..12e465fd0b 100644 --- a/.github/workflows/java-wasm-bindings.yml +++ b/.github/workflows/java-wasm-bindings.yml @@ -48,4 +48,4 @@ jobs: - uses: actions/upload-artifact@v4 with: name: prism.wasm - path: java-wasm/src/test/resources/prism.wasm + path: java-wasm/src/main/resources/prism.wasm diff --git a/Makefile b/Makefile index afeb58ecd0..a9a0f74c4a 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ all: shared static shared: build/libprism.$(SOEXT) static: build/libprism.a wasm: javascript/src/prism.wasm -java-wasm: java-wasm/src/test/resources/prism.wasm +java-wasm: java-wasm/src/main/resources/prism.wasm build/libprism.$(SOEXT): $(SHARED_OBJECTS) $(ECHO) "linking $@ with $(CC)" @@ -44,8 +44,9 @@ javascript/src/prism.wasm: Makefile $(SOURCES) $(HEADERS) $(ECHO) "building $@" $(Q) $(WASI_SDK_PATH)/bin/clang --sysroot=$(WASI_SDK_PATH)/share/wasi-sysroot/ $(DEBUG_FLAGS) -DPRISM_EXPORT_SYMBOLS -D_WASI_EMULATED_MMAN -lwasi-emulated-mman $(CPPFLAGS) $(CFLAGS) -Wl,--export-all -Wl,--no-entry -mexec-model=reactor -o $@ $(SOURCES) -java-wasm/src/test/resources/prism.wasm: Makefile $(SOURCES) $(HEADERS) +java-wasm/src/main/resources/prism.wasm: Makefile $(SOURCES) $(HEADERS) $(ECHO) "building $@" + $(Q) $(MAKEDIRS) $(@D) $(Q) $(WASI_SDK_PATH)/bin/clang $(DEBUG_FLAGS) -DPRISM_EXCLUDE_PRETTYPRINT -DPRISM_EXPORT_SYMBOLS -D_WASI_EMULATED_MMAN -lwasi-emulated-mman $(CPPFLAGS) $(JAVA_WASM_CFLAGS) -Wl,--export-all -Wl,--no-entry -mexec-model=reactor -lc++ -lc++abi -o $@ $(SOURCES) build/shared/%.o: src/%.c Makefile $(HEADERS) diff --git a/Rakefile b/Rakefile index 7a5e537039..45329fa637 100644 --- a/Rakefile +++ b/Rakefile @@ -52,6 +52,7 @@ end CLOBBER.concat(Prism::Template::TEMPLATES) CLOBBER.concat(["build"]) CLOBBER << "lib/prism/prism.#{RbConfig::CONFIG["DLEXT"]}" +CLOBBER << "java-wasm/src/main/resources/prism.wasm" Prism::Template::TEMPLATES.each do |filepath| desc "Generate #{filepath}" diff --git a/java-wasm/pom.xml b/java-wasm/pom.xml index ebe08728f2..8004087ba8 100644 --- a/java-wasm/pom.xml +++ b/java-wasm/pom.xml @@ -122,7 +122,7 @@ org.prism.Prism - src/test/resources/prism.wasm + src/main/resources/prism.wasm diff --git a/java-wasm/src/main/java-templates/org/prism/WasmResource.java b/java-wasm/src/main/java-templates/org/prism/WasmResource.java index 48f2671714..ccf1d0bcdd 100644 --- a/java-wasm/src/main/java-templates/org/prism/WasmResource.java +++ b/java-wasm/src/main/java-templates/org/prism/WasmResource.java @@ -1,7 +1,7 @@ package org.prism; public final class WasmResource { - public static final String absoluteFile = "file://${project.basedir}/src/test/resources/prism.wasm"; + public static final String absoluteFile = "file://${project.basedir}/src/main/resources/prism.wasm"; private WasmResource() {} } diff --git a/java-wasm/src/test/resources/.gitignore b/java-wasm/src/main/resources/.gitignore similarity index 100% rename from java-wasm/src/test/resources/.gitignore rename to java-wasm/src/main/resources/.gitignore From 742872cfc3bf29e9ab4b4ea4a73fdca25baed399 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 21 Jan 2026 19:26:47 -0600 Subject: [PATCH 04/14] Add basic bootstrapping instructions --- java-wasm/README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 java-wasm/README.md diff --git a/java-wasm/README.md b/java-wasm/README.md new file mode 100644 index 0000000000..2ff93a66d7 --- /dev/null +++ b/java-wasm/README.md @@ -0,0 +1,23 @@ +This dir contains the chicory-prism artifact, a version of prism compiled to WASM and then AOT compiled to JVM bytecode by the Chicory project. + +Generate the templated sources: + +``` +PRISM_EXCLUDE_PRETTYPRINT=1 PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS=1 PRISM_JAVA_BACKEND=jruby bundle exec rake templates +``` + +Compile to WASM using WASI SDK version 25: + +``` +make java-wasm WASI_SDK_PATH=.../wasi-sdk-25.0-arm64-macos +``` + +Build the AOT-compiled machine and wrapper library: + +``` +mvn -f java-wasm/pom.xml clean package +``` + +This should build the chicory-wasm jar file and pass some basic tests. + +The jar will be under `java-wasm/target/chicory-prism-#####-SNAPSHOT.jar` or can be installed by using `install` instead of `pacakge` in the `mvn` command line above. From 70c40c1b84c53594a4a653194b4880b72d8621a9 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 22 Jan 2026 01:17:12 -0600 Subject: [PATCH 05/14] Upgrade to chicory 1.6.1 --- java-wasm/pom.xml | 12 ++++++------ java-wasm/src/main/java/org/prism/Prism.java | 9 ++++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/java-wasm/pom.xml b/java-wasm/pom.xml index 8004087ba8..f0b54fd783 100644 --- a/java-wasm/pom.xml +++ b/java-wasm/pom.xml @@ -15,7 +15,7 @@ 11 11 - 1.2.1 + 1.6.1 5.12.1 @@ -47,7 +47,7 @@ com.dylibso.chicory - host-module-annotations-experimental + annotations provided @@ -81,7 +81,7 @@ com.dylibso.chicory - host-module-processor-experimental + annotations-processor ${chicory.version} @@ -112,16 +112,16 @@ com.dylibso.chicory - aot-maven-plugin-experimental + chicory-compiler-maven-plugin ${chicory.version} prism - wasm-aot-gen + compile - org.prism.Prism + org.prism.PrismParser src/main/resources/prism.wasm diff --git a/java-wasm/src/main/java/org/prism/Prism.java b/java-wasm/src/main/java/org/prism/Prism.java index a4a8e65111..ba13f18aed 100644 --- a/java-wasm/src/main/java/org/prism/Prism.java +++ b/java-wasm/src/main/java/org/prism/Prism.java @@ -1,11 +1,12 @@ package org.prism; +import com.dylibso.chicory.annotations.WasmModuleInterface; import com.dylibso.chicory.runtime.ByteArrayMemory; -import com.dylibso.chicory.experimental.hostmodule.annotations.WasmModuleInterface; import com.dylibso.chicory.runtime.ImportValues; import com.dylibso.chicory.runtime.Instance; import com.dylibso.chicory.wasi.WasiOptions; import com.dylibso.chicory.wasi.WasiPreview1; +import com.dylibso.chicory.wasm.WasmModule; import com.dylibso.chicory.wasm.types.MemoryLimits; import java.nio.charset.StandardCharsets; @@ -21,9 +22,11 @@ public Prism() { } public Prism(WasiOptions wasiOpts) { wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); - instance = Instance.builder(PrismModule.load()) + WasmModule module = PrismParser.load(); + PrismParser parser = new PrismParser(); + instance = Instance.builder(module) .withMemoryFactory(limits -> new ByteArrayMemory(new MemoryLimits(10, MemoryLimits.MAX_PAGES))) - .withMachineFactory(PrismModule::create) + .withMachineFactory(parser.machineFactory()) .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) .build(); exports = new Prism_ModuleExports(instance); From 098305dd0da56325d96b7f12069ccbe6eca35d94 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Sun, 25 Jan 2026 17:25:27 -0600 Subject: [PATCH 06/14] Simplify setup and teardown Allow the buffer functions to manage the internal pointer and use consistent argument order for calloc. --- java-wasm/src/main/java/org/prism/Prism.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/java-wasm/src/main/java/org/prism/Prism.java b/java-wasm/src/main/java/org/prism/Prism.java index ba13f18aed..51552569e6 100644 --- a/java-wasm/src/main/java/org/prism/Prism.java +++ b/java-wasm/src/main/java/org/prism/Prism.java @@ -40,10 +40,9 @@ public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) { int sourcePointer = 0; int optionsPointer = 0; int bufferPointer = 0; - int resultPointer = 0; byte[] result; try { - sourcePointer = exports.calloc(1, sourceLength); + sourcePointer = exports.calloc(sourceLength, 1); exports.memory().write(sourcePointer, source); optionsPointer = exports.calloc(1, packedOptions.length); @@ -54,10 +53,8 @@ public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) { exports.pmSerializeParse(bufferPointer, sourcePointer, sourceLength, optionsPointer); - resultPointer = exports.pmBufferValue(bufferPointer); - result = instance.memory().readBytes( - resultPointer, + exports.pmBufferValue(bufferPointer), exports.pmBufferLength(bufferPointer)); } finally { if (sourcePointer != 0) { @@ -67,11 +64,9 @@ public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) { exports.free(optionsPointer); } if (bufferPointer != 0) { + exports.pmBufferFree(bufferPointer); exports.free(bufferPointer); } - if (resultPointer != 0) { - exports.free(resultPointer); - } } return result; From 1cbe7984b2ea555a5f5bb297015b2926719443b8 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Mon, 26 Jan 2026 12:31:20 -0600 Subject: [PATCH 07/14] Support AOT and interp WASM Primarily for testing purposes. --- java-wasm/src/main/java/org/prism/Prism.java | 76 +++------------- .../src/main/java/org/prism/PrismAOT.java | 79 +++++++++++++++++ .../src/main/java/org/prism/PrismWASM.java | 70 +++++++++++++++ .../src/test/java/org/prism/DummyTest.java | 86 +++---------------- .../src/test/java/org/prism/JRubyTest.java | 4 + 5 files changed, 175 insertions(+), 140 deletions(-) create mode 100644 java-wasm/src/main/java/org/prism/PrismAOT.java create mode 100644 java-wasm/src/main/java/org/prism/PrismWASM.java create mode 100644 java-wasm/src/test/java/org/prism/JRubyTest.java diff --git a/java-wasm/src/main/java/org/prism/Prism.java b/java-wasm/src/main/java/org/prism/Prism.java index 51552569e6..8bb9c63365 100644 --- a/java-wasm/src/main/java/org/prism/Prism.java +++ b/java-wasm/src/main/java/org/prism/Prism.java @@ -1,89 +1,37 @@ package org.prism; import com.dylibso.chicory.annotations.WasmModuleInterface; -import com.dylibso.chicory.runtime.ByteArrayMemory; -import com.dylibso.chicory.runtime.ImportValues; -import com.dylibso.chicory.runtime.Instance; import com.dylibso.chicory.wasi.WasiOptions; import com.dylibso.chicory.wasi.WasiPreview1; -import com.dylibso.chicory.wasm.WasmModule; -import com.dylibso.chicory.wasm.types.MemoryLimits; import java.nio.charset.StandardCharsets; @WasmModuleInterface(WasmResource.absoluteFile) -public class Prism implements AutoCloseable { - private final WasiPreview1 wasi; - private final Instance instance; - private final Prism_ModuleExports exports; +public abstract class Prism implements AutoCloseable { + protected final WasiPreview1 wasi; - public Prism() { + Prism() { this(WasiOptions.builder().build()); } - public Prism(WasiOptions wasiOpts) { - wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); - WasmModule module = PrismParser.load(); - PrismParser parser = new PrismParser(); - instance = Instance.builder(module) - .withMemoryFactory(limits -> new ByteArrayMemory(new MemoryLimits(10, MemoryLimits.MAX_PAGES))) - .withMachineFactory(parser.machineFactory()) - .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) - .build(); - exports = new Prism_ModuleExports(instance); - } - public Prism_ModuleExports exports() { - return exports; + Prism(WasiOptions wasiOpts) { + wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); } - public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) { - int sourcePointer = 0; - int optionsPointer = 0; - int bufferPointer = 0; - byte[] result; - try { - sourcePointer = exports.calloc(sourceLength, 1); - exports.memory().write(sourcePointer, source); - - optionsPointer = exports.calloc(1, packedOptions.length); - exports.memory().write(optionsPointer, packedOptions); - - bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1); - exports.pmBufferInit(bufferPointer); - - exports.pmSerializeParse(bufferPointer, sourcePointer, sourceLength, optionsPointer); + public static Prism newInstance(boolean aot) { + if (aot) return new PrismAOT(); - result = instance.memory().readBytes( - exports.pmBufferValue(bufferPointer), - exports.pmBufferLength(bufferPointer)); - } finally { - if (sourcePointer != 0) { - exports.free(sourcePointer); - } - if (optionsPointer != 0) { - exports.free(optionsPointer); - } - if (bufferPointer != 0) { - exports.pmBufferFree(bufferPointer); - exports.free(bufferPointer); - } - } - - return result; + return new PrismWASM(); } - public ParseResult serializeParse(byte[] packedOptions, String source) { - var sourceBytes = source.getBytes(StandardCharsets.US_ASCII); + public abstract byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength); + public ParseResult serializeParse(byte[] packedOptions, String source) { + var sourceBytes = source.getBytes(StandardCharsets.ISO_8859_1); byte[] result = serialize(packedOptions, sourceBytes, sourceBytes.length); - return Loader.load(result, sourceBytes); } @Override - public void close() { - if (wasi != null) { - wasi.close(); - } - } + public abstract void close(); } diff --git a/java-wasm/src/main/java/org/prism/PrismAOT.java b/java-wasm/src/main/java/org/prism/PrismAOT.java new file mode 100644 index 0000000000..2637e12646 --- /dev/null +++ b/java-wasm/src/main/java/org/prism/PrismAOT.java @@ -0,0 +1,79 @@ +package org.prism; + +import com.dylibso.chicory.annotations.WasmModuleInterface; +import com.dylibso.chicory.runtime.ByteArrayMemory; +import com.dylibso.chicory.runtime.ImportValues; +import com.dylibso.chicory.runtime.Instance; +import com.dylibso.chicory.wasm.WasmModule; +import com.dylibso.chicory.wasm.types.MemoryLimits; + +import java.nio.charset.StandardCharsets; + +@WasmModuleInterface(WasmResource.absoluteFile) +public class PrismAOT extends Prism { + private final Instance instance; + private final Prism_ModuleExports exports; + + public PrismAOT() { + super(); + WasmModule module = PrismParser.load(); + PrismParser parser = new PrismParser(); + instance = Instance.builder(module) + .withMemoryFactory(limits -> new ByteArrayMemory(new MemoryLimits(10, MemoryLimits.MAX_PAGES))) + .withMachineFactory(parser.machineFactory()) + .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) + .build(); + exports = new Prism_ModuleExports(instance); + } + + public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) { + int sourcePointer = 0; + int optionsPointer = 0; + int bufferPointer = 0; + byte[] result; + try { + sourcePointer = exports.calloc(sourceLength, 1); + exports.memory().write(sourcePointer, source); + + optionsPointer = exports.calloc(1, packedOptions.length); + exports.memory().write(optionsPointer, packedOptions); + + bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1); + exports.pmBufferInit(bufferPointer); + + exports.pmSerializeParse(bufferPointer, sourcePointer, sourceLength, optionsPointer); + + result = instance.memory().readBytes( + exports.pmBufferValue(bufferPointer), + exports.pmBufferLength(bufferPointer)); + } finally { + if (sourcePointer != 0) { + exports.free(sourcePointer); + } + if (optionsPointer != 0) { + exports.free(optionsPointer); + } + if (bufferPointer != 0) { + exports.pmBufferFree(bufferPointer); + exports.free(bufferPointer); + } + } + + return result; + } + + public ParseResult serializeParse(byte[] packedOptions, String source) { + var sourceBytes = source.getBytes(StandardCharsets.US_ASCII); + + byte[] result = serialize(packedOptions, sourceBytes, sourceBytes.length); + + return Loader.load(result, sourceBytes); + } + + @Override + public void close() { + if (wasi != null) { + wasi.close(); + } + } +} diff --git a/java-wasm/src/main/java/org/prism/PrismWASM.java b/java-wasm/src/main/java/org/prism/PrismWASM.java new file mode 100644 index 0000000000..235bfb54cc --- /dev/null +++ b/java-wasm/src/main/java/org/prism/PrismWASM.java @@ -0,0 +1,70 @@ +package org.prism; + +import com.dylibso.chicory.annotations.WasmModuleInterface; +import com.dylibso.chicory.runtime.ByteArrayMemory; +import com.dylibso.chicory.runtime.ExportFunction; +import com.dylibso.chicory.runtime.ImportValues; +import com.dylibso.chicory.runtime.Instance; +import com.dylibso.chicory.runtime.WasmRuntimeException; +import com.dylibso.chicory.wasi.WasiOptions; +import com.dylibso.chicory.wasi.WasiPreview1; +import com.dylibso.chicory.wasm.WasmModule; +import com.dylibso.chicory.wasm.types.MemoryLimits; + +import java.nio.charset.StandardCharsets; + +@WasmModuleInterface(WasmResource.absoluteFile) +public class PrismWASM extends Prism { + private final ExportFunction calloc; + private final ExportFunction pmSerializeParse; + private final ExportFunction pmBufferInit; + private final ExportFunction pmBufferSizeof; + private final ExportFunction pmBufferValue; + private final ExportFunction pmBufferLength; + + private final Instance instance; + + public PrismWASM() { + super(); + instance = Instance.builder(PrismParser.load()) + .withMemoryFactory(ByteArrayMemory::new) + .withMachineFactory(PrismParser::create) + .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) + .build(); + + calloc = instance.exports().function("calloc"); + pmSerializeParse = instance.exports().function("pm_serialize_parse"); + pmBufferInit = instance.exports().function("pm_buffer_init"); + pmBufferSizeof = instance.exports().function("pm_buffer_sizeof"); + pmBufferValue = instance.exports().function("pm_buffer_value"); + pmBufferLength = instance.exports().function("pm_buffer_length"); + } + + @Override + public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLength) { + var sourcePointer = calloc.apply(1, sourceLength); + instance.memory().write((int) sourcePointer[0], sourceBytes, 0, sourceLength); + + var optionsPointer = calloc.apply(1, packedOptions.length); + instance.memory().write((int) optionsPointer[0], packedOptions); + + var bufferPointer = calloc.apply(pmBufferSizeof.apply()[0], 1); + pmBufferInit.apply(bufferPointer); + + pmSerializeParse.apply( + bufferPointer[0], sourcePointer[0], sourceLength, optionsPointer[0]); + + var result = instance.memory().readBytes( + (int) pmBufferValue.apply(bufferPointer[0])[0], + (int) pmBufferLength.apply(bufferPointer[0])[0]); + + return result; + } + + @Override + public void close() { + if (wasi != null) { + wasi.close(); + } + } +} diff --git a/java-wasm/src/test/java/org/prism/DummyTest.java b/java-wasm/src/test/java/org/prism/DummyTest.java index 9e49f1afdd..fe6d87d2e7 100644 --- a/java-wasm/src/test/java/org/prism/DummyTest.java +++ b/java-wasm/src/test/java/org/prism/DummyTest.java @@ -1,14 +1,7 @@ package org.prism; -import com.dylibso.chicory.runtime.ImportValues; -import com.dylibso.chicory.runtime.Instance; -import com.dylibso.chicory.wasi.WasiOptions; -import com.dylibso.chicory.wasi.WasiPreview1; -import com.dylibso.chicory.wasm.Parser; -import com.dylibso.chicory.wasm.types.Value; import org.junit.jupiter.api.Test; -import java.nio.charset.StandardCharsets; import java.util.EnumSet; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,42 +24,13 @@ public class DummyTest { @Test public void test1() { - WasiOptions wasiOpts = WasiOptions.builder().build(); - WasiPreview1 wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); - var wasmPrism = Instance.builder(Parser.parse(DummyTest.class.getResourceAsStream("/prism.wasm"))) - .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) - .build(); - var memory = wasmPrism.memory(); - var calloc = wasmPrism.export("calloc"); - var pmSerializeParse = wasmPrism.export("pm_serialize_parse"); - var pmBufferInit = wasmPrism.export("pm_buffer_init"); - var pmBufferSizeof = wasmPrism.export("pm_buffer_sizeof"); - var pmBufferValue = wasmPrism.export("pm_buffer_value"); - var pmBufferLength = wasmPrism.export("pm_buffer_length"); - // The Ruby source code to be processed var source = "1 + 1"; - var sourceBytes = source.getBytes(StandardCharsets.US_ASCII); - - var sourcePointer = calloc.apply(1, source.length()); - memory.writeString((int) sourcePointer[0], source); - - var optionsPointer = calloc.apply(1, packedOptions.length); - memory.write((int) optionsPointer[0], packedOptions); - - var bufferPointer = calloc.apply(pmBufferSizeof.apply()[0], 1); - pmBufferInit.apply(bufferPointer); - - pmSerializeParse.apply( - bufferPointer[0], sourcePointer[0], source.length(), optionsPointer[0]); - - var result = memory.readBytes( - (int) pmBufferValue.apply(bufferPointer[0])[0], - (int) pmBufferLength.apply(bufferPointer[0])[0]); - System.out.println("RESULT: " + new String(result)); - - ParseResult pr = Loader.load(result, sourceBytes); + ParseResult pr = null; + try (Prism prism = new PrismWASM()) { + pr = prism.serializeParse(packedOptions, source); + } assertEquals(1, pr.value.childNodes().length); System.out.println("Nodes:"); @@ -80,7 +44,7 @@ public void test1Aot() { var source = "1 + 1"; ParseResult pr = null; - try (Prism prism = new Prism()) { + try (Prism prism = new PrismAOT()) { pr = prism.serializeParse(packedOptions, source); } @@ -92,45 +56,15 @@ public void test1Aot() { @Test public void test2() { - WasiOptions wasiOpts = WasiOptions.builder().build(); - WasiPreview1 wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); - var wasmPrism = Instance.builder(Parser.parse(DummyTest.class.getResourceAsStream("/prism.wasm"))) - .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) - .build(); - var memory = wasmPrism.memory(); - var calloc = wasmPrism.export("calloc"); - var pmSerializeParse = wasmPrism.export("pm_serialize_parse"); - var pmBufferInit = wasmPrism.export("pm_buffer_init"); - var pmBufferSizeof = wasmPrism.export("pm_buffer_sizeof"); - var pmBufferValue = wasmPrism.export("pm_buffer_value"); - var pmBufferLength = wasmPrism.export("pm_buffer_length"); - // The Ruby source code to be processed var source = "puts \"h\ne\nl\nl\no\n\""; - var sourceBytes = source.getBytes(StandardCharsets.US_ASCII); - var sourcePointer = calloc.apply(1, source.length()); - memory.writeString((int) sourcePointer[0], source); - - var optionsPointer = calloc.apply(1, packedOptions.length); - memory.write((int) optionsPointer[0], packedOptions); - - var bufferPointer = calloc.apply(pmBufferSizeof.apply()[0], 1); - pmBufferInit.apply(bufferPointer); - - pmSerializeParse.apply( - bufferPointer[0], sourcePointer[0], source.length(), optionsPointer[0]); - - var result = memory.readBytes( - (int) pmBufferValue.apply(bufferPointer[0])[0], - (int) pmBufferLength.apply(bufferPointer[0])[0]); - - System.out.println("RESULT: " + new String(result)); - - ParseResult pr = Loader.load(result, sourceBytes); + ParseResult pr = null; + try (Prism prism = new PrismWASM()) { + pr = prism.serializeParse(packedOptions, source); + } assertEquals(1, pr.value.childNodes().length); - System.out.println("Nodes:"); System.out.println(pr.value.childNodes()[0]); assertTrue(pr.value.childNodes()[0].toString().contains("CallNode")); @@ -142,7 +76,7 @@ public void test2Aot() { var source = "puts \"h\ne\nl\nl\no\n\""; ParseResult pr = null; - try (Prism prism = new Prism()) { + try (Prism prism = new PrismAOT()) { pr = prism.serializeParse(packedOptions, source); } diff --git a/java-wasm/src/test/java/org/prism/JRubyTest.java b/java-wasm/src/test/java/org/prism/JRubyTest.java new file mode 100644 index 0000000000..1308f33b4c --- /dev/null +++ b/java-wasm/src/test/java/org/prism/JRubyTest.java @@ -0,0 +1,4 @@ +package org.prism; + +public class JRubyTest { +} From 8e1aed586f29817a85b7ed9a4a32c1fb9d373e91 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Mon, 26 Jan 2026 12:32:08 -0600 Subject: [PATCH 08/14] Add test that parses JRuby's boot files This is not all of the boot files but is sufficient to demonstrate a memory fault in the AOT WASM parser. --- java-wasm/pom.xml | 2 +- .../src/test/java/org/prism/JRubyTest.java | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/java-wasm/pom.xml b/java-wasm/pom.xml index bc43ba7274..b2fc01388d 100644 --- a/java-wasm/pom.xml +++ b/java-wasm/pom.xml @@ -61,7 +61,7 @@ org.jruby - jruby-base + jruby-complete 10.0.2.0 diff --git a/java-wasm/src/test/java/org/prism/JRubyTest.java b/java-wasm/src/test/java/org/prism/JRubyTest.java index 1308f33b4c..634d651592 100644 --- a/java-wasm/src/test/java/org/prism/JRubyTest.java +++ b/java-wasm/src/test/java/org/prism/JRubyTest.java @@ -1,4 +1,82 @@ package org.prism; +import org.jruby.Ruby; +import org.junit.jupiter.api.Test; + +import java.io.DataInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.EnumSet; + public class JRubyTest { + final static String[] JRUBY_BOOT_FILES = { + "jruby/java.rb", + "jruby/java/core_ext.rb", + "jruby/java/core_ext/object.rb", + "jruby/java/java_ext.rb", + "jruby/kernel.rb", + "jruby/kernel/signal.rb", + "jruby/kernel/kernel.rb", + "jruby/kernel/proc.rb", + "jruby/kernel/process.rb", + "jruby/kernel/enumerator.rb", + "jruby/kernel/enumerable.rb", + "jruby/kernel/io.rb", + "jruby/kernel/gc.rb", + "jruby/kernel/range.rb", + "jruby/kernel/file.rb", + "jruby/kernel/method.rb", + "jruby/kernel/thread.rb", + "jruby/kernel/integer.rb", + "jruby/kernel/time.rb", + "jruby/preludes.rb", + "jruby/kernel/prelude.rb", + "jruby/kernel/enc_prelude.rb", + "META-INF/jruby.home/lib/ruby/stdlib/unicode_normalize.rb", + "jruby/kernel/gem_prelude.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rbconfig.rb", + "jruby/kernel/rbconfig.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/compatibility.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/defaults.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/deprecate.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/errors.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/target_rbconfig.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/exceptions.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/unknown_command_spell_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/basic_specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/stub_specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/platform.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/specification_record.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/util/list.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/requirement.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/version.rb", + }; + + @Test + public void basicJRubyTest() throws Throwable{ + Prism prism = new PrismWASM(); + byte[] src = new byte[1024*1024]; + + for (var file : JRUBY_BOOT_FILES) { + byte[] options = ParsingOptions.serialize( + file.getBytes(StandardCharsets.UTF_8), + 1, + "UTF-8".getBytes(StandardCharsets.UTF_8), + false, + EnumSet.noneOf(ParsingOptions.CommandLine.class), + ParsingOptions.SyntaxVersion.LATEST, + false, + false, + false, + new byte[][][]{} + ); + try (InputStream fileIn = Ruby.getClassLoader().getResourceAsStream(file)) { + DataInputStream dis = new DataInputStream(fileIn); + int read = dis.read(src); + prism.serialize(options, src, read); + } + } + } } From 3236f86f4b6bb3e90a365cab947284338b5ccaff Mon Sep 17 00:00:00 2001 From: andreatp Date: Tue, 27 Jan 2026 14:07:03 +0000 Subject: [PATCH 09/14] Fix the memory leak --- java-wasm/src/main/java/org/prism/Prism.java | 6 +- .../src/main/java/org/prism/PrismAOT.java | 41 ++++------ .../src/main/java/org/prism/PrismWASM.java | 82 +++++++++---------- .../src/test/java/org/prism/JRubyTest.java | 26 +++++- 4 files changed, 85 insertions(+), 70 deletions(-) diff --git a/java-wasm/src/main/java/org/prism/Prism.java b/java-wasm/src/main/java/org/prism/Prism.java index 8bb9c63365..8c40c6dce5 100644 --- a/java-wasm/src/main/java/org/prism/Prism.java +++ b/java-wasm/src/main/java/org/prism/Prism.java @@ -33,5 +33,9 @@ public ParseResult serializeParse(byte[] packedOptions, String source) { } @Override - public abstract void close(); + public void close() { + if (wasi != null) { + wasi.close(); + } + } } diff --git a/java-wasm/src/main/java/org/prism/PrismAOT.java b/java-wasm/src/main/java/org/prism/PrismAOT.java index 2637e12646..fce4bd7168 100644 --- a/java-wasm/src/main/java/org/prism/PrismAOT.java +++ b/java-wasm/src/main/java/org/prism/PrismAOT.java @@ -9,45 +9,46 @@ import java.nio.charset.StandardCharsets; -@WasmModuleInterface(WasmResource.absoluteFile) public class PrismAOT extends Prism { - private final Instance instance; private final Prism_ModuleExports exports; + private final Instance instance; public PrismAOT() { super(); - WasmModule module = PrismParser.load(); - PrismParser parser = new PrismParser(); - instance = Instance.builder(module) - .withMemoryFactory(limits -> new ByteArrayMemory(new MemoryLimits(10, MemoryLimits.MAX_PAGES))) - .withMachineFactory(parser.machineFactory()) + instance = Instance.builder(PrismParser.load()) + .withMemoryFactory(ByteArrayMemory::new) + .withMachineFactory(PrismParser::create) .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) .build(); exports = new Prism_ModuleExports(instance); } - public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) { + @Override + public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLength) { int sourcePointer = 0; int optionsPointer = 0; int bufferPointer = 0; byte[] result; try { - sourcePointer = exports.calloc(sourceLength, 1); - exports.memory().write(sourcePointer, source); + sourcePointer = exports.calloc(1, sourceLength + 1); + instance.memory().write(sourcePointer, sourceBytes, 0, sourceLength); + instance.memory().writeByte(sourcePointer + sourceLength, (byte) 0); optionsPointer = exports.calloc(1, packedOptions.length); - exports.memory().write(optionsPointer, packedOptions); + instance.memory().write(optionsPointer, packedOptions); bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1); exports.pmBufferInit(bufferPointer); - exports.pmSerializeParse(bufferPointer, sourcePointer, sourceLength, optionsPointer); + exports.pmSerializeParse( + bufferPointer, sourcePointer, sourceLength, optionsPointer); result = instance.memory().readBytes( exports.pmBufferValue(bufferPointer), exports.pmBufferLength(bufferPointer)); } finally { if (sourcePointer != 0) { + exports.pmStringFree(sourcePointer); exports.free(sourcePointer); } if (optionsPointer != 0) { @@ -62,18 +63,8 @@ public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) { return result; } - public ParseResult serializeParse(byte[] packedOptions, String source) { - var sourceBytes = source.getBytes(StandardCharsets.US_ASCII); - - byte[] result = serialize(packedOptions, sourceBytes, sourceBytes.length); - - return Loader.load(result, sourceBytes); - } - - @Override - public void close() { - if (wasi != null) { - wasi.close(); - } + // DEBUG CHECK + public int memorySize() { + return instance.memory().pages(); } } diff --git a/java-wasm/src/main/java/org/prism/PrismWASM.java b/java-wasm/src/main/java/org/prism/PrismWASM.java index 235bfb54cc..a1b7c374e4 100644 --- a/java-wasm/src/main/java/org/prism/PrismWASM.java +++ b/java-wasm/src/main/java/org/prism/PrismWASM.java @@ -1,70 +1,70 @@ package org.prism; -import com.dylibso.chicory.annotations.WasmModuleInterface; import com.dylibso.chicory.runtime.ByteArrayMemory; -import com.dylibso.chicory.runtime.ExportFunction; import com.dylibso.chicory.runtime.ImportValues; import com.dylibso.chicory.runtime.Instance; -import com.dylibso.chicory.runtime.WasmRuntimeException; -import com.dylibso.chicory.wasi.WasiOptions; -import com.dylibso.chicory.wasi.WasiPreview1; +import com.dylibso.chicory.runtime.InterpreterMachine; +import com.dylibso.chicory.wasm.Parser; import com.dylibso.chicory.wasm.WasmModule; -import com.dylibso.chicory.wasm.types.MemoryLimits; -import java.nio.charset.StandardCharsets; - -@WasmModuleInterface(WasmResource.absoluteFile) public class PrismWASM extends Prism { - private final ExportFunction calloc; - private final ExportFunction pmSerializeParse; - private final ExportFunction pmBufferInit; - private final ExportFunction pmBufferSizeof; - private final ExportFunction pmBufferValue; - private final ExportFunction pmBufferLength; - + private final Prism_ModuleExports exports; private final Instance instance; public PrismWASM() { super(); - instance = Instance.builder(PrismParser.load()) + WasmModule module = Parser.parse( + PrismWASM.class.getResourceAsStream("/prism.wasm")); + instance = Instance.builder(module) .withMemoryFactory(ByteArrayMemory::new) - .withMachineFactory(PrismParser::create) + .withMachineFactory(InterpreterMachine::new) .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) .build(); - - calloc = instance.exports().function("calloc"); - pmSerializeParse = instance.exports().function("pm_serialize_parse"); - pmBufferInit = instance.exports().function("pm_buffer_init"); - pmBufferSizeof = instance.exports().function("pm_buffer_sizeof"); - pmBufferValue = instance.exports().function("pm_buffer_value"); - pmBufferLength = instance.exports().function("pm_buffer_length"); + exports = new Prism_ModuleExports(instance); } @Override public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLength) { - var sourcePointer = calloc.apply(1, sourceLength); - instance.memory().write((int) sourcePointer[0], sourceBytes, 0, sourceLength); + int sourcePointer = 0; + int optionsPointer = 0; + int bufferPointer = 0; + byte[] result; + try { + sourcePointer = exports.calloc(1, sourceLength + 1); + instance.memory().write(sourcePointer, sourceBytes, 0, sourceLength); + instance.memory().writeByte(sourcePointer + sourceLength, (byte) 0); - var optionsPointer = calloc.apply(1, packedOptions.length); - instance.memory().write((int) optionsPointer[0], packedOptions); + optionsPointer = exports.calloc(1, packedOptions.length); + instance.memory().write(optionsPointer, packedOptions); - var bufferPointer = calloc.apply(pmBufferSizeof.apply()[0], 1); - pmBufferInit.apply(bufferPointer); + bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1); + exports.pmBufferInit(bufferPointer); - pmSerializeParse.apply( - bufferPointer[0], sourcePointer[0], sourceLength, optionsPointer[0]); + exports.pmSerializeParse( + bufferPointer, sourcePointer, sourceLength, optionsPointer); - var result = instance.memory().readBytes( - (int) pmBufferValue.apply(bufferPointer[0])[0], - (int) pmBufferLength.apply(bufferPointer[0])[0]); + result = instance.memory().readBytes( + exports.pmBufferValue(bufferPointer), + exports.pmBufferLength(bufferPointer)); + } finally { + if (sourcePointer != 0) { + exports.pmStringFree(sourcePointer); + exports.free(sourcePointer); + } + if (optionsPointer != 0) { + exports.free(optionsPointer); + } + if (bufferPointer != 0) { + exports.pmBufferFree(bufferPointer); + exports.free(bufferPointer); + } + } return result; } - @Override - public void close() { - if (wasi != null) { - wasi.close(); - } + // DEBUG + public int memorySize() { + return instance.memory().pages(); } } diff --git a/java-wasm/src/test/java/org/prism/JRubyTest.java b/java-wasm/src/test/java/org/prism/JRubyTest.java index 634d651592..7dc265c983 100644 --- a/java-wasm/src/test/java/org/prism/JRubyTest.java +++ b/java-wasm/src/test/java/org/prism/JRubyTest.java @@ -8,6 +8,8 @@ import java.nio.charset.StandardCharsets; import java.util.EnumSet; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class JRubyTest { final static String[] JRUBY_BOOT_FILES = { "jruby/java.rb", @@ -55,9 +57,27 @@ public class JRubyTest { }; @Test - public void basicJRubyTest() throws Throwable{ - Prism prism = new PrismWASM(); - byte[] src = new byte[1024*1024]; + public void jrubyAOT() throws Exception { + var prismAot = new PrismAOT(); + + for (int i = 0; i < 3; i++) { + basicJRubyTest(prismAot); + } + var memoryPagesBefore = prismAot.memorySize(); + for (int i = 0; i < 100; i++) { + basicJRubyTest(prismAot); + } + var memoryPagesAfter = prismAot.memorySize(); + assertEquals(memoryPagesBefore, memoryPagesAfter); + } + + @Test + public void jrubyWASM() throws Exception { + basicJRubyTest(new PrismWASM()); + } + + private static void basicJRubyTest(Prism prism) throws Exception { + byte[] src = new byte[1024 * 1024]; for (var file : JRUBY_BOOT_FILES) { byte[] options = ParsingOptions.serialize( From 1e17c1be64557fbe3f6eb3825069fd97fcf7b9b9 Mon Sep 17 00:00:00 2001 From: andreatp Date: Tue, 27 Jan 2026 15:52:21 +0000 Subject: [PATCH 10/14] rem string free --- java-wasm/src/main/java/org/prism/PrismAOT.java | 1 - java-wasm/src/main/java/org/prism/PrismWASM.java | 1 - 2 files changed, 2 deletions(-) diff --git a/java-wasm/src/main/java/org/prism/PrismAOT.java b/java-wasm/src/main/java/org/prism/PrismAOT.java index fce4bd7168..f669c1ab07 100644 --- a/java-wasm/src/main/java/org/prism/PrismAOT.java +++ b/java-wasm/src/main/java/org/prism/PrismAOT.java @@ -48,7 +48,6 @@ public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLeng exports.pmBufferLength(bufferPointer)); } finally { if (sourcePointer != 0) { - exports.pmStringFree(sourcePointer); exports.free(sourcePointer); } if (optionsPointer != 0) { diff --git a/java-wasm/src/main/java/org/prism/PrismWASM.java b/java-wasm/src/main/java/org/prism/PrismWASM.java index a1b7c374e4..63afd5495e 100644 --- a/java-wasm/src/main/java/org/prism/PrismWASM.java +++ b/java-wasm/src/main/java/org/prism/PrismWASM.java @@ -48,7 +48,6 @@ public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLeng exports.pmBufferLength(bufferPointer)); } finally { if (sourcePointer != 0) { - exports.pmStringFree(sourcePointer); exports.free(sourcePointer); } if (optionsPointer != 0) { From d05601c40fd8de808520a772d2d5d2cbd8117205 Mon Sep 17 00:00:00 2001 From: andreatp Date: Wed, 28 Jan 2026 10:39:04 +0000 Subject: [PATCH 11/14] Cleanup and improvements --- .github/workflows/java-wasm-bindings.yml | 2 +- .gitignore | 2 + Makefile | 6 +- java-wasm/perf-test/.gitignore | 1 + java-wasm/perf-test/bench.sh | 6 ++ java-wasm/perf-test/test | 97 +++++++++++++++++++ java-wasm/pom.xml | 2 +- .../org/prism/WasmResource.java | 2 +- java-wasm/src/main/java/org/prism/Prism.java | 70 +++++++++++-- .../src/main/java/org/prism/PrismAOT.java | 69 ------------- .../src/main/java/org/prism/PrismWASM.java | 69 ------------- .../src/test/java/org/prism/DummyTest.java | 8 +- .../src/test/java/org/prism/JRubyTest.java | 33 ++++--- .../src/{main => test}/resources/.gitignore | 0 14 files changed, 199 insertions(+), 168 deletions(-) create mode 100644 java-wasm/perf-test/.gitignore create mode 100755 java-wasm/perf-test/bench.sh create mode 100755 java-wasm/perf-test/test delete mode 100644 java-wasm/src/main/java/org/prism/PrismAOT.java delete mode 100644 java-wasm/src/main/java/org/prism/PrismWASM.java rename java-wasm/src/{main => test}/resources/.gitignore (100%) diff --git a/.github/workflows/java-wasm-bindings.yml b/.github/workflows/java-wasm-bindings.yml index 5d130c07e1..3a0a066334 100644 --- a/.github/workflows/java-wasm-bindings.yml +++ b/.github/workflows/java-wasm-bindings.yml @@ -48,4 +48,4 @@ jobs: - uses: actions/upload-artifact@v6 with: name: prism.wasm - path: java-wasm/src/main/resources/prism.wasm + path: java-wasm/src/test/resources/prism.wasm diff --git a/.gitignore b/.gitignore index 6706482b64..b00333f107 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,5 @@ compile_commands.json .vscode/ tags + +wasi-sdk* diff --git a/Makefile b/Makefile index 42dbb07e8d..d531e13774 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ SOEXT ?= $(shell ruby -e 'puts RbConfig::CONFIG["SOEXT"]') CPPFLAGS := -Iinclude $(CPPFLAGS) CFLAGS := -g -O2 -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fPIC -fvisibility=hidden -Wimplicit-fallthrough $(CFLAGS) -JAVA_WASM_CFLAGS := -g -Oz -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fPIC -fvisibility=hidden -Wimplicit-fallthrough $(JAVA_WASM_CFLAGS) +JAVA_WASM_CFLAGS := -g0 -O2 -Oz -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fno-sanitize=all -fno-stack-protector -fPIC -fvisibility=hidden -Wimplicit-fallthrough $(JAVA_WASM_CFLAGS) CC ?= cc AR ?= ar ARFLAGS ?= -r$(V0:1=v) @@ -31,7 +31,7 @@ all: shared static shared: build/libprism.$(SOEXT) static: build/libprism.a wasm: javascript/src/prism.wasm -java-wasm: java-wasm/src/main/resources/prism.wasm +java-wasm: java-wasm/src/test/resources/prism.wasm build/libprism.$(SOEXT): $(SHARED_OBJECTS) $(ECHO) "linking $@ with $(CC)" @@ -51,7 +51,7 @@ javascript/src/prism.wasm: Makefile $(SOURCES) $(HEADERS) -Oz -g0 -flto -fdata-sections -ffunction-sections \ -o $@ $(SOURCES) -java-wasm/src/main/resources/prism.wasm: Makefile $(SOURCES) $(HEADERS) +java-wasm/src/test/resources/prism.wasm: Makefile $(SOURCES) $(HEADERS) $(ECHO) "building $@" $(Q) $(MAKEDIRS) $(@D) $(Q) $(WASI_SDK_PATH)/bin/clang $(DEBUG_FLAGS) -DPRISM_EXCLUDE_PRETTYPRINT -DPRISM_EXPORT_SYMBOLS -D_WASI_EMULATED_MMAN -lwasi-emulated-mman $(CPPFLAGS) $(JAVA_WASM_CFLAGS) -Wl,--export-all -Wl,--no-entry -mexec-model=reactor -lc++ -lc++abi -o $@ $(SOURCES) diff --git a/java-wasm/perf-test/.gitignore b/java-wasm/perf-test/.gitignore new file mode 100644 index 0000000000..60286da241 --- /dev/null +++ b/java-wasm/perf-test/.gitignore @@ -0,0 +1 @@ +profile.html diff --git a/java-wasm/perf-test/bench.sh b/java-wasm/perf-test/bench.sh new file mode 100755 index 0000000000..1ad69239b1 --- /dev/null +++ b/java-wasm/perf-test/bench.sh @@ -0,0 +1,6 @@ +#! /bin/bash +set -euxo pipefail + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +jbang --fresh --deps org.openjdk.jmh:jmh-generator-annprocess:1.36 --javaagent=ap-loader@jvm-profiling-tools/ap-loader=start,event=cpu,file=${SCRIPT_DIR}/profile.html ${SCRIPT_DIR}/test \ No newline at end of file diff --git a/java-wasm/perf-test/test b/java-wasm/perf-test/test new file mode 100755 index 0000000000..e3aed8347c --- /dev/null +++ b/java-wasm/perf-test/test @@ -0,0 +1,97 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? + +//DEPS org.jruby:chicory-prism:0.0.1-SNAPSHOT +//DEPS org.jruby:jruby-complete:10.0.2.0 + +import static java.lang.System.*; + +import java.io.DataInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.EnumSet; + +import org.jruby.Ruby; + +import org.prism.Prism; +import org.prism.ParsingOptions; + +public class test { + + final static String[] JRUBY_BOOT_FILES = { + "jruby/java.rb", + "jruby/java/core_ext.rb", + "jruby/java/core_ext/object.rb", + "jruby/java/java_ext.rb", + "jruby/kernel.rb", + "jruby/kernel/signal.rb", + "jruby/kernel/kernel.rb", + "jruby/kernel/proc.rb", + "jruby/kernel/process.rb", + "jruby/kernel/enumerator.rb", + "jruby/kernel/enumerable.rb", + "jruby/kernel/io.rb", + "jruby/kernel/gc.rb", + "jruby/kernel/range.rb", + "jruby/kernel/file.rb", + "jruby/kernel/method.rb", + "jruby/kernel/thread.rb", + "jruby/kernel/integer.rb", + "jruby/kernel/time.rb", + "jruby/preludes.rb", + "jruby/kernel/prelude.rb", + "jruby/kernel/enc_prelude.rb", + "META-INF/jruby.home/lib/ruby/stdlib/unicode_normalize.rb", + "jruby/kernel/gem_prelude.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rbconfig.rb", + "jruby/kernel/rbconfig.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/compatibility.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/defaults.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/deprecate.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/errors.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/target_rbconfig.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/exceptions.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/unknown_command_spell_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/basic_specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/stub_specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/platform.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/specification_record.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/util/list.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/requirement.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/version.rb", + }; + + public static void main(String... args) throws Exception { + out.println("Starting"); + var count = 100; + + var prism = new Prism(); + byte[] src = new byte[1024 * 1024]; + + for (int i = 0; i < count; i++) { + for (var file : JRUBY_BOOT_FILES) { + byte[] options = ParsingOptions.serialize( + file.getBytes(StandardCharsets.UTF_8), + 1, + "UTF-8".getBytes(StandardCharsets.UTF_8), + false, + EnumSet.noneOf(ParsingOptions.CommandLine.class), + ParsingOptions.SyntaxVersion.LATEST, + false, + false, + false, + new byte[][][]{} + ); + + try (InputStream fileIn = Ruby.getClassLoader().getResourceAsStream(file)) { + DataInputStream dis = new DataInputStream(fileIn); + int read = dis.read(src); + prism.serialize(options, src, read); + } + } + } + + out.println("End"); + } +} diff --git a/java-wasm/pom.xml b/java-wasm/pom.xml index b2fc01388d..df7176b110 100644 --- a/java-wasm/pom.xml +++ b/java-wasm/pom.xml @@ -131,7 +131,7 @@ org.prism.PrismParser - src/main/resources/prism.wasm + src/test/resources/prism.wasm diff --git a/java-wasm/src/main/java-templates/org/prism/WasmResource.java b/java-wasm/src/main/java-templates/org/prism/WasmResource.java index ccf1d0bcdd..48f2671714 100644 --- a/java-wasm/src/main/java-templates/org/prism/WasmResource.java +++ b/java-wasm/src/main/java-templates/org/prism/WasmResource.java @@ -1,7 +1,7 @@ package org.prism; public final class WasmResource { - public static final String absoluteFile = "file://${project.basedir}/src/main/resources/prism.wasm"; + public static final String absoluteFile = "file://${project.basedir}/src/test/resources/prism.wasm"; private WasmResource() {} } diff --git a/java-wasm/src/main/java/org/prism/Prism.java b/java-wasm/src/main/java/org/prism/Prism.java index 8c40c6dce5..a92709f932 100644 --- a/java-wasm/src/main/java/org/prism/Prism.java +++ b/java-wasm/src/main/java/org/prism/Prism.java @@ -1,30 +1,82 @@ package org.prism; import com.dylibso.chicory.annotations.WasmModuleInterface; +import com.dylibso.chicory.runtime.ByteArrayMemory; +import com.dylibso.chicory.runtime.ImportValues; +import com.dylibso.chicory.runtime.Instance; import com.dylibso.chicory.wasi.WasiOptions; import com.dylibso.chicory.wasi.WasiPreview1; import java.nio.charset.StandardCharsets; @WasmModuleInterface(WasmResource.absoluteFile) -public abstract class Prism implements AutoCloseable { - protected final WasiPreview1 wasi; +public class Prism implements AutoCloseable { + private final WasiPreview1 wasi; + protected final Prism_ModuleExports exports; + private final Instance instance; - Prism() { + private int bufferPointer; + private int preSourcePointer; + private int preOptionsPointer; + + private final int SOURCE_SIZE = 2 * 1024 * 1024; // 2 MiB + private final int PACKED_OPTIONS_BUFFER_SIZE = 1024; + + public Prism() { this(WasiOptions.builder().build()); } - Prism(WasiOptions wasiOpts) { + public Prism(WasiOptions wasiOpts) { wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); - } + instance = Instance.builder(PrismParser.load()) + .withMemoryFactory(ByteArrayMemory::new) + .withMachineFactory(PrismParser::create) + .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) + .build(); + exports = new Prism_ModuleExports(instance); - public static Prism newInstance(boolean aot) { - if (aot) return new PrismAOT(); + preOptionsPointer = exports.calloc(1, PACKED_OPTIONS_BUFFER_SIZE); + preSourcePointer = exports.calloc(1, SOURCE_SIZE); - return new PrismWASM(); + bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1); + exports.pmBufferInit(bufferPointer); } - public abstract byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength); + public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLength) { + int sourcePointer = 0; + boolean useDefaultSourcePointer = sourceLength + 1 > SOURCE_SIZE; + int optionsPointer = 0; + boolean useDefaultOptionsPointer = packedOptions.length > PACKED_OPTIONS_BUFFER_SIZE; + byte[] result; + try { + sourcePointer = (!useDefaultSourcePointer) ? + exports.calloc(1, sourceLength + 1) : preSourcePointer; + instance.memory().write(sourcePointer, sourceBytes, 0, sourceLength); + instance.memory().writeByte(sourcePointer + sourceLength, (byte) 0); + + optionsPointer = (!useDefaultOptionsPointer) ? + exports.calloc(1, packedOptions.length) : preOptionsPointer; + instance.memory().write(optionsPointer, packedOptions); + + exports.pmBufferClear(bufferPointer); + + exports.pmSerializeParse( + bufferPointer, sourcePointer, sourceLength, optionsPointer); + + result = instance.memory().readBytes( + exports.pmBufferValue(bufferPointer), + exports.pmBufferLength(bufferPointer)); + } finally { + if (!useDefaultSourcePointer) { + exports.free(sourcePointer); + } + if (!useDefaultOptionsPointer) { + exports.free(optionsPointer); + } + } + + return result; + } public ParseResult serializeParse(byte[] packedOptions, String source) { var sourceBytes = source.getBytes(StandardCharsets.ISO_8859_1); diff --git a/java-wasm/src/main/java/org/prism/PrismAOT.java b/java-wasm/src/main/java/org/prism/PrismAOT.java deleted file mode 100644 index f669c1ab07..0000000000 --- a/java-wasm/src/main/java/org/prism/PrismAOT.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.prism; - -import com.dylibso.chicory.annotations.WasmModuleInterface; -import com.dylibso.chicory.runtime.ByteArrayMemory; -import com.dylibso.chicory.runtime.ImportValues; -import com.dylibso.chicory.runtime.Instance; -import com.dylibso.chicory.wasm.WasmModule; -import com.dylibso.chicory.wasm.types.MemoryLimits; - -import java.nio.charset.StandardCharsets; - -public class PrismAOT extends Prism { - private final Prism_ModuleExports exports; - private final Instance instance; - - public PrismAOT() { - super(); - instance = Instance.builder(PrismParser.load()) - .withMemoryFactory(ByteArrayMemory::new) - .withMachineFactory(PrismParser::create) - .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) - .build(); - exports = new Prism_ModuleExports(instance); - } - - @Override - public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLength) { - int sourcePointer = 0; - int optionsPointer = 0; - int bufferPointer = 0; - byte[] result; - try { - sourcePointer = exports.calloc(1, sourceLength + 1); - instance.memory().write(sourcePointer, sourceBytes, 0, sourceLength); - instance.memory().writeByte(sourcePointer + sourceLength, (byte) 0); - - optionsPointer = exports.calloc(1, packedOptions.length); - instance.memory().write(optionsPointer, packedOptions); - - bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1); - exports.pmBufferInit(bufferPointer); - - exports.pmSerializeParse( - bufferPointer, sourcePointer, sourceLength, optionsPointer); - - result = instance.memory().readBytes( - exports.pmBufferValue(bufferPointer), - exports.pmBufferLength(bufferPointer)); - } finally { - if (sourcePointer != 0) { - exports.free(sourcePointer); - } - if (optionsPointer != 0) { - exports.free(optionsPointer); - } - if (bufferPointer != 0) { - exports.pmBufferFree(bufferPointer); - exports.free(bufferPointer); - } - } - - return result; - } - - // DEBUG CHECK - public int memorySize() { - return instance.memory().pages(); - } -} diff --git a/java-wasm/src/main/java/org/prism/PrismWASM.java b/java-wasm/src/main/java/org/prism/PrismWASM.java deleted file mode 100644 index 63afd5495e..0000000000 --- a/java-wasm/src/main/java/org/prism/PrismWASM.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.prism; - -import com.dylibso.chicory.runtime.ByteArrayMemory; -import com.dylibso.chicory.runtime.ImportValues; -import com.dylibso.chicory.runtime.Instance; -import com.dylibso.chicory.runtime.InterpreterMachine; -import com.dylibso.chicory.wasm.Parser; -import com.dylibso.chicory.wasm.WasmModule; - -public class PrismWASM extends Prism { - private final Prism_ModuleExports exports; - private final Instance instance; - - public PrismWASM() { - super(); - WasmModule module = Parser.parse( - PrismWASM.class.getResourceAsStream("/prism.wasm")); - instance = Instance.builder(module) - .withMemoryFactory(ByteArrayMemory::new) - .withMachineFactory(InterpreterMachine::new) - .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) - .build(); - exports = new Prism_ModuleExports(instance); - } - - @Override - public byte[] serialize(byte[] packedOptions, byte[] sourceBytes, int sourceLength) { - int sourcePointer = 0; - int optionsPointer = 0; - int bufferPointer = 0; - byte[] result; - try { - sourcePointer = exports.calloc(1, sourceLength + 1); - instance.memory().write(sourcePointer, sourceBytes, 0, sourceLength); - instance.memory().writeByte(sourcePointer + sourceLength, (byte) 0); - - optionsPointer = exports.calloc(1, packedOptions.length); - instance.memory().write(optionsPointer, packedOptions); - - bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1); - exports.pmBufferInit(bufferPointer); - - exports.pmSerializeParse( - bufferPointer, sourcePointer, sourceLength, optionsPointer); - - result = instance.memory().readBytes( - exports.pmBufferValue(bufferPointer), - exports.pmBufferLength(bufferPointer)); - } finally { - if (sourcePointer != 0) { - exports.free(sourcePointer); - } - if (optionsPointer != 0) { - exports.free(optionsPointer); - } - if (bufferPointer != 0) { - exports.pmBufferFree(bufferPointer); - exports.free(bufferPointer); - } - } - - return result; - } - - // DEBUG - public int memorySize() { - return instance.memory().pages(); - } -} diff --git a/java-wasm/src/test/java/org/prism/DummyTest.java b/java-wasm/src/test/java/org/prism/DummyTest.java index fe6d87d2e7..fd106e07a3 100644 --- a/java-wasm/src/test/java/org/prism/DummyTest.java +++ b/java-wasm/src/test/java/org/prism/DummyTest.java @@ -28,7 +28,7 @@ public void test1() { var source = "1 + 1"; ParseResult pr = null; - try (Prism prism = new PrismWASM()) { + try (Prism prism = new Prism()) { pr = prism.serializeParse(packedOptions, source); } @@ -44,7 +44,7 @@ public void test1Aot() { var source = "1 + 1"; ParseResult pr = null; - try (Prism prism = new PrismAOT()) { + try (Prism prism = new Prism()) { pr = prism.serializeParse(packedOptions, source); } @@ -60,7 +60,7 @@ public void test2() { var source = "puts \"h\ne\nl\nl\no\n\""; ParseResult pr = null; - try (Prism prism = new PrismWASM()) { + try (Prism prism = new Prism()) { pr = prism.serializeParse(packedOptions, source); } @@ -76,7 +76,7 @@ public void test2Aot() { var source = "puts \"h\ne\nl\nl\no\n\""; ParseResult pr = null; - try (Prism prism = new PrismAOT()) { + try (Prism prism = new Prism()) { pr = prism.serializeParse(packedOptions, source); } diff --git a/java-wasm/src/test/java/org/prism/JRubyTest.java b/java-wasm/src/test/java/org/prism/JRubyTest.java index 7dc265c983..c158568602 100644 --- a/java-wasm/src/test/java/org/prism/JRubyTest.java +++ b/java-wasm/src/test/java/org/prism/JRubyTest.java @@ -56,26 +56,36 @@ public class JRubyTest { "META-INF/jruby.home/lib/ruby/stdlib/rubygems/version.rb", }; + private class TestingPrism extends Prism { + TestingPrism() { + super(); + } + + public int memPages() { + return exports.memory().pages(); + } + } + @Test - public void jrubyAOT() throws Exception { - var prismAot = new PrismAOT(); + public void jrubyReproducer() throws Exception { + var prism = new TestingPrism(); for (int i = 0; i < 3; i++) { - basicJRubyTest(prismAot); + basicJRubyTest(prism); } - var memoryPagesBefore = prismAot.memorySize(); + var memoryPagesBefore = prism.memPages(); for (int i = 0; i < 100; i++) { - basicJRubyTest(prismAot); + var before = System.currentTimeMillis(); + + basicJRubyTest(prism); + + var after = System.currentTimeMillis(); + System.out.println("Elapsed: " + (after - before)); } - var memoryPagesAfter = prismAot.memorySize(); + var memoryPagesAfter = prism.memPages(); assertEquals(memoryPagesBefore, memoryPagesAfter); } - @Test - public void jrubyWASM() throws Exception { - basicJRubyTest(new PrismWASM()); - } - private static void basicJRubyTest(Prism prism) throws Exception { byte[] src = new byte[1024 * 1024]; @@ -92,6 +102,7 @@ private static void basicJRubyTest(Prism prism) throws Exception { false, new byte[][][]{} ); + try (InputStream fileIn = Ruby.getClassLoader().getResourceAsStream(file)) { DataInputStream dis = new DataInputStream(fileIn); int read = dis.read(src); diff --git a/java-wasm/src/main/resources/.gitignore b/java-wasm/src/test/resources/.gitignore similarity index 100% rename from java-wasm/src/main/resources/.gitignore rename to java-wasm/src/test/resources/.gitignore From 94ee315d214e8305aba53c2016d3331533417174 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Tue, 3 Feb 2026 13:42:07 -0600 Subject: [PATCH 12/14] Update java-wasm to Java 21 minimum This is only intended to ever be used with JRuby 10+, which already requires Java 21. --- .github/workflows/java-wasm-bindings.yml | 2 +- java-wasm/pom.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/java-wasm-bindings.yml b/.github/workflows/java-wasm-bindings.yml index 3a0a066334..679fb09a8b 100644 --- a/.github/workflows/java-wasm-bindings.yml +++ b/.github/workflows/java-wasm-bindings.yml @@ -38,7 +38,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '17' + java-version: '21' cache: maven - name: Run the tests diff --git a/java-wasm/pom.xml b/java-wasm/pom.xml index df7176b110..ced62df507 100644 --- a/java-wasm/pom.xml +++ b/java-wasm/pom.xml @@ -12,8 +12,8 @@ UTF-8 UTF-8 - 11 - 11 + 21 + 21 1.6.1 6.0.2 @@ -86,7 +86,7 @@ maven-compiler-plugin 3.14.1 - 11 + 21 com.dylibso.chicory From 2235128093bacc0b885ffcc738f6fe9d81a10a0c Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Tue, 3 Feb 2026 14:18:12 -0600 Subject: [PATCH 13/14] Move WASM parser to a JRuby package * org.jruby.parser is base package for JRuby parsers * org.jruby.parser.prism is for parsers based on Prism * org.jruby.parser.prism.wasm is for the WASM Prism wrapper --- java-wasm/perf-test/test | 2 +- java-wasm/pom.xml | 2 +- .../org/{prism => jruby/parser/prism/wasm}/WasmResource.java | 2 +- .../java/org/{prism => jruby/parser/prism/wasm}/Prism.java | 4 +++- .../test/java/org/{ => jruby/parser}/prism/DummyTest.java | 5 ++++- .../test/java/org/{ => jruby/parser}/prism/JRubyTest.java | 4 +++- 6 files changed, 13 insertions(+), 6 deletions(-) rename java-wasm/src/main/java-templates/org/{prism => jruby/parser/prism/wasm}/WasmResource.java (82%) rename java-wasm/src/main/java/org/{prism => jruby/parser/prism/wasm}/Prism.java (97%) rename java-wasm/src/test/java/org/{ => jruby/parser}/prism/DummyTest.java (94%) rename java-wasm/src/test/java/org/{ => jruby/parser}/prism/JRubyTest.java (97%) diff --git a/java-wasm/perf-test/test b/java-wasm/perf-test/test index e3aed8347c..e2c8dfb575 100755 --- a/java-wasm/perf-test/test +++ b/java-wasm/perf-test/test @@ -12,7 +12,7 @@ import java.util.EnumSet; import org.jruby.Ruby; -import org.prism.Prism; +import org.jruby.parser.prism.wasm.Prism; import org.prism.ParsingOptions; public class test { diff --git a/java-wasm/pom.xml b/java-wasm/pom.xml index ced62df507..a4bc82343b 100644 --- a/java-wasm/pom.xml +++ b/java-wasm/pom.xml @@ -130,7 +130,7 @@ compile - org.prism.PrismParser + org.jruby.parser.prism.wasm.PrismParser src/test/resources/prism.wasm diff --git a/java-wasm/src/main/java-templates/org/prism/WasmResource.java b/java-wasm/src/main/java-templates/org/jruby/parser/prism/wasm/WasmResource.java similarity index 82% rename from java-wasm/src/main/java-templates/org/prism/WasmResource.java rename to java-wasm/src/main/java-templates/org/jruby/parser/prism/wasm/WasmResource.java index 48f2671714..2b95406d13 100644 --- a/java-wasm/src/main/java-templates/org/prism/WasmResource.java +++ b/java-wasm/src/main/java-templates/org/jruby/parser/prism/wasm/WasmResource.java @@ -1,4 +1,4 @@ -package org.prism; +package org.jruby.parser.prism.wasm; public final class WasmResource { public static final String absoluteFile = "file://${project.basedir}/src/test/resources/prism.wasm"; diff --git a/java-wasm/src/main/java/org/prism/Prism.java b/java-wasm/src/main/java/org/jruby/parser/prism/wasm/Prism.java similarity index 97% rename from java-wasm/src/main/java/org/prism/Prism.java rename to java-wasm/src/main/java/org/jruby/parser/prism/wasm/Prism.java index a92709f932..dafba022e9 100644 --- a/java-wasm/src/main/java/org/prism/Prism.java +++ b/java-wasm/src/main/java/org/jruby/parser/prism/wasm/Prism.java @@ -1,4 +1,4 @@ -package org.prism; +package org.jruby.parser.prism.wasm; import com.dylibso.chicory.annotations.WasmModuleInterface; import com.dylibso.chicory.runtime.ByteArrayMemory; @@ -6,6 +6,8 @@ import com.dylibso.chicory.runtime.Instance; import com.dylibso.chicory.wasi.WasiOptions; import com.dylibso.chicory.wasi.WasiPreview1; +import org.prism.Loader; +import org.prism.ParseResult; import java.nio.charset.StandardCharsets; diff --git a/java-wasm/src/test/java/org/prism/DummyTest.java b/java-wasm/src/test/java/org/jruby/parser/prism/DummyTest.java similarity index 94% rename from java-wasm/src/test/java/org/prism/DummyTest.java rename to java-wasm/src/test/java/org/jruby/parser/prism/DummyTest.java index fd106e07a3..5c12fb45a1 100644 --- a/java-wasm/src/test/java/org/prism/DummyTest.java +++ b/java-wasm/src/test/java/org/jruby/parser/prism/DummyTest.java @@ -1,6 +1,9 @@ -package org.prism; +package org.jruby.parser.prism; +import org.jruby.parser.prism.wasm.Prism; import org.junit.jupiter.api.Test; +import org.prism.ParseResult; +import org.prism.ParsingOptions; import java.util.EnumSet; diff --git a/java-wasm/src/test/java/org/prism/JRubyTest.java b/java-wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java similarity index 97% rename from java-wasm/src/test/java/org/prism/JRubyTest.java rename to java-wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java index c158568602..5b6364a926 100644 --- a/java-wasm/src/test/java/org/prism/JRubyTest.java +++ b/java-wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java @@ -1,7 +1,9 @@ -package org.prism; +package org.jruby.parser.prism; import org.jruby.Ruby; +import org.jruby.parser.prism.wasm.Prism; import org.junit.jupiter.api.Test; +import org.prism.ParsingOptions; import java.io.DataInputStream; import java.io.InputStream; From 033392a7b148d565380e4497598cf37d3b43c323 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 4 Feb 2026 14:48:06 -0600 Subject: [PATCH 14/14] Rename Java package to org.ruby_lang.prism This moves all of the non-JRuby Java code from the org.prism package to the org.ruby_lang.prism package (corresponding to the ruby-lang.org domain and prism project). --- .gitignore | 6 +++--- docs/configuration.md | 6 +++--- docs/releasing.md | 6 +++--- java-wasm/perf-test/test | 2 +- .../src/main/java/org/jruby/parser/prism/wasm/Prism.java | 4 ++-- .../src/test/java/org/jruby/parser/prism/DummyTest.java | 4 ++-- .../src/test/java/org/jruby/parser/prism/JRubyTest.java | 2 +- java/org/{ => ruby_lang}/prism/MarkNewlinesVisitor.java | 2 +- java/org/{ => ruby_lang}/prism/ParseResult.java | 2 +- java/org/{ => ruby_lang}/prism/Parser.java | 2 +- java/org/{ => ruby_lang}/prism/ParsingOptions.java | 2 +- rakelib/serialization.rake | 2 +- .../org/{ => ruby_lang}/prism/AbstractNodeVisitor.java.erb | 2 +- templates/java/org/{ => ruby_lang}/prism/Loader.java.erb | 4 ++-- templates/java/org/{ => ruby_lang}/prism/Nodes.java.erb | 2 +- templates/template.rb | 6 +++--- 16 files changed, 27 insertions(+), 27 deletions(-) rename java/org/{ => ruby_lang}/prism/MarkNewlinesVisitor.java (98%) rename java/org/{ => ruby_lang}/prism/ParseResult.java (98%) rename java/org/{ => ruby_lang}/prism/Parser.java (92%) rename java/org/{ => ruby_lang}/prism/ParsingOptions.java (99%) rename templates/java/org/{ => ruby_lang}/prism/AbstractNodeVisitor.java.erb (94%) rename templates/java/org/{ => ruby_lang}/prism/Loader.java.erb (99%) rename templates/java/org/{ => ruby_lang}/prism/Nodes.java.erb (99%) diff --git a/.gitignore b/.gitignore index b00333f107..218a76cd00 100644 --- a/.gitignore +++ b/.gitignore @@ -39,9 +39,9 @@ out.svg /javascript/src/visitor.js /javascript/src/prism.wasm /javascript/src/*.d.ts -/java/org/prism/AbstractNodeVisitor.java -/java/org/prism/Loader.java -/java/org/prism/Nodes.java +/java/org/ruby_lang/prism/AbstractNodeVisitor.java +/java/org/ruby_lang/prism/Loader.java +/java/org/ruby_lang/prism/Nodes.java /lib/prism/compiler.rb /lib/prism/dispatcher.rb /lib/prism/dot_visitor.rb diff --git a/docs/configuration.md b/docs/configuration.md index 04887d6d9c..97e01c8d82 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -7,9 +7,9 @@ A lot of code in prism's repository is templated from a single configuration fil * `include/prism/diagnostic.h` - for defining the diagnostics * `javascript/src/deserialize.js` - for defining how to deserialize the nodes in JavaScript * `javascript/src/nodes.js` - for defining the nodes in JavaScript -* `java/org/prism/AbstractNodeVisitor.java` - for defining the visitor interface for the nodes in Java -* `java/org/prism/Loader.java` - for defining how to deserialize the nodes in Java -* `java/org/prism/Nodes.java` - for defining the nodes in Java +* `java/org/ruby_lang/prism/AbstractNodeVisitor.java` - for defining the visitor interface for the nodes in Java +* `java/org/ruby_lang/prism/Loader.java` - for defining how to deserialize the nodes in Java +* `java/org/ruby_lang/prism/Nodes.java` - for defining the nodes in Java * `lib/prism/compiler.rb` - for defining the compiler for the nodes in Ruby * `lib/prism/dispatcher.rb` - for defining the dispatch visitors for the nodes in Ruby * `lib/prism/dot_visitor.rb` - for defining the dot visitor for the nodes in Ruby diff --git a/docs/releasing.md b/docs/releasing.md index a7d3cf5eb3..6d0f892926 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -23,9 +23,9 @@ ruby -pi -e 'gsub(/PRISM_VERSION_PATCH \d+/, %Q{PRISM_VERSION_PATCH #{ENV["PRISM ruby -pi -e 'gsub(/PRISM_VERSION ".+?"/, %Q{PRISM_VERSION "#{ENV["PRISM_VERSION"]}"})' include/prism/version.h ruby -pi -e 'gsub(/"version": ".+?"/, %Q{"version": "#{ENV["PRISM_VERSION"]}"})' javascript/package.json ruby -pi -e 'gsub(/lossy\(\), ".+?"/, %Q{lossy(), "#{ENV["PRISM_VERSION"]}"})' rust/ruby-prism-sys/tests/utils_tests.rs -ruby -pi -e 'gsub(/\d+, "prism major/, %Q{#{ENV["PRISM_MAJOR"]}, "prism major})' templates/java/org/prism/Loader.java.erb -ruby -pi -e 'gsub(/\d+, "prism minor/, %Q{#{ENV["PRISM_MINOR"]}, "prism minor})' templates/java/org/prism/Loader.java.erb -ruby -pi -e 'gsub(/\d+, "prism patch/, %Q{#{ENV["PRISM_PATCH"]}, "prism patch})' templates/java/org/prism/Loader.java.erb +ruby -pi -e 'gsub(/\d+, "prism major/, %Q{#{ENV["PRISM_MAJOR"]}, "prism major})' templates/java/org/ruby_lang/prism/Loader.java.erb +ruby -pi -e 'gsub(/\d+, "prism minor/, %Q{#{ENV["PRISM_MINOR"]}, "prism minor})' templates/java/org/ruby_lang/prism/Loader.java.erb +ruby -pi -e 'gsub(/\d+, "prism patch/, %Q{#{ENV["PRISM_PATCH"]}, "prism patch})' templates/java/org/ruby_lang/prism/Loader.java.erb ruby -pi -e 'gsub(/MAJOR_VERSION = \d+/, %Q{MAJOR_VERSION = #{ENV["PRISM_MAJOR"]}})' templates/javascript/src/deserialize.js.erb ruby -pi -e 'gsub(/MINOR_VERSION = \d+/, %Q{MINOR_VERSION = #{ENV["PRISM_MINOR"]}})' templates/javascript/src/deserialize.js.erb ruby -pi -e 'gsub(/PATCH_VERSION = \d+/, %Q{PATCH_VERSION = #{ENV["PRISM_PATCH"]}})' templates/javascript/src/deserialize.js.erb diff --git a/java-wasm/perf-test/test b/java-wasm/perf-test/test index e2c8dfb575..f2d5492bc4 100755 --- a/java-wasm/perf-test/test +++ b/java-wasm/perf-test/test @@ -13,7 +13,7 @@ import java.util.EnumSet; import org.jruby.Ruby; import org.jruby.parser.prism.wasm.Prism; -import org.prism.ParsingOptions; +import org.ruby_lang.prism.ParsingOptions; public class test { diff --git a/java-wasm/src/main/java/org/jruby/parser/prism/wasm/Prism.java b/java-wasm/src/main/java/org/jruby/parser/prism/wasm/Prism.java index dafba022e9..6dd89c70cb 100644 --- a/java-wasm/src/main/java/org/jruby/parser/prism/wasm/Prism.java +++ b/java-wasm/src/main/java/org/jruby/parser/prism/wasm/Prism.java @@ -6,8 +6,8 @@ import com.dylibso.chicory.runtime.Instance; import com.dylibso.chicory.wasi.WasiOptions; import com.dylibso.chicory.wasi.WasiPreview1; -import org.prism.Loader; -import org.prism.ParseResult; +import org.ruby_lang.prism.Loader; +import org.ruby_lang.prism.ParseResult; import java.nio.charset.StandardCharsets; diff --git a/java-wasm/src/test/java/org/jruby/parser/prism/DummyTest.java b/java-wasm/src/test/java/org/jruby/parser/prism/DummyTest.java index 5c12fb45a1..4e89f6f6e3 100644 --- a/java-wasm/src/test/java/org/jruby/parser/prism/DummyTest.java +++ b/java-wasm/src/test/java/org/jruby/parser/prism/DummyTest.java @@ -2,8 +2,8 @@ import org.jruby.parser.prism.wasm.Prism; import org.junit.jupiter.api.Test; -import org.prism.ParseResult; -import org.prism.ParsingOptions; +import org.ruby_lang.prism.ParseResult; +import org.ruby_lang.prism.ParsingOptions; import java.util.EnumSet; diff --git a/java-wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java b/java-wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java index 5b6364a926..b8249f4c70 100644 --- a/java-wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java +++ b/java-wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java @@ -3,7 +3,7 @@ import org.jruby.Ruby; import org.jruby.parser.prism.wasm.Prism; import org.junit.jupiter.api.Test; -import org.prism.ParsingOptions; +import org.ruby_lang.prism.ParsingOptions; import java.io.DataInputStream; import java.io.InputStream; diff --git a/java/org/prism/MarkNewlinesVisitor.java b/java/org/ruby_lang/prism/MarkNewlinesVisitor.java similarity index 98% rename from java/org/prism/MarkNewlinesVisitor.java rename to java/org/ruby_lang/prism/MarkNewlinesVisitor.java index 27ab8643e7..8d08424942 100644 --- a/java/org/prism/MarkNewlinesVisitor.java +++ b/java/org/ruby_lang/prism/MarkNewlinesVisitor.java @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; // Keep in sync with Ruby MarkNewlinesVisitor final class MarkNewlinesVisitor extends AbstractNodeVisitor { diff --git a/java/org/prism/ParseResult.java b/java/org/ruby_lang/prism/ParseResult.java similarity index 98% rename from java/org/prism/ParseResult.java rename to java/org/ruby_lang/prism/ParseResult.java index 144ea16e36..aad05a892d 100644 --- a/java/org/prism/ParseResult.java +++ b/java/org/ruby_lang/prism/ParseResult.java @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; // @formatter:off public final class ParseResult { diff --git a/java/org/prism/Parser.java b/java/org/ruby_lang/prism/Parser.java similarity index 92% rename from java/org/prism/Parser.java rename to java/org/ruby_lang/prism/Parser.java index 717c3e5179..5c60977d80 100644 --- a/java/org/prism/Parser.java +++ b/java/org/ruby_lang/prism/Parser.java @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; public abstract class Parser { diff --git a/java/org/prism/ParsingOptions.java b/java/org/ruby_lang/prism/ParsingOptions.java similarity index 99% rename from java/org/prism/ParsingOptions.java rename to java/org/ruby_lang/prism/ParsingOptions.java index be0a8a7dba..8990f72104 100644 --- a/java/org/prism/ParsingOptions.java +++ b/java/org/ruby_lang/prism/ParsingOptions.java @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; diff --git a/rakelib/serialization.rake b/rakelib/serialization.rake index a57a7ba700..499be443ce 100644 --- a/rakelib/serialization.rake +++ b/rakelib/serialization.rake @@ -26,7 +26,7 @@ task "test:java_loader:internal" => :compile do puts path serialized = Prism.dump_file(path) source_bytes = File.binread(path).unpack('c*') - parse_result = org.prism.Loader.load(serialized.unpack('c*'), source_bytes) + parse_result = org.ruby_lang.prism.Loader.load(serialized.unpack('c*'), source_bytes) puts parse_result.value end end diff --git a/templates/java/org/prism/AbstractNodeVisitor.java.erb b/templates/java/org/ruby_lang/prism/AbstractNodeVisitor.java.erb similarity index 94% rename from templates/java/org/prism/AbstractNodeVisitor.java.erb rename to templates/java/org/ruby_lang/prism/AbstractNodeVisitor.java.erb index a66daedf63..58eabebae8 100644 --- a/templates/java/org/prism/AbstractNodeVisitor.java.erb +++ b/templates/java/org/ruby_lang/prism/AbstractNodeVisitor.java.erb @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; // GENERATED BY <%= File.basename(__FILE__) %> // @formatter:off diff --git a/templates/java/org/prism/Loader.java.erb b/templates/java/org/ruby_lang/prism/Loader.java.erb similarity index 99% rename from templates/java/org/prism/Loader.java.erb rename to templates/java/org/ruby_lang/prism/Loader.java.erb index aa8e1e30c9..af47aeed73 100644 --- a/templates/java/org/prism/Loader.java.erb +++ b/templates/java/org/ruby_lang/prism/Loader.java.erb @@ -1,7 +1,7 @@ <%- string_type = Prism::Template::JAVA_STRING_TYPE -%> -package org.prism; +package org.ruby_lang.prism; -import org.prism.Nodes; +import org.ruby_lang.prism.Nodes; import java.lang.Short; import java.math.BigInteger; diff --git a/templates/java/org/prism/Nodes.java.erb b/templates/java/org/ruby_lang/prism/Nodes.java.erb similarity index 99% rename from templates/java/org/prism/Nodes.java.erb rename to templates/java/org/ruby_lang/prism/Nodes.java.erb index 8b0a9ce20d..47b2ea0a7e 100644 --- a/templates/java/org/prism/Nodes.java.erb +++ b/templates/java/org/ruby_lang/prism/Nodes.java.erb @@ -1,5 +1,5 @@ <%- string_type = Prism::Template::JAVA_STRING_TYPE -%> -package org.prism; +package org.ruby_lang.prism; import java.lang.Override; import java.lang.String; diff --git a/templates/template.rb b/templates/template.rb index aca626b5eb..d0287464f6 100755 --- a/templates/template.rb +++ b/templates/template.rb @@ -656,9 +656,9 @@ def locals "javascript/src/deserialize.js", "javascript/src/nodes.js", "javascript/src/visitor.js", - "java/org/prism/Loader.java", - "java/org/prism/Nodes.java", - "java/org/prism/AbstractNodeVisitor.java", + "java/org/ruby_lang/prism/Loader.java", + "java/org/ruby_lang/prism/Nodes.java", + "java/org/ruby_lang/prism/AbstractNodeVisitor.java", "lib/prism/compiler.rb", "lib/prism/dispatcher.rb", "lib/prism/dot_visitor.rb",