gojs: drops HTTP support to be compatible with Go 1.21 (#1557)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// Package fstest defines filesystem test cases that help validate host
|
||||
// functions implementing WASI and `GOARCH=wasm GOOS=js`. Tests are defined
|
||||
// functions implementing WASI and `GOOS=js GOARCH=wasm`. Tests are defined
|
||||
// here to reduce duplication and drift.
|
||||
//
|
||||
// Here's an example using this inside code that compiles to wasm.
|
||||
@@ -15,7 +15,7 @@
|
||||
// for example, gojs, sysfs or wasi_snapshot_preview1.
|
||||
//
|
||||
// This package must have no dependencies. Otherwise, compiling this with
|
||||
// TinyGo or `GOARCH=wasm GOOS=js` can become bloated or complicated.
|
||||
// TinyGo or `GOOS=js GOARCH=wasm` can become bloated or complicated.
|
||||
package fstest
|
||||
|
||||
import (
|
||||
|
||||
@@ -15,8 +15,8 @@ func Test_argsAndEnv(t *testing.T) {
|
||||
return moduleConfig.WithEnv("c", "d").WithEnv("a", "b"), config.NewConfig()
|
||||
})
|
||||
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.Zero(t, stderr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `
|
||||
args 0 = test
|
||||
args 1 = argsenv
|
||||
|
||||
@@ -14,26 +14,18 @@ func newJsGlobal(config *config.Config) *jsVal {
|
||||
cwd: config.Workdir,
|
||||
umask: config.Umask,
|
||||
}
|
||||
rt := config.Rt
|
||||
|
||||
if config.Rt != nil {
|
||||
fetchProperty = goos.RefHttpFetch
|
||||
}
|
||||
|
||||
return newJsVal(goos.RefValueGlobal, "global").
|
||||
addProperties(map[string]interface{}{
|
||||
"Object": objectConstructor,
|
||||
"Array": arrayConstructor,
|
||||
"crypto": jsCrypto,
|
||||
"Uint8Array": uint8ArrayConstructor,
|
||||
"fetch": fetchProperty,
|
||||
"AbortController": goos.Undefined,
|
||||
"Headers": headersConstructor,
|
||||
"process": newJsProcess(uid, gid, euid, groups, proc),
|
||||
"fs": newJsFs(proc),
|
||||
"Date": jsDateConstructor,
|
||||
}).
|
||||
addFunction("fetch", &httpFetch{rt})
|
||||
"Object": objectConstructor,
|
||||
"Array": arrayConstructor,
|
||||
"crypto": jsCrypto,
|
||||
"Uint8Array": uint8ArrayConstructor,
|
||||
"fetch": fetchProperty,
|
||||
"process": newJsProcess(uid, gid, euid, groups, proc),
|
||||
"fs": newJsFs(proc),
|
||||
"Date": jsDateConstructor,
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -51,7 +43,7 @@ var (
|
||||
arrayConstructor = newJsVal(goos.RefArrayConstructor, "Array")
|
||||
|
||||
// uint8ArrayConstructor = js.Global().Get("Uint8Array")
|
||||
// // fs_js.go, rand_js.go, roundtrip_js.go init
|
||||
// // fs_js.go, rand_js.go init
|
||||
//
|
||||
// It has only one invocation pattern: `buf := uint8Array.New(len(b))`
|
||||
uint8ArrayConstructor = newJsVal(goos.RefUint8ArrayConstructor, "Uint8Array")
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -41,30 +42,27 @@ func compileAndRun(ctx context.Context, arg string, config newConfig) (stdout, s
|
||||
}
|
||||
|
||||
func compileAndRunWithRuntime(ctx context.Context, r wazero.Runtime, arg string, config newConfig) (stdout, stderr string, err error) {
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
|
||||
builder := r.NewHostModuleBuilder("go")
|
||||
gojs.NewFunctionExporter().ExportFunctions(builder)
|
||||
if _, err = builder.Instantiate(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Note: this hits the file cache.
|
||||
compiled, err := r.CompileModule(testCtx, testBin)
|
||||
if err != nil {
|
||||
var guest wazero.CompiledModule
|
||||
if guest, err = r.CompileModule(testCtx, testBin); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if _, err = gojs.Instantiate(ctx, r, guest); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
mc, c := config(wazero.NewModuleConfig().
|
||||
WithStdout(&stdoutBuf).
|
||||
WithStderr(&stderrBuf).
|
||||
WithArgs("test", arg))
|
||||
|
||||
var s *internalgojs.State
|
||||
s, err = run.RunAndReturnState(ctx, r, compiled, mc, c)
|
||||
s, err = run.RunAndReturnState(ctx, r, guest, mc, c)
|
||||
if err == nil {
|
||||
if !reflect.DeepEqual(s, internalgojs.NewState(c)) {
|
||||
log.Panicf("unexpected state: %v\n", s)
|
||||
if want, have := internalgojs.NewState(c), s; !reflect.DeepEqual(want, have) {
|
||||
log.Panicf("unexpected state: want %#v, have %#v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,3 +167,8 @@ func findGoBin() (string, error) {
|
||||
// Now, search the path
|
||||
return exec.LookPath(binName)
|
||||
}
|
||||
|
||||
// logString handles the "go" -> "gojs" module rename in Go 1.21
|
||||
func logString(log bytes.Buffer) string {
|
||||
return strings.ReplaceAll(log.String(), "==> gojs", "==> go")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -23,7 +22,6 @@ type Config struct {
|
||||
// Workdir is the actual working directory value.
|
||||
Workdir string
|
||||
Umask uint32
|
||||
Rt http.RoundTripper
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
@@ -36,7 +34,6 @@ func NewConfig() *Config {
|
||||
Groups: []int{0},
|
||||
Workdir: "/",
|
||||
Umask: uint32(0o0022),
|
||||
Rt: nil,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ func Test_crypto(t *testing.T) {
|
||||
stdout, stderr, err := compileAndRun(loggingCtx, "crypto", defaultConfig)
|
||||
|
||||
require.Zero(t, stderr)
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `7a0c9f9f0d
|
||||
`, stdout)
|
||||
require.Equal(t, `==> go.runtime.getRandomData(r_len=32)
|
||||
@@ -29,5 +29,5 @@ func Test_crypto(t *testing.T) {
|
||||
<==
|
||||
==> go.syscall/js.valueCall(crypto.getRandomValues(r_len=5))
|
||||
<== (n=5)
|
||||
`, log.String())
|
||||
`, logString(log))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Package custom is similar to the WebAssembly Custom Sections. These are
|
||||
// needed because `GOARCH=wasm GOOS=js` functions aren't defined naturally
|
||||
// needed because `GOOS=js GOARCH=wasm` functions aren't defined naturally
|
||||
// in WebAssembly. For example, every function has a single parameter "sp",
|
||||
// which implicitly maps to stack parameters in this package.
|
||||
package custom
|
||||
|
||||
@@ -2,6 +2,7 @@ package custom
|
||||
|
||||
const (
|
||||
NameProcess = "process"
|
||||
NameProcessArgv0 = "argv0"
|
||||
NameProcessCwd = "cwd"
|
||||
NameProcessChdir = "chdir"
|
||||
NameProcessGetuid = "getuid"
|
||||
@@ -15,6 +16,11 @@ const (
|
||||
// Results here are those set to the current event object, but effectively are
|
||||
// results of the host function.
|
||||
var ProcessNameSection = map[string]*Names{
|
||||
NameProcessArgv0: {
|
||||
Name: NameProcessArgv0,
|
||||
ParamNames: []string{},
|
||||
ResultNames: []string{"argv0"},
|
||||
},
|
||||
NameProcessCwd: {
|
||||
Name: NameProcessCwd,
|
||||
ParamNames: []string{},
|
||||
|
||||
@@ -20,7 +20,7 @@ func Test_fs(t *testing.T) {
|
||||
})
|
||||
|
||||
require.Zero(t, stderr)
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `sub mode drwxr-xr-x
|
||||
/animals.txt mode -rw-r--r--
|
||||
animals.txt mode -rw-r--r--
|
||||
@@ -50,7 +50,7 @@ func Test_testfs(t *testing.T) {
|
||||
})
|
||||
|
||||
require.Zero(t, stderr)
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, stdout)
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func Test_writefs(t *testing.T) {
|
||||
})
|
||||
|
||||
require.Zero(t, stderr)
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.NoError(t, err)
|
||||
|
||||
if platform.CompilerSupported() {
|
||||
// Note: as of Go 1.19, only the Sec field is set on update in fs_js.go.
|
||||
|
||||
@@ -48,8 +48,6 @@ const (
|
||||
IdJsCrypto
|
||||
IdJsDateConstructor
|
||||
IdJsDate
|
||||
IdHttpFetch
|
||||
IdHttpHeaders
|
||||
NextID
|
||||
)
|
||||
|
||||
@@ -63,17 +61,15 @@ const (
|
||||
RefValueGlobal = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdValueGlobal)
|
||||
RefJsGo = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsGo)
|
||||
|
||||
RefObjectConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdObjectConstructor)
|
||||
RefArrayConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdArrayConstructor)
|
||||
RefJsProcess = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsProcess)
|
||||
RefJsfs = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsfs)
|
||||
RefJsfsConstants = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsfsConstants)
|
||||
RefUint8ArrayConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdUint8ArrayConstructor)
|
||||
RefJsCrypto = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdJsCrypto)
|
||||
RefJsDateConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdJsDateConstructor)
|
||||
RefJsDate = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsDate)
|
||||
RefHttpFetch = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdHttpFetch)
|
||||
RefHttpHeadersConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdHttpHeaders)
|
||||
RefObjectConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdObjectConstructor)
|
||||
RefArrayConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdArrayConstructor)
|
||||
RefJsProcess = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsProcess)
|
||||
RefJsfs = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsfs)
|
||||
RefJsfsConstants = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsfsConstants)
|
||||
RefUint8ArrayConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdUint8ArrayConstructor)
|
||||
RefJsCrypto = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdJsCrypto)
|
||||
RefJsDateConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdJsDateConstructor)
|
||||
RefJsDate = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsDate)
|
||||
)
|
||||
|
||||
type TypeFlag byte
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
package gojs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"sort"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/goos"
|
||||
)
|
||||
|
||||
// headersConstructor = Get("Headers").New() // http.Roundtrip && "fetch"
|
||||
var headersConstructor = newJsVal(goos.RefHttpHeadersConstructor, "Headers")
|
||||
|
||||
// httpFetch implements jsFn for http.RoundTripper
|
||||
//
|
||||
// Reference in roundtrip_js.go init
|
||||
//
|
||||
// jsFetchMissing = js.Global().Get("fetch").IsUndefined()
|
||||
//
|
||||
// In http.Transport RoundTrip, this returns a promise
|
||||
//
|
||||
// fetchPromise := js.Global().Call("fetch", req.URL.String(), opt)
|
||||
type httpFetch struct{ rt http.RoundTripper }
|
||||
|
||||
func (h *httpFetch) invoke(ctx context.Context, _ api.Module, args ...interface{}) (interface{}, error) {
|
||||
rt := h.rt
|
||||
if rt == nil {
|
||||
panic("unexpected to reach here without roundtripper as property is nil checked")
|
||||
}
|
||||
url := args[0].(string)
|
||||
properties := args[1].(*object).properties
|
||||
req, err := http.NewRequestWithContext(ctx, properties["method"].(string), url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: headers properties[headers]
|
||||
v := &fetchPromise{rt: rt, req: req}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
type fetchPromise struct {
|
||||
rt http.RoundTripper
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
// call implements jsCall.call
|
||||
func (p *fetchPromise) call(ctx context.Context, mod api.Module, this goos.Ref, method string, args ...interface{}) (interface{}, error) {
|
||||
if method == "then" {
|
||||
if res, err := p.rt.RoundTrip(p.req); err != nil {
|
||||
failure := args[1].(funcWrapper)
|
||||
// HTTP is at the GOOS=js abstraction, so we can return any error.
|
||||
return failure.invoke(ctx, mod, this, err)
|
||||
} else {
|
||||
success := args[0].(funcWrapper)
|
||||
return success.invoke(ctx, mod, this, &fetchResult{res: res})
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("TODO: fetchPromise.%s", method))
|
||||
}
|
||||
|
||||
type fetchResult struct {
|
||||
res *http.Response
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on goos.GetFunction
|
||||
func (s *fetchResult) Get(propertyKey string) interface{} {
|
||||
switch propertyKey {
|
||||
case "headers":
|
||||
names := make([]string, 0, len(s.res.Header))
|
||||
for k := range s.res.Header {
|
||||
names = append(names, k)
|
||||
}
|
||||
// Sort names for consistent iteration
|
||||
sort.Strings(names)
|
||||
h := &headers{names: names, headers: s.res.Header}
|
||||
return h
|
||||
case "body":
|
||||
// return undefined as arrayPromise is more complicated than an array.
|
||||
return goos.Undefined
|
||||
case "status":
|
||||
return uint32(s.res.StatusCode)
|
||||
}
|
||||
panic(fmt.Sprintf("TODO: get fetchResult.%s", propertyKey))
|
||||
}
|
||||
|
||||
// call implements jsCall.call
|
||||
func (s *fetchResult) call(_ context.Context, _ api.Module, _ goos.Ref, method string, _ ...interface{}) (interface{}, error) {
|
||||
switch method {
|
||||
case "arrayBuffer":
|
||||
v := &arrayPromise{reader: s.res.Body}
|
||||
return v, nil
|
||||
}
|
||||
panic(fmt.Sprintf("TODO: call fetchResult.%s", method))
|
||||
}
|
||||
|
||||
type headers struct {
|
||||
headers http.Header
|
||||
names []string
|
||||
i int
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on goos.GetFunction
|
||||
func (h *headers) Get(propertyKey string) interface{} {
|
||||
switch propertyKey {
|
||||
case "done":
|
||||
return h.i == len(h.names)
|
||||
case "value":
|
||||
name := h.names[h.i]
|
||||
value := h.headers.Get(name)
|
||||
h.i++
|
||||
return &objectArray{[]interface{}{name, value}}
|
||||
}
|
||||
panic(fmt.Sprintf("TODO: get headers.%s", propertyKey))
|
||||
}
|
||||
|
||||
// call implements jsCall.call
|
||||
func (h *headers) call(_ context.Context, _ api.Module, _ goos.Ref, method string, args ...interface{}) (interface{}, error) {
|
||||
switch method {
|
||||
case "entries":
|
||||
// Sort names for consistent iteration
|
||||
sort.Strings(h.names)
|
||||
return h, nil
|
||||
case "next":
|
||||
return h, nil
|
||||
case "append":
|
||||
name := textproto.CanonicalMIMEHeaderKey(args[0].(string))
|
||||
value := args[1].(string)
|
||||
h.names = append(h.names, name)
|
||||
h.headers.Add(name, value)
|
||||
return nil, nil
|
||||
}
|
||||
panic(fmt.Sprintf("TODO: call headers.%s", method))
|
||||
}
|
||||
|
||||
type arrayPromise struct {
|
||||
reader io.ReadCloser
|
||||
}
|
||||
|
||||
// call implements jsCall.call
|
||||
func (p *arrayPromise) call(ctx context.Context, mod api.Module, this goos.Ref, method string, args ...interface{}) (interface{}, error) {
|
||||
switch method {
|
||||
case "then":
|
||||
defer p.reader.Close()
|
||||
if b, err := io.ReadAll(p.reader); err != nil {
|
||||
// HTTP is at the GOOS=js abstraction, so we can return any error.
|
||||
return args[1].(funcWrapper).invoke(ctx, mod, this, err)
|
||||
} else {
|
||||
return args[0].(funcWrapper).invoke(ctx, mod, this, goos.WrapByteArray(b))
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("TODO: call arrayPromise.%s", method))
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package gojs_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/config"
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
)
|
||||
|
||||
type roundTripperFunc func(r *http.Request) (*http.Response, error)
|
||||
|
||||
func (f roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
return f(r)
|
||||
}
|
||||
|
||||
func Test_http(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rt := roundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.Path == "/error" {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
if req.Body != nil {
|
||||
require.Equal(t, http.MethodPost, req.Method)
|
||||
bytes, err := io.ReadAll(req.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "ice cream", string(bytes))
|
||||
}
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: http.StatusText(http.StatusOK),
|
||||
Header: http.Header{"Custom": {"1"}},
|
||||
Body: io.NopCloser(strings.NewReader("abcdef")),
|
||||
ContentLength: 6,
|
||||
}, nil
|
||||
})
|
||||
|
||||
stdout, stderr, err := compileAndRun(testCtx, "http", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) {
|
||||
config := config.NewConfig()
|
||||
config.Rt = rt
|
||||
return moduleConfig.WithEnv("BASE_URL", "http://host"), config
|
||||
})
|
||||
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.Zero(t, stderr)
|
||||
require.Equal(t, `Get "http://host/error": net/http: fetch() failed: error
|
||||
1
|
||||
abcdef
|
||||
`, stdout)
|
||||
}
|
||||
@@ -23,12 +23,12 @@ func Test_exit(t *testing.T) {
|
||||
|
||||
stdout, stderr, err := compileAndRun(loggingCtx, "exit", defaultConfig)
|
||||
|
||||
require.EqualError(t, err, `module closed with exit_code(255)`)
|
||||
require.Zero(t, stderr)
|
||||
require.EqualError(t, err, `module closed with exit_code(255)`)
|
||||
require.Zero(t, stdout)
|
||||
require.Equal(t, `==> go.runtime.wasmExit(code=255)
|
||||
<==
|
||||
`, log.String()) // Note: gojs doesn't panic on exit, so you see "<=="
|
||||
`, logString(log)) // Note: gojs doesn't panic on exit, so you see "<=="
|
||||
}
|
||||
|
||||
func Test_goroutine(t *testing.T) {
|
||||
@@ -36,8 +36,8 @@ func Test_goroutine(t *testing.T) {
|
||||
|
||||
stdout, stderr, err := compileAndRun(testCtx, "goroutine", defaultConfig)
|
||||
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.Zero(t, stderr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `producer
|
||||
consumer
|
||||
`, stdout)
|
||||
@@ -52,12 +52,12 @@ func Test_mem(t *testing.T) {
|
||||
|
||||
stdout, stderr, err := compileAndRun(loggingCtx, "mem", defaultConfig)
|
||||
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.Zero(t, stderr)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, stdout)
|
||||
|
||||
// The memory view is reset at least once.
|
||||
require.Contains(t, log.String(), `==> go.runtime.resetMemoryDataView()
|
||||
require.Contains(t, logString(log), `==> go.runtime.resetMemoryDataView()
|
||||
<==
|
||||
`)
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func Test_stdio(t *testing.T) {
|
||||
})
|
||||
|
||||
require.Equal(t, "stderr 6\n", stderr)
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "stdout 6\n", stdout)
|
||||
}
|
||||
|
||||
@@ -89,17 +89,13 @@ func Test_stdio_large(t *testing.T) {
|
||||
return defaultConfig(moduleConfig.WithStdin(bytes.NewReader(input)))
|
||||
})
|
||||
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, fmt.Sprintf("stderr %d\n", size), stderr)
|
||||
require.Equal(t, fmt.Sprintf("stdout %d\n", size), stdout)
|
||||
|
||||
// We can't predict the precise ms the timeout event will be, so we partial match.
|
||||
require.Contains(t, log.String(), `==> go.runtime.scheduleTimeoutEvent(ms=`)
|
||||
require.Contains(t, log.String(), `<== (id=1)`)
|
||||
// There may be another timeout event between the first and its clear.
|
||||
require.Contains(t, log.String(), `==> go.runtime.clearTimeoutEvent(id=1)
|
||||
<==
|
||||
`)
|
||||
// There's no guarantee of a timeout event (in Go 1.21 there isn't), so we
|
||||
// don't verify this. gojs is in maintenance mode until it is removed after
|
||||
// Go 1.22 is out.
|
||||
}
|
||||
|
||||
func Test_gc(t *testing.T) {
|
||||
@@ -107,7 +103,7 @@ func Test_gc(t *testing.T) {
|
||||
|
||||
stdout, stderr, err := compileAndRun(testCtx, "gc", defaultConfig)
|
||||
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "", stderr)
|
||||
require.Equal(t, "before gc\nafter gc\n", stdout)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func Test_process(t *testing.T) {
|
||||
})
|
||||
|
||||
require.Zero(t, stderr)
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `syscall.Getpid()=1
|
||||
syscall.Getppid()=0
|
||||
syscall.Getuid()=0
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/internal/gojs"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/config"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func RunAndReturnState(
|
||||
@@ -40,5 +41,10 @@ func RunAndReturnState(
|
||||
|
||||
// Invoke the run function.
|
||||
_, err = mod.ExportedFunction("run").Call(ctx, uint64(argc), uint64(argv))
|
||||
if se, ok := err.(*sys.ExitError); ok {
|
||||
if se.ExitCode() == 0 { // Don't err on success.
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
return s, err
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
func NewState(config *config.Config) *State {
|
||||
return &State{
|
||||
config: config,
|
||||
values: values.NewValues(),
|
||||
valueGlobal: newJsGlobal(config),
|
||||
_nextCallbackTimeoutID: 1,
|
||||
@@ -100,8 +101,6 @@ func LoadValue(ctx context.Context, ref goos.Ref) interface{} { //nolint
|
||||
return jsDateConstructor
|
||||
case goos.RefJsDate:
|
||||
return jsDate
|
||||
case goos.RefHttpHeadersConstructor:
|
||||
return headersConstructor
|
||||
default:
|
||||
if f, ok := ref.ParseFloat(); ok { // numbers are passed through as a Ref
|
||||
return f
|
||||
@@ -169,6 +168,7 @@ func toFloatRef(f float64) goos.Ref {
|
||||
// State holds state used by the "go" imports used by gojs.
|
||||
// Note: This is module-scoped.
|
||||
type State struct {
|
||||
config *config.Config
|
||||
values *values.Values
|
||||
_pendingEvent *event
|
||||
// _lastEvent was the last _pendingEvent value
|
||||
@@ -208,11 +208,12 @@ func (s *State) close() {
|
||||
}
|
||||
// Reset all state recursively to their initial values. This allows our
|
||||
// unit tests to check we closed everything.
|
||||
s._scheduledTimeouts = map[uint32]chan bool{}
|
||||
s.values.Reset()
|
||||
s._pendingEvent = nil
|
||||
s._lastEvent = nil
|
||||
s.valueGlobal = newJsGlobal(s.config)
|
||||
s._nextCallbackTimeoutID = 1
|
||||
s._scheduledTimeouts = map[uint32]chan bool{}
|
||||
}
|
||||
|
||||
func toInt64(arg interface{}) int64 {
|
||||
|
||||
@@ -3,7 +3,6 @@ package gojs
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/custom"
|
||||
@@ -215,10 +214,6 @@ func valueNew(ctx context.Context, mod api.Module, stack goos.Stack) {
|
||||
result := &object{properties: map[string]interface{}{}}
|
||||
res = storeValue(ctx, result)
|
||||
ok = true
|
||||
case goos.RefHttpHeadersConstructor:
|
||||
result := &headers{headers: http.Header{}}
|
||||
res = storeValue(ctx, result)
|
||||
ok = true
|
||||
case goos.RefJsDateConstructor:
|
||||
res = goos.RefJsDate
|
||||
ok = true
|
||||
|
||||
32
internal/gojs/testdata/http/main.go
vendored
32
internal/gojs/testdata/http/main.go
vendored
@@ -1,32 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
url := os.Getenv("BASE_URL")
|
||||
res, err := http.Get(url + "/error")
|
||||
if err == nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
fmt.Println(err)
|
||||
|
||||
res, err = http.Post(url, "text/plain", io.NopCloser(strings.NewReader("ice cream")))
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
fmt.Println(res.Header.Get("Custom"))
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
3
internal/gojs/testdata/main.go
vendored
3
internal/gojs/testdata/main.go
vendored
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/fs"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/gc"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/goroutine"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/http"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/mem"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/process"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/stdio"
|
||||
@@ -31,8 +30,6 @@ func main() {
|
||||
fs.Main()
|
||||
case "gc":
|
||||
gc.Main()
|
||||
case "http":
|
||||
http.Main()
|
||||
case "goroutine":
|
||||
goroutine.Main()
|
||||
case "mem":
|
||||
|
||||
@@ -19,20 +19,21 @@ func Test_time(t *testing.T) {
|
||||
|
||||
stdout, stderr, err := compileAndRun(loggingCtx, "time", defaultConfig)
|
||||
|
||||
require.EqualError(t, err, `module closed with exit_code(0)`)
|
||||
require.Zero(t, stderr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `Local
|
||||
1ms
|
||||
`, stdout)
|
||||
|
||||
// To avoid multiple similar assertions, just check three functions we
|
||||
// expect were called.
|
||||
require.Contains(t, log.String(), `==> go.runtime.nanotime1()
|
||||
logString := logString(log)
|
||||
require.Contains(t, logString, `==> go.runtime.nanotime1()
|
||||
<== (nsec=0)`)
|
||||
require.Contains(t, log.String(), `==> go.runtime.walltime()
|
||||
require.Contains(t, logString, `==> go.runtime.walltime()
|
||||
<== (sec=1640995200,nsec=0)
|
||||
`)
|
||||
require.Contains(t, log.String(), `==> go.syscall/js.valueCall(Date.getTimezoneOffset())
|
||||
require.Contains(t, logString, `==> go.syscall/js.valueCall(Date.getTimezoneOffset())
|
||||
<== (tz=0)
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func Test_Values(t *testing.T) {
|
||||
err := require.CapturePanic(func() {
|
||||
_ = vs.Get(goos.NextID)
|
||||
})
|
||||
require.EqualError(t, err, "id 18 is out of range 0")
|
||||
require.Contains(t, err.Error(), "is out of range 0")
|
||||
|
||||
v1 := "foo"
|
||||
id1 := vs.Increment(v1)
|
||||
@@ -43,7 +43,7 @@ func Test_Values(t *testing.T) {
|
||||
err = require.CapturePanic(func() {
|
||||
_ = vs.Get(id1)
|
||||
})
|
||||
require.EqualError(t, err, "value for 18 was nil")
|
||||
require.Contains(t, err.Error(), "was nil")
|
||||
|
||||
// Since the ID is no longer in use, we should be able to revive it.
|
||||
require.Equal(t, id1, vs.Increment(v1))
|
||||
|
||||
Reference in New Issue
Block a user