diff --git a/compiler/symbol.go b/compiler/symbol.go index bf5ac5f1b7..af64a7e596 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -346,7 +346,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) { // The list of allowed types is based on this proposal: // https://github.com/golang/go/issues/59149 func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) { - if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" { + if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" || c.pkg.Path() == "syscall" { // The runtime is a special case. Allow all kinds of parameters // (importantly, including pointers). return diff --git a/src/syscall/env_libc.go b/src/syscall/env_libc.go new file mode 100644 index 0000000000..fc42392405 --- /dev/null +++ b/src/syscall/env_libc.go @@ -0,0 +1,59 @@ +//go:build (darwin || nintendoswitch || wasi || wasip1) && !wasip2 + +package syscall + +import ( + "unsafe" +) + +func Environ() []string { + + // This function combines all the environment into a single allocation. + // While this optimizes for memory usage and garbage collector + // overhead, it does run the risk of potentially pinning a "large" + // allocation if a user holds onto a single environment variable or + // value. Having each variable be its own allocation would make the + // trade-off in the other direction. + + // calculate total memory required + var length uintptr + var vars int + for environ := libc_environ; *environ != nil; { + length += libc_strlen(*environ) + vars++ + environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ))) + } + + // allocate our backing slice for the strings + b := make([]byte, length) + // and the slice we're going to return + envs := make([]string, 0, vars) + + // loop over the environment again, this time copying over the data to the backing slice + for environ := libc_environ; *environ != nil; { + length = libc_strlen(*environ) + // construct a Go string pointing at the libc-allocated environment variable data + var envVar string + rawEnvVar := (*struct { + ptr unsafe.Pointer + length uintptr + })(unsafe.Pointer(&envVar)) + rawEnvVar.ptr = *environ + rawEnvVar.length = length + // pull off the number of bytes we need for this environment variable + var bs []byte + bs, b = b[:length], b[length:] + // copy over the bytes to the Go heap + copy(bs, envVar) + // convert trimmed slice to string + s := *(*string)(unsafe.Pointer(&bs)) + // add s to our list of environment variables + envs = append(envs, s) + // environ++ + environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ))) + } + return envs +} + +//go:extern environ +var libc_environ *unsafe.Pointer diff --git a/src/syscall/env_wasip2.go b/src/syscall/env_wasip2.go new file mode 100644 index 0000000000..de7b1d8fd4 --- /dev/null +++ b/src/syscall/env_wasip2.go @@ -0,0 +1,51 @@ +//go:build wasip2 + +package syscall + +import ( + "unsafe" +) + +type __wasi_list_tuple struct { + ptr unsafe.Pointer + len uint32 +} + +type __wasi_tuple_string_string struct { + first __wasi_string + second __wasi_string +} + +type __wasi_string struct { + data unsafe.Pointer + len uint32 +} + +type _string struct { + ptr *byte + length uintptr +} + +//go:wasmimport wasi:cli/environment@0.2.0-rc-2023-11-10 get-environment +func __wasi_cli_environment_get_environment() __wasi_list_tuple + +func Environ() []string { + var envs []string + list_tuple := __wasi_cli_environment_get_environment() + envs = make([]string, list_tuple.len) + ptr := list_tuple.ptr + for i := uint32(0); i < list_tuple.len; i++ { + tuple := *(*__wasi_tuple_string_string)(ptr) + first, second := tuple.first, tuple.second + envKey := _string{ + (*byte)(first.data), uintptr(first.len), + } + envValue := _string{ + (*byte)(second.data), uintptr(second.len), + } + envs[i] = *(*string)(unsafe.Pointer(&envKey)) + "=" + *(*string)(unsafe.Pointer(&envValue)) + ptr = unsafe.Add(ptr, unsafe.Sizeof(__wasi_tuple_string_string{})) + } + + return envs +} diff --git a/src/syscall/libc_wasip2.go b/src/syscall/libc_wasip2.go index cab4013d34..56948168cf 100644 --- a/src/syscall/libc_wasip2.go +++ b/src/syscall/libc_wasip2.go @@ -152,9 +152,6 @@ func unlink(pathname *byte) int32 { return 0 } -//go:export environ -var environ *unsafe.Pointer - // int getpagesize(void); // //go:export getpagesize diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 7a13245b6d..a5b614c045 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -250,55 +250,6 @@ func Mprotect(b []byte, prot int) (err error) { return } -func Environ() []string { - - // This function combines all the environment into a single allocation. - // While this optimizes for memory usage and garbage collector - // overhead, it does run the risk of potentially pinning a "large" - // allocation if a user holds onto a single environment variable or - // value. Having each variable be its own allocation would make the - // trade-off in the other direction. - - // calculate total memory required - var length uintptr - var vars int - for environ := libc_environ; *environ != nil; { - length += libc_strlen(*environ) - vars++ - environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ))) - } - - // allocate our backing slice for the strings - b := make([]byte, length) - // and the slice we're going to return - envs := make([]string, 0, vars) - - // loop over the environment again, this time copying over the data to the backing slice - for environ := libc_environ; *environ != nil; { - length = libc_strlen(*environ) - // construct a Go string pointing at the libc-allocated environment variable data - var envVar string - rawEnvVar := (*struct { - ptr unsafe.Pointer - length uintptr - })(unsafe.Pointer(&envVar)) - rawEnvVar.ptr = *environ - rawEnvVar.length = length - // pull off the number of bytes we need for this environment variable - var bs []byte - bs, b = b[:length], b[length:] - // copy over the bytes to the Go heap - copy(bs, envVar) - // convert trimmed slice to string - s := *(*string)(unsafe.Pointer(&bs)) - // add s to our list of environment variables - envs = append(envs, s) - // environ++ - environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ))) - } - return envs -} - // BytePtrFromString returns a pointer to a NUL-terminated array of // bytes containing the text of s. If s contains a NUL byte at any // location, it returns (nil, EINVAL). @@ -430,6 +381,3 @@ func libc_readlink(path *byte, buf *byte, count uint) int // //export unlink func libc_unlink(pathname *byte) int32 - -//go:extern environ -var libc_environ *unsafe.Pointer