Repository: ruby/prism
Pull request #2239 (2024-01-22 18:04:20)

Test the generated wasm distribution on Chicory
Hi all 👋 !
Amazing project 🙂 !

In this PR we produce an unoptimized version of Prism that can be interpreted using the early stage build of [Chicory](https://github.com/dylibso/chicory).
The produced `prism.wasm` artifact is uploaded in CI for easy access.


Patch:
diff --git a/.github/workflows/java-wasm-bindings.yml b/.github/workflows/java-wasm-bindings.yml
new file mode 100644
index 00000000..03994a3d
--- /dev/null
+++ b/.github/workflows/java-wasm-bindings.yml
@@ -0,0 +1,51 @@
+name: Bindings to Chicory
+
+on:
+  push:
+    paths:
+      - ".github/workflows/java-wasm-bindings.yml"
+      - "include/"
+      - "src/"
+      - "*akefile*"
+    branches:
+      - main
+  pull_request:
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Ruby
+        uses: ruby/setup-ruby@v1
+        with:
+          ruby-version: head
+          bundler-cache: true
+
+      - name: rake templates
+        run: PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS=1 bundle exec rake templates
+
+      - name: Set up WASI-SDK
+        run: |
+          wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/wasi-sdk-21.0-linux.tar.gz
+          tar xvf wasi-sdk-21.0-linux.tar.gz
+
+      - name: Build the project
+        run: make java-wasm WASI_SDK_PATH=$(pwd)/wasi-sdk-21.0 PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS=1
+
+      - name: Set up Java
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'temurin'
+          java-version: '17'
+          cache: maven
+
+      - name: Run the tests
+        run: mvn -B install
+        working-directory: java-wasm
+
+      - uses: actions/upload-artifact@v3
+        with:
+          name: prism.wasm
+          path: java-wasm/src/test/resources/prism.wasm
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 722cf6de..f2a21c62 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -191,7 +191,7 @@ jobs:
         sudo apt-get update
         sudo apt-get install -y libc6-dbg
     - name: Cache valgrind
-      uses: actions/cache@v4
+      uses: actions/cache@v3
       id: cache-valgrind
       with:
         path: valgrind-3.20.0
diff --git a/.github/workflows/rust-bindings.yml b/.github/workflows/rust-bindings.yml
index 446d9839..f3841ba0 100644
--- a/.github/workflows/rust-bindings.yml
+++ b/.github/workflows/rust-bindings.yml
@@ -34,7 +34,7 @@ jobs:
         with:
           toolchain: "1.71.1"
           targets: wasm32-wasi
-      - uses: actions/cache@v4
+      - uses: actions/cache@v3
         with:
           path: |
             ~/.cargo/registry
@@ -66,7 +66,7 @@ jobs:
         with:
           toolchain: "1.71.1"
           components: clippy, rustfmt
-      - uses: actions/cache@v4
+      - uses: actions/cache@v3
         with:
           path: |
             ~/.cargo/registry
@@ -126,7 +126,7 @@ jobs:
   #       with:
   #         toolchain: "1.71.1"
   #         targets: wasm32-wasi
-  #     - uses: actions/cache@v4
+  #     - uses: actions/cache@v3
   #       with:
   #         path: |
   #           ~/.cargo/registry
diff --git a/.github/workflows/truffleruby.yml b/.github/workflows/truffleruby.yml
index 7f9993be..daedb7f5 100644
--- a/.github/workflows/truffleruby.yml
+++ b/.github/workflows/truffleruby.yml
@@ -36,7 +36,7 @@ jobs:
       run: echo "$PWD/truffleruby-ws/truffleruby/bin" >> $GITHUB_PATH
 
     - name: Restore ~/.mx/cache
-      uses: actions/cache@v4
+      uses: actions/cache@v3
       with:
         path: ~/.mx/cache
         key: mx-cache-${{ runner.os }}
diff --git a/Makefile b/Makefile
index 9e1417e5..d2ded6de 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@ SOEXT := $(shell ruby -e 'puts RbConfig::CONFIG["SOEXT"]')
 
 CPPFLAGS := -Iinclude
 CFLAGS := -g -O2 -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fPIC -fvisibility=hidden
+JAVA_WASM_CFLAGS := -O0 -g -o -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fPIC -fvisibility=hidden -nostartfiles -Wl,--no-entry -Wl,
 CC := cc
 WASI_SDK_PATH := /opt/wasi-sdk
 
@@ -25,6 +26,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
 
 build/libprism.$(SOEXT): $(SHARED_OBJECTS)
 	$(ECHO) "linking $@"
@@ -38,6 +40,10 @@ 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)
+	$(ECHO) "building $@"
+	$(Q) $(WASI_SDK_PATH)/bin/clang $(DEBUG_FLAGS) -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)
 	$(ECHO) "compiling $@"
 	$(Q) mkdir -p $(@D)
