diff --git a/.golangci.yml b/.golangci.yml index 713dea4b..6b71a25f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,7 +5,3 @@ issues: - pkg/worker - cmd/webworker - web - exclude-rules: - - path: 'internal/gowasm/(.+)\.go' - linters: - - typecheck diff --git a/HACKING.md b/HACKING.md index 565f11a7..ef62a5ff 100644 --- a/HACKING.md +++ b/HACKING.md @@ -51,7 +51,6 @@ WASM Go programs are located in [`cmd/wasm`](./cmd/wasm/) directory. | Package | Description | | ---------- | --------------------------------------------------------------------------------------------------------------------------- | | `analyzer` | Very simple Go syntax checker. | -| `go-repl` | Deprecated tool with [Yaegi](https://github.com/traefik/yaegi) Go interpreter. Used to run Go programs without compilation. | ### Tools & Scripts diff --git a/build.mk b/build.mk index 6cafb820..000a9050 100644 --- a/build.mk +++ b/build.mk @@ -70,12 +70,8 @@ wasm_exec.js: analyzer.wasm: $(call build_wasm_worker,analyzer) -.PHONY:go-repl.wasm -go-repl.wasm: - $(call build_wasm_worker,go-repl) - .PHONY:wasm -wasm: wasm_exec.js analyzer.wasm go-repl.wasm +wasm: wasm_exec.js analyzer.wasm .PHONY: build build: check-go check-yarn clean preinstall gen collect-meta build-server wasm pkg-index build-ui diff --git a/build/Dockerfile b/build/Dockerfile index ca21dce0..49dcab4b 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -43,11 +43,6 @@ RUN echo "Building server with version $APP_VERSION" && \ -buildvcs=false \ -ldflags "-s -w" \ -trimpath \ - -o ./go-repl@$WASM_API_VER.wasm ./cmd/wasm/go-repl && \ - GOOS=js GOARCH=wasm go build \ - -buildvcs=false \ - -ldflags "-s -w" \ - -trimpath \ -o ./analyzer@$WASM_API_VER.wasm ./cmd/wasm/analyzer && \ cp $(go env GOROOT)/misc/wasm/wasm_exec.js ./wasm_exec@v2.js && \ cp $(go env GOROOT)/misc/wasm/wasm_exec.js ./wasm_exec.js && \ diff --git a/build/release.dockerfile b/build/release.dockerfile index c0d066ce..b045c787 100644 --- a/build/release.dockerfile +++ b/build/release.dockerfile @@ -26,11 +26,6 @@ COPY go.sum . ## wasm_exec.js files are left for backwards compatibility for old clients. RUN echo "Building server with version $APP_VERSION" && \ go build -o server -ldflags="-X 'main.Version=$APP_VERSION'" ./cmd/playground && \ - GOOS=js GOARCH=wasm go build \ - -buildvcs=false \ - -ldflags "-s -w" \ - -trimpath \ - -o ./go-repl@$WASM_API_VER.wasm ./cmd/wasm/go-repl && \ GOOS=js GOARCH=wasm go build \ -buildvcs=false \ -ldflags "-s -w" \ diff --git a/cmd/wasm/go-repl/main.go b/cmd/wasm/go-repl/main.go deleted file mode 100644 index f83fb4f7..00000000 --- a/cmd/wasm/go-repl/main.go +++ /dev/null @@ -1,37 +0,0 @@ -//go:build js - -// -// "go-repl" is a self-contained Go REPL with package downloader to be run in web browser as WASM binary. -// DEPRECATED: Yaegi interpreter is removed in #348. -// - -package main - -import ( - "github.com/x1unix/go-playground/internal/gorepl" - "github.com/x1unix/go-playground/internal/gorepl/uihost" - "github.com/x1unix/go-playground/internal/gowasm" - "github.com/x1unix/go-playground/internal/gowasm/browserfs" - "github.com/x1unix/go-playground/internal/gowasm/packagedb" - "github.com/x1unix/go-playground/pkg/goproxy" -) - -func main() { - // Go memory ballast, 10MiB - _ = make([]byte, 10<<20) - - worker := gowasm.NewWorker() - - vendorFS := browserfs.NewFS() - pkgIndex := packagedb.NewPackageIndex() - client := goproxy.NewClientFromEnv() - pmObserver := uihost.NewPackageDownloadObserver() - pmObserver.Start(worker.Context()) - - runner := gorepl.NewRunner(vendorFS, pkgIndex, client, pmObserver) - handler := gorepl.NewHandler(client, runner) - worker.Export("runProgram", handler.HandleRunProgram) - worker.Export("terminateProgram", handler.HandleTerminateProgram) - worker.Export("updateGoProxyAddress", handler.HandleUpdateGoProxyAddress) - worker.Run() -} diff --git a/internal/gorepl/errors.go b/internal/gorepl/errors.go deleted file mode 100644 index 0a89f0d3..00000000 --- a/internal/gorepl/errors.go +++ /dev/null @@ -1,27 +0,0 @@ -package gorepl - -import "fmt" - -type BuildError struct { - msg string - err error -} - -func newBuildError(err error, msg string) BuildError { - return BuildError{ - msg: msg, - err: err, - } -} - -func (err BuildError) Error() string { - if len(err.msg) == 0 { - return err.err.Error() - } - - return fmt.Sprint(err.msg, ": ", err.err) -} - -func (err BuildError) Unwrap() error { - return err.err -} diff --git a/internal/gorepl/handler_js.go b/internal/gorepl/handler_js.go deleted file mode 100644 index fc6776fe..00000000 --- a/internal/gorepl/handler_js.go +++ /dev/null @@ -1,148 +0,0 @@ -package gorepl - -import ( - "context" - "errors" - "fmt" - "runtime/debug" - "sync" - "sync/atomic" - "syscall/js" - - "github.com/traefik/yaegi/interp" - "github.com/x1unix/go-playground/internal/gorepl/uihost" - "github.com/x1unix/go-playground/internal/gowasm" - "github.com/x1unix/go-playground/internal/gowasm/wlog" - "github.com/x1unix/go-playground/pkg/goproxy" -) - -// Handler handles requests from WebAssembly host (UI) to eval Go code. -type Handler struct { - runner *Runner - client *goproxy.Client - - lock sync.Mutex - cancelFn context.CancelFunc - isRunning atomic.Bool -} - -func NewHandler(c *goproxy.Client, r *Runner) *Handler { - return &Handler{ - runner: r, - client: c, - } -} - -// HandleRunProgram handles Go core run command from a client. -// -// Only one evaluation at a time is available. -// -// First argument should be a buffer size. -// Second argument should be an instance of Uint8Array or Uint8ClampedArray. -func (h *Handler) HandleRunProgram(ctx context.Context, args []js.Value) (any, error) { - if len(args) < 2 { - return nil, errors.New("missing argument. Buffer size and Uint8Array are required") - } - - if t := args[0].Type(); t != js.TypeNumber { - return nil, fmt.Errorf("first argument should be a number, got %q", t) - } - - strSize := args[0].Int() - uint8ArrRef := args[1] - - if t := uint8ArrRef.Type(); !uint8ArrRef.Truthy() || t != js.TypeObject { - return nil, fmt.Errorf("second argument should be Uint8Array, got %q", t) - } - - h.lock.Lock() - defer h.lock.Unlock() - if h.isRunning.Load() { - return nil, errors.New("another program is already running") - } - - code := make([]byte, strSize) - n, err := gowasm.CopyBytesToGo(code, uint8ArrRef) - if err != nil { - return nil, err - } - - if n != strSize { - wlog.Printf("Warning: expected %d bytes but got %d", strSize, n) - } - - var programCtx context.Context - programCtx, h.cancelFn = context.WithCancel(ctx) - h.isRunning.Store(true) - go h.runProgram(programCtx, code[:n]) - - return nil, nil -} - -// HandleTerminateProgram handles program terminate request. -func (h *Handler) HandleTerminateProgram(_ context.Context, _ []js.Value) (any, error) { - h.lock.Lock() - defer h.lock.Unlock() - - if !h.isRunning.Load() || h.cancelFn == nil { - return nil, nil - } - - h.cancelFn() - return nil, nil -} - -// HandleUpdateGoProxyAddress handles Go proxy URL change requests. -func (h *Handler) HandleUpdateGoProxyAddress(_ context.Context, args []js.Value) (any, error) { - if len(args) == 0 { - return nil, errors.New("missing argument") - } - - if t := args[0].Type(); t != js.TypeString { - return nil, fmt.Errorf("invalid argument type %q", t) - } - - newUrl := args[0].String() - wlog.Println("updated Go module proxy URL:", newUrl) - h.client.SetBaseURL(newUrl) - return nil, nil -} - -func (h *Handler) runProgram(ctx context.Context, src []byte) { - defer func() { - if r := recover(); r != nil { - uihost.ReportEvalError( - fmt.Errorf("Go WebAssembly worker panicked. Please report this issue!\n\n"+ - "panic: %s\nStacktrace:\n%s", r, debug.Stack()), - ) - } - }() - defer h.releaseLock() - - uihost.ReportEvalState(uihost.EvalStateBegin) - err := h.runner.Evaluate(ctx, src) - if err == nil { - uihost.ReportEvalState(uihost.EvalStateFinish) - return - } - - if errors.Is(err, context.Canceled) { - uihost.ReportEvalState(uihost.EvalStateFinish) - return - } - - var p interp.Panic - if errors.As(err, &p) { - uihost.ReportEvalPanic(p.Value, p.Stack) - return - } - - uihost.ReportEvalError(err) -} - -func (h *Handler) releaseLock() { - h.lock.Lock() - defer h.lock.Unlock() - h.isRunning.Store(false) - h.cancelFn = nil -} diff --git a/internal/gorepl/pacman/builtin_gen.go b/internal/gorepl/pacman/builtin_gen.go deleted file mode 100644 index 656be7ad..00000000 --- a/internal/gorepl/pacman/builtin_gen.go +++ /dev/null @@ -1,205 +0,0 @@ -package pacman - -// THIS FILE WAS GENERATED BY collect-builtin TOOL, DO NOT EDIT! -// -// Generated at: 2023-04-07 15:03:21.566451 +0200 CEST m=+0.007305042 -// Go version: go1.19.4 - -// List of built-in Go packages -var builtinPackages = map[string]struct{}{ - "archive": {}, - "archive/tar": {}, - "archive/zip": {}, - "bufio": {}, - "builtin": {}, - "bytes": {}, - "cmd": {}, - "cmd/addr2line": {}, - "cmd/api": {}, - "cmd/asm": {}, - "cmd/buildid": {}, - "cmd/cgo": {}, - "cmd/compile": {}, - "cmd/cover": {}, - "cmd/dist": {}, - "cmd/doc": {}, - "cmd/fix": {}, - "cmd/go": {}, - "cmd/gofmt": {}, - "cmd/link": {}, - "cmd/nm": {}, - "cmd/objdump": {}, - "cmd/pack": {}, - "cmd/pprof": {}, - "cmd/test2json": {}, - "cmd/trace": {}, - "cmd/trace/static": {}, - "cmd/vet": {}, - "compress": {}, - "compress/bzip2": {}, - "compress/flate": {}, - "compress/gzip": {}, - "compress/lzw": {}, - "compress/zlib": {}, - "container": {}, - "container/heap": {}, - "container/list": {}, - "container/ring": {}, - "context": {}, - "crypto": {}, - "crypto/aes": {}, - "crypto/boring": {}, - "crypto/cipher": {}, - "crypto/des": {}, - "crypto/dsa": {}, - "crypto/ecdsa": {}, - "crypto/ed25519": {}, - "crypto/elliptic": {}, - "crypto/hmac": {}, - "crypto/md5": {}, - "crypto/rand": {}, - "crypto/rc4": {}, - "crypto/rsa": {}, - "crypto/sha1": {}, - "crypto/sha256": {}, - "crypto/sha512": {}, - "crypto/subtle": {}, - "crypto/tls": {}, - "crypto/tls/fipsonly": {}, - "crypto/x509": {}, - "crypto/x509/pkix": {}, - "database": {}, - "database/sql": {}, - "database/sql/driver": {}, - "debug": {}, - "debug/buildinfo": {}, - "debug/dwarf": {}, - "debug/elf": {}, - "debug/gosym": {}, - "debug/macho": {}, - "debug/pe": {}, - "debug/plan9obj": {}, - "embed": {}, - "encoding": {}, - "encoding/ascii85": {}, - "encoding/asn1": {}, - "encoding/base32": {}, - "encoding/base64": {}, - "encoding/binary": {}, - "encoding/csv": {}, - "encoding/gob": {}, - "encoding/hex": {}, - "encoding/json": {}, - "encoding/pem": {}, - "encoding/xml": {}, - "errors": {}, - "expvar": {}, - "flag": {}, - "fmt": {}, - "go": {}, - "go/ast": {}, - "go/build": {}, - "go/build/constraint": {}, - "go/constant": {}, - "go/doc": {}, - "go/doc/comment": {}, - "go/format": {}, - "go/importer": {}, - "go/parser": {}, - "go/printer": {}, - "go/scanner": {}, - "go/token": {}, - "go/types": {}, - "hash": {}, - "hash/adler32": {}, - "hash/crc32": {}, - "hash/crc64": {}, - "hash/fnv": {}, - "hash/maphash": {}, - "html": {}, - "html/template": {}, - "image": {}, - "image/color": {}, - "image/color/palette": {}, - "image/draw": {}, - "image/gif": {}, - "image/jpeg": {}, - "image/png": {}, - "index": {}, - "index/suffixarray": {}, - "io": {}, - "io/fs": {}, - "io/ioutil": {}, - "log": {}, - "log/syslog": {}, - "math": {}, - "math/big": {}, - "math/bits": {}, - "math/cmplx": {}, - "math/rand": {}, - "mime": {}, - "mime/multipart": {}, - "mime/quotedprintable": {}, - "net": {}, - "net/http": {}, - "net/http/cgi": {}, - "net/http/cookiejar": {}, - "net/http/fcgi": {}, - "net/http/httptest": {}, - "net/http/httptrace": {}, - "net/http/httputil": {}, - "net/http/pprof": {}, - "net/mail": {}, - "net/netip": {}, - "net/rpc": {}, - "net/rpc/jsonrpc": {}, - "net/smtp": {}, - "net/textproto": {}, - "net/url": {}, - "os": {}, - "os/exec": {}, - "os/signal": {}, - "os/user": {}, - "path": {}, - "path/filepath": {}, - "plugin": {}, - "reflect": {}, - "regexp": {}, - "regexp/syntax": {}, - "runtime": {}, - "runtime/asan": {}, - "runtime/cgo": {}, - "runtime/debug": {}, - "runtime/metrics": {}, - "runtime/msan": {}, - "runtime/pprof": {}, - "runtime/race": {}, - "runtime/trace": {}, - "sort": {}, - "strconv": {}, - "strings": {}, - "sync": {}, - "sync/atomic": {}, - "syscall": {}, - "syscall/js": {}, - "testing": {}, - "testing/fstest": {}, - "testing/iotest": {}, - "testing/quick": {}, - "text": {}, - "text/scanner": {}, - "text/tabwriter": {}, - "text/template": {}, - "text/template/parse": {}, - "time": {}, - "time/tzdata": {}, - "unicode": {}, - "unicode/utf16": {}, - "unicode/utf8": {}, - "unsafe": {}, -} - -func isStandardGoPackage(pkgName string) bool { - _, ok := builtinPackages[pkgName] - return ok -} diff --git a/internal/gorepl/pacman/download.go b/internal/gorepl/pacman/download.go deleted file mode 100644 index 70bdd128..00000000 --- a/internal/gorepl/pacman/download.go +++ /dev/null @@ -1,22 +0,0 @@ -package pacman - -type downloadCallback = func(p Progress) -type progressReader struct { - total int64 - downloaded int64 - cb downloadCallback -} - -func newProgressReader(total int64, cb downloadCallback) *progressReader { - return &progressReader{total: total, cb: cb} -} - -func (r *progressReader) Write(p []byte) (int, error) { - n := len(p) - r.downloaded += int64(n) - r.cb(Progress{ - Total: int(r.total), - Current: int(r.downloaded), - }) - return n, nil -} diff --git a/internal/gorepl/pacman/file.go b/internal/gorepl/pacman/file.go deleted file mode 100644 index 0eee067f..00000000 --- a/internal/gorepl/pacman/file.go +++ /dev/null @@ -1,59 +0,0 @@ -package pacman - -import ( - "archive/zip" - "io" - "io/fs" - "sync" -) - -var _ fs.File = (*zipFSFile)(nil) - -// zipFSFile is fs.File adapter for zip.File -type zipFSFile struct { - file *zip.File - - lock sync.Mutex - reader io.ReadCloser -} - -func newZipFSFile(src *zip.File) *zipFSFile { - return &zipFSFile{ - file: src, - } -} - -func (z *zipFSFile) Stat() (fs.FileInfo, error) { - return z.file.FileInfo(), nil -} - -func (z *zipFSFile) Read(dst []byte) (int, error) { - z.lock.Lock() - defer z.lock.Unlock() - - if z.reader == nil { - var err error - z.reader, err = z.file.Open() - if err != nil { - return 0, err - } - } - - return z.reader.Read(dst) -} - -func (z *zipFSFile) Close() error { - z.lock.Lock() - defer z.lock.Unlock() - if z.reader == nil { - return &fs.PathError{ - Op: "close", - Path: z.file.Name, - Err: fs.ErrClosed, - } - } - - err := z.reader.Close() - z.reader = nil - return err -} diff --git a/internal/gorepl/pacman/gopathcache.go b/internal/gorepl/pacman/gopathcache.go deleted file mode 100644 index 5d345fa2..00000000 --- a/internal/gorepl/pacman/gopathcache.go +++ /dev/null @@ -1,110 +0,0 @@ -package pacman - -import ( - "fmt" - "io" - "io/fs" - "path" - "strings" - - "golang.org/x/mod/module" -) - -var _ PackageCache = (*SimpleFSCache)(nil) - -const ( - defaultFileMode = 0644 - defaultDirMode = 0755 -) - -type WritableFS interface { - // Stat returns file or directory info - Stat(name string) (fs.FileInfo, error) - - // WriteFile creates a new file or replaces file contents. - WriteFile(name string, data io.Reader, mode fs.FileMode) error - - // Mkdir creates a directory named path, along with any necessary - // parents, and returns nil, or else returns an error. - Mkdir(name string, mode fs.FileMode) error - - // Remove removes specified item and all children files. - Remove(name string) error -} - -type PackageIndex interface { - // LookupPackage retrieve a package from index. - // - // Returns fs.ErrNotExists if package not found. - LookupPackage(pkgName string) (*module.Version, error) - - // RegisterPackage adds package to the index. - RegisterPackage(pkg *module.Version) error - - // RemovePackage removes package from index. - RemovePackage(pkg *module.Version) error -} - -// SimpleFSCache is a simple gopath-like package cache. -// -// Doesn't support package versioning. -type SimpleFSCache struct { - location string - - fs WritableFS - index PackageIndex -} - -func NewSimpleFSCache(location string, fs WritableFS, index PackageIndex) *SimpleFSCache { - return &SimpleFSCache{location: location, fs: fs, index: index} -} - -func (c SimpleFSCache) TestImportPath(importPath string) error { - p := path.Join(c.location, importPath) - _, err := c.fs.Stat(p) - if err != nil { - return err - } - - return nil -} - -func (c SimpleFSCache) LookupPackage(pkgName string) (*module.Version, error) { - return c.index.LookupPackage(pkgName) -} - -func (c SimpleFSCache) RegisterPackage(pkg *module.Version) error { - return c.index.RegisterPackage(pkg) -} - -func (c SimpleFSCache) WritePackageFile(pkg *module.Version, filePath string, f fs.File) error { - // Keep packages in the same structure as GOPATH without version suffix. - // Same packages of different versions are not supported. - realPath := path.Join(c.location, removeVersionFromPath(pkg, filePath)) - - if err := c.fs.Mkdir(path.Dir(realPath), defaultDirMode); err != nil { - return err - } - - return c.fs.WriteFile(realPath, f, defaultFileMode) -} - -func (c SimpleFSCache) RemovePackage(pkg *module.Version) error { - if err := c.index.RemovePackage(pkg); err != nil { - return fmt.Errorf("failed to remove package %s from index: %w", pkg, err) - } - - pkgPath := path.Join(c.location, pkg.Path) - if err := c.fs.Remove(pkgPath); err != nil { - return fmt.Errorf("failed to remove package %s from storage: %w", pkgPath, err) - } - - return nil -} - -// removeVersionFromPath removes package version segment from path. -// -// Example: foo/bar@v1.2.3/baz -> foo/bar/baz -func removeVersionFromPath(module *module.Version, fpath string) string { - return strings.Replace(fpath, "@"+module.Version, "", 1) -} diff --git a/internal/gorepl/pacman/ignore.go b/internal/gorepl/pacman/ignore.go deleted file mode 100644 index 4a40f492..00000000 --- a/internal/gorepl/pacman/ignore.go +++ /dev/null @@ -1,53 +0,0 @@ -package pacman - -import ( - "path" - "strings" -) - -var ( - ignoredPaths = []string{ - "/testdata/", - ".github", - } - - ignoredSuffixes = []string{ - "_test.go", - ".md", - } - - ignoredNames = []string{ - "README", - "LICENSE", - "Makefile", - } -) - -// shouldSkipFile checks if file should be discarded -func shouldSkipFile(filename string) bool { - for _, segment := range ignoredPaths { - if strings.Contains(filename, segment) { - return true - } - } - - for _, segment := range ignoredSuffixes { - if strings.HasSuffix(filename, segment) { - return true - } - } - - baseName := path.Base(filename) - if baseName[0] == '.' { - // Skip hidden files - return true - } - - for _, segment := range ignoredNames { - if strings.HasPrefix(baseName, segment) { - return true - } - } - - return false -} diff --git a/internal/gorepl/pacman/imports.go b/internal/gorepl/pacman/imports.go deleted file mode 100644 index 5ae0de2e..00000000 --- a/internal/gorepl/pacman/imports.go +++ /dev/null @@ -1,42 +0,0 @@ -package pacman - -import ( - "go/parser" - "go/token" - "strconv" -) - -//go:generate go run ../../../tools/collect-builtin -out=builtin_gen.go - -// ParseFileImports returns imported packages from Go source text -func ParseFileImports(filename, moduleUrl string, code []byte) ([]string, error) { - fset := token.NewFileSet() - p, err := parser.ParseFile(fset, filename, code, parser.ImportsOnly) - if err != nil { - return nil, err - } - - if len(p.Imports) == 0 { - return nil, nil - } - - imports := make([]string, 0, len(p.Imports)) - for _, importDecl := range p.Imports { - importPath, err := strconv.Unquote(importDecl.Path.Value) - if err != nil { - importPath = importDecl.Path.Value - } - - if isStandardGoPackage(importPath) { - continue - } - - if isSelfModulePackage(moduleUrl, importPath) { - continue - } - - imports = append(imports, importPath) - } - - return imports, nil -} diff --git a/internal/gorepl/pacman/manager.go b/internal/gorepl/pacman/manager.go deleted file mode 100644 index de866549..00000000 --- a/internal/gorepl/pacman/manager.go +++ /dev/null @@ -1,388 +0,0 @@ -package pacman - -import ( - "archive/zip" - "bytes" - "context" - "errors" - "fmt" - "io" - "io/fs" - "os" - "regexp" - "strings" - - "github.com/samber/lo" - "github.com/x1unix/go-playground/internal/gowasm/wlog" - "github.com/x1unix/go-playground/internal/util/buffutil" - "github.com/x1unix/go-playground/internal/util/syncx" - "github.com/x1unix/go-playground/pkg/goproxy" - "golang.org/x/mod/modfile" - "golang.org/x/mod/module" - "golang.org/x/mod/semver" -) - -var buffPool = buffutil.NewBufferPool() - -var ( - versionBranchRegEx = regexp.MustCompile(`^v\d(.\d+)?(.\d+)?$`) - goPkgInPackage = regexp.MustCompile(`^[\w\d]+\.v\d(.\d+)?(.\d+)?$`) -) - -type PackageCache interface { - // TestImportPath tests if specified import path is accessible in cache. - // - // Returns fs.ErrNotExists in case of cache miss. - TestImportPath(importPath string) error - - // LookupPackage checks if package is cached and returns its version. - // - // Returns fs.ErrNotExists if package is not cached. - LookupPackage(pkgName string) (*module.Version, error) - - // RegisterPackage registers package in index - RegisterPackage(pkg *module.Version) error - - // WritePackageFile writes a single file of package to the storage. - WritePackageFile(pkg *module.Version, filePath string, f fs.File) error - - // RemovePackage removes all cached package files. - RemovePackage(pkg *module.Version) error -} - -type dependenciesJar map[string]*module.Version - -func (j dependenciesJar) compareAndStore(m *module.Version) { - oldVer, ok := j[m.Path] - if !ok { - j[m.Path] = m - return - } - - diff := semver.Compare(m.Version, oldVer.Version) - if diff != -1 { - j[m.Path] = m - } -} - -// PackageManager checks and stores program dependencies. -type PackageManager struct { - goModProxy *goproxy.Client - cachedPaths syncx.Map[string, *module.Version] - pkgCache PackageCache - progressReporter PMProgressObserver -} - -func NewPackageManager(modProxy *goproxy.Client, pkgCache PackageCache) *PackageManager { - return &PackageManager{ - pkgCache: pkgCache, - goModProxy: modProxy, - cachedPaths: syncx.NewMap[string, *module.Version](), - progressReporter: noopProgressObserver{}, - } -} - -// SetProgressObserver sets package manager event listener -func (mgr *PackageManager) SetProgressObserver(pm PMProgressObserver) { - mgr.progressReporter = pm -} - -// CheckDependencies checks if import paths are solvable and downloads dependencies if necessary. -func (mgr *PackageManager) CheckDependencies(ctx context.Context, importPaths []string) (err error) { - newProjectPackages := lo.Filter(importPaths, func(item string, _ int) bool { - if mgr.cachedPaths.Has(item) { - return false - } - - err := mgr.pkgCache.TestImportPath(item) - if err != nil { - if !os.IsNotExist(fs.ErrNotExist) { - wlog.Printf("Warning: can't stat %q: %s", item, err) - } - return true - } - - return false - }) - - wlog.Println("New packages:", len(newProjectPackages)) - if len(newProjectPackages) == 0 { - return nil - } - - defer mgr.progressReporter.DependencyCheckFinish(err) - mgr.progressReporter.DependencyResolveStart(len(newProjectPackages)) - - modsJar := make(dependenciesJar, len(newProjectPackages)*2) - for _, pkg := range newProjectPackages { - mgr.progressReporter.PackageSearchStart(pkg) - if err := mgr.requestPackageByImportPath(ctx, pkg, modsJar); err != nil { - return err - } - } - - for pkgName, ver := range modsJar { - // Some indirect dependencies might require update. - // Update them if necessary. - if !mgr.shouldUpdatePackage(ver) { - wlog.Printf("Package %s is already up to date, skip download...", ver) - continue - } - - if err := mgr.downloadModule(ctx, ver); err != nil { - return err - } - - mgr.cachedPaths.Put(pkgName, ver) - } - - return nil -} - -// shouldUpdatePackage checks if cached package version is outdated. -// Used to check the status of cached indirect dependencies (deps of deps). -func (mgr *PackageManager) shouldUpdatePackage(pkg *module.Version) bool { - gotVer, err := mgr.pkgCache.LookupPackage(pkg.Path) - if errors.Is(err, fs.ErrNotExist) { - return true - } - if err != nil { - wlog.Printf("LookupPackage failed for %q: %v", pkg.Path, err) - return true - } - - return isPackageOutdated(pkg, gotVer) -} - -func (mgr *PackageManager) requestPackageByImportPath(ctx context.Context, importPath string, out dependenciesJar) error { - wlog.Printf("resolving import %q...", importPath) - pkgInfo, err := mgr.findPackageByImport(ctx, importPath) - if err != nil { - return err - } - - return mgr.requestModule(ctx, pkgInfo, out) -} - -func (mgr *PackageManager) requestModule(ctx context.Context, pkgInfo *module.Version, out dependenciesJar) error { - wlog.Printf("Finding module %s...", pkgInfo) - deps, err := mgr.getModuleDependencies(ctx, pkgInfo, out) - if err != nil { - return err - } - - wlog.Printf("Module %s has %d dependencies", pkgInfo, len(deps)) - for _, dep := range deps { - out.compareAndStore(dep) - } - - out.compareAndStore(pkgInfo) - return nil -} - -func (mgr *PackageManager) downloadModule(ctx context.Context, pkgInfo *module.Version) error { - r, err := mgr.goModProxy.GetModuleSource(ctx, pkgInfo.Path, pkgInfo.Version) - if err != nil { - return err - } - - defer r.Close() - - wlog.Debugf("Downloading %s...", pkgInfo) - - buff := buffPool.Get() - buff.Grow(int(r.Size)) - defer buff.Close() - - callbackReader := newProgressReader(r.Size, func(p Progress) { - mgr.progressReporter.PackageDownload(pkgInfo, p) - }) - - if _, err := io.Copy(buff, io.TeeReader(r, callbackReader)); err != nil { - return fmt.Errorf("failed to download module %s: %w", pkgInfo, err) - } - - zr, err := zip.NewReader(bytes.NewReader(buff.Bytes()), r.Size) - if err != nil { - return fmt.Errorf("failed to open module archive %s: %w", pkgInfo, err) - } - - for i, file := range zr.File { - // TODO: ignore non-go files - if shouldSkipFile(file.Name) { - wlog.Debugf("File %s is ignored, skip", file.Name) - continue - } - - wlog.Debugf("Extracting %s...", file.Name) - mgr.progressReporter.PackageExtract(pkgInfo, Progress{ - Total: len(zr.File), - Current: i + 1, - }) - - if err := mgr.pkgCache.WritePackageFile(pkgInfo, file.Name, newZipFSFile(file)); err != nil { - // clean corrupted installation - wlog.Debugf("Failed to write file %q, removing package %q (err: %s)", - file.Name, pkgInfo, err) - _ = mgr.pkgCache.RemovePackage(pkgInfo) - return err - } - } - - if err := mgr.pkgCache.RegisterPackage(pkgInfo); err != nil { - return fmt.Errorf("failed to register package %s: %w", pkgInfo, err) - } - - return nil -} - -func (mgr *PackageManager) getModuleDependencies(ctx context.Context, mod *module.Version, out dependenciesJar) ([]*module.Version, error) { - // Module proxy still returns empty go.mod even if package doesn't have it. - modFileContents, err := mgr.goModProxy.GetModuleFile(ctx, mod.Path, mod.Version) - if err != nil { - return nil, err - } - - modFile, err := modfile.ParseLax("go.mod", modFileContents, nil) - if err != nil { - return nil, fmt.Errorf("failed to parse go.mod file of package %s: %w", mod, err) - } - - // TODO: handle old packages without "go.mod" - if len(modFile.Require) == 0 { - if modFile.Go == nil { - wlog.Printf("Warning: package %s doesn't have go.mod dependencies, pre-go.mod packages are not supported yet!", mod) - } - - return nil, nil - } - - // TODO: handle replaces - return lo.Map(modFile.Require, func(item *modfile.Require, _ int) *module.Version { - return &module.Version{ - Path: item.Mod.Path, - Version: item.Mod.Version, - } - }), nil -} - -func (mgr *PackageManager) findPackageByImport(ctx context.Context, pkgUrl string) (*module.Version, error) { - // Try to guess package import path - importPath, ok := guessPackageNameFromImport(pkgUrl) - if ok { - wlog.Printf("Detected package %q from import path, trying to request...", importPath) - verInfo, err := mgr.goModProxy.GetLatestVersion(ctx, importPath) - if err != nil { - return nil, err - } - - return &module.Version{ - Path: importPath, - Version: verInfo.Version, - }, nil - } - - // Otherwise - try to locate the longest valid queryable package name - // in the same manner as "go get" does. - segments := strings.Split(pkgUrl, "/") - var ( - verInfo *goproxy.VersionInfo - err error - ) - for i := len(segments) - 1; i > 0; i-- { - pkgUrl := strings.Join(segments[:i+1], "/") - wlog.Printf("Trying to find %q...", pkgUrl) - verInfo, err = mgr.goModProxy.GetLatestVersion(ctx, pkgUrl) - if err == nil { - return &module.Version{ - Path: pkgUrl, - Version: verInfo.Version, - }, nil - } - } - - return nil, err -} - -func isSelfModulePackage(goModulePath, importUrl string) bool { - if goModulePath == "" { - return false - } - - return strings.HasPrefix(importUrl, goModulePath) -} - -// guessPackageNameFromImport tries to detect package URL from import path -// by checking if it's first section match popular hostnames like GitHub, Gitlab, etc. -// -// Used to speed-up package name lookup and reduce proxy.golang.org calls count. -// -// Returns package URL to request in case of success. -func guessPackageNameFromImport(importPath string) (string, bool) { - segments := strings.Split(importPath, "/") - if len(segments) < 2 { - return importPath, false - } - - switch segments[0] { - case "github.com", "gitlab.com": - // All GitHub and most of a public Gitlab repo packages - // contain username and project name + optional "v" version suffix. - // - // ^gitlab.com/username/package(/v[\d])?$ - if len(segments) < 3 { - // Invalid path, skip - return "", false - } - - if len(segments) < 4 { - // Import path is already correct, skip - return importPath, true - } - - cutCount := 3 - if versionBranchRegEx.MatchString(segments[3]) { - // Some packages put tag in package import path. - cutCount = 4 - } - - return strings.Join(segments[:cutCount], "/"), true - case "gopkg.in": - // gopkg.in imports might be in 2 forms: - // - gopkg.in/foo.v1 - // - gopkg.in/foo/bar.v1 - - if len(segments) == 2 { - return importPath, goPkgInPackage.MatchString(segments[1]) - } - - // Check short import - if goPkgInPackage.MatchString(segments[1]) { - return strings.Join(segments[:2], "/"), true - } - - if goPkgInPackage.MatchString(segments[2]) { - return strings.Join(segments[:3], "/"), true - } - - return importPath, false - case "golang.org": - if segments[1] != "x" { - return importPath, false - } - - // Most experimental packages are not nested - // See: https://pkg.go.dev/golang.org/x/exp - return strings.Join(segments[:3], "/"), true - - case "google.golang.org": - return strings.Join(segments[:2], "/"), true - } - - return importPath, false -} - -func isPackageOutdated(newPkg, oldPkg *module.Version) bool { - result := semver.Compare(newPkg.Version, oldPkg.Version) - return result != -1 -} diff --git a/internal/gorepl/pacman/manager_test.go b/internal/gorepl/pacman/manager_test.go deleted file mode 100644 index 2739148c..00000000 --- a/internal/gorepl/pacman/manager_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package pacman - -import ( - "context" - "net/http" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "github.com/x1unix/go-playground/pkg/goproxy" -) - -func TestPackageManager_CheckDependencies(t *testing.T) { - cwd, err := os.Getwd() - require.NoError(t, err) - - client := goproxy.NewClient(http.DefaultClient, "https://proxy.golang.org") - ctx := context.Background() - - testDir := filepath.Join(cwd, "testdata", "pkg") - pkgIndex := NewMemoryPackageIndex() - testFS := NewHostFS() - pkgCache := NewSimpleFSCache(testDir, testFS, pkgIndex) - - pkgmgr := NewPackageManager(client, pkgCache) - err = pkgmgr.CheckDependencies(ctx, []string{ - "go.uber.org/zap", - "golang.org/x/tools/internal/imports", - }) - - require.NoError(t, err) -} - -func TestGuessPackageNameFromImport(t *testing.T) { - cases := map[string]struct { - path string - expect string - }{ - "too short": { - path: "github.com", - }, - "unknown": { - path: "rsc.io/quote", - }, - "github - too short": { - path: "github.com/foobar", - }, - "github - already valid": { - path: "github.com/foo/bar", - expect: "github.com/foo/bar", - }, - "github - regular import": { - path: "github.com/foo/bar/pkg/internal/test", - expect: "github.com/foo/bar", - }, - "github - import with tag": { - path: "github.com/foo/bar/v2/pkg/internal/test", - expect: "github.com/foo/bar/v2", - }, - "gopkgin - short unchanged": { - path: "gopkg.in/check.v1", - expect: "gopkg.in/check.v1", - }, - "gopkgin - long unchanged": { - path: "gopkg.in/fatih/pool.v2", - expect: "gopkg.in/fatih/pool.v2", - }, - "gopkgin - short": { - path: "gopkg.in/check.v1/foobar/baz", - expect: "gopkg.in/check.v1", - }, - "gopkgin - long": { - path: "gopkg.in/fatih/pool.v2/foobar/baz", - expect: "gopkg.in/fatih/pool.v2", - }, - "golang-org - experimental unchanged": { - path: "golang.org/x/sys", - expect: "golang.org/x/sys", - }, - "golang-org - experimental": { - path: "golang.org/x/sys/windows", - expect: "golang.org/x/sys", - }, - "golang-org - unknown": { - path: "golang.org/foobar", - }, - "google-golang-org": { - path: "google.golang.org/api/foobar/baz", - expect: "google.golang.org/api", - }, - } - - for n, c := range cases { - t.Run(n, func(t *testing.T) { - got, ok := guessPackageNameFromImport(c.path) - if c.expect == "" { - require.False(t, ok) - return - } - - require.True(t, ok) - require.Equal(t, c.expect, got) - }) - } -} diff --git a/internal/gorepl/pacman/observer.go b/internal/gorepl/pacman/observer.go deleted file mode 100644 index 507c2221..00000000 --- a/internal/gorepl/pacman/observer.go +++ /dev/null @@ -1,48 +0,0 @@ -package pacman - -import "golang.org/x/mod/module" - -type Progress struct { - Total int - Current int -} - -// PMProgressObserver capture progress events from package manager -type PMProgressObserver interface { - // DependencyCheckFinish is fired when dependency check finished or stopped due to error. - DependencyCheckFinish(err error) - - // DependencyResolveStart is called at the beginning of dependency resolve process. - // - // Accepts required packages count. - DependencyResolveStart(packagesCount int) - - // PackageSearchStart is called when package manager starts searching for package. - PackageSearchStart(pkgName string) - - // PackageDownload called during package download progress - PackageDownload(pkg *module.Version, progress Progress) - - // PackageExtract called during package extraction progress - PackageExtract(pkg *module.Version, progress Progress) -} - -type noopProgressObserver struct{} - -func (o noopProgressObserver) DependencyCheckFinish(err error) { -} - -func (o noopProgressObserver) DependencyResolveStart(packagesCount int) { -} - -func (o noopProgressObserver) PackageSearchStart(pkgName string) { -} - -func (o noopProgressObserver) DependencyLookupFailed(pkgName string, err error) { -} - -func (o noopProgressObserver) PackageDownload(pkg *module.Version, progress Progress) { -} - -func (o noopProgressObserver) PackageExtract(pkg *module.Version, progress Progress) { -} diff --git a/internal/gorepl/pacman/testdata/.gitignore b/internal/gorepl/pacman/testdata/.gitignore deleted file mode 100644 index 5fff1d9c..00000000 --- a/internal/gorepl/pacman/testdata/.gitignore +++ /dev/null @@ -1 +0,0 @@ -pkg diff --git a/internal/gorepl/pacman/testing.go b/internal/gorepl/pacman/testing.go deleted file mode 100644 index 7a7cafbd..00000000 --- a/internal/gorepl/pacman/testing.go +++ /dev/null @@ -1,79 +0,0 @@ -//go:build !js - -package pacman - -import ( - "io" - "io/fs" - "os" - - "golang.org/x/mod/module" -) - -var ( - _ PackageIndex = (*MemoryPackageIndex)(nil) - _ WritableFS = (*HostFS)(nil) -) - -type MemoryPackageIndex struct { - pkgCache map[string]*module.Version -} - -func NewMemoryPackageIndex() *MemoryPackageIndex { - return &MemoryPackageIndex{ - pkgCache: make(map[string]*module.Version, 10), - } -} - -func (c *MemoryPackageIndex) RegisterPackage(pkg *module.Version) error { - c.pkgCache[pkg.Path] = pkg - return nil -} - -func (c *MemoryPackageIndex) LookupPackage(pkgName string) (*module.Version, error) { - pkg, ok := c.pkgCache[pkgName] - if !ok { - return nil, fs.ErrNotExist - } - - return pkg, nil -} - -func (c *MemoryPackageIndex) RemovePackage(pkg *module.Version) error { - delete(c.pkgCache, pkg.Path) - return nil -} - -type HostFS struct{} - -func NewHostFS() HostFS { - return HostFS{} -} - -func (w HostFS) Stat(name string) (fs.FileInfo, error) { - s, err := os.Stat(name) - if err != nil { - return nil, err - } - - return s, nil -} - -func (w HostFS) Mkdir(name string, mode fs.FileMode) error { - return os.MkdirAll(name, mode) -} - -func (w HostFS) Remove(name string) error { - return os.RemoveAll(name) -} - -func (w HostFS) WriteFile(name string, data io.Reader, mode fs.FileMode) error { - f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) - if err != nil { - return err - } - - defer f.Close() - _, err = io.Copy(f, data) - return err -} diff --git a/internal/gorepl/runner.go b/internal/gorepl/runner.go deleted file mode 100644 index 1813d6c5..00000000 --- a/internal/gorepl/runner.go +++ /dev/null @@ -1,115 +0,0 @@ -package gorepl - -import ( - "context" - "fmt" - "io/fs" - "os" - "path" - - "github.com/traefik/yaegi/interp" - "github.com/traefik/yaegi/stdlib" - "github.com/x1unix/go-playground/internal/gorepl/pacman" - "github.com/x1unix/go-playground/internal/gorepl/symbols" - "github.com/x1unix/go-playground/internal/gowasm" - "github.com/x1unix/go-playground/pkg/goproxy" -) - -type ReadWriteFS interface { - fs.ReadDirFS - pacman.WritableFS -} - -type Runner struct { - vendorFs ReadWriteFS - goPath string - pkgMgr *pacman.PackageManager - pmObserver pacman.PMProgressObserver -} - -func NewRunner(vendorFs ReadWriteFS, pkgIndex pacman.PackageIndex, client *goproxy.Client, pmObserver pacman.PMProgressObserver) *Runner { - goPath := getGoPath() - cache := pacman.NewSimpleFSCache( - path.Join(goPath, "src/vendor"), - vendorFs, - pkgIndex, - ) - - pkgMgr := pacman.NewPackageManager(client, cache) - pkgMgr.SetProgressObserver(pmObserver) - - return &Runner{ - vendorFs: vendorFs, - goPath: goPath, - pkgMgr: pkgMgr, - pmObserver: pmObserver, - } -} - -func (w *Runner) Evaluate(ctx context.Context, code []byte) error { - if err := w.checkNewImports(ctx, code); err != nil { - return err - } - - vm := interp.New(interp.Options{ - BuildTags: []string{ - "wasm", "js", - }, - Stderr: gowasm.Stderr, - Stdout: gowasm.Stdout, - GoPath: w.goPath, - SourcecodeFilesystem: w.vendorFs, - }) - if err := vm.Use(stdlib.Symbols); err != nil { - return newBuildError(err, "failed to load Go runtime") - } - - // Load additional symbols - if err := vm.Use(symbols.Symbols); err != nil { - return newBuildError(err, "failed to load syscall/js symbols") - } - - prog, err := vm.Compile(string(code)) - if err != nil { - return newBuildError(err, "") - } - - err = executeSafely(ctx, vm, prog) - if err != nil { - return err - } - - return nil -} - -func (w *Runner) checkNewImports(ctx context.Context, code []byte) error { - rootImports, err := pacman.ParseFileImports("main.go", "", code) - if err != nil { - return err - } - - return w.pkgMgr.CheckDependencies(ctx, rootImports) -} - -func getGoPath() string { - goPath, ok := os.LookupEnv("GOPATH") - if !ok { - return "/go" - } - return goPath -} - -func executeSafely(ctx context.Context, vm *interp.Interpreter, prog *interp.Program) (err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("panic during execution: %s", err) - } - }() - - _, err = vm.ExecuteWithContext(ctx, prog) - if err != nil { - return err - } - - return nil -} diff --git a/internal/gorepl/runner_test.go b/internal/gorepl/runner_test.go deleted file mode 100644 index 38949679..00000000 --- a/internal/gorepl/runner_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package gorepl - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "github.com/x1unix/go-playground/internal/gorepl/pacman" -) - -func TestParseFileImports(t *testing.T) { - want := []string{ - "go.uber.org/zap", - "golang.org/x/tools/internal/imports", - } - sample := readTestdata(t, "sample.go.txt") - - v, err := pacman.ParseFileImports("sample.go.txt", "", sample) - require.NoError(t, err) - require.Equal(t, want, v) -} - -func readTestdata(t *testing.T, name string) []byte { - t.Helper() - data, err := os.ReadFile(filepath.Join("testdata", name)) - require.NoError(t, err) - return data -} diff --git a/internal/gorepl/symbols/env-wrap.sh b/internal/gorepl/symbols/env-wrap.sh deleted file mode 100755 index 75df9f4e..00000000 --- a/internal/gorepl/symbols/env-wrap.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -rm go1_*.go - -# Run any incoming program with wasm/js env vars -GOOS=js GOARCH=wasm $@ - -# Update build constraints -find . -name 'go1_*.go' -exec sed -i '' 's/^\/\/go:build go1\.\([0-9]*\)/\/\/go:build go1.\1 \&\& js/g' {} \; -find . -name 'go1_*.go' -exec sed -i '' 's/^\/\/ *+build go1\.\([0-9]*\)/\/\/+build go1.\1,js/g' {} \; \ No newline at end of file diff --git a/internal/gorepl/symbols/go1_21_syscall_js.go b/internal/gorepl/symbols/go1_21_syscall_js.go deleted file mode 100644 index d4bd750d..00000000 --- a/internal/gorepl/symbols/go1_21_syscall_js.go +++ /dev/null @@ -1,39 +0,0 @@ -// Code generated by 'yaegi extract syscall/js'. DO NOT EDIT. - -//go:build go1.21 && js -// +build go1.21,js - -package symbols - -import ( - "reflect" - "syscall/js" -) - -func init() { - Symbols["syscall/js/js"] = map[string]reflect.Value{ - // function, constant and variable definitions - "CopyBytesToGo": reflect.ValueOf(js.CopyBytesToGo), - "CopyBytesToJS": reflect.ValueOf(js.CopyBytesToJS), - "FuncOf": reflect.ValueOf(js.FuncOf), - "Global": reflect.ValueOf(js.Global), - "Null": reflect.ValueOf(js.Null), - "TypeBoolean": reflect.ValueOf(js.TypeBoolean), - "TypeFunction": reflect.ValueOf(js.TypeFunction), - "TypeNull": reflect.ValueOf(js.TypeNull), - "TypeNumber": reflect.ValueOf(js.TypeNumber), - "TypeObject": reflect.ValueOf(js.TypeObject), - "TypeString": reflect.ValueOf(js.TypeString), - "TypeSymbol": reflect.ValueOf(js.TypeSymbol), - "TypeUndefined": reflect.ValueOf(js.TypeUndefined), - "Undefined": reflect.ValueOf(js.Undefined), - "ValueOf": reflect.ValueOf(js.ValueOf), - - // type definitions - "Error": reflect.ValueOf((*js.Error)(nil)), - "Func": reflect.ValueOf((*js.Func)(nil)), - "Type": reflect.ValueOf((*js.Type)(nil)), - "Value": reflect.ValueOf((*js.Value)(nil)), - "ValueError": reflect.ValueOf((*js.ValueError)(nil)), - } -} diff --git a/internal/gorepl/symbols/symbols_gen.go b/internal/gorepl/symbols/symbols_gen.go deleted file mode 100644 index cf709e2a..00000000 --- a/internal/gorepl/symbols/symbols_gen.go +++ /dev/null @@ -1,13 +0,0 @@ -// Package symbols contains additional 'syscall/js' Go symbols for Yaegi which not present by default. -package symbols - -import "reflect" - -//go:generate go run -exec ./env-wrap.sh github.com/traefik/yaegi/internal/cmd/extract syscall/js - -// Symbols variable stores the map of stdlib symbols per package. -var Symbols = map[string]map[string]reflect.Value{} - -// MapTypes variable contains a map of functions which have an interface{} as parameter but -// do something special if the parameter implements a given interface. -var MapTypes = map[reflect.Value][]reflect.Type{} diff --git a/internal/gorepl/testdata/.gitignore b/internal/gorepl/testdata/.gitignore deleted file mode 100644 index 0c6117d9..00000000 --- a/internal/gorepl/testdata/.gitignore +++ /dev/null @@ -1 +0,0 @@ -pkg \ No newline at end of file diff --git a/internal/gorepl/testdata/sample.go.txt b/internal/gorepl/testdata/sample.go.txt deleted file mode 100644 index fb172fbb..00000000 --- a/internal/gorepl/testdata/sample.go.txt +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "go.uber.org/zap" - "testing" - _ "golang.org/x/tools/internal/imports" -) - -//import "golang.org/x/tools/internal/imports" - -func main() { - logger, err := zap.NewProduction() - if err != nil { - panic(err) - } - defer logger.Sync() // flushes buffer, if any - sugar := logger.Sugar() - sugar.Infow("hello world!!!") -} diff --git a/internal/gorepl/uihost/downloads.go b/internal/gorepl/uihost/downloads.go deleted file mode 100644 index d85be995..00000000 --- a/internal/gorepl/uihost/downloads.go +++ /dev/null @@ -1,114 +0,0 @@ -package uihost - -import ( - "context" - "sync" - - "github.com/x1unix/go-playground/internal/gorepl/pacman" - "golang.org/x/mod/module" -) - -const eventBufferSize = 10 - -type pmEventType = uint8 - -const ( - pmEventEmpty pmEventType = iota - pmEventDependencyCheckFinish - pmEventDependencyResolveStart - pmEventPackageSearchStart - pmEventPackageDownload - pmEventPackageExtract -) - -type packageManagerEvent struct { - eventType pmEventType - success bool - processedItems int - totalItems int - context string -} - -var _ pacman.PMProgressObserver = (*PackageDownloadObserver)(nil) - -// PackageDownloadObserver sends package manager events to web ui host. -type PackageDownloadObserver struct { - ch chan packageManagerEvent - once sync.Once -} - -func NewPackageDownloadObserver() *PackageDownloadObserver { - observer := &PackageDownloadObserver{ - ch: make(chan packageManagerEvent, eventBufferSize), - } - - return observer -} - -// Start starts background events channel listener that sends events to web ui host. -func (o *PackageDownloadObserver) Start(ctx context.Context) { - o.once.Do(func() { - go o.handleEvents(ctx) - }) -} - -func (o *PackageDownloadObserver) DependencyCheckFinish(err error) { - var msg string - if err != nil { - msg = err.Error() - } - - o.ch <- packageManagerEvent{ - eventType: pmEventDependencyCheckFinish, - success: err == nil, - context: msg, - } -} - -func (o *PackageDownloadObserver) DependencyResolveStart(packagesCount int) { - o.ch <- packageManagerEvent{ - eventType: pmEventDependencyResolveStart, - totalItems: packagesCount, - } -} - -func (o *PackageDownloadObserver) PackageSearchStart(pkgName string) { - o.ch <- packageManagerEvent{ - eventType: pmEventPackageSearchStart, - context: pkgName, - } -} - -func (o *PackageDownloadObserver) PackageDownload(pkg *module.Version, progress pacman.Progress) { - o.ch <- packageManagerEvent{ - eventType: pmEventPackageDownload, - processedItems: progress.Current, - totalItems: progress.Total, - context: pkg.String(), - } -} - -func (o *PackageDownloadObserver) PackageExtract(pkg *module.Version, progress pacman.Progress) { - o.ch <- packageManagerEvent{ - eventType: pmEventPackageExtract, - processedItems: progress.Current, - totalItems: progress.Total, - context: pkg.String(), - } -} - -func (o *PackageDownloadObserver) handleEvents(ctx context.Context) { - for { - select { - case <-ctx.Done(): - close(o.ch) - return - case event, ok := <-o.ch: - if !ok { - return - } - - onPackageManagerEvent(event) - } - } -} diff --git a/internal/gorepl/uihost/downloads_js.go b/internal/gorepl/uihost/downloads_js.go deleted file mode 100644 index 1a7f9b79..00000000 --- a/internal/gorepl/uihost/downloads_js.go +++ /dev/null @@ -1,4 +0,0 @@ -package uihost - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gorepl/uihost.onPackageManagerEvent -func onPackageManagerEvent(e packageManagerEvent) diff --git a/internal/gorepl/uihost/downloads_stubs.go b/internal/gorepl/uihost/downloads_stubs.go deleted file mode 100644 index 9a356b62..00000000 --- a/internal/gorepl/uihost/downloads_stubs.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !js - -package uihost - -import "github.com/x1unix/go-playground/internal/gowasm/wlog" - -func onPackageManagerEvent(e packageManagerEvent) { - wlog.Printf("onPackageManagerEvent: %#v\n", e) -} diff --git a/internal/gorepl/uihost/runner.go b/internal/gorepl/uihost/runner.go deleted file mode 100644 index 24003325..00000000 --- a/internal/gorepl/uihost/runner.go +++ /dev/null @@ -1,38 +0,0 @@ -package uihost - -import "fmt" - -// EvalState is current Go program evaluation state. -type EvalState uint8 - -const ( - EvalStateZero EvalState = iota - EvalStateBegin - EvalStateFinish - EvalStateError - EvalStatePanic -) - -// ReportEvalPanic reports Go panic during evaluation -func ReportEvalPanic(value any, stack []byte) { - onProgramEvalStateChange(EvalStatePanic, fmt.Sprintf("panic: %s\n%s", - value, stack, - )) -} - -// ReportEvalError reports program eval error. -func ReportEvalError(err error) { - if err == nil { - ReportEvalState(EvalStateError) - return - } - - onProgramEvalStateChange(EvalStateError, err.Error()) -} - -// ReportEvalState reports program evaluation state change. -// -// Used to notify about program execution start or end. -func ReportEvalState(state EvalState) { - onProgramEvalStateChange(state, "") -} diff --git a/internal/gorepl/uihost/runner_js.go b/internal/gorepl/uihost/runner_js.go deleted file mode 100644 index 6e6c0386..00000000 --- a/internal/gorepl/uihost/runner_js.go +++ /dev/null @@ -1,6 +0,0 @@ -package uihost - -// onProgramEvalStateChange reports program execution result to UI -// -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gorepl/uihost.onProgramEvalStateChange -func onProgramEvalStateChange(state EvalState, msg string) diff --git a/internal/gorepl/uihost/runner_stubs.go b/internal/gorepl/uihost/runner_stubs.go deleted file mode 100644 index b46c4075..00000000 --- a/internal/gorepl/uihost/runner_stubs.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !js - -package uihost - -import "github.com/x1unix/go-playground/internal/gowasm/wlog" - -func onProgramEvalStateChange(state EvalState, msg string) { - wlog.Printf("onProgramEvalStateChange: state=%d msg=%s", state, msg) -} diff --git a/internal/gowasm/IMPORTS.md b/internal/gowasm/IMPORTS.md deleted file mode 100644 index 2c0d78bf..00000000 --- a/internal/gowasm/IMPORTS.md +++ /dev/null @@ -1,67 +0,0 @@ -# WebAssembly Imports List - -Here is a list of WASM module imports that the package expects from WASM host. - -Each function has a prefix of package name in format: `path/to/package.functionName` - -## Core - -List of core functions required for all gowasm-based functionality. - -**Import name prefix:** `github.com/x1unix/go-playground/internal/gowasm` - -**Imports:** - -* `func registerCallbackHandler(fn js.Func)` -* `func wasmConsoleWrite(fd int, data []byte)` - -See: - -* [callback_js.s](callback_js.s) - -## Wlog - -Wlog is logging interface for WASM workers. Used by other wasm-related packages. - -**Import name prefix:** `github.com/x1unix/go-playground/internal/gowasm/wlog` - -* `func logWrite(level uint8, data []byte)` - -See: - -* * [logger_js.s](wlog/writer_js.s) - -## BrowserFS - -BrowserFS package implements `fs.FS` interface for IndexedDB-based file system. - -**Import name prefix:** `github.com/x1unix/go-playground/internal/gowasm/browserfs` - -**Imports:** - - * `func stat(name string, out *inode, cb int)` - * `func readDir(name string, out []inode, cb int)` - * `func readFile(f inode, out []byte, cb int)` - * `func writeFile(name string, data []byte, cb int)` - * `func makeDir(name string, cb int)` - * `func unlink(name string, cb int)` - -See: - -* [browserfs/syscall_js.s](browserfs/syscall_js.s) - -## PackageDB - -Interface to access Go modules cache stored in IndexedDB. - -**Import name prefix:** `github.com/x1unix/go-playground/internal/gowasm/packagedb` - -**Imports:** - -* `func lookupPackage(pkgName string, out []byte, cb int)` -* `func registerPackage(pkgName, version string, cb int)` -* `func removePackage(okgName string, cb int)` - -See: - -* [packagedb/syscall_js.s](packagedb/syscall_js.s) diff --git a/internal/gowasm/README.md b/internal/gowasm/README.md new file mode 100644 index 00000000..f06d686b --- /dev/null +++ b/internal/gowasm/README.md @@ -0,0 +1,5 @@ +# gowasm + +This package provides framework for building Go WebAssembly workers and provides tools for async communication with JavaScript host. + +Package was created as a part of embedding Yaegi initiative. Although Yaegi was removed in Playground v2, this package still remains to be used in future. diff --git a/internal/gowasm/browserfs/dir.go b/internal/gowasm/browserfs/dir.go deleted file mode 100644 index eed35e88..00000000 --- a/internal/gowasm/browserfs/dir.go +++ /dev/null @@ -1,36 +0,0 @@ -package browserfs - -import ( - "io/fs" - "syscall" - - "github.com/x1unix/go-playground/internal/gowasm/wlog" -) - -var ( - _ fs.File = (*dirFile)(nil) -) - -type dirFile struct { - attrs inode - name string -} - -func (f dirFile) Stat() (fs.FileInfo, error) { - wlog.Debugf("dirFile.Stat: %q", f.name) - return newFileInfo(f.attrs), nil -} - -func (f dirFile) Read(_ []byte) (int, error) { - wlog.Debugf("dirFile.Read: %q", f.name) - return 0, &fs.PathError{ - Op: "read", - Path: f.name, - Err: syscall.EISDIR, - } -} - -func (f dirFile) Close() error { - wlog.Debugf("dirFile.Close: %q", f.name) - return nil -} diff --git a/internal/gowasm/browserfs/direntry.go b/internal/gowasm/browserfs/direntry.go deleted file mode 100644 index 7ce6776c..00000000 --- a/internal/gowasm/browserfs/direntry.go +++ /dev/null @@ -1,32 +0,0 @@ -package browserfs - -import "io/fs" - -var _ fs.DirEntry = (*dirEntry)(nil) - -type dirEntry struct { - fileInfo -} - -func newDirEntry(attrs inode) dirEntry { - return dirEntry{ - fileInfo: fileInfo{ - attrs: attrs, - }, - } -} - -func (d dirEntry) Type() fs.FileMode { - switch d.attrs.fileType { - case fileTypeDirectory: - return fs.ModeDir - case fileTypeSymlink: - return fs.ModeSymlink - default: - return 0 - } -} - -func (d dirEntry) Info() (fs.FileInfo, error) { - return d.fileInfo, nil -} diff --git a/internal/gowasm/browserfs/file.go b/internal/gowasm/browserfs/file.go deleted file mode 100644 index 8ce7440f..00000000 --- a/internal/gowasm/browserfs/file.go +++ /dev/null @@ -1,118 +0,0 @@ -package browserfs - -import ( - "io" - "io/fs" - "sync" - - "github.com/x1unix/go-playground/internal/gowasm" - "github.com/x1unix/go-playground/internal/gowasm/wlog" -) - -var _ fs.File = (*file)(nil) - -type file struct { - attrs inode - name string - - lock sync.Mutex - initialized bool - closed bool - data []byte -} - -func newFile(name string, attrs inode) *file { - return &file{ - name: name, - attrs: attrs, - } -} - -func (f *file) Stat() (fs.FileInfo, error) { - wlog.Debugf("file.Stat: %q", f.name) - return newFileInfo(f.attrs), nil -} - -func (f *file) Read(dst []byte) (int, error) { - wlog.Debugf("file.Read: %q", f.name) - if err := f.prefetchData(); err != nil { - return 0, err - } - - if f.eof() { - return 0, io.EOF - } - - n := 0 - if l := len(dst); l > 0 { - for n < l { - dst[n] = f.readByte() - n++ - if f.eof() { - // free memory - f.data = []byte{} - break - } - } - } - - return n, nil -} - -func (f *file) eof() bool { - return len(f.data) == 0 -} - -func (f *file) readByte() byte { - // this function assumes that eof() check was done before - b := f.data[0] - f.data = f.data[1:] - return b -} - -func (f *file) prefetchData() error { - f.lock.Lock() - defer f.lock.Unlock() - - if f.closed { - return &fs.PathError{ - Op: "read", - Path: f.name, - Err: fs.ErrClosed, - } - } - - if f.initialized { - return nil - } - - // Fetch file contents into internal buffer - f.data = make([]byte, 0, f.attrs.size) - cb := gowasm.RequestCallback() - go readFile(f.attrs.id, &f.data, cb) - err := awaitCallback(cb) - if err != nil { - return &fs.PathError{ - Op: "read", - Path: f.name, - Err: err, - } - } - - f.initialized = true - return nil -} - -func (f *file) Close() error { - wlog.Debugf("file.Close: %q", f.name) - f.lock.Lock() - defer f.lock.Unlock() - - if f.closed { - return fs.ErrClosed - } - - f.closed = true - f.data = nil - return nil -} diff --git a/internal/gowasm/browserfs/fileinfo.go b/internal/gowasm/browserfs/fileinfo.go deleted file mode 100644 index b50b7b61..00000000 --- a/internal/gowasm/browserfs/fileinfo.go +++ /dev/null @@ -1,49 +0,0 @@ -package browserfs - -import ( - "io/fs" - "time" -) - -var _ fs.FileInfo = (*fileInfo)(nil) - -type fileInfo struct { - attrs inode -} - -func newFileInfo(attrs inode) fileInfo { - return fileInfo{ - attrs: attrs, - } -} - -func (f fileInfo) Name() string { - return f.attrs.name.string() -} - -func (f fileInfo) Size() int64 { - return f.attrs.size -} - -func (f fileInfo) Mode() fs.FileMode { - switch f.attrs.fileType { - case fileTypeDirectory: - return fs.ModeDir | fs.ModePerm - case fileTypeSymlink: - return fs.ModeSymlink | fs.ModePerm - default: - return fs.ModePerm - } -} - -func (f fileInfo) ModTime() time.Time { - return time.UnixMilli(f.attrs.createdAt) -} - -func (f fileInfo) IsDir() bool { - return f.attrs.fileType == fileTypeDirectory -} - -func (f fileInfo) Sys() any { - return f.attrs -} diff --git a/internal/gowasm/browserfs/fs.go b/internal/gowasm/browserfs/fs.go deleted file mode 100644 index 5cbfa051..00000000 --- a/internal/gowasm/browserfs/fs.go +++ /dev/null @@ -1,189 +0,0 @@ -package browserfs - -import ( - "io" - "io/fs" - "path" - "strings" - "syscall" - - "github.com/samber/lo" - "github.com/x1unix/go-playground/internal/gorepl/pacman" - "github.com/x1unix/go-playground/internal/gowasm" - "github.com/x1unix/go-playground/internal/gowasm/wlog" - "github.com/x1unix/go-playground/internal/util/buffutil" -) - -var ( - _ fs.ReadDirFS = (*FS)(nil) - _ pacman.WritableFS = (*FS)(nil) -) - -var ( - buffPool = buffutil.NewBufferPool() - dirEntryPool = buffutil.NewSlicePool[inode](64) -) - -type FS struct{} - -func NewFS() FS { - return FS{} -} - -func (s FS) Stat(name string) (fs.FileInfo, error) { - wlog.Debugf("fs.Stat - %q\n", name) - cb := gowasm.RequestCallback() - inode := new(inode) - go stat(cleanPath(name), inode, cb) - - if err := awaitCallback(cb); err != nil { - return nil, &fs.PathError{ - Op: "stat", - Path: name, - Err: err, - } - } - - return newFileInfo(*inode), nil -} - -func (s FS) WriteFile(name string, src io.Reader, _ fs.FileMode) error { - wlog.Debugf("fs.WriteFile - %q\n", name) - buff := buffPool.Get() - defer buff.Close() - - _, err := io.Copy(buff, src) - if err != nil { - return err - } - - cb := gowasm.RequestCallback() - go writeFile(cleanPath(name), buff.Bytes(), cb) - if err := awaitCallback(cb); err != nil { - return &fs.PathError{ - Op: "create", - Path: name, - Err: err, - } - } - - return nil -} - -func (s FS) Mkdir(name string, _ fs.FileMode) error { - wlog.Debugf("fs.Mkdir - %q\n", name) - cb := gowasm.RequestCallback() - go makeDir(cleanPath(name), cb) - if err := awaitCallback(cb); err != nil { - return &fs.PathError{ - Op: "mkdir", - Path: name, - Err: err, - } - } - - return nil -} - -func (s FS) Remove(name string) error { - wlog.Debugf("fs.Remove - %q\n", name) - cb := gowasm.RequestCallback() - go unlink(cleanPath(name), cb) - if err := awaitCallback(cb); err != nil { - return &fs.PathError{ - Op: "unlink", - Path: name, - Err: err, - } - } - - return nil -} - -func (s FS) ReadDir(name string) ([]fs.DirEntry, error) { - wlog.Debugf("fs.ReadDir - %q\n", name) - cb := gowasm.RequestCallback() - - // Since it's impossible to access to Go's memory allocator at JS side, - // the best way to obtain a slice of results is to pass a slice with - // enough size to JS and reuse it later. - // - // Basically we follow the same rules as for CGO. - results := dirEntryPool.Get() - defer dirEntryPool.Put(results) - go readDir(name, &results, cb) - - if err := awaitCallback(cb); err != nil { - return nil, &fs.PathError{ - Op: "readdir", - Path: name, - Err: err, - } - } - - return lo.Map(results, func(item inode, _ int) fs.DirEntry { - return newDirEntry(item) - }), nil -} - -func (s FS) Open(name string) (fs.File, error) { - wlog.Debugf("fs.Open - %q\n", name) - cb := gowasm.RequestCallback() - attrs := inode{} - go stat(name, &attrs, cb) - - if err := awaitCallback(cb); err != nil { - return nil, &fs.PathError{ - Op: "open", - Path: name, - Err: err, - } - } - - if attrs.fileType == fileTypeDirectory { - return dirFile{ - attrs: attrs, - name: name, - }, nil - } - - return newFile(name, attrs), nil -} - -// mapSyscallError maps syscall error to fs errors. -// -// Returns original error when no match found. -func mapSyscallError(errno syscall.Errno) error { - switch errno { - case syscall.ENOENT: - return fs.ErrNotExist - case syscall.EEXIST: - return fs.ErrExist - case syscall.EBADF: - return fs.ErrClosed - case syscall.EPERM: - return fs.ErrPermission - default: - return errno - } -} - -func awaitCallback(cb gowasm.CallbackID) error { - defer gowasm.ReleaseCallback(cb) - - err := gowasm.AwaitCallback(cb) - if err == nil { - return nil - } - - syscallErr, ok := err.(syscall.Errno) - if !ok { - return err - } - - return mapSyscallError(syscallErr) -} - -func cleanPath(p string) string { - return strings.TrimPrefix(path.Clean(p), "/") -} diff --git a/internal/gowasm/browserfs/inode.go b/internal/gowasm/browserfs/inode.go deleted file mode 100644 index 7f408fc0..00000000 --- a/internal/gowasm/browserfs/inode.go +++ /dev/null @@ -1,34 +0,0 @@ -package browserfs - -import "github.com/x1unix/go-playground/pkg/util/mathx" - -type fileType uint8 - -const ( - fileTypeZero fileType = iota - fileTypeRegular - fileTypeDirectory - fileTypeSymlink - - maxFileNameLen = 128 -) - -// inode contains IndexedDB fs entry attributes -type inode struct { - id uint64 - parentId uint64 //nolint:unused - fileType fileType - size int64 - createdAt int64 - name sizedFileName -} - -type sizedFileName struct { - len uint8 - data [maxFileNameLen]byte -} - -func (n sizedFileName) string() string { - nameLen := mathx.Min(n.len, maxFileNameLen) - return string(n.data[:nameLen]) -} diff --git a/internal/gowasm/browserfs/syscall_js.go b/internal/gowasm/browserfs/syscall_js.go deleted file mode 100644 index b97761f3..00000000 --- a/internal/gowasm/browserfs/syscall_js.go +++ /dev/null @@ -1,19 +0,0 @@ -package browserfs - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gowasm/browserfs.stat -func stat(name string, out *inode, cb int) - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gowasm/browserfs.readDir -func readDir(name string, out *[]inode, cb int) - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gowasm/browserfs.readFile -func readFile(fid uint64, out *[]byte, cb int) - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gowasm/browserfs.writeFile -func writeFile(name string, data []byte, cb int) - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gowasm/browserfs.makeDir -func makeDir(name string, cb int) - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gowasm/browserfs.unlink -func unlink(name string, cb int) diff --git a/internal/gowasm/browserfs/syscall_stubs.go b/internal/gowasm/browserfs/syscall_stubs.go deleted file mode 100644 index d5f68eb4..00000000 --- a/internal/gowasm/browserfs/syscall_stubs.go +++ /dev/null @@ -1,29 +0,0 @@ -//go:build !js - -package browserfs - -// Stubs for native - -func stat(name string, out *inode, cb int) { - panic("not implemented") -} - -func readDir(name string, out *[]inode, cb int) { - panic("not implemented") -} - -func readFile(fd uint64, out *[]byte, cb int) { - panic("not implemented") -} - -func writeFile(name string, data []byte, cb int) { - panic("not implemented") -} - -func makeDir(name string, cb int) { - panic("not implemented") -} - -func unlink(name string, cb int) { - panic("not implemented") -} diff --git a/internal/gowasm/packagedb/index.go b/internal/gowasm/packagedb/index.go deleted file mode 100644 index 8fdabbaf..00000000 --- a/internal/gowasm/packagedb/index.go +++ /dev/null @@ -1,80 +0,0 @@ -package packagedb - -import ( - "io/fs" - "syscall" - - "github.com/x1unix/go-playground/internal/gorepl/pacman" - "github.com/x1unix/go-playground/internal/gowasm" - "github.com/x1unix/go-playground/internal/gowasm/wlog" - "golang.org/x/mod/module" -) - -var _ pacman.PackageIndex = (*PackageIndex)(nil) - -// maxPackageVersionLength is theoretical max length of package version string. -// -// This value is based on length of untagged version string for legacy packages -// generated by go.mod. -// -// Example: v2.0.4-0.20190803094908-fe8645b0a904+incompatible -const maxPackageVersionLength = 50 - -// PackageIndex is pacman.PackageIndex implementation to communicate -// with IndexDB-based package registry on browser side. -type PackageIndex struct{} - -func NewPackageIndex() PackageIndex { - return PackageIndex{} -} - -func (p PackageIndex) LookupPackage(pkgName string) (*module.Version, error) { - wlog.Debugln("LookupPackage: ", pkgName) - cb := gowasm.RequestCallback() - defer gowasm.ReleaseCallback(cb) - - // Since JS host can't access Go memory allocator, - // preallocate enough memory for result. - buff := make([]byte, 0, maxPackageVersionLength) - go lookupPackage(pkgName, &buff, cb) - - err := gowasm.AwaitCallback(cb) - if err == syscall.ENOENT { - return nil, fs.ErrNotExist - } - - if err != nil { - return nil, err - } - - return &module.Version{ - Path: pkgName, - Version: string(buff), - }, nil -} - -func (p PackageIndex) RegisterPackage(pkg *module.Version) error { - wlog.Debugln("RegisterPackage: ", pkg) - cb := gowasm.RequestCallback() - defer gowasm.ReleaseCallback(cb) - - go registerPackage(pkg.Path, pkg.Version, cb) - if err := gowasm.AwaitCallback(cb); err != nil { - return err - } - - return nil -} - -func (p PackageIndex) RemovePackage(pkg *module.Version) error { - wlog.Debugln("RemovePackage: ", pkg) - cb := gowasm.RequestCallback() - defer gowasm.ReleaseCallback(cb) - - go removePackage(pkg.Path, cb) - if err := gowasm.AwaitCallback(cb); err != nil { - return err - } - - return nil -} diff --git a/internal/gowasm/packagedb/syscall_js.go b/internal/gowasm/packagedb/syscall_js.go deleted file mode 100644 index 58450d50..00000000 --- a/internal/gowasm/packagedb/syscall_js.go +++ /dev/null @@ -1,10 +0,0 @@ -package packagedb - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gowasm/packagedb.lookupPackage -func lookupPackage(pkgName string, out *[]byte, cb int) - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gowasm/packagedb.registerPackage -func registerPackage(pkgName, version string, cb int) - -//go:wasmimport gojs github.com/x1unix/go-playground/internal/gowasm/packagedb.removePackage -func removePackage(pkgName string, cb int) diff --git a/internal/gowasm/packagedb/syscall_stubs.go b/internal/gowasm/packagedb/syscall_stubs.go deleted file mode 100644 index 0556f7b5..00000000 --- a/internal/gowasm/packagedb/syscall_stubs.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build !js - -package packagedb - -func lookupPackage(pkgName string, out *[]byte, cb int) { - panic("not implemented") -} -func registerPackage(pkgName, version string, cb int) { - panic("not implemented") -} -func removePackage(okgName string, cb int) { - panic("not implemented") -} diff --git a/web/src/lib/go/types/spec.ts b/web/src/lib/go/types/spec.ts index e73d8345..15c03781 100644 --- a/web/src/lib/go/types/spec.ts +++ b/web/src/lib/go/types/spec.ts @@ -24,7 +24,7 @@ export abstract class AbstractTypeSpec { private _size = 0 private _align = 1 private _skip = 0 - private readonly _name = '' + private readonly _name: string = '' /** * @param name Original type name. diff --git a/web/src/lib/gowasm/binder.ts b/web/src/lib/gowasm/binder.ts deleted file mode 100644 index 0b2c6402..00000000 --- a/web/src/lib/gowasm/binder.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { type CallImportHandler, type GoWrapper } from '~/lib/go/wrapper/wrapper' - -const goExportMetadataKey = Symbol('@GoExportMetadata') - -/** - * Base class for all classes who export methods to Go. - */ -// eslint-disable-next-line @typescript-eslint/no-extraneous-class -export class PackageBinding { - private static [goExportMetadataKey]?: GoExportMetadata -} - -interface GoExportMetadata { - packageName: string - symbols: Array<[string, CallImportHandler]> -} - -const defineExportMetadata = (target: any, meta: GoExportMetadata) => { - Object.defineProperty(target, goExportMetadataKey, { - configurable: false, - enumerable: false, - writable: false, - value: meta, - }) -} - -const getGoExportMetadata = (target): GoExportMetadata | undefined => { - return target[goExportMetadataKey] -} - -/** - * Package decorator adds base package name for all exported class methods. - * @param pkgName Go package name - * @constructor - */ -export const Package = - (pkgName: string): ClassDecorator => - (constructor: Function) => { - const meta = getGoExportMetadata(constructor.prototype) - if (meta) { - meta.packageName = pkgName - return - } - - defineExportMetadata(constructor.prototype, { - packageName: pkgName, - symbols: [], - }) - } - -/** - * WasmExport decorator adds class method to exports with given symbol name. - * @param funcName Go function name to be linked with. - * @constructor - */ -export const WasmExport = - (funcName: string): MethodDecorator => - (target, propertyKey, descriptor) => { - const func = descriptor.value! as unknown as CallImportHandler - const meta = getGoExportMetadata(target) - - if (meta) { - meta.symbols.push([funcName, func]) - return - } - - defineExportMetadata(target, { - packageName: '', - symbols: [[funcName, func]], - }) - } - -/** - * Adds exports to Go from given class instance. - * - * @param go Go wrapper instance - * @param srcObj instance - */ -export const registerExportObject = (go: GoWrapper, srcObj: PackageBinding) => { - const meta = getGoExportMetadata(srcObj) - if (!meta) { - throw new Error(`Go export metadata is missing.`) - } - - const { packageName, symbols } = meta - for (const [funcName, func] of symbols) { - go.exportFunction(`${packageName}.${funcName}`, (sp, reader, mem) => func.call(srcObj, sp, reader, mem)) - } -} diff --git a/web/src/lib/gowasm/bindings/browserfs/binding.ts b/web/src/lib/gowasm/bindings/browserfs/binding.ts deleted file mode 100644 index 87fe448e..00000000 --- a/web/src/lib/gowasm/bindings/browserfs/binding.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { - Int, - UintPtr, - ArrayTypeSpec, - GoStringType, - type MemoryView, - type SliceHeader, - SliceHeaderType, - type StackReader, - stringEncoder, -} from '~/lib/go' -import { Errno, SyscallError } from '~/lib/go/pkg/syscall' -import { Package, PackageBinding, WasmExport } from '../../binder' -import { type Inode, MAX_FILE_NAME_LEN, TInode } from './types' -import type SyscallHelper from '../../syscall' -import { type FileStore } from './filestore' - -const checkFileNameLimit = (strLen: number) => { - if (!strLen) { - throw new SyscallError(Errno.EINVAL) - } - - if (strLen > MAX_FILE_NAME_LEN) { - throw new SyscallError(Errno.ENAMETOOLONG) - } -} - -const validateFileName = (name: string) => { - checkFileNameLimit(name.length) -} - -/** - * WASM imports binding for emulated package cache filesystem. - * - * @see internal/gowasm/browserfs/syscall_js.go - */ -@Package('github.com/x1unix/go-playground/internal/gowasm/browserfs') -export class BrowserFSBinding extends PackageBinding { - constructor( - private readonly helper: SyscallHelper, - private readonly store: FileStore, - ) { - super() - } - - // func stat(name string, out *inode, cb int) - @WasmExport('stat') - stat(sp: number, reader: StackReader, mem: MemoryView) { - reader.skipHeader() - const fileName = reader.next(GoStringType) - const outPtr = reader.next(UintPtr) - const cbId = reader.next(Int) - - if (!cbId) { - throw new SyscallError(Errno.EBADSLT) - } - - this.helper.doAsync(cbId, async () => { - validateFileName(fileName) - - if (!outPtr) { - throw new SyscallError(Errno.EFAULT) - } - - const { name, ...result } = await this.store.stat(fileName) - mem.write(outPtr, TInode, { - ...result, - name: { - len: name.length, - data: stringEncoder.encode(name), - }, - }) - }) - } - - // func readDir(name string, out *[]inode, cb int) - @WasmExport('readDir') - readDir(sp: number, reader: StackReader, mem: MemoryView) { - reader.skipHeader() - const dirName = reader.next(GoStringType) - const outSlicePtr = reader.next(UintPtr) - const cbId = reader.next(Int) - - if (!cbId) { - throw new SyscallError(Errno.EBADSLT) - } - - this.helper.doAsync(cbId, async () => { - validateFileName(dirName) - - if (!outSlicePtr) { - throw new SyscallError(Errno.EFAULT) - } - - const outSlice = mem.read(outSlicePtr, SliceHeaderType) - const items = await this.store.readDir(dirName) - if (items.length > outSlice.cap) { - throw new SyscallError(Errno.ENOMEM) - } - - const inodes = items.map(({ name, ...props }) => { - const encodedName = stringEncoder.encode(name) - return { - ...props, - name: { - len: encodedName.length, - data: encodedName, - }, - } - }) - - // Update slice length - outSlice.len = items.length - mem.write(outSlicePtr, SliceHeaderType, outSlice) - mem.write(outSlice.data, new ArrayTypeSpec(TInode, items.length), inodes) - }) - } - - // func readFile(f inode, out *[]byte, cb int) - @WasmExport('readFile') - readFile(sp: number, reader: StackReader, mem: MemoryView) { - reader.skipHeader() - const fileId = reader.next(UintPtr) - const slicePtr = reader.next(UintPtr) - const cbId = reader.next(Int) - - if (!cbId) { - throw new SyscallError(Errno.EBADSLT) - } - - this.helper.doAsync(cbId, async () => { - if (!slicePtr) { - throw new SyscallError(Errno.EFAULT) - } - - const data = await this.store.readFile(fileId) - - const dstSlice = mem.read(slicePtr, SliceHeaderType) - if (data.length > dstSlice.cap) { - throw new SyscallError(Errno.ENOMEM) - } - - // Update slice length - dstSlice.len = data.length - mem.write(slicePtr, SliceHeaderType, dstSlice) - mem.set(dstSlice.data, data) - }) - } - - // func writeFile(name string, data []byte, cb int) - @WasmExport('writeFile') - writeFile(sp: number, reader: StackReader, mem: MemoryView) { - reader.skipHeader() - const fname = reader.next(GoStringType) - const srcSlice = reader.next(SliceHeaderType) - const cbId = reader.next(Int) - - if (!cbId) { - throw new SyscallError(Errno.EBADSLT) - } - - this.helper.doAsync(cbId, async () => { - validateFileName(fname) - let data: Uint8Array - if (srcSlice.len > 0) { - if (!srcSlice.data) { - throw new SyscallError(Errno.EFAULT) - } - - data = mem.get(srcSlice.data, srcSlice.len) - } else { - data = new Uint8Array() - } - - await this.store.writeFile(fname, data) - }) - } - - // func makeDir(name string, cb int) - @WasmExport('makeDir') - makeDir(sp: number, reader: StackReader) { - reader.skipHeader() - const fname = reader.next(GoStringType) - const cbId = reader.next(Int) - if (!cbId) { - throw new SyscallError(Errno.EBADSLT) - } - - this.helper.doAsync(cbId, async () => { - validateFileName(fname) - await this.store.makeDir(fname) - }) - } - - // func unlink(name string, cb int) - @WasmExport('unlink') - unlink(sp: number, reader: StackReader) { - reader.skipHeader() - const fname = reader.next(GoStringType) - const cbId = reader.next(Int) - if (!cbId) { - throw new SyscallError(Errno.EBADSLT) - } - - this.helper.doAsync(cbId, async () => { - validateFileName(fname) - await this.store.unlink(fname) - }) - } -} diff --git a/web/src/lib/gowasm/bindings/browserfs/filestore.ts b/web/src/lib/gowasm/bindings/browserfs/filestore.ts deleted file mode 100644 index 3a61c72b..00000000 --- a/web/src/lib/gowasm/bindings/browserfs/filestore.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { type Inode } from './types' - -export interface FileInfo extends Omit { - name: string -} - -/** - * FileStore is abstract implementation of file store. - */ -export interface FileStore { - /** - * Checks if file entry exists and returns information about a file. - * - * @throws SyscallError(Errno.ENOENT) if file doesn't exist. - * @param name - */ - stat: (name: string) => Promise - - /** - * Returns a list of entries in directory. - * - * @throws SyscallError(Errno.ENOENT) if directory doesn't exist. - * @param name - */ - readDir: (name: string) => Promise - - /** - * Returns file contents. - * - * @throws SyscallError(Errno.ENOENT) if file doesn't exist. - * @param fileId - */ - readFile: (fileId: number) => Promise - - /** - * Creates a new or overwrites an existing file with specified contents. - * - * @param name File name - * @param data New contents - */ - writeFile: (name: string, data: Uint8Array) => Promise - - /** - * Creates a new directory, including its parent. - * - * @param name Directory name. - */ - makeDir: (name: string) => Promise - - /** - * Removes file or directory, including its contents. - * - * @throws SyscallError(Errno.ENOENT) if file doesn't exist. - * @param name - */ - unlink: (name: string) => Promise -} diff --git a/web/src/lib/gowasm/bindings/browserfs/index.ts b/web/src/lib/gowasm/bindings/browserfs/index.ts deleted file mode 100644 index 3a6820f7..00000000 --- a/web/src/lib/gowasm/bindings/browserfs/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './binding' -export * from './filestore' -export * from './types' diff --git a/web/src/lib/gowasm/bindings/browserfs/types.ts b/web/src/lib/gowasm/bindings/browserfs/types.ts deleted file mode 100644 index 70472254..00000000 --- a/web/src/lib/gowasm/bindings/browserfs/types.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { UInt8ArrayTypeSpec, Int64, Struct, Uint8, UintPtr } from '~/lib/go' -import { newPackageSymbolFunc } from '../../utils' - -export const MAX_FILE_NAME_LEN = 128 -export const PKG_NAME = 'github.com/x1unix/go-playground/internal/gowasm/browserfs' - -export const sym = newPackageSymbolFunc(PKG_NAME) - -export interface SizedFileName { - len: number - data: Uint8Array -} - -export enum FileType { - Zero = 0, - Regular = 1, - Directory = 2, - SymLink = 3, -} - -export interface Inode { - id: number - parentId: number - fileType: FileType - size: number - createdAt: number - name: SizedFileName -} - -export const TSizedFileName = Struct(sym('sizedFileName'), [ - { key: 'len', type: Uint8 }, - { key: 'data', type: new UInt8ArrayTypeSpec(MAX_FILE_NAME_LEN) }, -]) - -export const TInode = Struct(sym('inode'), [ - { key: 'id', type: UintPtr }, - { key: 'parentId', type: UintPtr }, - { key: 'fileType', type: Uint8 }, - { key: 'size', type: Int64 }, - { key: 'createdAt', type: Int64 }, - { key: 'name', type: TSizedFileName }, -]) diff --git a/web/src/lib/gowasm/bindings/packagedb/binding.ts b/web/src/lib/gowasm/bindings/packagedb/binding.ts deleted file mode 100644 index e83522f0..00000000 --- a/web/src/lib/gowasm/bindings/packagedb/binding.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Package, PackageBinding, WasmExport } from '../../binder' -import type SyscallHelper from '../../syscall' -import { type PackageIndex } from './pkgindex' -import { Errno, SyscallError } from '~/lib/go/pkg/syscall' -import { - GoStringType, - Int, - type MemoryView, - type SliceHeader, - SliceHeaderType, - type StackReader, - stringEncoder, - UintPtr, -} from '~/lib/go' - -/** - * @see internal/gowasm/packagedb/index.go - */ -const MAX_PACKAGE_VERSION_LENGTH = 50 - -const checkVersionStringLimit = (version: string) => { - if (!version.length) { - throw new SyscallError(Errno.EINVAL) - } - - if (version.length > MAX_PACKAGE_VERSION_LENGTH) { - throw new SyscallError(Errno.ENAMETOOLONG) - } -} - -/** - * WASM imports binding to Go packages registry. - * - * @see internal/gowasm/packagedb/syscall_js.go - */ -@Package('github.com/x1unix/go-playground/internal/gowasm/packagedb') -export class PackageDBBinding extends PackageBinding { - constructor( - private readonly helper: SyscallHelper, - private readonly index: PackageIndex, - ) { - super() - } - - // func lookupPackage(pkgName string, out *[]byte, cb int) - @WasmExport('lookupPackage') - lookupPackage(sp: number, stack: StackReader, mem: MemoryView) { - stack.skipHeader() - const pkgName = stack.next(GoStringType) - const outPtr = stack.next(UintPtr) - const cbId = stack.next(Int) - if (!cbId) { - throw new SyscallError(Errno.EBADSLT) - } - - this.helper.doAsync(cbId, async () => { - if (!outPtr) { - throw new SyscallError(Errno.EFAULT) - } - - const dstSlice = mem.read(outPtr, SliceHeaderType) - const version = await this.index.lookupPackage(pkgName) - if (!version) { - throw new SyscallError(Errno.ENOENT) - } - - if (dstSlice.cap < version.length) { - throw new SyscallError(Errno.ENOMEM) - } - - if (!dstSlice.data) { - throw new SyscallError(Errno.EFAULT) - } - - const versionBytes = stringEncoder.encode(version) - - // Update slice length - dstSlice.len = versionBytes.length - mem.write(outPtr, SliceHeaderType, dstSlice) - mem.set(dstSlice.data, versionBytes) - }) - } - - // func registerPackage(pkgName, version string, cb int) - @WasmExport('registerPackage') - registerPackage(sp: number, stack: StackReader, mem: MemoryView) { - stack.skipHeader() - const pkgName = stack.next(GoStringType) - const version = stack.next(GoStringType) - const cbId = stack.next(Int) - if (!cbId) { - throw new SyscallError(Errno.EBADSLT) - } - - this.helper.doAsync(cbId, async () => { - checkVersionStringLimit(version) - if (!pkgName.length) { - throw new SyscallError(Errno.EINVAL) - } - - await this.index.registerPackage(pkgName, version) - }) - } - - // func removePackage(pkgName string, cb int) - @WasmExport('removePackage') - removePackage(sp: number, stack: StackReader, mem: MemoryView) { - stack.skipHeader() - const pkgName = stack.next(GoStringType) - const cbId = stack.next(Int) - if (!cbId) { - throw new SyscallError(Errno.EBADSLT) - } - - this.helper.doAsync(cbId, async () => { - if (!pkgName.length) { - throw new SyscallError(Errno.EINVAL) - } - - const ok = await this.index.removePackage(pkgName) - if (!ok) { - throw new SyscallError(Errno.ENOENT) - } - }) - } -} diff --git a/web/src/lib/gowasm/bindings/packagedb/index.ts b/web/src/lib/gowasm/bindings/packagedb/index.ts deleted file mode 100644 index d2558b91..00000000 --- a/web/src/lib/gowasm/bindings/packagedb/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './binding' -export * from './pkgindex' diff --git a/web/src/lib/gowasm/bindings/packagedb/pkgindex.ts b/web/src/lib/gowasm/bindings/packagedb/pkgindex.ts deleted file mode 100644 index 4b1b40d2..00000000 --- a/web/src/lib/gowasm/bindings/packagedb/pkgindex.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Package index interface - */ -export interface PackageIndex { - /** - * Looks up for a package and returns its version. - * Returns null if no package was found in cache. - * - * @param name Package name - */ - lookupPackage: (name: string) => Promise - - /** - * Registers or updates an existing package information in index. - * - * @param pkgName Package name - * @param version Version - */ - registerPackage: (pkgName: string, version: string) => Promise - - /** - * Removes package from index. - * Returns false if package didn't exist. - * - * @param pkgName Package name - */ - removePackage: (pkgName: string) => Promise -} diff --git a/web/src/lib/gowasm/bindings/stdio/binding.ts b/web/src/lib/gowasm/bindings/stdio/binding.ts deleted file mode 100644 index 03053a29..00000000 --- a/web/src/lib/gowasm/bindings/stdio/binding.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Package, PackageBinding, WasmExport } from '~/lib/gowasm' -import { Int, type MemoryView, type SliceHeader, SliceHeaderType, type StackReader, stringDecoder } from '~/lib/go' -import { type ConsoleHandler, type ConsoleStreamType } from './console' - -/** - * WASM imports binding to console input/output. - * - * @see internal/gowasm/stdio_js.go - */ -@Package('github.com/x1unix/go-playground/internal/gowasm') -export class ConsoleBinding extends PackageBinding { - constructor(private readonly handler: ConsoleHandler) { - super() - } - - // func wasmConsoleWrite(fd int, data []byte) - @WasmExport('wasmConsoleWrite') - consoleWrite(sp: number, stack: StackReader, mem: MemoryView) { - stack.skipHeader() - const fd = stack.next(Int) - const slice = stack.next(SliceHeaderType) - - if (!slice.len || !slice.data) { - return - } - - const msg = stringDecoder.decode(mem.get(slice.data, slice.len, false)) - this.handler.write(fd, msg) - } -} diff --git a/web/src/lib/gowasm/bindings/stdio/console.ts b/web/src/lib/gowasm/bindings/stdio/console.ts deleted file mode 100644 index b66975a6..00000000 --- a/web/src/lib/gowasm/bindings/stdio/console.ts +++ /dev/null @@ -1,9 +0,0 @@ -export enum ConsoleStreamType { - Stdin = 0, - Stdout = 1, - Stderr = 2, -} - -export interface ConsoleHandler { - write: (fd: ConsoleStreamType, msg: string) => any -} diff --git a/web/src/lib/gowasm/bindings/stdio/index.ts b/web/src/lib/gowasm/bindings/stdio/index.ts deleted file mode 100644 index e44d61b5..00000000 --- a/web/src/lib/gowasm/bindings/stdio/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './binding' -export * from './console' diff --git a/web/src/lib/gowasm/bindings/wlog/binding.ts b/web/src/lib/gowasm/bindings/wlog/binding.ts deleted file mode 100644 index fb778e3a..00000000 --- a/web/src/lib/gowasm/bindings/wlog/binding.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Package, PackageBinding, WasmExport } from '~/lib/gowasm' -import { type MemoryView, type SliceHeader, SliceHeaderType, type StackReader, stringDecoder, Uint8 } from '~/lib/go' -import { ConsoleLogger, type Logger } from '~/lib/gowasm/bindings/wlog/logger' - -enum LogLevel { - Debug = 0, - Info = 1, -} - -@Package('github.com/x1unix/go-playground/internal/gowasm/wlog') -export class LoggerBinding extends PackageBinding { - constructor(private readonly logger: Logger = ConsoleLogger) { - super() - } - - @WasmExport('logWrite') - logWrite(sp: number, stack: StackReader, mem: MemoryView) { - stack.skipHeader() - const level = stack.next(Uint8) - const msgSlice = stack.next(SliceHeaderType) - const msg = stringDecoder.decode(mem.get(msgSlice.data, msgSlice.len, false)) - - if (level === LogLevel.Debug) { - this.logger.debug(msg) - return - } - - this.logger.info(msg) - } -} diff --git a/web/src/lib/gowasm/bindings/wlog/index.ts b/web/src/lib/gowasm/bindings/wlog/index.ts deleted file mode 100644 index 31a5a39b..00000000 --- a/web/src/lib/gowasm/bindings/wlog/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './binding' -export * from './logger' diff --git a/web/src/lib/gowasm/bindings/wlog/logger.ts b/web/src/lib/gowasm/bindings/wlog/logger.ts deleted file mode 100644 index 8732abc8..00000000 --- a/web/src/lib/gowasm/bindings/wlog/logger.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Logger implements service logger interface - */ -export interface Logger { - info: (msg: string) => any - debug: (msg: string) => any -} - -export const ConsoleLogger: Logger = { - info: console.log, - debug: console.log, -} diff --git a/web/src/lib/gowasm/bindings/worker/binding.ts b/web/src/lib/gowasm/bindings/worker/binding.ts deleted file mode 100644 index 3f7fb1bb..00000000 --- a/web/src/lib/gowasm/bindings/worker/binding.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Package, PackageBinding, WasmExport } from '~/lib/gowasm' -import { type Func, FuncType } from '~/lib/go/pkg/syscall/js' -import { type MemoryView, type StackReader, SliceOf, GoStringType, type GoWrapper } from '~/lib/go' -import { type Worker, type WorkerListener } from './types' - -@Package('github.com/x1unix/go-playground/internal/gowasm') -export class WorkerBinding extends PackageBinding { - constructor( - private readonly go: GoWrapper, - private readonly handler: WorkerListener, - ) { - super() - } - - // func registerWorkerEntrypoint(methods []string, handler js.Func) - @WasmExport('registerWorkerEntrypoint') - registerWorkerEntrypoint(sp: number, stack: StackReader, mem: MemoryView) { - // TODO: figure out why js.Func is not valid (Go: 24 but calculated size is 20) - stack.skipHeader() - const methods = stack.next(SliceOf(GoStringType)) - const callbackHandler = stack.next(FuncType) - const exportObj: T = Object.fromEntries( - methods.map((m) => [m, (...args) => this.go.callFunc(callbackHandler, [m, ...args])]), - ) as any - this.handler.onWorkerRegister(exportObj) - } -} diff --git a/web/src/lib/gowasm/bindings/worker/index.ts b/web/src/lib/gowasm/bindings/worker/index.ts deleted file mode 100644 index c49a0e76..00000000 --- a/web/src/lib/gowasm/bindings/worker/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './binding' -export * from './types' diff --git a/web/src/lib/gowasm/bindings/worker/types.ts b/web/src/lib/gowasm/bindings/worker/types.ts deleted file mode 100644 index 3d4c617a..00000000 --- a/web/src/lib/gowasm/bindings/worker/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface Worker { - exit: () => any -} - -export interface WorkerListener { - onWorkerRegister: (worker: T) => any -} diff --git a/web/src/lib/gowasm/index.ts b/web/src/lib/gowasm/index.ts deleted file mode 100644 index c341bac0..00000000 --- a/web/src/lib/gowasm/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './binder' diff --git a/web/src/lib/gowasm/syscall.ts b/web/src/lib/gowasm/syscall.ts deleted file mode 100644 index 32c77be3..00000000 --- a/web/src/lib/gowasm/syscall.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { WasmExport, Package, PackageBinding } from '~/lib/gowasm/binder' -import { type GoWrapper, js, type StackReader } from '~/lib/go' -import { Errno, SyscallError } from '~/lib/go/pkg/syscall' - -// list of syscall errors which should not be logged. -const suppressedErrors = new Set([Errno.ENOENT]) - -/** - * SyscallHelper contains extensions required for "gowasm" package. - * - * See: internal/gowasm/callback_js.go - */ -@Package('github.com/x1unix/go-playground/internal/gowasm') -export default class SyscallHelper extends PackageBinding { - private callbackFunc?: js.Func - - constructor( - private readonly go: GoWrapper, - private readonly debug = false, - ) { - super() - } - - @WasmExport('registerCallbackHandler') - private registerCallbackHandler(sp: number, reader: StackReader) { - reader.skipHeader() - const callbackFunc = reader.next(js.FuncType) - console.log('js: registered callback handler', callbackFunc) - this.callbackFunc = callbackFunc - } - - /** - * Send and notify Go about callback result. - * @param callbackId Callback ID - * @param result Result - */ - sendCallbackResult(callbackId: number, result: number) { - if (!this.callbackFunc) { - throw new Error('SyscallHelper: callback handler not registered.') - } - - if (this.debug) { - console.log('SyscallHelper: sendCallbackResult', { callbackId, result }) - } - - this.go.callFunc(this.callbackFunc, [callbackId, result]) - } - - /** - * Reports async error back to Go caller. - * - * If passed error is SyscallError, it will use its origin error code. - * @param callbackId - * @param err - */ - sendErrorResult(callbackId: number, err: Error | DOMException | Errno) { - const sysErr = SyscallError.fromError(err) - if (!suppressedErrors.has(sysErr.errno)) { - // eslint-disable-next-line @typescript-eslint/no-base-to-string - console.error(`gowasm: async callback thrown an error: ${err} (errno: ${sysErr.errno}, id: ${callbackId})`) - } - this.sendCallbackResult(callbackId, sysErr.errno) - } - - /** - * Perform async operation and return result to the Go worker by callback ID. - * - * Any throw error will be sent as error code back to Go worker. - * - * @param callbackId Callback ID - * @param fn Async function - */ - doAsync(callbackId: number, fn: () => Promise) { - try { - fn() - .then(() => { - this.sendCallbackResult(callbackId, 0) - }) - .catch((err: Error) => { - this.sendErrorResult(callbackId, err) - }) - } catch (err) { - this.sendErrorResult(callbackId, err as Error) - } - } -} diff --git a/web/src/lib/gowasm/utils.ts b/web/src/lib/gowasm/utils.ts deleted file mode 100644 index 3df5b167..00000000 --- a/web/src/lib/gowasm/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Returns a new function which appends package name to function/symbol name. - * - * @param pkgName Go Package name - */ -export const newPackageSymbolFunc = (pkgName) => (fnName) => `${pkgName}.${fnName}` diff --git a/web/src/services/go/tests/go.wasm b/web/src/services/go/tests/go.wasm deleted file mode 100755 index 43ba2f8a..00000000 Binary files a/web/src/services/go/tests/go.wasm and /dev/null differ