-
Notifications
You must be signed in to change notification settings - Fork 258
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add experimental ImportResolver (#2298)
Closes #2294 Signed-off-by: Bjørn Erik Pedersen <[email protected]>
- Loading branch information
Showing
11 changed files
with
276 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package experimental | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/tetratelabs/wazero/api" | ||
"github.com/tetratelabs/wazero/internal/expctxkeys" | ||
) | ||
|
||
// ImportResolver is an experimental func type that, if set, | ||
// will be used as the first step in resolving imports. | ||
// See issue 2294. | ||
// If the import name is not found, it should return nil. | ||
type ImportResolver func(name string) api.Module | ||
|
||
// WithImportResolver returns a new context with the given ImportResolver. | ||
func WithImportResolver(ctx context.Context, resolver ImportResolver) context.Context { | ||
return context.WithValue(ctx, expctxkeys.ImportResolverKey{}, resolver) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package experimental_test | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
_ "embed" | ||
"fmt" | ||
"log" | ||
|
||
"github.com/tetratelabs/wazero" | ||
"github.com/tetratelabs/wazero/api" | ||
"github.com/tetratelabs/wazero/experimental" | ||
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" | ||
) | ||
|
||
var ( | ||
// These wasm files were generated by the following: | ||
// cd testdata | ||
// wat2wasm --debug-names inoutdispatcher.wat | ||
// wat2wasm --debug-names inoutdispatcherclient.wat | ||
|
||
//go:embed testdata/inoutdispatcher.wasm | ||
inoutdispatcherWasm []byte | ||
//go:embed testdata/inoutdispatcherclient.wasm | ||
inoutdispatcherclientWasm []byte | ||
) | ||
|
||
func Example_importResolver() { | ||
ctx := context.Background() | ||
|
||
r := wazero.NewRuntime(ctx) | ||
defer r.Close(ctx) | ||
|
||
// The client imports the inoutdispatcher module that reads from stdin and writes to stdout. | ||
// This means that we need multiple instances of the inoutdispatcher module to have different stdin/stdout. | ||
// This example demonstrates a way to do that. | ||
type mod struct { | ||
in bytes.Buffer | ||
out bytes.Buffer | ||
|
||
client api.Module | ||
} | ||
|
||
wasi_snapshot_preview1.MustInstantiate(ctx, r) | ||
|
||
const numInstances = 3 | ||
mods := make([]*mod, numInstances) | ||
for i := range mods { | ||
mods[i] = &mod{} | ||
m := mods[i] | ||
idm, err := r.CompileModule(ctx, inoutdispatcherWasm) | ||
if err != nil { | ||
log.Panicln(err) | ||
} | ||
idcm, err := r.CompileModule(ctx, inoutdispatcherclientWasm) | ||
if err != nil { | ||
log.Panicln(err) | ||
} | ||
|
||
const inoutDispatcherModuleName = "inoutdispatcher" | ||
|
||
dispatcherInstance, err := r.InstantiateModule(ctx, idm, | ||
wazero.NewModuleConfig(). | ||
WithStdin(&m.in). | ||
WithStdout(&m.out). | ||
WithName("")) // Makes it an anonymous module. | ||
if err != nil { | ||
log.Panicln(err) | ||
} | ||
|
||
ctx = experimental.WithImportResolver(ctx, func(name string) api.Module { | ||
if name == inoutDispatcherModuleName { | ||
return dispatcherInstance | ||
} | ||
return nil | ||
}) | ||
|
||
m.client, err = r.InstantiateModule(ctx, idcm, wazero.NewModuleConfig().WithName(fmt.Sprintf("m%d", i))) | ||
if err != nil { | ||
log.Panicln(err) | ||
} | ||
|
||
} | ||
|
||
for i, m := range mods { | ||
m.in.WriteString(fmt.Sprintf("Module instance #%d", i)) | ||
_, err := m.client.ExportedFunction("dispatch").Call(ctx) | ||
if err != nil { | ||
log.Panicln(err) | ||
} | ||
} | ||
|
||
for i, m := range mods { | ||
fmt.Printf("out%d: %s\n", i, m.out.String()) | ||
} | ||
|
||
// Output: | ||
// out0: Module instance #0 | ||
// out1: Module instance #1 | ||
// out2: Module instance #2 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package experimental_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/tetratelabs/wazero" | ||
"github.com/tetratelabs/wazero/api" | ||
"github.com/tetratelabs/wazero/experimental" | ||
"github.com/tetratelabs/wazero/internal/testing/binaryencoding" | ||
"github.com/tetratelabs/wazero/internal/testing/require" | ||
"github.com/tetratelabs/wazero/internal/wasm" | ||
) | ||
|
||
func TestImportResolver(t *testing.T) { | ||
ctx := context.Background() | ||
|
||
r := wazero.NewRuntime(ctx) | ||
defer r.Close(ctx) | ||
|
||
for i := 0; i < 5; i++ { | ||
var callCount int | ||
start := func(ctx context.Context) { | ||
callCount++ | ||
} | ||
modImport, err := r.NewHostModuleBuilder(fmt.Sprintf("env%d", i)). | ||
NewFunctionBuilder().WithFunc(start).Export("start"). | ||
Compile(ctx) | ||
require.NoError(t, err) | ||
// Anonymous module, it will be resolved by the import resolver. | ||
instanceImport, err := r.InstantiateModule(ctx, modImport, wazero.NewModuleConfig().WithName("")) | ||
require.NoError(t, err) | ||
|
||
resolveImport := func(name string) api.Module { | ||
if name == "env" { | ||
return instanceImport | ||
} | ||
return nil | ||
} | ||
|
||
// Set the import resolver in the context. | ||
ctx = experimental.WithImportResolver(context.Background(), resolveImport) | ||
|
||
one := uint32(1) | ||
binary := binaryencoding.EncodeModule(&wasm.Module{ | ||
TypeSection: []wasm.FunctionType{{}}, | ||
ImportSection: []wasm.Import{{Module: "env", Name: "start", Type: wasm.ExternTypeFunc, DescFunc: 0}}, | ||
FunctionSection: []wasm.Index{0}, | ||
CodeSection: []wasm.Code{ | ||
{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, // Call the imported env.start. | ||
}, | ||
StartSection: &one, | ||
}) | ||
|
||
modMain, err := r.CompileModule(ctx, binary) | ||
require.NoError(t, err) | ||
|
||
_, err = r.InstantiateModule(ctx, modMain, wazero.NewModuleConfig()) | ||
require.NoError(t, err) | ||
require.Equal(t, 1, callCount) | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
(module | ||
(import "wasi_snapshot_preview1" "fd_read" (func $fd_read (param i32 i32 i32 i32) (result i32))) | ||
(import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) | ||
(memory 1 1 ) | ||
(func (export "dispatch") | ||
;; Buffer of 100 chars to read into. | ||
(i32.store (i32.const 4) (i32.const 12)) | ||
(i32.store (i32.const 8) (i32.const 100)) | ||
|
||
(block $done | ||
(loop $read | ||
;; Read from stdin. | ||
(call $fd_read | ||
(i32.const 0) ;; fd; 0 is stdin. | ||
(i32.const 4) ;; iovs | ||
(i32.const 1) ;; iovs_len | ||
(i32.const 8) ;; nread | ||
) | ||
|
||
;; If nread is 0, we're done. | ||
(if (i32.eq (i32.load (i32.const 8)) (i32.const 0)) | ||
(then br $done) | ||
) | ||
|
||
;; Write to stdout. | ||
(drop (call $fd_write | ||
(i32.const 1) ;; fd; 1 is stdout. | ||
(i32.const 4) ;; iovs | ||
(i32.const 1) ;; iovs_len | ||
(i32.const 0) ;; nwritten | ||
)) | ||
(br $read) | ||
|
||
) | ||
) | ||
) | ||
|
||
) |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
(module | ||
(import "inoutdispatcher" "dispatch" (func $dispatch)) | ||
(func (export "dispatch") | ||
(call $dispatch) | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package expctxkeys | ||
|
||
// ImportResolverKey is a context.Context Value key. | ||
// Its associated value should be an ImportResolver. | ||
// See issue 2294. | ||
type ImportResolverKey struct{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.