diff --git a/ext/prism/extension.c b/ext/prism/extension.c
index 416e682f..b7c59e51 100644
--- a/ext/prism/extension.c
+++ b/ext/prism/extension.c
@@ -987,12 +987,12 @@ inspect_node(VALUE self, VALUE source) {
 
 /**
  * call-seq:
- *   Debug::format_errors(source, colorize) -> String
+ *   Debug::format_errors(source) -> String
  *
  * Format the errors that are found when parsing the given source string.
  */
 static VALUE
-format_errors(VALUE self, VALUE source, VALUE colorize) {
+format_errors(VALUE self, VALUE source) {
     pm_string_t input;
     input_load_string(&input, source);
 
@@ -1002,7 +1002,7 @@ format_errors(VALUE self, VALUE source, VALUE colorize) {
     pm_node_t *node = pm_parse(&parser);
     pm_buffer_t buffer = { 0 };
 
-    pm_parser_errors_format(&parser, &buffer, RTEST(colorize));
+    pm_parser_errors_format(&parser, &buffer, true);
 
     rb_encoding *encoding = rb_enc_find(parser.encoding->name);
     VALUE result = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), encoding);
@@ -1093,7 +1093,7 @@ Init_prism(void) {
     rb_define_singleton_method(rb_cPrismDebug, "memsize", memsize, 1);
     rb_define_singleton_method(rb_cPrismDebug, "profile_file", profile_file, 1);
     rb_define_singleton_method(rb_cPrismDebug, "inspect_node", inspect_node, 1);
-    rb_define_singleton_method(rb_cPrismDebug, "format_errors", format_errors, 2);
+    rb_define_singleton_method(rb_cPrismDebug, "format_errors", format_errors, 1);
 
     // Next, initialize the other APIs.
     Init_prism_api_node();
diff --git a/java-wasm/.gitignore b/java-wasm/.gitignore
new file mode 100644
index 00000000..c5078494
--- /dev/null
+++ b/java-wasm/.gitignore
@@ -0,0 +1,2 @@
+target
+.idea
diff --git a/java-wasm/pom.xml b/java-wasm/pom.xml
new file mode 100644
index 00000000..89c504a1
--- /dev/null
+++ b/java-wasm/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>com.prism</groupId>
+  <artifactId>java-prism</artifactId>
+  <version>999-SNAPSHOT</version>
+  <name>Java Prism</name>
+  <description>Pure Java Prism interpreting WASM</description>
+
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <maven.compiler.source>11</maven.compiler.source>
+    <maven.compiler.target>11</maven.compiler.target>
+
+    <junit.version>5.10.1</junit.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.dylibso.chicory</groupId>
+      <artifactId>runtime</artifactId>
+      <version>0.0.5</version>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <version>${junit.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <sourceDirectory>../java</sourceDirectory>
+    <plugins>
+      <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <version>3.2.5</version>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/rakelib/check_manifest.rake b/rakelib/check_manifest.rake
index bb28ad6a..011565ad 100644
--- a/rakelib/check_manifest.rake
+++ b/rakelib/check_manifest.rake
@@ -17,6 +17,7 @@ task check_manifest: :templates do
     fuzz
     javascript
     java
+    java-wasm
     pkg
     rakelib
     rust
diff --git a/src/prism.c b/src/prism.c
index 6b534175..161744e4 100644
--- a/src/prism.c
+++ b/src/prism.c
@@ -1352,13 +1352,6 @@ pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *oper
         end = key->location.end;
     }
 
-    // Hash string keys will be frozen, so we can mark them as frozen here so
-    // that the compiler picks them up and also when we check for static literal
-    // on the keys it gets factored in.
-    if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
-        key->flags |= PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL;
-    }
-
     // If the key and value of this assoc node are both static literals, then
     // we can mark this node as a static literal.
     pm_node_flags_t flags = 0;
@@ -1366,6 +1359,11 @@ pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *oper
         flags = key->flags & value->flags & PM_NODE_FLAG_STATIC_LITERAL;
     }
 
+    // Hash string keys should be frozen
+    if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
+        key->flags |= PM_STRING_FLAGS_FROZEN;
+    }
+
     *node = (pm_assoc_node_t) {
         {
             .type = PM_ASSOC_NODE,
@@ -12721,24 +12719,20 @@ parse_string_part(pm_parser_t *parser) {
  * automatically drop trailing `@` characters. This happens at the parser level,
  * such that `~@` is parsed as `~` and `!@` is parsed as `!`. We do that here.
  */
-static const uint8_t *
-parse_operator_symbol_name(const pm_token_t *name) {
-    switch (name->type) {
-        case PM_TOKEN_TILDE:
-        case PM_TOKEN_BANG:
-            if (name->end[-1] == '@') return name->end - 1;
-        /* fallthrough */
-        default:
-            return name->end;
-    }
-}
-
 static pm_node_t *
 parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_state_t next_state) {
     pm_token_t closing = not_provided(parser);
     pm_symbol_node_t *symbol = pm_symbol_node_create(parser, opening, &parser->current, &closing);
 
-    const uint8_t *end = parse_operator_symbol_name(&parser->current);
+    const uint8_t *end = parser->current.end;
+    switch (parser->current.type) {
+        case PM_TOKEN_TILDE:
+        case PM_TOKEN_BANG:
+            if (parser->current.end[-1] == '@') end--;
+            break;
+        default:
+            break;
+    }
 
     if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
     parser_lex(parser);
@@ -15385,13 +15379,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
             pm_parser_scope_pop(parser);
             pm_parser_current_param_name_restore(parser, saved_param_name);
 
-            /**
-             * If the final character is @. As is the case when defining
-             * methods to override the unary operators, we should ignore
-             * the @ in the same way we do for symbols.
-             */
-            name.end = parse_operator_symbol_name(&name);
-
             return (pm_node_t *) pm_def_node_create(
                 parser,
                 &name,
@@ -17875,7 +17862,7 @@ pm_parser_errors_format_sort(const pm_list_t *error_list, const pm_newline_list_
         if (start.line == end.line) {
             column_end = (uint32_t) end.column;
         } else {
-            column_end = (uint32_t) (newline_list->offsets[start.line] - newline_list->offsets[start.line - 1] - 1);
+            column_end = (uint32_t) (newline_list->offsets[start.line + 1] - newline_list->offsets[start.line] - 1);
         }
 
         // Ensure we have at least one column of error.
@@ -17894,16 +17881,16 @@ pm_parser_errors_format_sort(const pm_list_t *error_list, const pm_newline_list_
 
 static inline void
 pm_parser_errors_format_line(const pm_parser_t *parser, const pm_newline_list_t *newline_list, const char *number_prefix, size_t line, pm_buffer_t *buffer) {
-    const uint8_t *start = &parser->start[newline_list->offsets[line - 1]];
+    const uint8_t *start = &parser->start[newline_list->offsets[line]];
     const uint8_t *end;
 
-    if (line >= newline_list->size) {
+    if (line + 1 >= newline_list->size) {
         end = parser->end;
     } else {
-        end = &parser->start[newline_list->offsets[line]];
+        end = &parser->start[newline_list->offsets[line + 1]];
     }
 
-    pm_buffer_append_format(buffer, number_prefix, (uint32_t) line);
+    pm_buffer_append_format(buffer, number_prefix, (uint32_t) (line + 1));
     pm_buffer_append_string(buffer, (const char *) start, (size_t) (end - start));
 
     if (end == parser->end && end[-1] != '\n') {
@@ -17928,7 +17915,7 @@ pm_parser_errors_format(const pm_parser_t *parser, pm_buffer_t *buffer, bool col
     // blank lines based on the maximum number of digits in the line numbers
     // that are going to be displayed.
     pm_error_format_t error_format;
-    size_t max_line_number = errors[error_list->size - 1].line;
+    size_t max_line_number = errors[error_list->size - 1].line + 1;
 
     if (max_line_number < 10) {
         if (colorize) {
@@ -18010,7 +17997,7 @@ pm_parser_errors_format(const pm_parser_t *parser, pm_buffer_t *buffer, bool col
     // the source before the error to give some context. We'll be careful not to
     // display the same line twice in case the errors are close enough in the
     // source.
-    uint32_t last_line = 0;
+    uint32_t last_line = (uint32_t) -1;
     const pm_encoding_t *encoding = parser->encoding;
 
     for (size_t index = 0; index < error_list->size; index++) {
@@ -18056,7 +18043,7 @@ pm_parser_errors_format(const pm_parser_t *parser, pm_buffer_t *buffer, bool col
         pm_buffer_append_string(buffer, error_format.blank_prefix, error_format.blank_prefix_length);
 
         size_t column = 0;
-        const uint8_t *start = &parser->start[newline_list->offsets[error->line - 1]];
+        const uint8_t *start = &parser->start[newline_list->offsets[error->line]];
 
         while (column < error->column_end) {
             if (column < error->column_start) {
@@ -18080,7 +18067,7 @@ pm_parser_errors_format(const pm_parser_t *parser, pm_buffer_t *buffer, bool col
         // Here we determine how many lines of padding to display after the
         // error, depending on where the next error is in source.
         last_line = error->line;
-        size_t next_line = (index == error_list->size - 1) ? newline_list->size : errors[index + 1].line;
+        size_t next_line = (index == error_list->size - 1) ? newline_list->size - 1 : errors[index + 1].line;
 
         if (next_line - last_line > 1) {
             pm_buffer_append_string(buffer, "  ", 2);
diff --git a/src/util/pm_newline_list.c b/src/util/pm_newline_list.c
index 724d840d..32a4a050 100644
--- a/src/util/pm_newline_list.c
+++ b/src/util/pm_newline_list.c
@@ -62,7 +62,7 @@ pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor
         size_t mid = left + (right - left) / 2;
 
         if (list->offsets[mid] == offset) {
-            return ((pm_line_column_t) { mid + 1, 0 });
+            return ((pm_line_column_t) { mid, 0 });
         }
 
         if (list->offsets[mid] < offset) {
@@ -72,7 +72,7 @@ pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor
         }
     }
 
-    return ((pm_line_column_t) { left, offset - list->offsets[left - 1] });
+    return ((pm_line_column_t) { left - 1, offset - list->offsets[left - 1] });
 }
 
 /**
diff --git a/templates/src/prettyprint.c.erb b/templates/src/prettyprint.c.erb
index 605795b6..02b89758 100644
--- a/templates/src/prettyprint.c.erb
+++ b/templates/src/prettyprint.c.erb
@@ -40,7 +40,7 @@ static inline void
 prettyprint_location(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_location_t *location) {
     pm_line_column_t start = pm_newline_list_line_column(&parser->newline_list, location->start);
     pm_line_column_t end = pm_newline_list_line_column(&parser->newline_list, location->end);
-    pm_buffer_append_format(output_buffer, "(%lu,%lu)-(%lu,%lu)", (unsigned long) start.line, (unsigned long) start.column, (unsigned long) end.line, (unsigned long) end.column);
+    pm_buffer_append_format(output_buffer, "(%lu,%lu)-(%lu,%lu)", (unsigned long) (start.line + 1), (unsigned long) start.column, (unsigned long) (end.line + 1), (unsigned long) end.column);
 }
 
 static inline void


Test patch:
diff --git a/java-wasm/src/test/java/org/prism/DummyTest.java b/java-wasm/src/test/java/org/prism/DummyTest.java
new file mode 100644
index 00000000..092be29d
--- /dev/null
+++ b/java-wasm/src/test/java/org/prism/DummyTest.java
@@ -0,0 +1,103 @@
+package org.prism;
+
+import com.dylibso.chicory.runtime.HostImports;
+import com.dylibso.chicory.runtime.Module;
+import com.dylibso.chicory.wasm.types.Value;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class DummyTest {
+
+    @Test
+    public void test1() {
+        var wasmPrism = Module.build(new File("src/test/resources/prism.wasm")).instantiate();
+        var memory = wasmPrism.getMemory();
+        var calloc = wasmPrism.getExport("calloc");
+        var pmSerializeParse = wasmPrism.getExport("pm_serialize_parse");
+        var pmBufferInit = wasmPrism.getExport("pm_buffer_init");
+        var pmBufferSizeof = wasmPrism.getExport("pm_buffer_sizeof");
+        var pmBufferValue = wasmPrism.getExport("pm_buffer_value");
+        var pmBufferLength = wasmPrism.getExport("pm_buffer_length");
+
+        // The Ruby source code to be processed
+        var source = "1 + 1";
+
+        var sourcePointer = calloc.apply(Value.i32(1), Value.i32(source.length()));
+        memory.writeString(sourcePointer[0].asInt(), source);
+
+        var packedOptions = new byte[] {};
+        var optionsPointer = calloc.apply(Value.i32(1), Value.i32(packedOptions.length));
+        memory.write(optionsPointer[0].asInt(), packedOptions);
+
+        var bufferPointer = calloc.apply(pmBufferSizeof.apply()[0], Value.i32(1));
+        pmBufferInit.apply(bufferPointer);
+
+        pmSerializeParse.apply(
+                bufferPointer[0], sourcePointer[0], Value.i32(source.length()), optionsPointer[0]);
+
+        var result = memory.readBytes(
+                pmBufferValue.apply(bufferPointer[0])[0].asInt(),
+                pmBufferLength.apply(bufferPointer[0])[0].asInt());
+
+        System.out.println("RESULT: " + new String(result));
+
+        var byteResults = result;
+        Nodes.Source nodeSource = new Nodes.Source(byteResults);
+        ParseResult pr = Loader.load(byteResults, nodeSource);
+
+        assertEquals(1, pr.value.childNodes().length);
+        System.out.println("Nodes:");
+        System.out.println(pr.value.childNodes()[0]);
+        assertTrue(pr.value.childNodes()[0].toString().contains("IntegerNode"));
+    }
+
+    @Test
+    public void test2() {
+        var wasmPrism = Module.build(new File("src/test/resources/prism.wasm")).instantiate();
+        var memory = wasmPrism.getMemory();
+        var calloc = wasmPrism.getExport("calloc");
+        var pmSerializeParse = wasmPrism.getExport("pm_serialize_parse");
+        var pmBufferInit = wasmPrism.getExport("pm_buffer_init");
+        var pmBufferSizeof = wasmPrism.getExport("pm_buffer_sizeof");
+        var pmBufferValue = wasmPrism.getExport("pm_buffer_value");
+        var pmBufferLength = wasmPrism.getExport("pm_buffer_length");
+
+        // The Ruby source code to be processed
+        var source = "puts \"h\ne\nl\nl\no\n\"";
+
+        var sourcePointer = calloc.apply(Value.i32(1), Value.i32(source.length()));
+        memory.writeString(sourcePointer[0].asInt(), source);
+
+        var packedOptions = new byte[] {};
+        var optionsPointer = calloc.apply(Value.i32(1), Value.i32(packedOptions.length));
+        memory.write(optionsPointer[0].asInt(), packedOptions);
+
+        var bufferPointer = calloc.apply(pmBufferSizeof.apply()[0], Value.i32(1));
+        pmBufferInit.apply(bufferPointer);
+
+        pmSerializeParse.apply(
+                bufferPointer[0], sourcePointer[0], Value.i32(source.length()), optionsPointer[0]);
+
+        var result = memory.readBytes(
+                pmBufferValue.apply(bufferPointer[0])[0].asInt(),
+                pmBufferLength.apply(bufferPointer[0])[0].asInt());
+
+        System.out.println("RESULT: " + new String(result));
+
+        var byteResults = result;
+        Nodes.Source nodeSource = new Nodes.Source(byteResults);
+        ParseResult pr = Loader.load(byteResults, nodeSource);
+
+        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"));
+    }
+
+}
diff --git a/java-wasm/src/test/resources/.gitignore b/java-wasm/src/test/resources/.gitignore
new file mode 100644
index 00000000..602a7583
--- /dev/null
+++ b/java-wasm/src/test/resources/.gitignore
@@ -0,0 +1,1 @@
+prism.wasm
diff --git a/test/prism/format_errors_test.rb b/test/prism/format_errors_test.rb
deleted file mode 100644
index 3533a738..00000000
--- a/test/prism/format_errors_test.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "test_helper"
-
-return if Prism::BACKEND == :FFI
-
-module Prism
-  class FormatErrorsTest < TestCase
-    def test_format_errors
-      assert_equal <<~ERROR, Debug.format_errors("<>", false)
-        > 1 | <>
-            | ^ cannot parse the expression
-            |  ^ cannot parse the expression
-      ERROR
-    end
-  end
-end
diff --git a/test/prism/snapshots/seattlerb/defn_unary_not.txt b/test/prism/snapshots/seattlerb/defn_unary_not.txt
index a10614fd..9f3fc271 100644
--- a/test/prism/snapshots/seattlerb/defn_unary_not.txt
+++ b/test/prism/snapshots/seattlerb/defn_unary_not.txt
@@ -4,8 +4,8 @@
     @ StatementsNode (location: (1,0)-(1,17))
     └── body: (length: 1)
         └── @ DefNode (location: (1,0)-(1,17))
-            ├── name: :!
-            ├── name_loc: (1,4)-(1,5) = "!"
+            ├── name: :"!@"
+            ├── name_loc: (1,4)-(1,6) = "!@"
             ├── receiver: ∅
             ├── parameters: ∅
             ├── body: