package experimental_test import ( "context" _ "embed" "fmt" "log" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/experimental" ) // snapshotWasm was generated by the following: // // cd testdata; wat2wasm snapshot.wat // //go:embed testdata/snapshot.wasm var snapshotWasm []byte type snapshotsKey struct{} func Example_enableSnapshotterKey() { ctx := context.Background() rt := wazero.NewRuntime(ctx) defer rt.Close(ctx) // This closes everything this Runtime created. // Enable experimental snapshotting functionality by setting it to context. We use this // context when invoking functions, indicating to wazero to enable it. ctx = experimental.WithSnapshotter(ctx) // Also place a mutable holder of snapshots to be referenced during restore. var snapshots []experimental.Snapshot ctx = context.WithValue(ctx, snapshotsKey{}, &snapshots) // Register host functions using snapshot and restore. Generally snapshot is saved // into a mutable location in context to be referenced during restore. _, err := rt.NewHostModuleBuilder("example"). NewFunctionBuilder(). WithFunc(func(ctx context.Context, mod api.Module, snapshotPtr uint32) int32 { // Because we enabled snapshots with WithSnapshotter, this is non-nil. snapshot := experimental.GetSnapshotter(ctx).Snapshot() // Get our mutable snapshots holder to be able to add to it. Our example only calls snapshot // and restore once but real programs will often call them at multiple layers within a call // stack with various e.g., try/catch statements. snapshots := ctx.Value(snapshotsKey{}).(*[]experimental.Snapshot) idx := len(*snapshots) *snapshots = append(*snapshots, snapshot) // Write a value to be passed back to restore. This is meant to be opaque to the guest // and used to re-reference the snapshot. ok := mod.Memory().WriteUint32Le(snapshotPtr, uint32(idx)) if !ok { log.Panicln("failed to write snapshot index") } return 0 }). Export("snapshot"). NewFunctionBuilder(). WithFunc(func(ctx context.Context, mod api.Module, snapshotPtr uint32) { // Read the value written by snapshot to re-reference the snapshot. idx, ok := mod.Memory().ReadUint32Le(snapshotPtr) if !ok { log.Panicln("failed to read snapshot index") } // Get the snapshot snapshots := ctx.Value(snapshotsKey{}).(*[]experimental.Snapshot) snapshot := (*snapshots)[idx] // Restore! The invocation of this function will end as soon as we invoke // Restore, so we also pass in our return value. The guest function run // will finish with this return value. snapshot.Restore([]uint64{5}) }). Export("restore"). Instantiate(ctx) if err != nil { log.Panicln(err) } mod, err := rt.Instantiate(ctx, snapshotWasm) // Instantiate the actual code if err != nil { log.Panicln(err) } // Call the guest entrypoint. res, err := mod.ExportedFunction("run").Call(ctx) if err != nil { log.Panicln(err) } // We restored and returned the restore value, so it's our result. If restore // was instead a no-op, we would have returned 10 from normal code flow. fmt.Println(res[0]) // Output: // 5 }