Copilot commented on code in PR #6356:
URL: https://github.com/apache/shenyu/pull/6356#discussion_r3370646430


##########
shenyu-plugin/shenyu-plugin-wasm-api/src/main/java/org/apache/shenyu/plugin/wasm/api/AbstractWasmPlugin.java:
##########
@@ -78,15 +75,14 @@ public Mono<Void> execute(final ServerWebExchange exchange, 
final ShenyuPluginCh
      * @return {@code Mono<Void>} to indicate when request handling is complete
      */
     protected abstract Mono<Void> doExecute(ServerWebExchange exchange, 
ShenyuPluginChain chain, Long argumentId);
-    
-    private Long callWASI(final ServerWebExchange exchange, final 
ShenyuPluginChain chain, final Extern execute) {
+
+    private Long callWASI(final ServerWebExchange exchange, final 
ShenyuPluginChain chain, final ExportFunction execute) {
         // WASI cannot easily pass Java objects like JNI, here we pass Long as 
arg
         // then we can get the argument by Long
         final Long argumentId = getArgumentId(exchange, chain);
         ARGUMENTS.put(argumentId, new Argument(exchange, chain));
         // call WASI function
-        WasmFunctions.consumer(super.getStore(), execute.func(), 
WasmValType.I64)
-                .accept(argumentId);
+        execute.apply(argumentId);
         ARGUMENTS.remove(argumentId);

Review Comment:
   ARGUMENTS.remove(argumentId) will be skipped if the WASM call traps/throws, 
leaving stale entries in the static ARGUMENTS map. This can cause memory growth 
and incorrect lookups on subsequent calls. Wrap the WASM invocation in 
try/finally so cleanup is guaranteed.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPlugin.java:
##########
@@ -112,14 +120,13 @@ private Long callWASI(final ServerWebExchange exchange,
                           final ShenyuPluginChain chain,
                           final SelectorData selector,
                           final RuleData rule,
-                          final Extern doExecute) {
+                          final ExportFunction doExecute) {
         // WASI cannot easily pass Java objects like JNI, here we pass Long as 
arg
         // then we can get the argument by Long
         final Long argumentId = getArgumentId(exchange, chain, selector, rule);
         ARGUMENTS.put(argumentId, new Argument(exchange, chain, selector, 
rule));
         // call WASI function
-        WasmFunctions.consumer(wasmLoader.getStore(), doExecute.func(), 
WasmValType.I64)
-                .accept(argumentId);
+        doExecute.apply(argumentId);
         ARGUMENTS.remove(argumentId);
         return argumentId;

Review Comment:
   ARGUMENTS.remove(argumentId) will not run if doExecute.apply(...) throws 
(trap, validation error, etc.), leaving stale Argument entries in the static 
map. Use try/finally to ensure ARGUMENTS is always cleaned up.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/handler/AbstractWasmPluginDataHandler.java:
##########
@@ -87,39 +85,36 @@ public void removeRule(final RuleData ruleData) {
         super.getWasmExtern(REMOVE_RULE_METHOD_NAME)
                 .ifPresent(handlerPlugin -> callWASI(ruleData, handlerPlugin));
     }
-    
-    private Long callWASI(final PluginData pluginData, final Extern execute) {
+
+    private Long callWASI(final PluginData pluginData, final ExportFunction 
execute) {
         // WASI cannot easily pass Java objects like JNI, here we pass Long as 
arg
         // then we can get the argument by Long
         final Long argumentId = getPluginArgumentId(pluginData);
         PLUGIN_ARGUMENTS.put(argumentId, pluginData);
         // call WASI function
-        WasmFunctions.consumer(super.getStore(), execute.func(), 
WasmValType.I64)
-                .accept(argumentId);
+        execute.apply(argumentId);
         PLUGIN_ARGUMENTS.remove(argumentId);
         return argumentId;
     }
-    
-    private Long callWASI(final RuleData ruleData, final Extern execute) {
+
+    private Long callWASI(final RuleData ruleData, final ExportFunction 
execute) {
         // WASI cannot easily pass Java objects like JNI, here we pass Long as 
arg
         // then we can get the argument by Long
         final Long argumentId = getRuleArgumentId(ruleData);
         RULE_ARGUMENTS.put(argumentId, ruleData);
         // call WASI function
-        WasmFunctions.consumer(super.getStore(), execute.func(), 
WasmValType.I64)
-                .accept(argumentId);
+        execute.apply(argumentId);
         RULE_ARGUMENTS.remove(argumentId);
         return argumentId;
     }
-    
-    private Long callWASI(final SelectorData selectorData, final Extern 
execute) {
+
+    private Long callWASI(final SelectorData selectorData, final 
ExportFunction execute) {
         // WASI cannot easily pass Java objects like JNI, here we pass Long as 
arg
         // then we can get the argument by Long
         final Long argumentId = getSelectorArgumentId(selectorData);
         SELECTOR_ARGUMENTS.put(argumentId, selectorData);
         // call WASI function
-        WasmFunctions.consumer(super.getStore(), execute.func(), 
WasmValType.I64)
-                .accept(argumentId);
+        execute.apply(argumentId);
         SELECTOR_ARGUMENTS.remove(argumentId);
         return argumentId;

Review Comment:
   SELECTOR_ARGUMENTS.remove(argumentId) will be skipped if execute.apply(...) 
throws, leaving stale entries in the static map. Wrap the WASM call in 
try/finally to guarantee cleanup.



##########
shenyu-plugin/shenyu-plugin-wasm-api/src/main/java/org/apache/shenyu/plugin/wasm/api/loader/WasmLoader.java:
##########
@@ -93,91 +91,225 @@ public WasmLoader(final Class<?> wasmClass, final 
Function<Store<Void>, Map<Stri
             if (Objects.isNull(resource)) {
                 throw new ShenyuWasmInitException("Can't find wasm file: " + 
wasmName);
             }
-            // Reads the WebAssembly module as bytes.
-            byte[] wasmBytes = Files.readAllBytes(Paths.get(resource.toURI()));
             // Instantiates the WebAssembly module.
+
+            this.store = new Store();
+
+            // Register WASI preview1 functions via Chicory's built-in 
implementation,
+            // then override proc_exit with a no-op to avoid WasiExitException.
+            registerWasiStubs(store);
+
+            // Allow subclasses to register custom host functions 
(get_args/put_result).
             if (Objects.nonNull(initializer)) {
-                Map<String, Func> wasmFunctionMap = initializer.apply(store);
-                if (Objects.nonNull(wasmFunctionMap) && 
!wasmFunctionMap.isEmpty()) {
-                    wasmCallJavaFuncMap.putAll(wasmFunctionMap);
-                }
+                initializer.accept(store);
+            } else {
+                registerBuiltinHostFunctions(store);
             }
-            Map<String, Func> wasmFunctionMap = initWasmCallJavaFunc(store);
-            if (Objects.nonNull(wasmFunctionMap) && 
!wasmFunctionMap.isEmpty()) {
-                wasmCallJavaFuncMap.putAll(wasmFunctionMap);
+
+            // Allow WasmLoader subclasses to override and register additional 
host functions.
+            // Only called when not using Consumer initializer, to avoid 
double invocation.
+            if (Objects.isNull(initializer)) {
+                initWasmCallJavaFunc(store);
             }
-            this.module = Module.fromBinary(store.engine(), wasmBytes);
-            WasiCtx.addToLinker(linker);
-            // maybe need define many functions
-            if (!wasmCallJavaFuncMap.isEmpty()) {
-                wasmCallJavaFuncMap.forEach((funcName, wasmCallJavaFunc) ->
-                        linker.define(store, IMPORT_WASM_MODULE_NAME, 
funcName, Extern.fromFunc(wasmCallJavaFunc)));
+
+            // Reads the WebAssembly module as bytes.
+            byte[] wasmBytes = Files.readAllBytes(Paths.get(resource.toURI()));
+            WasmModule module = Parser.parse(wasmBytes);
+
+            // Try runtime compiler (WASM → JVM bytecode) for better execution 
speed.
+            // Falls back gracefully to interpreter if something goes wrong.
+            try {
+                this.instance = Instance.builder(module)
+                        .withImportValues(store.toImportValues())
+                        
.withMachineFactory(MachineFactoryCompiler.compile(module))
+                        .withStart(false)
+                        .build();
+                LOG.debug("Using runtime compiler for {}", wasmName);
+            } catch (LinkageError | RuntimeException compilerError) {
+                LOG.warn("Runtime compiler unavailable, falling back to 
interpreter for {}: {}",
+                        wasmName, compilerError.getMessage());
+                this.instance = Instance.builder(module)
+                         .withImportValues(store.toImportValues())
+                         .withStart(false)
+                         .build();
             }
-            linker.module(store, "", module);
-            // Let the `wasmCallJavaFunc` function to refer this as a 
placeholder of Memory because
-            // we have to add the function as import before loading the module 
exporting Memory.
-            Optional<Extern> extern = this.getWasmExtern(MEMORY_METHOD_NAME);
-            if (!extern.isPresent()) {
-                throw new ShenyuWasmInitException(MEMORY_METHOD_NAME + " 
function not find in wasm file: " + wasmName);
+
+            // Call _initialize if present (required by TinyGo -target 
wasm-unknown).
+            try {
+                ExportFunction initFn = instance.export("_initialize");
+                if (Objects.nonNull(initFn)) {
+                    initFn.apply();
+                    LOG.debug("Called _initialize for {}", wasmName);
+                }
+            } catch (com.dylibso.chicory.wasm.InvalidException e) {
+                LOG.debug("No _initialize export in {}", wasmName);
+            }
+
+            if (Objects.isNull(instance.memory())) {
+                throw new ShenyuWasmInitException("memory not available in 
wasm file: " + wasmName);
             }
-            this.memRef = extern.get().memory();
+
             Runtime.getRuntime().addShutdownHook(new Thread(this::close));
         } catch (URISyntaxException | IOException e) {
             throw new ShenyuWasmInitException(e);
         }
     }
-    
-    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store) {
-        return null;
+
+    private void registerWasiStubs(final Store store) {
+        // Use Chicory's built-in full WASI preview1 implementation for 
comprehensive
+        // WASI function coverage, matching the previous wasmtime-java 
behavior.
+        // We inherit system stdout/stderr so that fd_write output (e.g. Go 
println,
+        // Rust eprintln!) is visible for debugging and logging purposes.
+        var options = com.dylibso.chicory.wasi.WasiOptions.builder()
+                .inheritSystem()
+                .build();
+        var wasi = com.dylibso.chicory.wasi.WasiPreview1.builder()
+                .withOptions(options)
+                .build();
+        store.addFunction(wasi.toHostFunctions());
+
+        // Override proc_exit with a no-op: valid WASI program exit 
(proc_exit(0))
+        // is normal termination and should not propagate as a Java exception 
in
+        // a hosted plugin runtime.
+        store.addFunction(new HostFunction(
+                "wasi_snapshot_preview1", "proc_exit",
+                FunctionType.of(List.of(ValType.I32), List.of()),
+                (inst, args) -> new long[0]
+        ));

Review Comment:
   proc_exit is currently overridden as a no-op for all exit codes. This hides 
abnormal terminations (non-zero exit status) and can lead to silent data loss / 
hard-to-debug failures when a WASM module calls proc_exit(>0). Consider 
treating non-zero exit codes as errors (throw) while still suppressing the 
normal proc_exit(0) path.



##########
shenyu-plugin/shenyu-plugin-wasm-api/src/test/go-wasm-plugin/Makefile:
##########
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+WASM_FILE := 
org.apache.shenyu.plugin.wasm.api.AbstractWasmPluginTest\$$GoWasmPlugin.wasm
+RESOURCES_DIR := ../resources
+
+.PHONY: build clean
+
+build:
+       @echo "Building Go WASM plugin..."
+       tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o 
plugin.wasm main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise `make 
build` will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-plugin-data-handler/Makefile:
##########
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+WASM_FILE := 
org.apache.shenyu.plugin.wasm.base.handler.AbstractWasmPluginDataHandlerTest\$$TestGoWasmPluginDataHandler.wasm
+RESOURCES_DIR := ../resources
+
+.PHONY: build clean
+
+build:
+       @echo "Building Go Plugin Data Handler..."
+       tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o 
plugin.wasm main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise `make 
build` will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-api/src/test/go-wasm-plugin/README.md:
##########
@@ -0,0 +1,29 @@
+# How to build the wasm file
+
+1. install tinygo
+
+2. generate the wasm file
+
+Option A (recommended): build and copy with Makefile
+
+```shell
+cd {shenyu}/shenyu-plugin/shenyu-plugin-wasm-api/src/test/go-wasm-plugin
+make build
+```
+
+then you will see the wasm file
+in 
`{shenyu}/shenyu-plugin/shenyu-plugin-wasm-api/src/test/resources/org.apache.shenyu.plugin.wasm.api.AbstractWasmPluginTest$GoWasmPlugin.wasm`
+
+Option B: manual build
+
+```shell
+cd {shenyu}/shenyu-plugin/shenyu-plugin-wasm-api/src/test/go-wasm-plugin
+tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o plugin.wasm 
main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise the 
manual build command will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/handler/AbstractWasmPluginDataHandler.java:
##########
@@ -87,39 +85,36 @@ public void removeRule(final RuleData ruleData) {
         super.getWasmExtern(REMOVE_RULE_METHOD_NAME)
                 .ifPresent(handlerPlugin -> callWASI(ruleData, handlerPlugin));
     }
-    
-    private Long callWASI(final PluginData pluginData, final Extern execute) {
+
+    private Long callWASI(final PluginData pluginData, final ExportFunction 
execute) {
         // WASI cannot easily pass Java objects like JNI, here we pass Long as 
arg
         // then we can get the argument by Long
         final Long argumentId = getPluginArgumentId(pluginData);
         PLUGIN_ARGUMENTS.put(argumentId, pluginData);
         // call WASI function
-        WasmFunctions.consumer(super.getStore(), execute.func(), 
WasmValType.I64)
-                .accept(argumentId);
+        execute.apply(argumentId);
         PLUGIN_ARGUMENTS.remove(argumentId);
         return argumentId;

Review Comment:
   PLUGIN_ARGUMENTS.remove(argumentId) will be skipped if execute.apply(...) 
throws, leaving stale entries in the static map. Wrap the WASM call in 
try/finally to guarantee cleanup.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/handler/AbstractWasmPluginDataHandler.java:
##########
@@ -87,39 +85,36 @@ public void removeRule(final RuleData ruleData) {
         super.getWasmExtern(REMOVE_RULE_METHOD_NAME)
                 .ifPresent(handlerPlugin -> callWASI(ruleData, handlerPlugin));
     }
-    
-    private Long callWASI(final PluginData pluginData, final Extern execute) {
+
+    private Long callWASI(final PluginData pluginData, final ExportFunction 
execute) {
         // WASI cannot easily pass Java objects like JNI, here we pass Long as 
arg
         // then we can get the argument by Long
         final Long argumentId = getPluginArgumentId(pluginData);
         PLUGIN_ARGUMENTS.put(argumentId, pluginData);
         // call WASI function
-        WasmFunctions.consumer(super.getStore(), execute.func(), 
WasmValType.I64)
-                .accept(argumentId);
+        execute.apply(argumentId);
         PLUGIN_ARGUMENTS.remove(argumentId);
         return argumentId;
     }
-    
-    private Long callWASI(final RuleData ruleData, final Extern execute) {
+
+    private Long callWASI(final RuleData ruleData, final ExportFunction 
execute) {
         // WASI cannot easily pass Java objects like JNI, here we pass Long as 
arg
         // then we can get the argument by Long
         final Long argumentId = getRuleArgumentId(ruleData);
         RULE_ARGUMENTS.put(argumentId, ruleData);
         // call WASI function
-        WasmFunctions.consumer(super.getStore(), execute.func(), 
WasmValType.I64)
-                .accept(argumentId);
+        execute.apply(argumentId);
         RULE_ARGUMENTS.remove(argumentId);
         return argumentId;

Review Comment:
   RULE_ARGUMENTS.remove(argumentId) will be skipped if execute.apply(...) 
throws, leaving stale entries in the static map. Wrap the WASM call in 
try/finally to guarantee cleanup.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/handler/AbstractWasmMetaDataHandler.java:
##########
@@ -54,15 +52,14 @@ public void remove(final MetaData metaData) {
                 .map(remove -> callWASI(metaData, remove))
                 .orElseThrow(() -> new 
ShenyuWasmInitException(REMOVE_METHOD_NAME + " function not find in wasm file: 
" + getWasmName()));
     }
-    
-    private Long callWASI(final MetaData metaData, final Extern execute) {
+
+    private Long callWASI(final MetaData metaData, final ExportFunction 
execute) {
         // WASI cannot easily pass Java objects like JNI, here we pass Long as 
arg
         // then we can get the argument by Long
         final Long argumentId = getArgumentId(metaData);
         ARGUMENTS.put(argumentId, metaData);
         // call WASI function
-        WasmFunctions.consumer(super.getStore(), execute.func(), 
WasmValType.I64)
-                .accept(argumentId);
+        execute.apply(argumentId);
         ARGUMENTS.remove(argumentId);
         return argumentId;

Review Comment:
   ARGUMENTS.remove(argumentId) will be skipped if execute.apply(...) throws, 
leaving stale MetaData entries in the static map. Wrap the WASM call in 
try/finally to guarantee cleanup.



##########
shenyu-plugin/shenyu-plugin-wasm-api/src/main/java/org/apache/shenyu/plugin/wasm/api/loader/WasmLoader.java:
##########
@@ -93,91 +91,225 @@ public WasmLoader(final Class<?> wasmClass, final 
Function<Store<Void>, Map<Stri
             if (Objects.isNull(resource)) {
                 throw new ShenyuWasmInitException("Can't find wasm file: " + 
wasmName);
             }
-            // Reads the WebAssembly module as bytes.
-            byte[] wasmBytes = Files.readAllBytes(Paths.get(resource.toURI()));
             // Instantiates the WebAssembly module.
+
+            this.store = new Store();
+
+            // Register WASI preview1 functions via Chicory's built-in 
implementation,
+            // then override proc_exit with a no-op to avoid WasiExitException.
+            registerWasiStubs(store);
+
+            // Allow subclasses to register custom host functions 
(get_args/put_result).
             if (Objects.nonNull(initializer)) {
-                Map<String, Func> wasmFunctionMap = initializer.apply(store);
-                if (Objects.nonNull(wasmFunctionMap) && 
!wasmFunctionMap.isEmpty()) {
-                    wasmCallJavaFuncMap.putAll(wasmFunctionMap);
-                }
+                initializer.accept(store);
+            } else {
+                registerBuiltinHostFunctions(store);
             }
-            Map<String, Func> wasmFunctionMap = initWasmCallJavaFunc(store);
-            if (Objects.nonNull(wasmFunctionMap) && 
!wasmFunctionMap.isEmpty()) {
-                wasmCallJavaFuncMap.putAll(wasmFunctionMap);
+
+            // Allow WasmLoader subclasses to override and register additional 
host functions.
+            // Only called when not using Consumer initializer, to avoid 
double invocation.
+            if (Objects.isNull(initializer)) {
+                initWasmCallJavaFunc(store);
             }
-            this.module = Module.fromBinary(store.engine(), wasmBytes);
-            WasiCtx.addToLinker(linker);
-            // maybe need define many functions
-            if (!wasmCallJavaFuncMap.isEmpty()) {
-                wasmCallJavaFuncMap.forEach((funcName, wasmCallJavaFunc) ->
-                        linker.define(store, IMPORT_WASM_MODULE_NAME, 
funcName, Extern.fromFunc(wasmCallJavaFunc)));
+
+            // Reads the WebAssembly module as bytes.
+            byte[] wasmBytes = Files.readAllBytes(Paths.get(resource.toURI()));
+            WasmModule module = Parser.parse(wasmBytes);
+
+            // Try runtime compiler (WASM → JVM bytecode) for better execution 
speed.
+            // Falls back gracefully to interpreter if something goes wrong.
+            try {
+                this.instance = Instance.builder(module)
+                        .withImportValues(store.toImportValues())
+                        
.withMachineFactory(MachineFactoryCompiler.compile(module))
+                        .withStart(false)
+                        .build();
+                LOG.debug("Using runtime compiler for {}", wasmName);
+            } catch (LinkageError | RuntimeException compilerError) {
+                LOG.warn("Runtime compiler unavailable, falling back to 
interpreter for {}: {}",
+                        wasmName, compilerError.getMessage());
+                this.instance = Instance.builder(module)
+                         .withImportValues(store.toImportValues())
+                         .withStart(false)
+                         .build();
             }
-            linker.module(store, "", module);
-            // Let the `wasmCallJavaFunc` function to refer this as a 
placeholder of Memory because
-            // we have to add the function as import before loading the module 
exporting Memory.
-            Optional<Extern> extern = this.getWasmExtern(MEMORY_METHOD_NAME);
-            if (!extern.isPresent()) {
-                throw new ShenyuWasmInitException(MEMORY_METHOD_NAME + " 
function not find in wasm file: " + wasmName);
+
+            // Call _initialize if present (required by TinyGo -target 
wasm-unknown).
+            try {
+                ExportFunction initFn = instance.export("_initialize");
+                if (Objects.nonNull(initFn)) {
+                    initFn.apply();
+                    LOG.debug("Called _initialize for {}", wasmName);
+                }
+            } catch (com.dylibso.chicory.wasm.InvalidException e) {
+                LOG.debug("No _initialize export in {}", wasmName);
+            }
+
+            if (Objects.isNull(instance.memory())) {
+                throw new ShenyuWasmInitException("memory not available in 
wasm file: " + wasmName);
             }
-            this.memRef = extern.get().memory();
+
             Runtime.getRuntime().addShutdownHook(new Thread(this::close));

Review Comment:
   A shutdown hook thread is registered for every WasmLoader instance. In a 
Spring app these loaders can be created multiple times (tests, reloads), which 
can accumulate shutdown hooks and threads. Since close() is currently a no-op, 
consider removing this hook (or making it a single static hook) to avoid 
unnecessary resource usage.



##########
shenyu-plugin/shenyu-plugin-wasm-api/src/main/java/org/apache/shenyu/plugin/wasm/api/loader/WasmLoader.java:
##########
@@ -93,91 +91,225 @@ public WasmLoader(final Class<?> wasmClass, final 
Function<Store<Void>, Map<Stri
             if (Objects.isNull(resource)) {
                 throw new ShenyuWasmInitException("Can't find wasm file: " + 
wasmName);
             }
-            // Reads the WebAssembly module as bytes.
-            byte[] wasmBytes = Files.readAllBytes(Paths.get(resource.toURI()));
             // Instantiates the WebAssembly module.
+
+            this.store = new Store();
+
+            // Register WASI preview1 functions via Chicory's built-in 
implementation,
+            // then override proc_exit with a no-op to avoid WasiExitException.
+            registerWasiStubs(store);
+
+            // Allow subclasses to register custom host functions 
(get_args/put_result).
             if (Objects.nonNull(initializer)) {
-                Map<String, Func> wasmFunctionMap = initializer.apply(store);
-                if (Objects.nonNull(wasmFunctionMap) && 
!wasmFunctionMap.isEmpty()) {
-                    wasmCallJavaFuncMap.putAll(wasmFunctionMap);
-                }
+                initializer.accept(store);
+            } else {
+                registerBuiltinHostFunctions(store);
             }
-            Map<String, Func> wasmFunctionMap = initWasmCallJavaFunc(store);
-            if (Objects.nonNull(wasmFunctionMap) && 
!wasmFunctionMap.isEmpty()) {
-                wasmCallJavaFuncMap.putAll(wasmFunctionMap);
+
+            // Allow WasmLoader subclasses to override and register additional 
host functions.
+            // Only called when not using Consumer initializer, to avoid 
double invocation.
+            if (Objects.isNull(initializer)) {
+                initWasmCallJavaFunc(store);
             }
-            this.module = Module.fromBinary(store.engine(), wasmBytes);
-            WasiCtx.addToLinker(linker);
-            // maybe need define many functions
-            if (!wasmCallJavaFuncMap.isEmpty()) {
-                wasmCallJavaFuncMap.forEach((funcName, wasmCallJavaFunc) ->
-                        linker.define(store, IMPORT_WASM_MODULE_NAME, 
funcName, Extern.fromFunc(wasmCallJavaFunc)));
+
+            // Reads the WebAssembly module as bytes.
+            byte[] wasmBytes = Files.readAllBytes(Paths.get(resource.toURI()));
+            WasmModule module = Parser.parse(wasmBytes);
+
+            // Try runtime compiler (WASM → JVM bytecode) for better execution 
speed.
+            // Falls back gracefully to interpreter if something goes wrong.
+            try {
+                this.instance = Instance.builder(module)
+                        .withImportValues(store.toImportValues())
+                        
.withMachineFactory(MachineFactoryCompiler.compile(module))
+                        .withStart(false)
+                        .build();
+                LOG.debug("Using runtime compiler for {}", wasmName);
+            } catch (LinkageError | RuntimeException compilerError) {
+                LOG.warn("Runtime compiler unavailable, falling back to 
interpreter for {}: {}",
+                        wasmName, compilerError.getMessage());
+                this.instance = Instance.builder(module)
+                         .withImportValues(store.toImportValues())
+                         .withStart(false)
+                         .build();
             }
-            linker.module(store, "", module);
-            // Let the `wasmCallJavaFunc` function to refer this as a 
placeholder of Memory because
-            // we have to add the function as import before loading the module 
exporting Memory.
-            Optional<Extern> extern = this.getWasmExtern(MEMORY_METHOD_NAME);
-            if (!extern.isPresent()) {
-                throw new ShenyuWasmInitException(MEMORY_METHOD_NAME + " 
function not find in wasm file: " + wasmName);
+
+            // Call _initialize if present (required by TinyGo -target 
wasm-unknown).
+            try {
+                ExportFunction initFn = instance.export("_initialize");
+                if (Objects.nonNull(initFn)) {
+                    initFn.apply();
+                    LOG.debug("Called _initialize for {}", wasmName);
+                }
+            } catch (com.dylibso.chicory.wasm.InvalidException e) {
+                LOG.debug("No _initialize export in {}", wasmName);
+            }
+
+            if (Objects.isNull(instance.memory())) {
+                throw new ShenyuWasmInitException("memory not available in 
wasm file: " + wasmName);
             }
-            this.memRef = extern.get().memory();
+
             Runtime.getRuntime().addShutdownHook(new Thread(this::close));
         } catch (URISyntaxException | IOException e) {
             throw new ShenyuWasmInitException(e);
         }
     }
-    
-    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store) {
-        return null;
+
+    private void registerWasiStubs(final Store store) {
+        // Use Chicory's built-in full WASI preview1 implementation for 
comprehensive
+        // WASI function coverage, matching the previous wasmtime-java 
behavior.
+        // We inherit system stdout/stderr so that fd_write output (e.g. Go 
println,
+        // Rust eprintln!) is visible for debugging and logging purposes.
+        var options = com.dylibso.chicory.wasi.WasiOptions.builder()
+                .inheritSystem()
+                .build();

Review Comment:
   WasiOptions.builder().inheritSystem() likely exposes more than stdout/stderr 
(e.g., host env/args and preopened filesystem dirs) to the WASM guest. 
Previously wasmtime-java only inherited stdout/stderr. For a plugin runtime 
this is a security hardening concern; consider wiring only the required stdio 
streams instead of inheriting the full host system context.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-shenyu-wasm-plugin/Makefile:
##########
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+WASM_FILE := 
org.apache.shenyu.plugin.wasm.base.AbstractShenyuWasmPluginTest\$$TestGoShenyuWasmPlugin.wasm
+RESOURCES_DIR := ../resources
+
+.PHONY: build clean
+
+build:
+       @echo "Building Go ShenYu WASM plugin..."
+       tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o 
plugin.wasm main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise `make 
build` will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/handler/AbstractWasmDiscoveryHandler.java:
##########
@@ -45,8 +43,7 @@ public void handlerDiscoveryUpstreamData(final 
DiscoverySyncData discoverySyncDa
                     final Long argumentId = getArgumentId(discoverySyncData);
                     ARGUMENTS.put(argumentId, discoverySyncData);
                     // call WASI function
-                    WasmFunctions.consumer(super.getStore(), 
handlerDiscoveryUpstreamData.func(), WasmValType.I64)
-                            .accept(argumentId);
+                    handlerDiscoveryUpstreamData.apply(argumentId);
                     ARGUMENTS.remove(argumentId);
                     return argumentId;

Review Comment:
   ARGUMENTS.remove(argumentId) will be skipped if 
handlerDiscoveryUpstreamData.apply(...) throws, leaving stale DiscoverySyncData 
entries in the static map. Use try/finally inside the lambda to guarantee 
cleanup.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-meta-data-handler/Makefile:
##########
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+WASM_FILE := 
org.apache.shenyu.plugin.wasm.base.handler.AbstractWasmMetaDataHandlerTest\$$TestGoWasmMetaDataHandler.wasm
+RESOURCES_DIR := ../resources
+
+.PHONY: build clean
+
+build:
+       @echo "Building Go Meta Data Handler..."
+       tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o 
plugin.wasm main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise `make 
build` will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-discovery-handler/Makefile:
##########
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+WASM_FILE := 
org.apache.shenyu.plugin.wasm.base.handler.AbstractWasmDiscoveryHandlerTest\$$TestGoWasmPluginDiscoveryHandler.wasm
+RESOURCES_DIR := ../resources
+
+.PHONY: build clean
+
+build:
+       @echo "Building Go Discovery Handler..."
+       tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o 
plugin.wasm main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise `make 
build` will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-shenyu-wasm-plugin/README.md:
##########
@@ -0,0 +1,29 @@
+# How to build the wasm file
+
+1. install tinygo
+
+2. generate the wasm file
+
+Option A (recommended): build and copy with Makefile
+
+```shell
+cd 
{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-shenyu-wasm-plugin
+make build
+```
+
+then you will see the wasm file
+in 
`{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/resources/org.apache.shenyu.plugin.wasm.base.AbstractShenyuWasmPluginTest$TestGoShenyuWasmPlugin.wasm`
+
+Option B: manual build
+
+```shell
+cd 
{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-shenyu-wasm-plugin
+tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o plugin.wasm 
main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise the 
manual build command will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-meta-data-handler/README.md:
##########
@@ -0,0 +1,29 @@
+# How to build the wasm file
+
+1. install tinygo
+
+2. generate the wasm file
+
+Option A (recommended): build and copy with Makefile
+
+```shell
+cd {shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-meta-data-handler
+make build
+```
+
+then you will see the wasm file
+in 
`{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/resources/org.apache.shenyu.plugin.wasm.base.handler.AbstractWasmMetaDataHandlerTest$TestGoWasmMetaDataHandler.wasm`
+
+Option B: manual build
+
+```shell
+cd {shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-meta-data-handler
+tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o plugin.wasm 
main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise the 
manual build command will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-plugin-data-handler/README.md:
##########
@@ -0,0 +1,29 @@
+# How to build the wasm file
+
+1. install tinygo
+
+2. generate the wasm file
+
+Option A (recommended): build and copy with Makefile
+
+```shell
+cd 
{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-plugin-data-handler
+make build
+```
+
+then you will see the wasm file
+in 
`{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/resources/org.apache.shenyu.plugin.wasm.base.handler.AbstractWasmPluginDataHandlerTest$TestGoWasmPluginDataHandler.wasm`
+
+Option B: manual build
+
+```shell
+cd 
{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-plugin-data-handler
+tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o plugin.wasm 
main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise the 
manual build command will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-discovery-handler/README.md:
##########
@@ -0,0 +1,29 @@
+# How to build the wasm file
+
+1. install tinygo
+
+2. generate the wasm file
+
+Option A (recommended): build and copy with Makefile
+
+```shell
+cd {shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-discovery-handler
+make build
+```
+
+then you will see the wasm file
+in 
`{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/resources/org.apache.shenyu.plugin.wasm.base.handler.AbstractWasmDiscoveryHandlerTest$TestGoWasmPluginDiscoveryHandler.wasm`
+
+Option B: manual build
+
+```shell
+cd {shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-discovery-handler
+tinygo build -target wasm-unknown -opt=2 -no-debug -panic=trap -o plugin.wasm 
main.go

Review Comment:
   TinyGo does not recognize `-target wasm-unknown` (it is a Rust-style target 
triple). For WASI builds, the TinyGo target should be `wasi`, otherwise the 
manual build command will fail.



##########
shenyu-plugin/shenyu-plugin-wasm-api/src/main/java/org/apache/shenyu/plugin/wasm/api/loader/WasmLoader.java:
##########
@@ -93,91 +91,225 @@ public WasmLoader(final Class<?> wasmClass, final 
Function<Store<Void>, Map<Stri
             if (Objects.isNull(resource)) {
                 throw new ShenyuWasmInitException("Can't find wasm file: " + 
wasmName);
             }
-            // Reads the WebAssembly module as bytes.
-            byte[] wasmBytes = Files.readAllBytes(Paths.get(resource.toURI()));
             // Instantiates the WebAssembly module.
+
+            this.store = new Store();
+
+            // Register WASI preview1 functions via Chicory's built-in 
implementation,
+            // then override proc_exit with a no-op to avoid WasiExitException.
+            registerWasiStubs(store);
+
+            // Allow subclasses to register custom host functions 
(get_args/put_result).
             if (Objects.nonNull(initializer)) {
-                Map<String, Func> wasmFunctionMap = initializer.apply(store);
-                if (Objects.nonNull(wasmFunctionMap) && 
!wasmFunctionMap.isEmpty()) {
-                    wasmCallJavaFuncMap.putAll(wasmFunctionMap);
-                }
+                initializer.accept(store);
+            } else {
+                registerBuiltinHostFunctions(store);
             }
-            Map<String, Func> wasmFunctionMap = initWasmCallJavaFunc(store);
-            if (Objects.nonNull(wasmFunctionMap) && 
!wasmFunctionMap.isEmpty()) {
-                wasmCallJavaFuncMap.putAll(wasmFunctionMap);
+
+            // Allow WasmLoader subclasses to override and register additional 
host functions.
+            // Only called when not using Consumer initializer, to avoid 
double invocation.
+            if (Objects.isNull(initializer)) {
+                initWasmCallJavaFunc(store);
             }
-            this.module = Module.fromBinary(store.engine(), wasmBytes);
-            WasiCtx.addToLinker(linker);
-            // maybe need define many functions
-            if (!wasmCallJavaFuncMap.isEmpty()) {
-                wasmCallJavaFuncMap.forEach((funcName, wasmCallJavaFunc) ->
-                        linker.define(store, IMPORT_WASM_MODULE_NAME, 
funcName, Extern.fromFunc(wasmCallJavaFunc)));
+
+            // Reads the WebAssembly module as bytes.
+            byte[] wasmBytes = Files.readAllBytes(Paths.get(resource.toURI()));
+            WasmModule module = Parser.parse(wasmBytes);

Review Comment:
   Reading the .wasm resource via Paths.get(resource.toURI()) will fail when 
the resource is packaged inside a JAR (e.g., jar:file:...!/...) because it is 
not a real filesystem path. Use resource.openStream() (or getResourceAsStream) 
to read bytes from the classpath in both exploded and packaged deployments.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/go-meta-data-handler/main.go:
##########
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Go WASM handler for AbstractWasmMetaDataHandler.
+// Exports handleMetaData, removeMetaData (i64) -> () and refresh () -> ().
+// Compile with: tinygo build -target wasm-unknown -opt=2 -no-debug 
-panic=trap -o plugin.wasm main.go
+
+package main
+
+import (
+       "shenyu/wasmabi"
+       "strconv"
+       "unsafe"
+)
+
+func handle(argId int64) {
+       buf := make([]byte, 1024)
+       wasmabi.Eprintln("go side-> buffer base address: " + 
strconv.FormatUint(uint64(uintptr(unsafe.Pointer(&buf[0]))), 10))
+       input := wasmabi.GetArgs(argId, buf)
+       wasmabi.Eprintln("go side-> GetArgs returned " + 
strconv.Itoa(len(input)) + ", recv:" + string(input))
+       wasmabi.PutResult(argId, []byte("go result"))
+}
+
+//go:wasmexport handleMetaData
+func handleMetaData(argId int64) {
+       wasmabi.Eprintln("go side-> handleMetaData")
+       handle(argId)
+}
+
+//go:wasmexport removeMetaData
+func removeMetaData(argId int64) {
+       wasmabi.Eprintln("go side-> removeMetaData")
+       handle(argId)
+}
+
+//go:wasmexport refresh
+// NOTE: refresh takes NO arguments — Java calls 
WasmFunctions.consumer(...).accept() with zero params.

Review Comment:
   This comment references the old wasmtime-java API 
(WasmFunctions.consumer(...).accept()). The runtime has been migrated to 
Chicory, so this is misleading for anyone updating the Go WASM handler 
signature.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to