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


##########
shenyu-plugin/shenyu-plugin-wasm-api/src/main/java/org/apache/shenyu/plugin/wasm/api/loader/WasmLoader.java:
##########
@@ -93,91 +92,273 @@ 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 stubs. Both Go and Rust modules need 
these,
+            // but we implement our own no-op versions instead of using 
Chicory's
+            // WasiPreview1 to avoid WasiExitException from proc_exit(0).
+            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) {
+        // proc_exit(code: i32) -> ()
+        store.addFunction(new HostFunction(
+                "wasi_snapshot_preview1", "proc_exit",
+                FunctionType.of(List.of(ValType.I32), List.of()),
+                (inst, args) -> new long[0]

Review Comment:
   WasmLoader currently registers only a small subset of WASI imports 
(proc_exit/random_get/fd_write/environ_*). This is a behavioral regression from 
the previous Wasmtime-based implementation that provided full WASI; any plugin 
that imports other WASI functions (e.g., clock_time_get, fd_read, path_open, 
etc.) will now fail to instantiate.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/java/org/apache/shenyu/plugin/wasm/base/handler/AbstractWasmPluginDataHandlerTest.java:
##########
@@ -39,128 +43,95 @@
 @ExtendWith(MockitoExtension.class)
 @MockitoSettings(strictness = Strictness.LENIENT)
 public final class AbstractWasmPluginDataHandlerTest {
-    
-    private RuleData ruleData;
-    
+
     private PluginData pluginData;
-    
-    private SelectorData selectorData;
-    
-    private SimpleTestHandler testWasmPluginDataHandler;
-    
-    private PluginDataHandler pluginDataHandler;
-    
+
     @BeforeEach
     public void setUp() {
-        this.ruleData = mock(RuleData.class);
         this.pluginData = mock(PluginData.class);
-        this.selectorData = mock(SelectorData.class);
-        // Use a simple test handler instead of WebAssembly-dependent handler
-        this.testWasmPluginDataHandler = new SimpleTestHandler();
-        this.pluginDataHandler = () -> "SHENYU";
-        when(ruleData.getId()).thenReturn("SHENYU");
         when(pluginData.getId()).thenReturn("SHENYU");
-        when(selectorData.getId()).thenReturn("SHENYU");
-    }
-    
-    /**
-     * The handler plugin test.
-     */
-    @Test
-    public void handlerPluginTest() {
-        pluginDataHandler.handlerPlugin(pluginData);
-        testWasmPluginDataHandler.handlerPlugin(pluginData);
-    }
-    
-    /**
-     * The remove plugin test.
-     */
-    @Test
-    public void removePluginTest() {
-        pluginDataHandler.removePlugin(pluginData);
-        testWasmPluginDataHandler.handlerPlugin(pluginData);
-        testWasmPluginDataHandler.removePlugin(pluginData);
-    }
-    
-    /**
-     * The handler selector test.
-     */
-    @Test
-    public void handlerSelectorTest() {
-        pluginDataHandler.handlerSelector(selectorData);
-        testWasmPluginDataHandler.handlerSelector(selectorData);
     }
-    
-    /**
-     * The remove selector test.
-     */
-    @Test
-    public void removeSelectorTest() {
-        pluginDataHandler.removeSelector(selectorData);
-        testWasmPluginDataHandler.handlerSelector(selectorData);
-        testWasmPluginDataHandler.removeSelector(selectorData);
-    }
-    
-    /**
-     * The handler rule test.
-     */
-    @Test
-    public void handlerRuleTest() {
-        pluginDataHandler.handlerRule(ruleData);
-        testWasmPluginDataHandler.handlerRule(ruleData);
-    }
-    
-    /**
-     * The remove rule test.
-     */
+
+    /** Go WASM test. */
     @Test
-    public void removeRuleTest() {
-        pluginDataHandler.removeRule(ruleData);
-        testWasmPluginDataHandler.handlerRule(ruleData);
-        testWasmPluginDataHandler.removeRule(ruleData);
+    public void goHandlerPluginTest() {
+        final TestGoWasmPluginDataHandler goHandler = new 
TestGoWasmPluginDataHandler("go result");
+        goHandler.handlerPlugin(pluginData);
     }

Review Comment:
   goHandlerPluginTest only exercises handlerPlugin(), but 
AbstractWasmPluginDataHandler also routes 
removePlugin/handlerSelector/removeSelector/handlerRule/removeRule through WASM 
exports. Without calling them here, a mismatched export name/signature in the 
Go .wasm could slip through CI.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/pkg/wasmabi/abi.go:
##########
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+// Package wasmabi provides helper functions for communicating with the Java
+// ShenYu host via shared memory. TinyGo WASM plugins should use GetArgs and
+// PutResult instead of calling the raw host imports directly.
+package wasmabi

Review Comment:
   The package comment says this is an ABI helper that TinyGo WASM plugins 
“should use”, but the implementation lives under src/test (not 
shipped/published). This is either a mismatch with the PR description’s “new 
shenyu/wasmabi Go package” claim, or the file should be moved to a non-test 
location so external plugin authors can actually depend on it.



##########
shenyu-plugin/shenyu-plugin-wasm-base/src/test/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPluginTest.java:
##########
@@ -62,236 +70,158 @@ public final class AbstractShenyuWasmPluginTest {
     private ConditionData conditionData;
     
     private ServerWebExchange exchange;
-    
-    private ShenyuPlugin testShenyuWasmPlugin;
-    
+
     private ShenyuPluginChain shenyuPluginChain;
     
     @BeforeEach
     public void setUp() {
         mockShenyuConfig();
         this.ruleData = RuleData.builder()
-                .id("1")
-                .pluginName("SHENYU")
-                .selectorId("1")
-                .enabled(true)
-                .loged(true)
-                .matchRestful(false)
-                .sort(1).build();
+                .id("1").pluginName("SHENYU").selectorId("1")
+                .enabled(true).loged(true).matchRestful(false).sort(1).build();
         this.conditionData = new ConditionData();
         this.conditionData.setOperator("match");
         this.conditionData.setParamName("/");
         this.conditionData.setParamType("uri");
         this.conditionData.setParamValue("/http/**");
         this.shenyuPluginChain = mock(ShenyuPluginChain.class);
-        this.pluginData = PluginData.builder()
-                .name("SHENYU")
-                .enabled(true).build();
+        this.pluginData = 
PluginData.builder().name("SHENYU").enabled(true).build();
         this.selectorData = SelectorData.builder()
-                .id("1").pluginName("SHENYU")
-                .enabled(true)
-                .matchRestful(false)
-                .type(SelectorTypeEnum.CUSTOM_FLOW.getCode()).build();
-        // Use a simple test plugin instead of WebAssembly-dependent plugin
-        this.testShenyuWasmPlugin = spy(new SimpleTestPlugin());
-        this.exchange = 
MockServerWebExchange.from(MockServerHttpRequest.get("/http/SHENYU/SHENYU")
-                .build());
+                .id("1").pluginName("SHENYU").enabled(true)
+                
.matchRestful(false).type(SelectorTypeEnum.CUSTOM_FLOW.getCode()).build();
+        this.exchange = 
MockServerWebExchange.from(MockServerHttpRequest.get("/http/SHENYU/SHENYU").build());
         ShenyuContext context = mock(ShenyuContext.class);
         exchange.getAttributes().put(Constants.CONTEXT, context);
         clearCache();
         when(shenyuPluginChain.execute(exchange)).thenReturn(Mono.empty());
     }
-    
-    /**
-     * The plugin is null test.
-     */
-    @Test
-    public void executePluginIsNullTest() {
-        StepVerifier.create(testShenyuWasmPlugin.execute(exchange, 
shenyuPluginChain)).expectSubscription().verifyComplete();
-        verify(shenyuPluginChain).execute(exchange);
-    }
-    
-    /**
-     * The selector is null test.
-     */
-    @Test
-    public void executeSelectorIsNullTest() {
-        BaseDataCache.getInstance().cachePluginData(pluginData);
-        StepVerifier.create(testShenyuWasmPlugin.execute(exchange, 
shenyuPluginChain)).expectSubscription().verifyComplete();
-        verify(shenyuPluginChain).execute(exchange);
-    }
-    
-    /**
-     * The selector data is null test.
-     */
+
+    /** Go WASM test with selector/rule matching. */
     @Test
-    public void executeSelectorDataIsNullTest() {
-        BaseDataCache.getInstance().cachePluginData(pluginData);
-        BaseDataCache.getInstance().cacheSelectData(selectorData);
-        StepVerifier.create(testShenyuWasmPlugin.execute(exchange, 
shenyuPluginChain)).expectSubscription().verifyComplete();
+    public void executeGoWasmPluginTest() {
+        setupCacheData();
+        final TestGoShenyuWasmPlugin goPlugin = new TestGoShenyuWasmPlugin("go 
result");
+        StepVerifier.create(goPlugin.execute(exchange, 
shenyuPluginChain)).expectSubscription().verifyComplete();
         verify(shenyuPluginChain).execute(exchange);
     }
-    
-    /**
-     * The rule is null test.
-     */
+
+    /** Rust WASM test with selector/rule matching. */
     @Test
-    public void executeRuleIsNullTest() {
-        List<ConditionData> conditionDataList = 
Collections.singletonList(conditionData);
-        this.selectorData.setMatchMode(0);
-        this.selectorData.setLogged(true);
-        this.selectorData.setMatchRestful(false);
-        this.selectorData.setConditionList(conditionDataList);
-        BaseDataCache.getInstance().cachePluginData(pluginData);
-        BaseDataCache.getInstance().cacheSelectData(selectorData);
-        StepVerifier.create(testShenyuWasmPlugin.execute(exchange, 
shenyuPluginChain)).expectSubscription().verifyComplete();
+    public void executeRustWasmPluginTest() {
+        setupCacheData();
+        final TestShenyuWasmPlugin rustPlugin = new TestShenyuWasmPlugin("rust 
result");
+        StepVerifier.create(rustPlugin.execute(exchange, 
shenyuPluginChain)).expectSubscription().verifyComplete();
         verify(shenyuPluginChain).execute(exchange);
     }

Review Comment:
   This test class used to cover several control-flow branches (e.g., 
plugin/selector/rule cache missing, and multi-match selector/rule scenarios). 
After the refactor it only validates the happy path with one selector + one 
rule, which reduces regression coverage for AbstractShenyuWasmPlugin's 
matching/dispatch behavior.



-- 
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