diff --git a/std/wasi/README.md b/std/wasi/README.md index 1f78e9ac72..829b25174f 100644 --- a/std/wasi/README.md +++ b/std/wasi/README.md @@ -68,21 +68,5 @@ const instance = await WebAssembly.instantiate(module, { "wasi_snapshot_preview1": context.exports, }); -const { - _start: start, - _initialize: initialize, - memory, -} = instance.exports; - -context.memory = memory as WebAssembly.Memory; - -if (start instanceof Function) { - start(); -} else if (initialize instanceof Function) { - initialize(); -} else { - throw new Error( - "No '_start' or '_initialize' entry point found in WebAssembly module, make sure to compile with wasm32-wasi as the target.", - ); -} +context.start(instance); ``` diff --git a/std/wasi/snapshot_preview1.ts b/std/wasi/snapshot_preview1.ts index 1d9549f150..eb296c47db 100644 --- a/std/wasi/snapshot_preview1.ts +++ b/std/wasi/snapshot_preview1.ts @@ -270,7 +270,6 @@ export interface ContextOptions { args?: string[]; env?: { [key: string]: string | undefined }; preopens?: { [key: string]: string }; - memory?: WebAssembly.Memory; } export default class Context { @@ -285,7 +284,7 @@ export default class Context { constructor(options: ContextOptions) { this.args = options.args ? options.args : []; this.env = options.env ? options.env : {}; - this.memory = options.memory!; + this.memory = null!; this.fds = [ { @@ -1554,4 +1553,39 @@ export default class Context { }), }; } + + /** + * Attempt to begin execution of instance as a command by invoking its + * _start() export. + * + * If the instance does not contain a _start() export, or if the instance + * contains an _initialize export an error will be thrown. + * + * The instance must also have a WebAssembly.Memory export named "memory" + * which will be used as the address space, if it does not an error will be + * thrown. + */ + start(instance: WebAssembly.Instance) { + const { _start, _initialize, memory } = instance.exports; + + if (!(memory instanceof WebAssembly.Memory)) { + throw new TypeError("WebAsembly.instance must provide a memory export"); + } + + this.memory = memory; + + if (typeof _initialize == "function") { + throw new TypeError( + "WebAsembly.instance export _initialize must not be a function", + ); + } + + if (typeof _start != "function") { + throw new TypeError( + "WebAssembly.Instance export _start must be a function", + ); + } + + _start(); + } } diff --git a/std/wasi/snapshot_preview1_test.ts b/std/wasi/snapshot_preview1_test.ts index 0ea57e089f..44877117cc 100644 --- a/std/wasi/snapshot_preview1_test.ts +++ b/std/wasi/snapshot_preview1_test.ts @@ -1,5 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; +import Context from "./snapshot_preview1.ts"; +import { assertEquals, assertThrows } from "../testing/asserts.ts"; import { copy } from "../fs/mod.ts"; import * as path from "../path/mod.ts"; @@ -137,3 +138,45 @@ for (const pathname of tests) { }, }); } + +Deno.test("context_start", function () { + assertThrows( + () => { + const context = new Context({}); + context.start({ + exports: { + _start() {}, + }, + }); + }, + TypeError, + "must provide a memory export", + ); + + assertThrows( + () => { + const context = new Context({}); + context.start({ + exports: { + _initialize() {}, + memory: new WebAssembly.Memory({ initial: 1 }), + }, + }); + }, + TypeError, + "export _initialize must not be a function", + ); + + assertThrows( + () => { + const context = new Context({}); + context.start({ + exports: { + memory: new WebAssembly.Memory({ initial: 1 }), + }, + }); + }, + TypeError, + "export _start must be a function", + ); +}); diff --git a/std/wasi/snapshot_preview1_test_runner.ts b/std/wasi/snapshot_preview1_test_runner.ts index 39f32db57e..65cb7bd3b8 100644 --- a/std/wasi/snapshot_preview1_test_runner.ts +++ b/std/wasi/snapshot_preview1_test_runner.ts @@ -16,8 +16,4 @@ const instance = new WebAssembly.Instance(module, { "wasi_snapshot_preview1": context.exports, }); -const memory = instance.exports.memory as WebAssembly.Memory; -context.memory = memory; - -const start = instance.exports._start as CallableFunction; -start(); +context.start(instance);