diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 12e79c50940..52cb9ba7039 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -368,6 +368,9 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface { if (!Properties::isConstantExpression(value)) { throw FailToEvalException("tableLoad of non-literal"); } + if (auto* r = value->dynCast()) { + return instance->makeFuncData(r->func, r->type.getHeapType()); + } return Properties::getLiteral(value); } diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index c8494c025b5..918e7f83674 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -3561,15 +3561,16 @@ class ModuleRunnerBase : public ExpressionRunner { trap("non-function target in call_indirect"); } - auto* func = self()->getModule()->getFunction(funcref.getFunc()); - if (!HeapType::isSubType(func->type, curr->heapType)) { + // TODO: Throw a non-constant exception if the reference is to an imported + // function that has a supertype of the expected type. + if (!HeapType::isSubType(funcref.type.getHeapType(), curr->heapType)) { trap("callIndirect: non-subtype"); } #if WASM_INTERPRETER_DEBUG std::cout << self()->indent() << "(calling table)\n"; #endif - Flow ret = callFunction(funcref.getFunc(), arguments); + Flow ret = funcref.getFuncData()->doCall(arguments); #if WASM_INTERPRETER_DEBUG std::cout << self()->indent() << "(returned to " << scope->function->name << ")\n"; diff --git a/test/spec/call_indirect_cross_module.wast b/test/spec/call_indirect_cross_module.wast new file mode 100644 index 00000000000..128605a5422 --- /dev/null +++ b/test/spec/call_indirect_cross_module.wast @@ -0,0 +1,55 @@ + +;; Test for confusion regarding internal function names when using +;; call_indirect. + +(module $primary + (import "spectest" "print_i32" (func $log (param i32))) + + (func $run (export "run") (result funcref) + (call $log (i32.const -1)) + (ref.func $run) + ) +) + +(register "primary") + +(module $secondary + (type $func (func (result funcref))) + + (import "spectest" "print_i32" (func $log (param i32))) + + (import "primary" "run" (func $primary_run (result funcref))) + + (table $table 10 20 funcref) + + (global $sum (mut i32) (i32.const 0)) + + (func $run (export "secondary_run") (result funcref) + (global.set $sum + (i32.add + (global.get $sum) + (i32.const 1) + ) + ) + (call $log (i32.const 10)) + (table.set $table + (i32.const 0) + (call $primary_run) + ) + (call $log (i32.const 20)) + ;; This should call primary's $run, not ours. The overlap in internal name + ;; should not confuse us. If we do get confused, we'd recurse here and keep + ;; incrementing $sum. + (call_indirect (type $func) + (i32.const 0) + ) + ) + + (func $sum (export "secondary_sum") (result i32) + (global.get $sum) + ) +) + +(invoke "secondary_run") +(assert_return (invoke "secondary_sum") (i32.const 1)) +