diff --git a/main.go b/main.go index 254e140cf2..c7c797dc8b 100644 --- a/main.go +++ b/main.go @@ -770,12 +770,11 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error { // for the given emulator. func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) (builder.BuildResult, error) { // Determine whether we're on a system that supports environment variables - // and command line parameters (operating systems, WASI) or not (baremetal, - // WebAssembly in the browser). If we're on a system without an environment, - // we need to pass command line arguments and environment variables through - // global variables (built into the binary directly) instead of the - // conventional way. - needsEnvInVars := config.GOOS() == "js" + // and command line parameters (operating systems, WASI) or not (baremetal). + // If we're on a system without an environment, we need to pass command line + // arguments and environment variables through global variables (built into + // the binary directly) instead of the conventional way. + needsEnvInVars := false for _, tag := range config.BuildTags() { if tag == "baremetal" { needsEnvInVars = true diff --git a/src/runtime/nonhosted.go b/src/runtime/nonhosted.go index ca5ab4c3c8..680fffcb5b 100644 --- a/src/runtime/nonhosted.go +++ b/src/runtime/nonhosted.go @@ -1,4 +1,4 @@ -//go:build baremetal || js || wasm_unknown +//go:build baremetal || wasm_unknown package runtime diff --git a/src/runtime/runtime_tinygowasm.go b/src/runtime/runtime_tinygowasm.go index f791ffacdf..cfe3fb1547 100644 --- a/src/runtime/runtime_tinygowasm.go +++ b/src/runtime/runtime_tinygowasm.go @@ -29,6 +29,44 @@ func proc_exit(exitcode uint32) //export __stdio_exit func __stdio_exit() +var args []string + +//go:linkname os_runtime_args os.runtime_args +func os_runtime_args() []string { + if args == nil { + // Read the number of args (argc) and the buffer size required to store + // all these args (argv). + var argc, argv_buf_size uint32 + args_sizes_get(&argc, &argv_buf_size) + if argc == 0 { + return nil + } + + // Obtain the command line arguments + argsSlice := make([]unsafe.Pointer, argc) + buf := make([]byte, argv_buf_size) + args_get(&argsSlice[0], unsafe.Pointer(&buf[0])) + + // Convert the array of C strings to an array of Go strings. + args = make([]string, argc) + for i, cstr := range argsSlice { + length := strlen(cstr) + argString := _string{ + length: length, + ptr: (*byte)(cstr), + } + args[i] = *(*string)(unsafe.Pointer(&argString)) + } + } + return args +} + +//go:wasmimport wasi_snapshot_preview1 args_get +func args_get(argv *unsafe.Pointer, argv_buf unsafe.Pointer) (errno uint16) + +//go:wasmimport wasi_snapshot_preview1 args_sizes_get +func args_sizes_get(argc *uint32, argv_buf_size *uint32) (errno uint16) + const ( putcharBufferSize = 120 stdout = 1 diff --git a/src/runtime/runtime_wasip1.go b/src/runtime/runtime_wasip1.go index ad66b0d860..3605a58ce9 100644 --- a/src/runtime/runtime_wasip1.go +++ b/src/runtime/runtime_wasip1.go @@ -2,10 +2,6 @@ package runtime -import ( - "unsafe" -) - type timeUnit int64 // libc constructors @@ -21,38 +17,6 @@ func init() { __wasm_call_ctors() } -var args []string - -//go:linkname os_runtime_args os.runtime_args -func os_runtime_args() []string { - if args == nil { - // Read the number of args (argc) and the buffer size required to store - // all these args (argv). - var argc, argv_buf_size uint32 - args_sizes_get(&argc, &argv_buf_size) - if argc == 0 { - return nil - } - - // Obtain the command line arguments - argsSlice := make([]unsafe.Pointer, argc) - buf := make([]byte, argv_buf_size) - args_get(&argsSlice[0], unsafe.Pointer(&buf[0])) - - // Convert the array of C strings to an array of Go strings. - args = make([]string, argc) - for i, cstr := range argsSlice { - length := strlen(cstr) - argString := _string{ - length: length, - ptr: (*byte)(cstr), - } - args[i] = *(*string)(unsafe.Pointer(&argString)) - } - } - return args -} - func ticksToNanoseconds(ticks timeUnit) int64 { return int64(ticks) } @@ -97,12 +61,6 @@ func beforeExit() { // Implementations of WASI APIs -//go:wasmimport wasi_snapshot_preview1 args_get -func args_get(argv *unsafe.Pointer, argv_buf unsafe.Pointer) (errno uint16) - -//go:wasmimport wasi_snapshot_preview1 args_sizes_get -func args_sizes_get(argc *uint32, argv_buf_size *uint32) (errno uint16) - //go:wasmimport wasi_snapshot_preview1 clock_time_get func clock_time_get(clockid uint32, precision uint64, time *uint64) (errno uint16) diff --git a/src/syscall/env_libc.go b/src/syscall/env_libc.go index 4ad078dc54..b534b527a8 100644 --- a/src/syscall/env_libc.go +++ b/src/syscall/env_libc.go @@ -1,4 +1,4 @@ -//go:build nintendoswitch || wasip1 +//go:build js || nintendoswitch || wasip1 package syscall diff --git a/src/syscall/errno_other.go b/src/syscall/errno_other.go index 3a06ac018f..8c58f5f01a 100644 --- a/src/syscall/errno_other.go +++ b/src/syscall/errno_other.go @@ -1,4 +1,4 @@ -//go:build !wasip1 && !wasip2 +//go:build !js && !wasip1 && !wasip2 package syscall diff --git a/src/syscall/errno_wasip1.go b/src/syscall/errno_wasmlibc.go similarity index 83% rename from src/syscall/errno_wasip1.go rename to src/syscall/errno_wasmlibc.go index c494d7da09..106b192ce4 100644 --- a/src/syscall/errno_wasip1.go +++ b/src/syscall/errno_wasmlibc.go @@ -1,4 +1,4 @@ -//go:build wasip1 +//go:build js || wasip1 package syscall diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 67cf6681f7..03c966683e 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -1,4 +1,4 @@ -//go:build nintendoswitch || wasip1 || wasip2 +//go:build js || nintendoswitch || wasip1 || wasip2 package syscall diff --git a/src/syscall/syscall_libc_wasi.go b/src/syscall/syscall_libc_wasi.go index 06169bb2b9..09536cc0b0 100644 --- a/src/syscall/syscall_libc_wasi.go +++ b/src/syscall/syscall_libc_wasi.go @@ -1,4 +1,4 @@ -//go:build wasip1 || wasip2 +//go:build js || wasip1 || wasip2 package syscall diff --git a/src/syscall/syscall_nonhosted.go b/src/syscall/syscall_nonhosted.go index b56d71af64..0d215af086 100644 --- a/src/syscall/syscall_nonhosted.go +++ b/src/syscall/syscall_nonhosted.go @@ -1,4 +1,4 @@ -//go:build baremetal || js || wasm_unknown +//go:build baremetal || wasm_unknown package syscall diff --git a/src/syscall/tables_nonhosted.go b/src/syscall/tables_nonhosted.go index e66620cbf4..a45834827e 100644 --- a/src/syscall/tables_nonhosted.go +++ b/src/syscall/tables_nonhosted.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build baremetal || nintendoswitch || js || wasm_unknown +//go:build baremetal || nintendoswitch || wasm_unknown package syscall diff --git a/targets/wasm_exec.js b/targets/wasm_exec.js index c430cc2b23..f5b76bbab3 100644 --- a/targets/wasm_exec.js +++ b/targets/wasm_exec.js @@ -135,6 +135,8 @@ global.Go = class { constructor() { + this.argv = ["js"]; + this.env = {}; this._callbackTimeouts = new Map(); this._nextCallbackTimeoutID = 1; @@ -235,9 +237,58 @@ return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len)); } + const storeStringArraySizes = (array, num_ptr, buf_size_ptr) => { + let buf_size = 0; + for (let s of array) { + buf_size += s.length + 1; + } + mem().setUint32(num_ptr, array.length, true); + mem().setUint32(buf_size_ptr, buf_size, true); + } + + const storeStringArray = (array, ptrs_ptr, buf_ptr) => { + for (let s of array) { + // Put string data in buffer. + let data = encoder.encode(s); + let dest = new Uint8Array(this._inst.exports.memory.buffer, buf_ptr, data.length); + dest.set(data); + mem().setUint8(buf_ptr+data.length, 0); + + // Put pointer to buffer in pointers array. + mem().setUint32(ptrs_ptr, buf_ptr, true); + + // Advance to the next element in the array. + ptrs_ptr += 4; + buf_ptr += data.length + 1; + } + } + + const envArray = () => { + let array = []; + for (let [key, value] of Object.entries(this.env)) { + array.push(`${key}=${value}`); + } + return array; + } + const timeOrigin = Date.now() - performance.now(); this.importObject = { wasi_snapshot_preview1: { + args_sizes_get: (argc_ptr, argv_buf_size_ptr) => { + storeStringArraySizes(this.argv, argc_ptr, argv_buf_size_ptr); + return 0; + }, + args_get: (argv_ptr, argv_buf_ptr) => { + storeStringArray(this.argv, argv_ptr, argv_buf_ptr); + return 0; + }, + environ_get: (env_ptr, env_buf_ptr) => { + storeStringArray(envArray(), env_ptr, env_buf_ptr); + }, + environ_sizes_get: (env_ptr, env_buf_size_ptr) => { + storeStringArraySizes(envArray(), env_ptr, env_buf_size_ptr); + return 0; + }, // https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) { let nwritten = 0; @@ -504,12 +555,14 @@ global.process.versions && !global.process.versions.electron ) { - if (process.argv.length != 3) { + if (process.argv.length < 3) { console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); process.exit(1); } const go = new Go(); + go.argv = process.argv.slice(2); + go.env = process.env; WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { return go.run(result.instance); }).catch((err) => {