Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasm: use wasip1 API for parameter/env passing #4565

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/nonhosted.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build baremetal || js || wasm_unknown
//go:build baremetal || wasm_unknown

package runtime

Expand Down
38 changes: 38 additions & 0 deletions src/runtime/runtime_tinygowasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 0 additions & 42 deletions src/runtime/runtime_wasip1.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@

package runtime

import (
"unsafe"
)

type timeUnit int64

// libc constructors
Expand All @@ -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)
}
Expand Down Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion src/syscall/env_libc.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build nintendoswitch || wasip1
//go:build js || nintendoswitch || wasip1

package syscall

Expand Down
2 changes: 1 addition & 1 deletion src/syscall/errno_other.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !wasip1 && !wasip2
//go:build !js && !wasip1 && !wasip2

package syscall

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build wasip1
//go:build js || wasip1

package syscall

Expand Down
2 changes: 1 addition & 1 deletion src/syscall/syscall_libc.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build nintendoswitch || wasip1 || wasip2
//go:build js || nintendoswitch || wasip1 || wasip2

package syscall

Expand Down
2 changes: 1 addition & 1 deletion src/syscall/syscall_libc_wasi.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build wasip1 || wasip2
//go:build js || wasip1 || wasip2

package syscall

Expand Down
2 changes: 1 addition & 1 deletion src/syscall/syscall_nonhosted.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build baremetal || js || wasm_unknown
//go:build baremetal || wasm_unknown

package syscall

Expand Down
2 changes: 1 addition & 1 deletion src/syscall/tables_nonhosted.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
55 changes: 54 additions & 1 deletion targets/wasm_exec.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@

global.Go = class {
constructor() {
this.argv = ["js"];
this.env = {};
this._callbackTimeouts = new Map();
this._nextCallbackTimeoutID = 1;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) => {
Expand Down
Loading