diff --git a/.github/workflows/measure-size.yml b/.github/workflows/measure-size.yml new file mode 100644 index 00000000..7118f3fa --- /dev/null +++ b/.github/workflows/measure-size.yml @@ -0,0 +1,28 @@ +name: Measure canonical app size + +on: ['pull_request'] + +jobs: + measure: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-go@v2 + with: + go-version: '~1.18.5' + - uses: gopherjs/output-size-action/measure@main + with: + name: jQuery TodoMVC + repo: https://github.com/gopherjs/todomvc + go-package: github.com/gopherjs/todomvc + report_json: /tmp/report.json + report_md: /tmp/report.md + - uses: actions/upload-artifact@v2 + with: + name: size_report + path: | + /tmp/report.json + /tmp/report.md + diff --git a/.github/workflows/publish-size.yml b/.github/workflows/publish-size.yml new file mode 100644 index 00000000..ae011147 --- /dev/null +++ b/.github/workflows/publish-size.yml @@ -0,0 +1,14 @@ +name: Publish canonical app size + +on: + workflow_run: + workflows: ["Measure canonical app size"] + types: ["completed"] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: gopherjs/output-size-action/publish@main + with: + report_artifact: size_report diff --git a/.std_test_pkg_exclusions b/.std_test_pkg_exclusions index 38ac7786..ae9691df 100644 --- a/.std_test_pkg_exclusions +++ b/.std_test_pkg_exclusions @@ -1,65 +1,22 @@ -context -crypto -crypto/internal/cipherhw -crypto/tls -crypto/x509/pkix -debug/gosym -debug/plan9obj -encoding +encoding/xml go/build -go/importer -go/internal/gccgoimporter -go/internal/gcimporter go/internal/srcimporter go/types -hash -image/color/palette -image/internal/imageutil -internal/cpu -internal/goroot -internal/nettrace -internal/poll -internal/race -internal/singleflight -internal/syscall/unix +internal/abi +internal/intern internal/syscall/windows internal/syscall/windows/registry internal/syscall/windows/sysdll -internal/testenv -internal/testlog -internal/trace internal/x/net/nettest -log -log/syslog -net -net/http -net/http/cgi net/http/httptest -net/http/httptrace net/http/httputil -net/http/internal net/http/pprof -net/internal/socktest net/rpc -net/smtp -os -os/exec -os/signal os/signal/internal/pty -os/user -plugin runtime runtime/cgo runtime/debug runtime/internal/atomic -runtime/internal/math -runtime/internal/sys runtime/pprof runtime/pprof/internal/profile -runtime/race runtime/trace -syscall -testing -testing/internal/testdeps -testing/iotest -unsafe diff --git a/README.md b/README.md index 64b2b22a..40c00263 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,48 @@ GopherJS - A compiler from Go to JavaScript [![Sourcegraph](https://sourcegraph.com/github.com/gopherjs/gopherjs/-/badge.svg)](https://sourcegraph.com/github.com/gopherjs/gopherjs?badge) [![Circle CI](https://circleci.com/gh/gopherjs/gopherjs.svg?style=svg)](https://circleci.com/gh/gopherjs/gopherjs) -GopherJS compiles Go code ([golang.org](https://golang.org/)) to pure JavaScript code. Its main purpose is to give you the opportunity to write front-end code in Go which will still run in all browsers. +GopherJS compiles Go code ([go.dev](https://go.dev/)) to pure JavaScript code. Its main purpose is to give you the opportunity to write front-end code in Go which will still run in all browsers. + +### Help us make GopherJS better! + + - ⤴️ **Help us make better decisions by filling a quick 15-question [GopherJS user survey](https://forms.gle/WEjZqZaPxTxjD9YP8)**. + - 📢 Report and discuss [issues](https://github.com/gopherjs/gopherjs/issues). + - 🎓 Share your knowledge and experience through [articles](https://github.com/gopherjs/gopherjs/wiki/Community-Tutorials-and-Blogs) and [documentation](https://github.com/gopherjs/gopherjs/tree/master/doc). + - 🛠️ Write GopherJS [bindings](https://github.com/gopherjs/gopherjs/wiki/Bindings) for other libraries or [contribute](https://github.com/gopherjs/gopherjs/wiki/Developer-Guidelines) to GopherJS itself. + +### What's new? + + - 2022-08-18: Go 1.18 support is [available](https://github.com/gopherjs/gopherjs/releases/tag/v1.18.0-beta1%2Bgo1.18.5)! + - 2021-09-19: Go 1.17 support is available! + - 2021-08-23: Go Modules are now fully supported. + - 2021-06-19: Complete `syscall/js` package implementation compatible with the upstream Go 1.16. + - 2021-04-04: **Go 1.16 is now officially supported!** 🎉 🎉 🎉 ### Playground + Give GopherJS a try on the [GopherJS Playground](http://gopherjs.github.io/playground/). ### What is supported? -Nearly everything, including Goroutines ([compatibility table](https://github.com/gopherjs/gopherjs/blob/master/doc/packages.md)). Performance is quite good in most cases, see [HTML5 game engine benchmark](https://ajhager.github.io/engi/demos/botmark.html). Cgo is not supported. + +Nearly everything, including Goroutines ([compatibility documentation](https://github.com/gopherjs/gopherjs/blob/master/doc/compatibility.md)). Performance is quite good in most cases, see [HTML5 game engine benchmark](https://ajhager.github.io/engi/demos/botmark.html). Cgo is not supported. ### Installation and Usage -Get or update GopherJS and dependencies with: + +GopherJS [requires Go 1.18 or newer](https://github.com/gopherjs/gopherjs/blob/master/doc/compatibility.md#go-version-compatibility). If you need an older Go +version, you can use an [older GopherJS release](https://github.com/gopherjs/gopherjs/releases). + +Install GopherJS with `go install`: ``` -go get -u github.com/gopherjs/gopherjs +go install github.com/gopherjs/gopherjs@latest # Or replace 'latest' with another version. +``` + +If your local Go distribution as reported by `go version` is newer than Go 1.18, then you need to set the `GOPHERJS_GOROOT` environment variable to a directory that contains a Go 1.18 distribution. For example: + +``` +go install golang.org/dl/go1.18.6@latest +go1.18.6 download +export GOPHERJS_GOROOT="$(go1.18.6 env GOROOT)" # Also add this line to your .profile or equivalent. ``` Now you can use `gopherjs build [package]`, `gopherjs build [files]` or `gopherjs install [package]` which behave similar to the `go` tool. For `main` packages, these commands create a `.js` file and `.js.map` source map in the current directory or in `$GOPATH/bin`. The generated JavaScript file can be used as usual in a website. Use `gopherjs help [command]` to get a list of possible command line flags, e.g. for minification and automatically watching for changes. @@ -46,6 +75,16 @@ Refreshing in the browser will rebuild the served files if needed. Compilation e If you include an argument, it will be the root from which everything is served. For example, if you run `gopherjs serve github.com/user/project` then the generated JavaScript for the package github.com/user/project/mypkg will be served at http://localhost:8080/mypkg/mypkg.js. +#### Environment Variables + +There are some GopherJS-specific environment variables: + + - `GOPHERJS_GOROOT` - if set, GopherJS uses this value as the default GOROOT + value, instead of using the system GOROOT as the default GOROOT value + - `GOPHERJS_SKIP_VERSION_CHECK` - if set to true, GopherJS will not check + Go version in the GOROOT for compatibility with the GopherJS release. This + is primarily useful for testing GopherJS against unreleased versions of Go. + ### Performance Tips - Use the `-m` command line flag to generate minified code. @@ -58,7 +97,7 @@ If you include an argument, it will be the root from which everything is served. - [Bindings to JavaScript APIs and libraries](https://github.com/gopherjs/gopherjs/wiki/bindings) - [GopherJS Blog](https://medium.com/gopherjs) - [GopherJS on Twitter](https://twitter.com/GopherJS) - +- [Examples, tutorials and blogs](https://github.com/gopherjs/gopherjs/wiki/Community-Tutorials-and-Blogs) ### Getting started #### Interacting with the DOM @@ -112,7 +151,7 @@ For more details see [Jason Stone's blog post](http://legacytotheedge.blogspot.d ### Architecture #### General -GopherJS emulates a 32-bit environment. This means that `int`, `uint` and `uintptr` have a precision of 32 bits. However, the explicit 64-bit integer types `int64` and `uint64` are supported. The `GOARCH` value of GopherJS is "js". You may use it as a build constraint: `// +build js`. +GopherJS emulates a 32-bit environment. This means that `int`, `uint` and `uintptr` have a precision of 32 bits. However, the explicit 64-bit integer types `int64` and `uint64` are supported. The `GOARCH` value of GopherJS is "js". You may use it as a build constraint: `// +build js,-wasm`. #### Application Lifecycle diff --git a/build/build.go b/build/build.go index 7ec14daf..f8603e71 100644 --- a/build/build.go +++ b/build/build.go @@ -1,3 +1,8 @@ +// Package build implements GopherJS build system. +// +// WARNING: This package's API is treated as internal and currently doesn't +// provide any API stability guarantee, use it at your own risk. If you need a +// stable interface, prefer invoking the gopherjs CLI tool as a subprocess. package build import ( @@ -8,100 +13,59 @@ import ( "go/scanner" "go/token" "go/types" - "io" - "io/ioutil" + "io/fs" "os" "os/exec" "path" "path/filepath" - "runtime" + "sort" "strconv" "strings" "time" + "github.com/gopherjs/gopherjs/internal/typeparams" + "github.com/fsnotify/fsnotify" "github.com/gopherjs/gopherjs/compiler" - "github.com/gopherjs/gopherjs/compiler/gopherjspkg" - "github.com/gopherjs/gopherjs/compiler/natives" + "github.com/gopherjs/gopherjs/compiler/astutil" + log "github.com/sirupsen/logrus" + "github.com/neelance/sourcemap" - "github.com/shurcooL/httpfs/vfsutil" "golang.org/x/tools/go/buildutil" -) -type ImportCError struct { - pkgPath string -} + "github.com/gopherjs/gopherjs/build/cache" +) -func (e *ImportCError) Error() string { - return e.pkgPath + `: importing "C" is not supported by GopherJS` -} +// DefaultGOROOT is the default GOROOT value for builds. +// +// It uses the GOPHERJS_GOROOT environment variable if it is set, +// or else the default GOROOT value of the system Go distribution. +var DefaultGOROOT = func() string { + if goroot, ok := os.LookupEnv("GOPHERJS_GOROOT"); ok { + // GopherJS-specific GOROOT value takes precedence. + return goroot + } + // The usual default GOROOT. + return build.Default.GOROOT +}() // NewBuildContext creates a build context for building Go packages // with GopherJS compiler. // // Core GopherJS packages (i.e., "github.com/gopherjs/gopherjs/js", "github.com/gopherjs/gopherjs/nosync") -// are loaded from gopherjspkg.FS virtual filesystem rather than GOPATH. -func NewBuildContext(installSuffix string, buildTags []string) *build.Context { - gopherjsRoot := filepath.Join(build.Default.GOROOT, "src", "github.com", "gopherjs", "gopherjs") - return &build.Context{ - GOROOT: build.Default.GOROOT, - GOPATH: build.Default.GOPATH, - GOOS: build.Default.GOOS, - GOARCH: "js", - InstallSuffix: installSuffix, - Compiler: "gc", - BuildTags: append(buildTags, - "netgo", // See https://godoc.org/net#hdr-Name_Resolution. - "purego", // See https://golang.org/issues/23172. - ), - ReleaseTags: build.Default.ReleaseTags, - CgoEnabled: true, // detect `import "C"` to throw proper error - - IsDir: func(path string) bool { - if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) { - path = filepath.ToSlash(path[len(gopherjsRoot):]) - if fi, err := vfsutil.Stat(gopherjspkg.FS, path); err == nil { - return fi.IsDir() - } - } - fi, err := os.Stat(path) - return err == nil && fi.IsDir() - }, - ReadDir: func(path string) ([]os.FileInfo, error) { - if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) { - path = filepath.ToSlash(path[len(gopherjsRoot):]) - if fis, err := vfsutil.ReadDir(gopherjspkg.FS, path); err == nil { - return fis, nil - } - } - return ioutil.ReadDir(path) - }, - OpenFile: func(path string) (io.ReadCloser, error) { - if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) { - path = filepath.ToSlash(path[len(gopherjsRoot):]) - if f, err := gopherjspkg.FS.Open(path); err == nil { - return f, nil - } - } - return os.Open(path) - }, +// are loaded from gopherjspkg.FS virtual filesystem if not present in GOPATH or +// go.mod. +func NewBuildContext(installSuffix string, buildTags []string) XContext { + e := DefaultEnv() + e.InstallSuffix = installSuffix + e.BuildTags = buildTags + realGOROOT := goCtx(e) + return &chainedCtx{ + primary: realGOROOT, + secondary: gopherjsCtx(e), } } -// statFile returns an os.FileInfo describing the named file. -// For files in "$GOROOT/src/github.com/gopherjs/gopherjs" directory, -// gopherjspkg.FS is consulted first. -func statFile(path string) (os.FileInfo, error) { - gopherjsRoot := filepath.Join(build.Default.GOROOT, "src", "github.com", "gopherjs", "gopherjs") - if strings.HasPrefix(path, gopherjsRoot+string(filepath.Separator)) { - path = filepath.ToSlash(path[len(gopherjsRoot):]) - if fi, err := vfsutil.Stat(gopherjspkg.FS, path); err == nil { - return fi, nil - } - } - return os.Stat(path) -} - // Import returns details about the Go package named by the import path. If the // path is a local import path naming a package that can be imported using // a standard import path, the returned package will set p.ImportPath to @@ -119,85 +83,13 @@ func statFile(path string) (os.FileInfo, error) { func Import(path string, mode build.ImportMode, installSuffix string, buildTags []string) (*PackageData, error) { wd, err := os.Getwd() if err != nil { - // Getwd may fail if we're in GOARCH=js mode. That's okay, handle + // Getwd may fail if we're in GOOS=js mode. That's okay, handle // it by falling back to empty working directory. It just means // Import will not be able to resolve relative import paths. wd = "" } - bctx := NewBuildContext(installSuffix, buildTags) - return importWithSrcDir(*bctx, path, wd, mode, installSuffix) -} - -func importWithSrcDir(bctx build.Context, path string, srcDir string, mode build.ImportMode, installSuffix string) (*PackageData, error) { - // bctx is passed by value, so it can be modified here. - var isVirtual bool - switch path { - case "syscall": - // syscall needs to use a typical GOARCH like amd64 to pick up definitions for _Socklen, BpfInsn, IFNAMSIZ, Timeval, BpfStat, SYS_FCNTL, Flock_t, etc. - bctx.GOARCH = runtime.GOARCH - bctx.InstallSuffix = "js" - if installSuffix != "" { - bctx.InstallSuffix += "_" + installSuffix - } - case "syscall/js": - // There are no buildable files in this package, but we need to use files in the virtual directory. - mode |= build.FindOnly - case "math/big": - // Use pure Go version of math/big; we don't want non-Go assembly versions. - bctx.BuildTags = append(bctx.BuildTags, "math_big_pure_go") - case "crypto/x509", "os/user": - // These stdlib packages have cgo and non-cgo versions (via build tags); we want the latter. - bctx.CgoEnabled = false - case "github.com/gopherjs/gopherjs/js", "github.com/gopherjs/gopherjs/nosync": - // These packages are already embedded via gopherjspkg.FS virtual filesystem (which can be - // safely vendored). Don't try to use vendor directory to resolve them. - mode |= build.IgnoreVendor - isVirtual = true - } - pkg, err := bctx.Import(path, srcDir, mode) - if err != nil { - return nil, err - } - - switch path { - case "os": - pkg.GoFiles = excludeExecutable(pkg.GoFiles) // Need to exclude executable implementation files, because some of them contain package scope variables that perform (indirectly) syscalls on init. - case "runtime": - pkg.GoFiles = []string{"error.go"} - case "runtime/internal/sys": - pkg.GoFiles = []string{fmt.Sprintf("zgoos_%s.go", bctx.GOOS), "zversion.go"} - case "runtime/pprof": - pkg.GoFiles = nil - case "internal/poll": - pkg.GoFiles = exclude(pkg.GoFiles, "fd_poll_runtime.go") - case "crypto/rand": - pkg.GoFiles = []string{"rand.go", "util.go"} - pkg.TestGoFiles = exclude(pkg.TestGoFiles, "rand_linux_test.go") // Don't want linux-specific tests (since linux-specific package files are excluded too). - } - - if len(pkg.CgoFiles) > 0 { - return nil, &ImportCError{path} - } - - if pkg.IsCommand() { - pkg.PkgObj = filepath.Join(pkg.BinDir, filepath.Base(pkg.ImportPath)+".js") - } - - if _, err := os.Stat(pkg.PkgObj); os.IsNotExist(err) && strings.HasPrefix(pkg.PkgObj, build.Default.GOROOT) { - // fall back to GOPATH - firstGopathWorkspace := filepath.SplitList(build.Default.GOPATH)[0] // TODO: Need to check inside all GOPATH workspaces. - gopathPkgObj := filepath.Join(firstGopathWorkspace, pkg.PkgObj[len(build.Default.GOROOT):]) - if _, err := os.Stat(gopathPkgObj); err == nil { - pkg.PkgObj = gopathPkgObj - } - } - - jsFiles, err := jsFilesFromDir(&bctx, pkg.Dir) - if err != nil { - return nil, err - } - - return &PackageData{Package: pkg, JSFiles: jsFiles, IsVirtual: isVirtual}, nil + xctx := NewBuildContext(installSuffix, buildTags) + return xctx.Import(path, wd, mode) } // excludeExecutable excludes all executable implementation .go files. @@ -228,21 +120,22 @@ Outer: return s } +func include(files []string, includes ...string) []string { + files = exclude(files, includes...) // Ensure there won't be duplicates. + files = append(files, includes...) + return files +} + // ImportDir is like Import but processes the Go package found in the named // directory. func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTags []string) (*PackageData, error) { - bctx := NewBuildContext(installSuffix, buildTags) - pkg, err := bctx.ImportDir(dir, mode) + xctx := NewBuildContext(installSuffix, buildTags) + pkg, err := xctx.Import(".", dir, mode) if err != nil { return nil, err } - jsFiles, err := jsFilesFromDir(bctx, pkg.Dir) - if err != nil { - return nil, err - } - - return &PackageData{Package: pkg, JSFiles: jsFiles}, nil + return pkg, nil } // parseAndAugment parses and returns all .go files of given pkg. @@ -256,67 +149,23 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag // as an existing file from the standard library). For all identifiers that exist // in the original AND the overrides, the original identifier in the AST gets // replaced by `_`. New identifiers that don't exist in original package get added. -func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileSet *token.FileSet) ([]*ast.File, error) { +func parseAndAugment(xctx XContext, pkg *PackageData, isTest bool, fileSet *token.FileSet) ([]*ast.File, []JSFile, error) { var files []*ast.File replacedDeclNames := make(map[string]bool) - funcName := func(d *ast.FuncDecl) string { - if d.Recv == nil || len(d.Recv.List) == 0 { - return d.Name.Name - } - recv := d.Recv.List[0].Type - if star, ok := recv.(*ast.StarExpr); ok { - recv = star.X - } - return recv.(*ast.Ident).Name + "." + d.Name.Name - } + pruneOriginalFuncs := make(map[string]bool) + isXTest := strings.HasSuffix(pkg.ImportPath, "_test") importPath := pkg.ImportPath if isXTest { importPath = importPath[:len(importPath)-5] } - nativesContext := &build.Context{ - GOROOT: "/", - GOOS: build.Default.GOOS, - GOARCH: "js", - Compiler: "gc", - JoinPath: path.Join, - SplitPathList: func(list string) []string { - if list == "" { - return nil - } - return strings.Split(list, "/") - }, - IsAbsPath: path.IsAbs, - IsDir: func(name string) bool { - dir, err := natives.FS.Open(name) - if err != nil { - return false - } - defer dir.Close() - info, err := dir.Stat() - if err != nil { - return false - } - return info.IsDir() - }, - HasSubdir: func(root, name string) (rel string, ok bool) { - panic("not implemented") - }, - ReadDir: func(name string) (fi []os.FileInfo, err error) { - dir, err := natives.FS.Open(name) - if err != nil { - return nil, err - } - defer dir.Close() - return dir.Readdir(0) - }, - OpenFile: func(name string) (r io.ReadCloser, err error) { - return natives.FS.Open(name) - }, - } + jsFiles := []JSFile{} + + nativesContext := overlayCtx(xctx.Env()) if nativesPkg, err := nativesContext.Import(importPath, "", 0); err == nil { + jsFiles = nativesPkg.JSFiles names := nativesPkg.GoFiles if isTest { names = append(names, nativesPkg.TestGoFiles...) @@ -326,11 +175,14 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS } for _, name := range names { fullPath := path.Join(nativesPkg.Dir, name) - r, err := nativesContext.OpenFile(fullPath) + r, err := nativesContext.bctx.OpenFile(fullPath) if err != nil { panic(err) } - file, err := parser.ParseFile(fileSet, fullPath, r, parser.ParseComments) + // Files should be uniquely named and in the original package directory in order to be + // ordered correctly + newPath := path.Join(pkg.Dir, "gopherjs__"+name) + file, err := parser.ParseFile(fileSet, newPath, r, parser.ParseComments) if err != nil { panic(err) } @@ -338,7 +190,9 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: - replacedDeclNames[funcName(d)] = true + k := astutil.FuncKey(d) + replacedDeclNames[k] = true + pruneOriginalFuncs[k] = astutil.PruneOriginal(d) case *ast.GenDecl: switch d.Tok { case token.TYPE: @@ -364,9 +218,9 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS if !filepath.IsAbs(name) { // name might be absolute if specified directly. E.g., `gopherjs build /abs/file.go`. name = filepath.Join(pkg.Dir, name) } - r, err := buildutil.OpenFile(bctx, name) + r, err := buildutil.OpenFile(pkg.bctx, name) if err != nil { - return nil, err + return nil, nil, err } file, err := parser.ParseFile(fileSet, name, r, parser.ParseComments) r.Close() @@ -385,7 +239,7 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS } switch pkg.ImportPath { - case "crypto/rand", "encoding/gob", "encoding/json", "expvar", "go/token", "log", "math/big", "math/rand", "regexp", "testing", "time": + case "crypto/rand", "encoding/gob", "encoding/json", "expvar", "go/token", "log", "math/big", "math/rand", "regexp", "time": for _, spec := range file.Imports { path, _ := strconv.Unquote(spec.Path.Value) if path == "sync" { @@ -400,8 +254,14 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: - if replacedDeclNames[funcName(d)] { + k := astutil.FuncKey(d) + if replacedDeclNames[k] { d.Name = ast.NewIdent("_") + if pruneOriginalFuncs[k] { + // Prune function bodies, since it may contain code invalid for + // GopherJS and pin unwanted imports. + d.Body = nil + } } case *ast.GenDecl: switch d.Tok { @@ -410,6 +270,8 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS s := spec.(*ast.TypeSpec) if replacedDeclNames[s.Name.Name] { s.Name = ast.NewIdent("_") + s.Type = &ast.StructType{Struct: s.Pos(), Fields: &ast.FieldList{}} + typeparams.TypeSpecRemoveTypeParam(s) } } case token.VAR, token.CONST: @@ -424,17 +286,18 @@ func parseAndAugment(bctx *build.Context, pkg *build.Package, isTest bool, fileS } } } + files = append(files, file) } + if errList != nil { - return nil, errList + return nil, nil, errList } - return files, nil + return files, jsFiles, nil } +// Options controls build process behavior. type Options struct { - GOROOT string - GOPATH string Verbose bool Quiet bool Watch bool @@ -443,8 +306,11 @@ type Options struct { Minify bool Color bool BuildTags []string + TestedPackage string + NoCache bool } +// PrintError message to the terminal. func (o *Options) PrintError(format string, a ...interface{}) { if o.Color { format = "\x1B[31m" + format + "\x1B[39m" @@ -452,6 +318,7 @@ func (o *Options) PrintError(format string, a ...interface{}) { fmt.Fprintf(os.Stderr, format, a...) } +// PrintSuccess message to the terminal. func (o *Options) PrintSuccess(format string, a ...interface{}) { if o.Color { format = "\x1B[32m" + format + "\x1B[39m" @@ -459,37 +326,169 @@ func (o *Options) PrintSuccess(format string, a ...interface{}) { fmt.Fprintf(os.Stderr, format, a...) } +// JSFile represents a *.inc.js file metadata and content. +type JSFile struct { + Path string // Full file path for the build context the file came from. + ModTime time.Time + Content []byte +} + +// PackageData is an extension of go/build.Package with additional metadata +// GopherJS requires. type PackageData struct { *build.Package - JSFiles []string - IsTest bool // IsTest is true if the package is being built for running tests. + JSFiles []JSFile + // IsTest is true if the package is being built for running tests. + IsTest bool SrcModTime time.Time UpToDate bool - IsVirtual bool // If true, the package does not have a corresponding physical directory on disk. + // If true, the package does not have a corresponding physical directory on disk. + IsVirtual bool + + bctx *build.Context // The original build context this package came from. } -type Session struct { - options *Options - bctx *build.Context - Archives map[string]*compiler.Archive - Types map[string]*types.Package - Watcher *fsnotify.Watcher +func (p PackageData) String() string { + return fmt.Sprintf("%s [is_test=%v]", p.ImportPath, p.IsTest) +} + +// FileModTime returns the most recent modification time of the package's source +// files. This includes all .go and .inc.js that would be included in the build, +// but excludes any dependencies. +func (p PackageData) FileModTime() time.Time { + newest := time.Time{} + for _, file := range p.JSFiles { + if file.ModTime.After(newest) { + newest = file.ModTime + } + } + + // Unfortunately, build.Context methods don't allow us to Stat and individual + // file, only to enumerate a directory. So we first get mtimes for all files + // in the package directory, and then pick the newest for the relevant GoFiles. + mtimes := map[string]time.Time{} + files, err := buildutil.ReadDir(p.bctx, p.Dir) + if err != nil { + log.Errorf("Failed to enumerate files in the %q in context %v: %s. Assuming time.Now().", p.Dir, p.bctx, err) + return time.Now() + } + for _, file := range files { + mtimes[file.Name()] = file.ModTime() + } + + for _, file := range p.GoFiles { + t, ok := mtimes[file] + if !ok { + log.Errorf("No mtime found for source file %q of package %q, assuming time.Now().", file, p.Name) + return time.Now() + } + if t.After(newest) { + newest = t + } + } + return newest +} + +// InternalBuildContext returns the build context that produced the package. +// +// WARNING: This function is a part of internal API and will be removed in +// future. +func (p *PackageData) InternalBuildContext() *build.Context { + return p.bctx } -func NewSession(options *Options) *Session { - if options.GOROOT == "" { - options.GOROOT = build.Default.GOROOT +// TestPackage returns a variant of the package with "internal" tests. +func (p *PackageData) TestPackage() *PackageData { + return &PackageData{ + Package: &build.Package{ + Name: p.Name, + ImportPath: p.ImportPath, + Dir: p.Dir, + GoFiles: append(p.GoFiles, p.TestGoFiles...), + Imports: append(p.Imports, p.TestImports...), + EmbedPatternPos: joinEmbedPatternPos(p.EmbedPatternPos, p.TestEmbedPatternPos), + }, + IsTest: true, + JSFiles: p.JSFiles, + bctx: p.bctx, } - if options.GOPATH == "" { - options.GOPATH = build.Default.GOPATH +} + +// XTestPackage returns a variant of the package with "external" tests. +func (p *PackageData) XTestPackage() *PackageData { + return &PackageData{ + Package: &build.Package{ + Name: p.Name + "_test", + ImportPath: p.ImportPath + "_test", + Dir: p.Dir, + GoFiles: p.XTestGoFiles, + Imports: p.XTestImports, + EmbedPatternPos: p.XTestEmbedPatternPos, + }, + IsTest: true, + bctx: p.bctx, } +} + +// InstallPath returns the path where "gopherjs install" command should place the +// generated output. +func (p *PackageData) InstallPath() string { + if p.IsCommand() { + name := filepath.Base(p.ImportPath) + ".js" + // For executable packages, mimic go tool behavior if possible. + if gobin := os.Getenv("GOBIN"); gobin != "" { + return filepath.Join(gobin, name) + } else if gopath := os.Getenv("GOPATH"); gopath != "" { + return filepath.Join(gopath, "bin", name) + } else if home, err := os.UserHomeDir(); err == nil { + return filepath.Join(home, "go", "bin", name) + } + } + return p.PkgObj +} + +// Session manages internal state GopherJS requires to perform a build. +// +// This is the main interface to GopherJS build system. Session lifetime is +// roughly equivalent to a single GopherJS tool invocation. +type Session struct { + options *Options + xctx XContext + buildCache cache.BuildCache + + // Binary archives produced during the current session and assumed to be + // up to date with input sources and dependencies. In the -w ("watch") mode + // must be cleared upon entering watching. + UpToDateArchives map[string]*compiler.Archive + Types map[string]*types.Package + Watcher *fsnotify.Watcher +} + +// NewSession creates a new GopherJS build session. +func NewSession(options *Options) (*Session, error) { options.Verbose = options.Verbose || options.Watch s := &Session{ - options: options, - Archives: make(map[string]*compiler.Archive), + options: options, + UpToDateArchives: make(map[string]*compiler.Archive), + } + s.xctx = NewBuildContext(s.InstallSuffix(), s.options.BuildTags) + env := s.xctx.Env() + + // Go distribution version check. + if err := compiler.CheckGoVersion(env.GOROOT); err != nil { + return nil, err + } + + s.buildCache = cache.BuildCache{ + GOOS: env.GOOS, + GOARCH: env.GOARCH, + GOROOT: env.GOROOT, + GOPATH: env.GOPATH, + BuildTags: append([]string{}, env.BuildTags...), + Minify: options.Minify, + TestedPackage: options.TestedPackage, } - s.bctx = NewBuildContext(s.InstallSuffix(), s.options.BuildTags) s.Types = make(map[string]*types.Package) if options.Watch { if out, err := exec.Command("ulimit", "-n").Output(); err == nil { @@ -501,15 +500,16 @@ func NewSession(options *Options) *Session { var err error s.Watcher, err = fsnotify.NewWatcher() if err != nil { - panic(err) + return nil, err } } - return s + return s, nil } -// BuildContext returns the session's build context. -func (s *Session) BuildContext() *build.Context { return s.bctx } +// XContext returns the session's build context. +func (s *Session) XContext() XContext { return s.xctx } +// InstallSuffix returns the suffix added to the generated output file. func (s *Session) InstallSuffix() string { if s.options.Minify { return "min" @@ -517,50 +517,90 @@ func (s *Session) InstallSuffix() string { return "" } -func (s *Session) BuildDir(packagePath string, importPath string, pkgObj string) error { - if s.Watcher != nil { - s.Watcher.Add(packagePath) +// GoRelease returns Go release version this session is building with. +func (s *Session) GoRelease() string { + return compiler.GoRelease(s.xctx.Env().GOROOT) +} + +// BuildFiles passed to the GopherJS tool as if they were a package. +// +// A ephemeral package will be created with only the provided files. This +// function is intended for use with, for example, `gopherjs run main.go`. +func (s *Session) BuildFiles(filenames []string, pkgObj string, cwd string) error { + if len(filenames) == 0 { + return fmt.Errorf("no input sources are provided") } - buildPkg, err := s.bctx.ImportDir(packagePath, 0) - if err != nil { - return err + + normalizedDir := func(filename string) string { + d := filepath.Dir(filename) + if !filepath.IsAbs(d) { + d = filepath.Join(cwd, d) + } + return filepath.Clean(d) } - pkg := &PackageData{Package: buildPkg} - jsFiles, err := jsFilesFromDir(s.bctx, pkg.Dir) - if err != nil { - return err + + // Ensure all source files are in the same directory. + dirSet := map[string]bool{} + for _, file := range filenames { + dirSet[normalizedDir(file)] = true } - pkg.JSFiles = jsFiles - archive, err := s.BuildPackage(pkg) - if err != nil { - return err + dirList := []string{} + for dir := range dirSet { + dirList = append(dirList, dir) } - if pkgObj == "" { - pkgObj = filepath.Base(packagePath) + ".js" + sort.Strings(dirList) + if len(dirList) != 1 { + return fmt.Errorf("named files must all be in one directory; have: %v", strings.Join(dirList, ", ")) } - if pkg.IsCommand() && !pkg.UpToDate { - if err := s.WriteCommandPackage(archive, pkgObj); err != nil { - return err + + root := dirList[0] + ctx := build.Default + ctx.UseAllFiles = true + ctx.ReadDir = func(dir string) ([]fs.FileInfo, error) { + n := len(filenames) + infos := make([]fs.FileInfo, n) + for i := 0; i < n; i++ { + info, err := os.Stat(filenames[i]) + if err != nil { + return nil, err + } + infos[i] = info } + return infos, nil } - return nil -} + p, err := ctx.Import(".", root, 0) + if err != nil { + return err + } + p.Name = "main" + p.ImportPath = "main" -func (s *Session) BuildFiles(filenames []string, pkgObj string, packagePath string) error { pkg := &PackageData{ - Package: &build.Package{ - Name: "main", - ImportPath: "main", - Dir: packagePath, - }, + Package: p, + // This ephemeral package doesn't have a unique import path to be used as a + // build cache key, so we never cache it. + SrcModTime: time.Now().Add(time.Hour), + bctx: &goCtx(s.xctx.Env()).bctx, } for _, file := range filenames { - if strings.HasSuffix(file, ".inc.js") { - pkg.JSFiles = append(pkg.JSFiles, file) + if !strings.HasSuffix(file, ".inc.js") { continue } - pkg.GoFiles = append(pkg.GoFiles, file) + + content, err := os.ReadFile(file) + if err != nil { + return fmt.Errorf("failed to read %s: %w", file, err) + } + info, err := os.Stat(file) + if err != nil { + return fmt.Errorf("failed to stat %s: %w", file, err) + } + pkg.JSFiles = append(pkg.JSFiles, JSFile{ + Path: filepath.Join(pkg.Dir, filepath.Base(file)), + ModTime: info.ModTime(), + Content: content, + }) } archive, err := s.BuildPackage(pkg) @@ -573,13 +613,20 @@ func (s *Session) BuildFiles(filenames []string, pkgObj string, packagePath stri return s.WriteCommandPackage(archive, pkgObj) } +// BuildImportPath loads and compiles package with the given import path. +// +// Relative paths are interpreted relative to the current working dir. func (s *Session) BuildImportPath(path string) (*compiler.Archive, error) { _, archive, err := s.buildImportPathWithSrcDir(path, "") return archive, err } +// buildImportPathWithSrcDir builds the package specified by the import path. +// +// Relative import paths are interpreted relative to the passed srcDir. If +// srcDir is empty, current working directory is assumed. func (s *Session) buildImportPathWithSrcDir(path string, srcDir string) (*PackageData, *compiler.Archive, error) { - pkg, err := importWithSrcDir(*s.bctx, path, srcDir, 0, s.InstallSuffix()) + pkg, err := s.xctx.Import(path, srcDir, 0) if s.Watcher != nil && pkg != nil { // add watch even on error s.Watcher.Add(pkg.Dir) } @@ -595,122 +642,83 @@ func (s *Session) buildImportPathWithSrcDir(path string, srcDir string) (*Packag return pkg, archive, nil } +// BuildPackage compiles an already loaded package. func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) { - if archive, ok := s.Archives[pkg.ImportPath]; ok { + if archive, ok := s.UpToDateArchives[pkg.ImportPath]; ok { return archive, nil } - if pkg.PkgObj != "" { - var fileInfo os.FileInfo - gopherjsBinary, err := os.Executable() - if err == nil { - fileInfo, err = os.Stat(gopherjsBinary) - if err == nil { - pkg.SrcModTime = fileInfo.ModTime() - } + var fileInfo os.FileInfo + gopherjsBinary, err := os.Executable() + if err == nil { + fileInfo, err = os.Stat(gopherjsBinary) + if err == nil && fileInfo.ModTime().After(pkg.SrcModTime) { + pkg.SrcModTime = fileInfo.ModTime() } - if err != nil { - os.Stderr.WriteString("Could not get GopherJS binary's modification timestamp. Please report issue.\n") - pkg.SrcModTime = time.Now() - } - - for _, importedPkgPath := range pkg.Imports { - // Ignore all imports that aren't mentioned in import specs of pkg. - // For example, this ignores imports such as runtime/internal/sys and runtime/internal/atomic. - ignored := true - for _, pos := range pkg.ImportPos[importedPkgPath] { - importFile := filepath.Base(pos.Filename) - for _, file := range pkg.GoFiles { - if importFile == file { - ignored = false - break - } - } - if !ignored { - break - } - } + } + if err != nil { + os.Stderr.WriteString("Could not get GopherJS binary's modification timestamp. Please report issue.\n") + pkg.SrcModTime = time.Now() + } - if importedPkgPath == "unsafe" || ignored { - continue - } - importedPkg, _, err := s.buildImportPathWithSrcDir(importedPkgPath, pkg.Dir) - if err != nil { - return nil, err - } - impModTime := importedPkg.SrcModTime - if impModTime.After(pkg.SrcModTime) { - pkg.SrcModTime = impModTime - } + for _, importedPkgPath := range pkg.Imports { + if importedPkgPath == "unsafe" { + continue } - - for _, name := range append(pkg.GoFiles, pkg.JSFiles...) { - fileInfo, err := statFile(filepath.Join(pkg.Dir, name)) - if err != nil { - return nil, err - } - if fileInfo.ModTime().After(pkg.SrcModTime) { - pkg.SrcModTime = fileInfo.ModTime() - } + importedPkg, _, err := s.buildImportPathWithSrcDir(importedPkgPath, pkg.Dir) + if err != nil { + return nil, err } - pkgObjFileInfo, err := os.Stat(pkg.PkgObj) - if err == nil && !pkg.SrcModTime.After(pkgObjFileInfo.ModTime()) { - // package object is up to date, load from disk if library - pkg.UpToDate = true - if pkg.IsCommand() { - return nil, nil - } + impModTime := importedPkg.SrcModTime + if impModTime.After(pkg.SrcModTime) { + pkg.SrcModTime = impModTime + } + } - objFile, err := os.Open(pkg.PkgObj) - if err != nil { - return nil, err - } - defer objFile.Close() + if pkg.FileModTime().After(pkg.SrcModTime) { + pkg.SrcModTime = pkg.FileModTime() + } - archive, err := compiler.ReadArchive(pkg.PkgObj, pkg.ImportPath, objFile, s.Types) - if err != nil { - return nil, err + if !s.options.NoCache { + archive := s.buildCache.LoadArchive(pkg.ImportPath) + if archive != nil && !pkg.SrcModTime.After(archive.BuildTime) { + if err := archive.RegisterTypes(s.Types); err != nil { + panic(fmt.Errorf("Failed to load type information from %v: %w", archive, err)) } - - s.Archives[pkg.ImportPath] = archive - return archive, err + s.UpToDateArchives[pkg.ImportPath] = archive + // Existing archive is up to date, no need to build it from scratch. + return archive, nil } } + // Existing archive is out of date or doesn't exist, let's build the package. fileSet := token.NewFileSet() - files, err := parseAndAugment(s.bctx, pkg.Package, pkg.IsTest, fileSet) + files, overlayJsFiles, err := parseAndAugment(s.xctx, pkg, pkg.IsTest, fileSet) if err != nil { return nil, err } - localImportPathCache := make(map[string]*compiler.Archive) + embed, err := embedFiles(pkg, fileSet, files) + if err != nil { + return nil, err + } + if embed != nil { + files = append(files, embed) + } + importContext := &compiler.ImportContext{ Packages: s.Types, - Import: func(path string) (*compiler.Archive, error) { - if archive, ok := localImportPathCache[path]; ok { - return archive, nil - } - _, archive, err := s.buildImportPathWithSrcDir(path, pkg.Dir) - if err != nil { - return nil, err - } - localImportPathCache[path] = archive - return archive, nil - }, + Import: s.ImportResolverFor(pkg), } archive, err := compiler.Compile(pkg.ImportPath, files, fileSet, importContext, s.options.Minify) if err != nil { return nil, err } - for _, jsFile := range pkg.JSFiles { - code, err := ioutil.ReadFile(filepath.Join(pkg.Dir, jsFile)) - if err != nil { - return nil, err - } + for _, jsFile := range append(pkg.JSFiles, overlayJsFiles...) { archive.IncJSCode = append(archive.IncJSCode, []byte("\t(function() {\n")...) - archive.IncJSCode = append(archive.IncJSCode, code...) + archive.IncJSCode = append(archive.IncJSCode, jsFile.Content...) archive.IncJSCode = append(archive.IncJSCode, []byte("\n\t}).call($global);\n")...) } @@ -718,41 +726,31 @@ func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) { fmt.Println(pkg.ImportPath) } - s.Archives[pkg.ImportPath] = archive + s.buildCache.StoreArchive(archive) + s.UpToDateArchives[pkg.ImportPath] = archive - if pkg.PkgObj == "" || pkg.IsCommand() { - return archive, nil - } + return archive, nil +} - if err := s.writeLibraryPackage(archive, pkg.PkgObj); err != nil { - if strings.HasPrefix(pkg.PkgObj, s.options.GOROOT) { - // fall back to first GOPATH workspace - firstGopathWorkspace := filepath.SplitList(s.options.GOPATH)[0] - if err := s.writeLibraryPackage(archive, filepath.Join(firstGopathWorkspace, pkg.PkgObj[len(s.options.GOROOT):])); err != nil { - return nil, err - } +// ImportResolverFor returns a function which returns a compiled package archive +// given an import path. +func (s *Session) ImportResolverFor(pkg *PackageData) func(string) (*compiler.Archive, error) { + return func(path string) (*compiler.Archive, error) { + if archive, ok := s.UpToDateArchives[path]; ok { return archive, nil } - return nil, err + _, archive, err := s.buildImportPathWithSrcDir(path, pkg.Dir) + return archive, err } - - return archive, nil } -func (s *Session) writeLibraryPackage(archive *compiler.Archive, pkgObj string) error { - if err := os.MkdirAll(filepath.Dir(pkgObj), 0777); err != nil { - return err - } - - objFile, err := os.Create(pkgObj) - if err != nil { - return err - } - defer objFile.Close() - - return compiler.WriteArchive(archive, objFile) +// SourceMappingCallback returns a call back for compiler.SourceMapFilter +// configured for the current build session. +func (s *Session) SourceMappingCallback(m *sourcemap.Map) func(generatedLine, generatedColumn int, originalPos token.Position) { + return NewMappingCallback(m, s.xctx.Env().GOROOT, s.xctx.Env().GOPATH, s.options.MapToLocalDisk) } +// WriteCommandPackage writes the final JavaScript output file at pkgObj path. func (s *Session) WriteCommandPackage(archive *compiler.Archive, pkgObj string) error { if err := os.MkdirAll(filepath.Dir(pkgObj), 0777); err != nil { return err @@ -777,11 +775,11 @@ func (s *Session) WriteCommandPackage(archive *compiler.Archive, pkgObj string) fmt.Fprintf(codeFile, "//# sourceMappingURL=%s.map\n", filepath.Base(pkgObj)) }() - sourceMapFilter.MappingCallback = NewMappingCallback(m, s.options.GOROOT, s.options.GOPATH, s.options.MapToLocalDisk) + sourceMapFilter.MappingCallback = s.SourceMappingCallback(m) } deps, err := compiler.ImportDependencies(archive, func(path string) (*compiler.Archive, error) { - if archive, ok := s.Archives[path]; ok { + if archive, ok := s.UpToDateArchives[path]; ok { return archive, nil } _, archive, err := s.buildImportPathWithSrcDir(path, "") @@ -790,9 +788,10 @@ func (s *Session) WriteCommandPackage(archive *compiler.Archive, pkgObj string) if err != nil { return err } - return compiler.WriteProgramCode(deps, sourceMapFilter) + return compiler.WriteProgramCode(deps, sourceMapFilter, s.GoRelease()) } +// NewMappingCallback creates a new callback for source map generation. func NewMappingCallback(m *sourcemap.Map, goroot, gopath string, localMap bool) func(generatedLine, generatedColumn int, originalPos token.Position) { return func(generatedLine, generatedColumn int, originalPos token.Position) { if !originalPos.IsValid() { @@ -817,20 +816,6 @@ func NewMappingCallback(m *sourcemap.Map, goroot, gopath string, localMap bool) } } -func jsFilesFromDir(bctx *build.Context, dir string) ([]string, error) { - files, err := buildutil.ReadDir(bctx, dir) - if err != nil { - return nil, err - } - var jsFiles []string - for _, file := range files { - if strings.HasSuffix(file.Name(), ".inc.js") && file.Name()[0] != '_' && file.Name()[0] != '.' { - jsFiles = append(jsFiles, file.Name()) - } - } - return jsFiles, nil -} - // hasGopathPrefix returns true and the length of the matched GOPATH workspace, // iff file has a prefix that matches one of the GOPATH workspaces. func hasGopathPrefix(file, gopath string) (hasGopathPrefix bool, prefixLen int) { @@ -844,7 +829,14 @@ func hasGopathPrefix(file, gopath string) (hasGopathPrefix bool, prefixLen int) return false, 0 } +// WaitForChange watches file system events and returns if either when one of +// the source files is modified. func (s *Session) WaitForChange() { + // Will need to re-validate up-to-dateness of all archives, so flush them from + // memory. + s.UpToDateArchives = map[string]*compiler.Archive{} + s.Types = map[string]*types.Package{} + s.options.PrintSuccess("watching for changes...\n") for { select { diff --git a/build/build_test.go b/build/build_test.go index 659aff3e..2fa17e2c 100644 --- a/build/build_test.go +++ b/build/build_test.go @@ -5,10 +5,8 @@ import ( gobuild "go/build" "go/token" "strconv" - "strings" "testing" - "github.com/kisielk/gotool" "github.com/shurcooL/go/importgraphutil" ) @@ -24,9 +22,13 @@ import ( func TestNativesDontImportExtraPackages(t *testing.T) { // Calculate the forward import graph for all standard library packages. // It's needed for populateImportSet. - stdOnly := gobuild.Default - stdOnly.GOPATH = "" // We only care about standard library, so skip all GOPATH packages. - forward, _, err := importgraphutil.BuildNoTests(&stdOnly) + stdOnly := goCtx(DefaultEnv()) + // Skip post-load package tweaks, since we are interested in the complete set + // of original sources. + stdOnly.noPostTweaks = true + // We only care about standard library, so skip all GOPATH packages. + stdOnly.bctx.GOPATH = "" + forward, _, err := importgraphutil.BuildNoTests(&stdOnly.bctx) if err != nil { t.Fatalf("importgraphutil.BuildNoTests: %v", err) } @@ -38,18 +40,20 @@ func TestNativesDontImportExtraPackages(t *testing.T) { // Note, this does not include transitive imports of test/xtest packages, // which could cause some false positives. It currently doesn't, but if it does, // then support for that should be added here. - populateImportSet := func(imports []string, set *stringSet) { + populateImportSet := func(imports []string) stringSet { + set := stringSet{} for _, p := range imports { - (*set)[p] = struct{}{} + set[p] = struct{}{} switch p { case "sync": - (*set)["github.com/gopherjs/gopherjs/nosync"] = struct{}{} + set["github.com/gopherjs/gopherjs/nosync"] = struct{}{} } transitiveImports := forward.Search(p) for p := range transitiveImports { - (*set)[p] = struct{}{} + set[p] = struct{}{} } } + return set } // Check all standard library packages. @@ -64,126 +68,52 @@ func TestNativesDontImportExtraPackages(t *testing.T) { // Then, github.com/gopherjs/gopherjs/build.parseAndAugment(*build.Package) returns []*ast.File. // Those augmented parsed Go files of the package are checked, one file at at time, one import // at a time. Each import is verified to belong in the set of allowed real imports. - for _, pkg := range gotool.ImportPaths([]string{"std"}) { - // Normal package. - { - // Import the real normal package, and populate its real import set. - bpkg, err := gobuild.Import(pkg, "", gobuild.ImportComment) - if err != nil { - t.Fatalf("gobuild.Import: %v", err) - } - realImports := make(stringSet) - populateImportSet(bpkg.Imports, &realImports) - - // Use parseAndAugment to get a list of augmented AST files. - fset := token.NewFileSet() - files, err := parseAndAugment(NewBuildContext("", nil), bpkg, false, fset) - if err != nil { - t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err) - } - - // Verify imports of normal augmented AST files. - for _, f := range files { - fileName := fset.File(f.Pos()).Name() - normalFile := !strings.HasSuffix(fileName, "_test.go") - if !normalFile { - continue - } - for _, imp := range f.Imports { - importPath, err := strconv.Unquote(imp.Path.Value) - if err != nil { - t.Fatalf("strconv.Unquote(%v): %v", imp.Path.Value, err) - } - if importPath == "github.com/gopherjs/gopherjs/js" { - continue - } - if _, ok := realImports[importPath]; !ok { - t.Errorf("augmented normal package %q imports %q in file %v, but real %q doesn't:\nrealImports = %v", bpkg.ImportPath, importPath, fileName, bpkg.ImportPath, realImports) - } - } - } - } - - // Test package. - { - // Import the real test package, and populate its real import set. - bpkg, err := gobuild.Import(pkg, "", gobuild.ImportComment) - if err != nil { - t.Fatalf("gobuild.Import: %v", err) - } - realTestImports := make(stringSet) - populateImportSet(bpkg.TestImports, &realTestImports) - - // Use parseAndAugment to get a list of augmented AST files. - fset := token.NewFileSet() - files, err := parseAndAugment(NewBuildContext("", nil), bpkg, true, fset) - if err != nil { - t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err) - } - - // Verify imports of test augmented AST files. - for _, f := range files { - fileName, pkgName := fset.File(f.Pos()).Name(), f.Name.String() - testFile := strings.HasSuffix(fileName, "_test.go") && !strings.HasSuffix(pkgName, "_test") - if !testFile { - continue - } - for _, imp := range f.Imports { - importPath, err := strconv.Unquote(imp.Path.Value) - if err != nil { - t.Fatalf("strconv.Unquote(%v): %v", imp.Path.Value, err) - } - if importPath == "github.com/gopherjs/gopherjs/js" { - continue - } - if _, ok := realTestImports[importPath]; !ok { - t.Errorf("augmented test package %q imports %q in file %v, but real %q doesn't:\nrealTestImports = %v", bpkg.ImportPath, importPath, fileName, bpkg.ImportPath, realTestImports) - } - } - } - } + matches, matchErr := stdOnly.Match([]string{"std"}) + if matchErr != nil { + t.Fatalf("Failed to list standard library packages: %s", err) + } + for _, pkgName := range matches { + pkgName := pkgName // Capture for the goroutine. + t.Run(pkgName, func(t *testing.T) { + t.Parallel() - // External test package. - { - // Import the real external test package, and populate its real import set. - bpkg, err := gobuild.Import(pkg, "", gobuild.ImportComment) + pkg, err := stdOnly.Import(pkgName, "", gobuild.ImportComment) if err != nil { t.Fatalf("gobuild.Import: %v", err) } - realXTestImports := make(stringSet) - populateImportSet(bpkg.XTestImports, &realXTestImports) - // Add _test suffix to import path to cause parseAndAugment to use external test mode. - bpkg.ImportPath += "_test" + for _, pkgVariant := range []*PackageData{pkg, pkg.TestPackage(), pkg.XTestPackage()} { + t.Logf("Checking package %s...", pkgVariant) - // Use parseAndAugment to get a list of augmented AST files, then check only the external test files. - fset := token.NewFileSet() - files, err := parseAndAugment(NewBuildContext("", nil), bpkg, true, fset) - if err != nil { - t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err) - } + // Capture the set of unmodified package imports. + realImports := populateImportSet(pkgVariant.Imports) - // Verify imports of external test augmented AST files. - for _, f := range files { - fileName, pkgName := fset.File(f.Pos()).Name(), f.Name.String() - xTestFile := strings.HasSuffix(fileName, "_test.go") && strings.HasSuffix(pkgName, "_test") - if !xTestFile { - continue + // Use parseAndAugment to get a list of augmented AST files. + fset := token.NewFileSet() + files, _, err := parseAndAugment(stdOnly, pkgVariant, pkgVariant.IsTest, fset) + if err != nil { + t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err) } - for _, imp := range f.Imports { - importPath, err := strconv.Unquote(imp.Path.Value) - if err != nil { - t.Fatalf("strconv.Unquote(%v): %v", imp.Path.Value, err) - } - if importPath == "github.com/gopherjs/gopherjs/js" { - continue - } - if _, ok := realXTestImports[importPath]; !ok { - t.Errorf("augmented external test package %q imports %q in file %v, but real %q doesn't:\nrealXTestImports = %v", bpkg.ImportPath, importPath, fileName, bpkg.ImportPath, realXTestImports) + + // Verify imports of augmented AST files. + for _, f := range files { + fileName := fset.File(f.Pos()).Name() + for _, imp := range f.Imports { + importPath, err := strconv.Unquote(imp.Path.Value) + if err != nil { + t.Fatalf("strconv.Unquote(%v): %v", imp.Path.Value, err) + } + if importPath == "github.com/gopherjs/gopherjs/js" { + continue + } + if _, ok := realImports[importPath]; !ok { + t.Errorf("augmented package %q imports %q in file %v, but real %q doesn't:\nrealImports = %v", + pkgVariant, importPath, fileName, pkgVariant.ImportPath, realImports) + } } } } - } + }) } } diff --git a/build/cache/cache.go b/build/cache/cache.go new file mode 100644 index 00000000..79e0471c --- /dev/null +++ b/build/cache/cache.go @@ -0,0 +1,161 @@ +// Package cache solves one of the hardest computer science problems in +// application to GopherJS compiler outputs. +package cache + +import ( + "crypto/sha256" + "fmt" + "go/build" + "os" + "path" + "path/filepath" + + "github.com/gopherjs/gopherjs/compiler" + log "github.com/sirupsen/logrus" +) + +// cacheRoot is the base path for GopherJS's own build cache. +// +// It serves a similar function to the Go build cache, but is a lot more +// simplistic and therefore not compatible with Go. We use this cache directory +// to store build artifacts for packages loaded from a module, for which PkgObj +// provided by go/build points inside the module source tree, which can cause +// inconvenience with version control, etc. +var cacheRoot = func() string { + path, err := os.UserCacheDir() + if err == nil { + return filepath.Join(path, "gopherjs", "build_cache") + } + + return filepath.Join(build.Default.GOPATH, "pkg", "gopherjs_build_cache") +}() + +// cachedPath returns a location inside the build cache for a given set of key +// strings. The set of keys must uniquely identify cacheable object. Prefer +// using more specific functions to ensure key consistency. +func cachedPath(keys ...string) string { + key := path.Join(keys...) + if key == "" { + panic("CachedPath() must not be used with an empty string") + } + sum := fmt.Sprintf("%x", sha256.Sum256([]byte(key))) + return filepath.Join(cacheRoot, sum[0:2], sum) +} + +// Clear the cache. This will remove *all* cached artifacts from *all* build +// configurations. +func Clear() error { + return os.RemoveAll(cacheRoot) +} + +// BuildCache manages build artifacts that are cached for incremental builds. +// +// Cache is designed to be non-durable: any store and load errors are swallowed +// and simply lead to a cache miss. The caller must be able to handle cache +// misses. Nil pointer to BuildCache is valid and simply disables caching. +// +// BuildCache struct fields represent build parameters which change invalidates +// the cache. For example, any artifacts that were cached for a minified build +// must not be reused for a non-minified build. GopherJS version change also +// invalidates the cache. It is callers responsibility to ensure that artifacts +// passed the the StoreArchive function were generated with the same build +// parameters as the cache is configured. +// +// There is no upper limit for the total cache size. It can be cleared +// programmatically via the Clear() function, or the user can just delete the +// directory if it grows too big. +// +// TODO(nevkontakte): changes in the input sources or dependencies doesn't +// currently invalidate the cache. This is handled at the higher level by +// checking cached archive timestamp against loaded package modification time. +// +// TODO(nevkontakte): this cache could benefit from checksum integrity checks. +type BuildCache struct { + GOOS string + GOARCH string + GOROOT string + GOPATH string + BuildTags []string + Minify bool + // When building for tests, import path of the package being tested. The + // package under test is built with *_test.go sources included, and since it + // may be imported by other packages in the binary we can't reuse the "normal" + // cache. + TestedPackage string +} + +func (bc BuildCache) String() string { + return fmt.Sprintf("%#v", bc) +} + +// StoreArchive compiled archive in the cache. Any error inside this method +// will cause the cache not to be persisted. +func (bc *BuildCache) StoreArchive(a *compiler.Archive) { + if bc == nil { + return // Caching is disabled. + } + path := cachedPath(bc.archiveKey(a.ImportPath)) + if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil { + log.Warningf("Failed to create build cache directory: %v", err) + return + } + // Write the archive in a temporary file first to avoid concurrency errors. + f, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path)) + if err != nil { + log.Warningf("Failed to temporary build cache file: %v", err) + return + } + defer f.Close() + if err := compiler.WriteArchive(a, f); err != nil { + log.Warningf("Failed to write build cache archive %q: %v", a, err) + // Make sure we don't leave a half-written archive behind. + os.Remove(f.Name()) + return + } + f.Close() + // Rename fully written file into its permanent name. + if err := os.Rename(f.Name(), path); err != nil { + log.Warningf("Failed to rename build cache archive to %q: %v", path, err) + } + log.Infof("Successfully stored build archive %q as %q.", a, path) +} + +// LoadArchive returns a previously cached archive of the given package or nil +// if it wasn't previously stored. +// +// The returned archive would have been built with the same configuration as +// the build cache was. +func (bc *BuildCache) LoadArchive(importPath string) *compiler.Archive { + if bc == nil { + return nil // Caching is disabled. + } + path := cachedPath(bc.archiveKey(importPath)) + f, err := os.Open(path) + if err != nil { + if os.IsNotExist(err) { + log.Infof("No cached package archive for %q.", importPath) + } else { + log.Warningf("Failed to open cached package archive for %q: %v", importPath, err) + } + return nil // Cache miss. + } + defer f.Close() + a, err := compiler.ReadArchive(importPath, f) + if err != nil { + log.Warningf("Failed to read cached package archive for %q: %v", importPath, err) + return nil // Invalid/corrupted archive, cache miss. + } + log.Infof("Found cached package archive for %q, built at %v.", importPath, a.BuildTime) + return a +} + +// commonKey returns a part of the cache key common for all artifacts generated +// under a given BuildCache configuration. +func (bc *BuildCache) commonKey() string { + return fmt.Sprintf("%#v + %v", *bc, compiler.Version) +} + +// archiveKey returns a full cache key for a package's compiled archive. +func (bc *BuildCache) archiveKey(importPath string) string { + return path.Join("archive", bc.commonKey(), importPath) +} diff --git a/build/cache/cache_test.go b/build/cache/cache_test.go new file mode 100644 index 00000000..fd89ec18 --- /dev/null +++ b/build/cache/cache_test.go @@ -0,0 +1,78 @@ +package cache + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/gopherjs/gopherjs/compiler" +) + +func TestStore(t *testing.T) { + cacheForTest(t) + + want := &compiler.Archive{ + ImportPath: "fake/package", + Imports: []string{"fake/dep"}, + } + + bc := BuildCache{} + if got := bc.LoadArchive(want.ImportPath); got != nil { + t.Errorf("Got: %s was found in the cache. Want: empty cache.", got.ImportPath) + } + bc.StoreArchive(want) + got := bc.LoadArchive(want.ImportPath) + if got == nil { + t.Errorf("Got: %s wan not found in the cache. Want: archive is can be loaded after store.", want.ImportPath) + } + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("Loaded archive is different from stored (-want,+got):\n%s", diff) + } + + // Make sure the package names are a part of the cache key. + if got := bc.LoadArchive("fake/other"); got != nil { + t.Errorf("Got: fake/other was found in cache: %#v. Want: nil for packages that weren't cached.", got) + } +} + +func TestInvalidation(t *testing.T) { + cacheForTest(t) + + tests := []struct { + cache1 BuildCache + cache2 BuildCache + }{ + { + cache1: BuildCache{Minify: true}, + cache2: BuildCache{Minify: false}, + }, { + cache1: BuildCache{GOOS: "dos"}, + cache2: BuildCache{GOOS: "amiga"}, + }, { + cache1: BuildCache{GOARCH: "m68k"}, + cache2: BuildCache{GOARCH: "mos6502"}, + }, { + cache1: BuildCache{GOROOT: "here"}, + cache2: BuildCache{GOROOT: "there"}, + }, { + cache1: BuildCache{GOPATH: "home"}, + cache2: BuildCache{GOPATH: "away"}, + }, + } + + for _, test := range tests { + a := &compiler.Archive{ImportPath: "package/fake"} + test.cache1.StoreArchive(a) + + if got := test.cache2.LoadArchive(a.ImportPath); got != nil { + t.Logf("-cache1,+cache2:\n%s", cmp.Diff(test.cache1, test.cache2)) + t.Errorf("Got: %v loaded from cache. Want: build parameter change invalidates cache.", got) + } + } +} + +func cacheForTest(t *testing.T) { + t.Helper() + originalRoot := cacheRoot + t.Cleanup(func() { cacheRoot = originalRoot }) + cacheRoot = t.TempDir() +} diff --git a/build/context.go b/build/context.go new file mode 100644 index 00000000..0d3b9fae --- /dev/null +++ b/build/context.go @@ -0,0 +1,478 @@ +package build + +import ( + "fmt" + "go/build" + "go/token" + "io" + "net/http" + "os" + "os/exec" + "path" + "path/filepath" + "sort" + "strings" + + _ "github.com/gopherjs/gopherjs/build/versionhack" // go/build release tags hack. + "github.com/gopherjs/gopherjs/compiler/gopherjspkg" + "github.com/gopherjs/gopherjs/compiler/natives" + "golang.org/x/tools/go/buildutil" +) + +// Env contains build environment configuration required to define an instance +// of XContext. +type Env struct { + GOROOT string + GOPATH string + + GOOS string + GOARCH string + + BuildTags []string + InstallSuffix string +} + +// DefaultEnv creates a new instance of build Env according to environment +// variables. +// +// By default, GopherJS will use GOOS=js GOARCH=ecmascript to build non-standard +// library packages. If GOOS or GOARCH environment variables are set and not +// empty, user-provided values will be used instead. This is done to facilitate +// transition from the legacy GopherJS behavior, which used native GOOS, and may +// be removed in future. +func DefaultEnv() Env { + e := Env{} + e.GOROOT = DefaultGOROOT + e.GOPATH = build.Default.GOPATH + + if val := os.Getenv("GOOS"); val != "" { + e.GOOS = val + } else { + e.GOOS = "js" + } + + if val := os.Getenv("GOARCH"); val != "" { + e.GOARCH = val + } else { + e.GOARCH = "ecmascript" + } + return e +} + +// XContext is an extension of go/build.Context with GopherJS-specific features. +// +// It abstracts away several different sources GopherJS can load its packages +// from, with a minimal API. +type XContext interface { + // Import returns details about the Go package named by the importPath, + // interpreting local import paths relative to the srcDir directory. + Import(path string, srcDir string, mode build.ImportMode) (*PackageData, error) + + // Env returns build environment configuration this context has been set up for. + Env() Env + + // Match explans build patterns into a set of matching import paths (see go help packages). + Match(patterns []string) ([]string, error) +} + +// simpleCtx is a wrapper around go/build.Context with support for GopherJS-specific +// features. +type simpleCtx struct { + bctx build.Context + isVirtual bool // Imported packages don't have a physical directory on disk. + noPostTweaks bool // Don't apply post-load tweaks to packages. For tests only. +} + +// Import implements XContext.Import(). +func (sc simpleCtx) Import(importPath string, srcDir string, mode build.ImportMode) (*PackageData, error) { + bctx, mode := sc.applyPreloadTweaks(importPath, srcDir, mode) + pkg, err := bctx.Import(importPath, srcDir, mode) + if err != nil { + return nil, err + } + jsFiles, err := jsFilesFromDir(&sc.bctx, pkg.Dir) + if err != nil { + return nil, fmt.Errorf("failed to enumerate .inc.js files in %s: %w", pkg.Dir, err) + } + if !path.IsAbs(pkg.Dir) { + pkg.Dir = mustAbs(pkg.Dir) + } + pkg = sc.applyPostloadTweaks(pkg) + + return &PackageData{ + Package: pkg, + IsVirtual: sc.isVirtual, + JSFiles: jsFiles, + bctx: &sc.bctx, + }, nil +} + +// Match implements XContext.Match. +func (sc simpleCtx) Match(patterns []string) ([]string, error) { + if sc.isVirtual { + // We can't use go tool to enumerate packages in a virtual file system, + // so we fall back onto a simpler implementation provided by the buildutil + // package. It doesn't support all valid patterns, but should be good enough. + // + // Note: this code path will become unnecessary after + // https://github.com/gopherjs/gopherjs/issues/1021 is implemented. + args := []string{} + for _, p := range patterns { + switch p { + case "all": + args = append(args, "...") + case "std", "main", "cmd": + // These patterns are not supported by buildutil.ExpandPatterns(), + // but they would be matched by the real context correctly, so skip them. + default: + args = append(args, p) + } + } + matches := []string{} + for importPath := range buildutil.ExpandPatterns(&sc.bctx, args) { + if importPath[0] == '.' { + p, err := sc.Import(importPath, ".", build.FindOnly) + // Resolve relative patterns into canonical import paths. + if err != nil { + continue + } + importPath = p.ImportPath + } + matches = append(matches, importPath) + } + sort.Strings(matches) + return matches, nil + } + + args := append([]string{ + "-e", "-compiler=gc", + "-tags=" + strings.Join(sc.bctx.BuildTags, ","), + "-installsuffix=" + sc.bctx.InstallSuffix, + "-f={{.ImportPath}}", + "--", + }, patterns...) + + out, err := sc.gotool("list", args...) + if err != nil { + return nil, fmt.Errorf("failed to list packages on FS: %w", err) + } + matches := strings.Split(strings.TrimSpace(out), "\n") + sort.Strings(matches) + return matches, nil +} + +func (sc simpleCtx) Env() Env { + return Env{ + GOROOT: sc.bctx.GOROOT, + GOPATH: sc.bctx.GOPATH, + GOOS: sc.bctx.GOOS, + GOARCH: sc.bctx.GOARCH, + BuildTags: sc.bctx.BuildTags, + InstallSuffix: sc.bctx.InstallSuffix, + } +} + +// gotool executes the go tool set up for the build context and returns standard output. +func (sc simpleCtx) gotool(subcommand string, args ...string) (string, error) { + if sc.isVirtual { + panic(fmt.Errorf("can't use go tool with a virtual build context")) + } + args = append([]string{subcommand}, args...) + cmd := exec.Command("go", args...) + + if sc.bctx.Dir != "" { + cmd.Dir = sc.bctx.Dir + } + + var stdout, stderr strings.Builder + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + cgo := "0" + if sc.bctx.CgoEnabled { + cgo = "1" + } + cmd.Env = append(os.Environ(), + "GOOS="+sc.bctx.GOOS, + "GOARCH="+sc.bctx.GOARCH, + "GOROOT="+sc.bctx.GOROOT, + "GOPATH="+sc.bctx.GOPATH, + "CGO_ENABLED="+cgo, + ) + + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("go tool error: %v: %w\n%s", cmd, err, stderr.String()) + } + return stdout.String(), nil +} + +// applyPreloadTweaks makes several package-specific adjustments to package importing. +// +// Ideally this method would not be necessary, but currently several packages +// require special handing in order to be compatible with GopherJS. This method +// returns a copy of the build context, keeping the original one intact. +func (sc simpleCtx) applyPreloadTweaks(importPath string, srcDir string, mode build.ImportMode) (build.Context, build.ImportMode) { + bctx := sc.bctx + if sc.isStd(importPath, srcDir) { + // For most of the platform-dependent code in the standard library we simply + // reuse implementations targeting WebAssembly. For the user-supplied we use + // regular gopherjs-specific GOOS/GOARCH. + bctx.GOOS = "js" + bctx.GOARCH = "wasm" + } + switch importPath { + case "github.com/gopherjs/gopherjs/js", "github.com/gopherjs/gopherjs/nosync": + // These packages are already embedded via gopherjspkg.FS virtual filesystem + // (which can be safely vendored). Don't try to use vendor directory to + // resolve them. + mode |= build.IgnoreVendor + } + + return bctx, mode +} + +// applyPostloadTweaks makes adjustments to the contents of the loaded package. +// +// Some of the standard library packages require additional tweaks that are not +// covered by our augmentation logic, for example excluding or including +// particular source files. This method ensures that all such tweaks are applied +// before the package is returned to the caller. +func (sc simpleCtx) applyPostloadTweaks(pkg *build.Package) *build.Package { + if sc.isVirtual { + // GopherJS overlay package sources don't need tweaks to their content, + // since we already control them directly. + return pkg + } + if sc.noPostTweaks { + return pkg + } + switch pkg.ImportPath { + case "runtime": + pkg.GoFiles = []string{} // Package sources are completely replaced in natives. + case "runtime/pprof": + pkg.GoFiles = nil + case "sync": + // GopherJS completely replaces sync.Pool implementation with a simpler one, + // since it always executes in a single-threaded environment. + pkg.GoFiles = exclude(pkg.GoFiles, "pool.go") + case "syscall/js": + // Reuse upstream tests to ensure conformance, but completely replace + // implementation. + pkg.GoFiles = []string{} + pkg.TestGoFiles = []string{} + } + + pkg.Imports, pkg.ImportPos = updateImports(pkg.GoFiles, pkg.ImportPos) + pkg.TestImports, pkg.TestImportPos = updateImports(pkg.TestGoFiles, pkg.TestImportPos) + pkg.XTestImports, pkg.XTestImportPos = updateImports(pkg.XTestGoFiles, pkg.XTestImportPos) + + return pkg +} + +// isStd returns true if the given importPath resolves into a standard library +// package. Relative paths are interpreted relative to srcDir. +func (sc simpleCtx) isStd(importPath, srcDir string) bool { + pkg, err := sc.bctx.Import(importPath, srcDir, build.FindOnly) + if err != nil { + return false + } + return pkg.Goroot +} + +var defaultBuildTags = []string{ + "netgo", // See https://godoc.org/net#hdr-Name_Resolution. + "purego", // See https://golang.org/issues/23172. + "math_big_pure_go", // Use pure Go version of math/big. + // We can't set compiler to gopherjs, since Go tooling doesn't support that, + // but, we can at least always set this build tag. + "gopherjs", +} + +// embeddedCtx creates simpleCtx that imports from a virtual FS embedded into +// the GopherJS compiler. +func embeddedCtx(embedded http.FileSystem, e Env) *simpleCtx { + fs := &vfs{embedded} + ec := goCtx(e) + ec.bctx.GOPATH = "" + + // Path functions must behave unix-like to work with the VFS. + ec.bctx.JoinPath = path.Join + ec.bctx.SplitPathList = splitPathList + ec.bctx.IsAbsPath = path.IsAbs + ec.bctx.HasSubdir = hasSubdir + + // Substitute real FS with the embedded one. + ec.bctx.IsDir = fs.IsDir + ec.bctx.ReadDir = fs.ReadDir + ec.bctx.OpenFile = fs.OpenFile + ec.isVirtual = true + return ec +} + +// overlayCtx creates simpleCtx that imports from the embedded standard library +// overlays. +func overlayCtx(e Env) *simpleCtx { + return embeddedCtx(&withPrefix{fs: http.FS(natives.FS), prefix: e.GOROOT}, e) +} + +// gopherjsCtx creates a simpleCtx that imports from the embedded gopherjs +// packages in case they are not present in the user's source tree. +func gopherjsCtx(e Env) *simpleCtx { + gopherjsRoot := filepath.Join(e.GOROOT, "src", "github.com", "gopherjs", "gopherjs") + return embeddedCtx(&withPrefix{gopherjspkg.FS, gopherjsRoot}, e) +} + +// goCtx creates simpleCtx that imports from the real file system GOROOT, GOPATH +// or Go Modules. +func goCtx(e Env) *simpleCtx { + gc := simpleCtx{ + bctx: build.Context{ + GOROOT: e.GOROOT, + GOPATH: e.GOPATH, + GOOS: e.GOOS, + GOARCH: e.GOARCH, + InstallSuffix: e.InstallSuffix, + Compiler: "gc", + BuildTags: append(append([]string{}, e.BuildTags...), defaultBuildTags...), + CgoEnabled: false, // CGo is not supported by GopherJS. + + // go/build supports modules, but only when no FS access functions are + // overridden and when provided ReleaseTags match those of the default + // context (matching Go compiler's version). + // This limitation stems from the fact that it will invoke the Go tool + // which can only see files on the real FS and will assume release tags + // based on the Go tool's version. + // + // See also comments to the versionhack package. + ReleaseTags: build.Default.ReleaseTags, //[:compiler.GoVersion], + }, + } + return &gc +} + +// chainedCtx combines two build contexts. Secondary context acts as a fallback +// when a package is not found in the primary, and is ignored otherwise. +// +// This allows GopherJS to load its core "js" and "nosync" packages from the +// embedded VFS whenever user's code doesn't directly depend on them, but +// augmented stdlib does. +type chainedCtx struct { + primary XContext + secondary XContext +} + +// Import implements buildCtx.Import(). +func (cc chainedCtx) Import(importPath string, srcDir string, mode build.ImportMode) (*PackageData, error) { + pkg, err := cc.primary.Import(importPath, srcDir, mode) + if err == nil { + return pkg, nil + } else if IsPkgNotFound(err) { + return cc.secondary.Import(importPath, srcDir, mode) + } else { + return nil, err + } +} + +func (cc chainedCtx) Env() Env { return cc.primary.Env() } + +// Match implements XContext.Match(). +// +// Packages from both contexts are included and returned as a deduplicated +// sorted list. +func (cc chainedCtx) Match(patterns []string) ([]string, error) { + m1, err := cc.primary.Match(patterns) + if err != nil { + return nil, fmt.Errorf("failed to list packages in the primary context: %s", err) + } + m2, err := cc.secondary.Match(patterns) + if err != nil { + return nil, fmt.Errorf("failed to list packages in the secondary context: %s", err) + } + + seen := map[string]bool{} + matches := []string{} + for _, m := range append(m1, m2...) { + if seen[m] { + continue + } + seen[m] = true + matches = append(matches, m) + } + sort.Strings(matches) + return matches, nil +} + +// IsPkgNotFound returns true if the error was caused by package not found. +// +// Unfortunately, go/build doesn't make use of typed errors, so we have to +// rely on the error message. +func IsPkgNotFound(err error) bool { + return err != nil && + (strings.Contains(err.Error(), "cannot find package") || // Modules off. + strings.Contains(err.Error(), "is not in GOROOT")) // Modules on. +} + +// updateImports package's list of import paths to only those present in sources +// after post-load tweaks. +func updateImports(sources []string, importPos map[string][]token.Position) (newImports []string, newImportPos map[string][]token.Position) { + if importPos == nil { + // Short-circuit for tests when no imports are loaded. + return nil, nil + } + sourceSet := map[string]bool{} + for _, source := range sources { + sourceSet[source] = true + } + + newImportPos = map[string][]token.Position{} + for importPath, positions := range importPos { + for _, pos := range positions { + if sourceSet[filepath.Base(pos.Filename)] { + newImportPos[importPath] = append(newImportPos[importPath], pos) + } + } + } + + for importPath := range newImportPos { + newImports = append(newImports, importPath) + } + sort.Strings(newImports) + return newImports, newImportPos +} + +// jsFilesFromDir finds and loads any *.inc.js packages in the build context +// directory. +func jsFilesFromDir(bctx *build.Context, dir string) ([]JSFile, error) { + files, err := buildutil.ReadDir(bctx, dir) + if err != nil { + return nil, err + } + var jsFiles []JSFile + for _, file := range files { + if !strings.HasSuffix(file.Name(), ".inc.js") || file.IsDir() { + continue + } + if file.Name()[0] == '_' || file.Name()[0] == '.' { + continue // Skip "hidden" files that are typically ignored by the Go build system. + } + + path := buildutil.JoinPath(bctx, dir, file.Name()) + f, err := buildutil.OpenFile(bctx, path) + if err != nil { + return nil, fmt.Errorf("failed to open %s from %v: %w", path, bctx, err) + } + defer f.Close() + + content, err := io.ReadAll(f) + if err != nil { + return nil, fmt.Errorf("failed to read %s from %v: %w", path, bctx, err) + } + + jsFiles = append(jsFiles, JSFile{ + Path: path, + ModTime: file.ModTime(), + Content: content, + }) + } + return jsFiles, nil +} diff --git a/build/context_test.go b/build/context_test.go new file mode 100644 index 00000000..4376f979 --- /dev/null +++ b/build/context_test.go @@ -0,0 +1,209 @@ +package build + +import ( + "fmt" + "go/build" + "path/filepath" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/gopherjs/gopherjs/compiler/gopherjspkg" + "golang.org/x/tools/go/buildutil" +) + +func TestSimpleCtx(t *testing.T) { + e := DefaultEnv() + + gopherjsRoot := filepath.Join(e.GOROOT, "src", "github.com", "gopherjs", "gopherjs") + fs := &withPrefix{gopherjspkg.FS, gopherjsRoot} + ec := embeddedCtx(fs, e) + ec.bctx.JoinPath = filepath.Join // Avoid diffs in the test on Windows. + + gc := goCtx(e) + + t.Run("exists", func(t *testing.T) { + tests := []struct { + buildCtx XContext + wantPkg *PackageData + }{ + { + buildCtx: ec, + wantPkg: &PackageData{ + Package: expectedPackage(&ec.bctx, "github.com/gopherjs/gopherjs/js", "wasm"), + IsVirtual: true, + }, + }, { + buildCtx: gc, + wantPkg: &PackageData{ + Package: expectedPackage(&gc.bctx, "fmt", "wasm"), + IsVirtual: false, + }, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%T", test.buildCtx), func(t *testing.T) { + importPath := test.wantPkg.ImportPath + got, err := test.buildCtx.Import(importPath, "", build.FindOnly) + if err != nil { + t.Fatalf("ec.Import(%q) returned error: %s. Want: no error.", importPath, err) + } + if diff := cmp.Diff(test.wantPkg, got, cmpopts.IgnoreUnexported(*got)); diff != "" { + t.Errorf("ec.Import(%q) returned diff (-want,+got):\n%s", importPath, diff) + } + }) + } + }) + + t.Run("not found", func(t *testing.T) { + tests := []struct { + buildCtx XContext + importPath string + }{ + { + buildCtx: ec, + importPath: "package/not/found", + }, { + // Outside of the main module. + buildCtx: gc, + importPath: "package/not/found", + }, { + // In the main module. + buildCtx: gc, + importPath: "github.com/gopherjs/gopherjs/not/found", + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%T", test.buildCtx), func(t *testing.T) { + _, err := ec.Import(test.importPath, "", build.FindOnly) + want := "cannot find package" + if err == nil || !strings.Contains(err.Error(), want) { + t.Errorf("ec.Import(%q) returned error: %s. Want error containing %q.", test.importPath, err, want) + } + }) + } + }) +} + +func TestChainedCtx(t *testing.T) { + // Construct a chained context of two fake contexts so that we could verify + // fallback behavior. + cc := chainedCtx{ + primary: simpleCtx{ + bctx: *buildutil.FakeContext(map[string]map[string]string{ + "primaryonly": {"po.go": "package primaryonly"}, + "both": {"both.go": "package both"}, + }), + isVirtual: false, + }, + secondary: simpleCtx{ + bctx: *buildutil.FakeContext(map[string]map[string]string{ + "both": {"both_secondary.go": "package both"}, + "secondaryonly": {"so.go": "package secondaryonly"}, + }), + isVirtual: true, + }, + } + + tests := []struct { + importPath string + wantFromPrimary bool + }{ + { + importPath: "primaryonly", + wantFromPrimary: true, + }, { + importPath: "both", + wantFromPrimary: true, + }, { + importPath: "secondaryonly", + wantFromPrimary: false, + }, + } + + for _, test := range tests { + t.Run(test.importPath, func(t *testing.T) { + pkg, err := cc.Import(test.importPath, "", 0) + if err != nil { + t.Errorf("cc.Import() returned error: %v. Want: no error.", err) + } + gotFromPrimary := !pkg.IsVirtual + if gotFromPrimary != test.wantFromPrimary { + t.Errorf("Got package imported from primary: %t. Want: %t.", gotFromPrimary, test.wantFromPrimary) + } + }) + } +} + +func TestIsStd(t *testing.T) { + realGOROOT := goCtx(DefaultEnv()) + overlayGOROOT := overlayCtx(DefaultEnv()) + gopherjsPackages := gopherjsCtx(DefaultEnv()) + tests := []struct { + descr string + importPath string + context *simpleCtx + want bool + }{ + { + descr: "real goroot, standard package", + importPath: "fmt", + context: realGOROOT, + want: true, + }, + { + descr: "real goroot, non-standard package", + importPath: "github.com/gopherjs/gopherjs/build", + context: realGOROOT, + want: false, + }, + { + descr: "real goroot, non-exiting package", + importPath: "does/not/exist", + context: realGOROOT, + want: false, + }, + { + descr: "overlay goroot, standard package", + importPath: "fmt", + context: overlayGOROOT, + want: true, + }, + { + descr: "embedded gopherjs packages, gopherjs/js package", + importPath: "github.com/gopherjs/gopherjs/js", + context: gopherjsPackages, + // When user's source tree doesn't contain gopherjs package (e.g. it uses + // syscall/js API only), we pretend that gopherjs/js package is included + // in the standard library. + want: true, + }, + } + + for _, test := range tests { + t.Run(test.descr, func(t *testing.T) { + got := test.context.isStd(test.importPath, "") + if got != test.want { + t.Errorf("Got: simpleCtx.isStd(%q) = %v. Want: %v", test.importPath, got, test.want) + } + }) + } +} + +func expectedPackage(bctx *build.Context, importPath string, goarch string) *build.Package { + targetRoot := filepath.Clean(filepath.Join(bctx.GOROOT, "pkg", bctx.GOOS+"_"+goarch)) + return &build.Package{ + Dir: filepath.Join(bctx.GOROOT, "src", importPath), + ImportPath: importPath, + Root: bctx.GOROOT, + SrcRoot: filepath.Join(bctx.GOROOT, "src"), + PkgRoot: filepath.Join(bctx.GOROOT, "pkg"), + PkgTargetRoot: targetRoot, + BinDir: filepath.Join(bctx.GOROOT, "bin"), + Goroot: true, + PkgObj: filepath.Join(targetRoot, importPath+".a"), + } +} diff --git a/build/embed.go b/build/embed.go new file mode 100644 index 00000000..2b5661f6 --- /dev/null +++ b/build/embed.go @@ -0,0 +1,175 @@ +package build + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" + "strconv" + + "github.com/visualfc/goembed" +) + +func buildIdent(name string) string { + return fmt.Sprintf("__gopherjs_embed_%x__", name) +} + +var embed_head = `package %v + +import ( + "embed" + _ "unsafe" +) + +//go:linkname __gopherjs_embed_buildFS__ embed.buildFS +func __gopherjs_embed_buildFS__(list []struct { + name string + data string + hash [16]byte +}) (f embed.FS) +` + +// embedFiles generates an additional source file, which initializes all variables in the package with a go:embed directive. +func embedFiles(pkg *PackageData, fset *token.FileSet, files []*ast.File) (*ast.File, error) { + if len(pkg.EmbedPatternPos) == 0 { + return nil, nil + } + + ems, err := goembed.CheckEmbed(pkg.EmbedPatternPos, fset, files) + if err != nil { + return nil, err + } + + r := goembed.NewResolve() + for _, em := range ems { + fs, err := r.Load(pkg.Dir, fset, em) + if err != nil { + return nil, err + } + switch em.Kind { + case goembed.EmbedMaybeAlias: + // value = Type(data) + // valid alias string or []byte type used by types.check + em.Spec.Values = []ast.Expr{ + &ast.CallExpr{ + Fun: em.Spec.Type, + Args: []ast.Expr{ + &ast.Ident{Name: buildIdent(fs[0].Name), + NamePos: em.Spec.Names[0].NamePos}, + }, + }} + case goembed.EmbedBytes: + // value = []byte(data) + em.Spec.Values = []ast.Expr{ + &ast.CallExpr{ + Fun: em.Spec.Type, + Args: []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))}, + }} + case goembed.EmbedString: + // value = data + em.Spec.Values = []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))} + case goembed.EmbedFiles: + // value = __gopherjs_embed_buildFS__([]struct{name string; data string; hash [16]byte}{...}) + fs = goembed.BuildFS(fs) + elts := make([]ast.Expr, len(fs)) + for i, f := range fs { + if len(f.Data) == 0 { + elts[i] = &ast.CompositeLit{ + Elts: []ast.Expr{ + &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)}, + &ast.BasicLit{Kind: token.STRING, Value: `""`}, + &ast.CompositeLit{ + Type: &ast.ArrayType{ + Len: &ast.BasicLit{Kind: token.INT, Value: "16"}, + Elt: ast.NewIdent("byte"), + }, + }, + }, + } + } else { + var hash [16]ast.Expr + for j, v := range f.Hash { + hash[j] = &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(int(v))} + } + elts[i] = &ast.CompositeLit{ + Elts: []ast.Expr{ + &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)}, + ast.NewIdent(buildIdent(f.Name)), + &ast.CompositeLit{ + Type: &ast.ArrayType{ + Len: &ast.BasicLit{Kind: token.INT, Value: "16"}, + Elt: ast.NewIdent("byte"), + }, + Elts: hash[:], + }, + }, + } + } + } + call := &ast.CallExpr{ + Fun: ast.NewIdent("__gopherjs_embed_buildFS__"), + Args: []ast.Expr{ + &ast.CompositeLit{ + Type: &ast.ArrayType{ + Elt: &ast.StructType{ + Fields: &ast.FieldList{ + List: []*ast.Field{ + &ast.Field{ + Names: []*ast.Ident{ast.NewIdent("name")}, + Type: ast.NewIdent("string"), + }, + &ast.Field{ + Names: []*ast.Ident{ast.NewIdent("data")}, + Type: ast.NewIdent("string"), + }, + &ast.Field{ + Names: []*ast.Ident{ast.NewIdent("hash")}, + Type: &ast.ArrayType{ + Len: &ast.BasicLit{Kind: token.INT, Value: "16"}, + Elt: ast.NewIdent("byte"), + }, + }, + }, + }, + }, + }, + Elts: elts, + }, + }, + } + em.Spec.Values = []ast.Expr{call} + } + } + + var buf bytes.Buffer + fmt.Fprintf(&buf, embed_head, pkg.Name) + buf.WriteString("\nconst (\n") + for _, f := range r.Files() { + if len(f.Data) == 0 { + fmt.Fprintf(&buf, "\t%v = \"\"\n", buildIdent(f.Name)) + } else { + fmt.Fprintf(&buf, "\t%v = \"%v\"\n", buildIdent(f.Name), goembed.BytesToHex(f.Data)) + } + } + buf.WriteString(")\n\n") + f, err := parser.ParseFile(fset, "js_embed.go", buf.String(), parser.ParseComments) + if err != nil { + return nil, err + } + return f, nil +} + +func joinEmbedPatternPos(m1, m2 map[string][]token.Position) map[string][]token.Position { + if len(m1) == 0 && len(m2) == 0 { + return nil + } + m := make(map[string][]token.Position) + for k, v := range m1 { + m[k] = v + } + for k, v := range m2 { + m[k] = append(m[k], v...) + } + return m +} diff --git a/build/fsutil.go b/build/fsutil.go new file mode 100644 index 00000000..cee763f1 --- /dev/null +++ b/build/fsutil.go @@ -0,0 +1,28 @@ +package build + +import ( + "fmt" + "os" + "path/filepath" +) + +func mustAbs(p string) string { + a, err := filepath.Abs(p) + if err != nil { + panic(fmt.Errorf("failed to get absolute path to %s", p)) + } + return a +} + +// makeWritable attempts to make the given path writable by its owner. +func makeWritable(path string) error { + info, err := os.Stat(path) + if err != nil { + return err + } + err = os.Chmod(path, info.Mode()|0700) + if err != nil { + return err + } + return nil +} diff --git a/build/versionhack/versionhack.go b/build/versionhack/versionhack.go new file mode 100644 index 00000000..14a3301e --- /dev/null +++ b/build/versionhack/versionhack.go @@ -0,0 +1,49 @@ +//go:build go1.17 +// +build go1.17 + +// Package versionhack makes sure go/build doesn't disable module support +// whenever GopherJS is compiled by a different Go version than it's targeted +// Go version. +// +// Under the hood, go/build relies on `go list` utility for module support; more +// specifically, for package location discovery. Since ReleaseTags are +// effectively baked into the go binary and can't be overridden, it needs to +// ensure that ReleaseTags set in a go/build.Context instance match the Go tool. +// +// However, it naively assumes that the go tool version in the PATH matches the +// version that was used to build GopherJS and disables module support whenever +// ReleaseTags in the context are set to anything other than the default. This, +// unfortunately, isn't very helpful since gopherjs may be built by a Go version +// other than the PATH's default. +// +// Luckily, even if go tool version is mismatched, it's only used for discovery +// of the package locations, and go/build evaluates build constraints on its own +// with ReleaseTags we've passed. +// +// A better solution would've been for go/build to use go tool from GOROOT and +// check its version against build tags: https://github.com/golang/go/issues/46856. +// +// Until that issue is fixed, we trick go/build into thinking that whatever +// ReleaseTags we've passed are indeed the default. We gain access to the +// variable go/build checks against using "go:linkname" directive and override +// its content as we wish. +package versionhack + +import ( + // Must be initialized before this package. + + "go/build" + _ "unsafe" // For go:linkname +) + +//go:linkname releaseTags go/build.defaultReleaseTags +var releaseTags []string + +//go:linkname toolTags go/build.defaultToolTags +var toolTags []string + +func init() { + //releaseTags = build.Default.ReleaseTags //[:compiler.GoVersion] + toolTags = []string{} + build.Default.ToolTags = []string{} +} diff --git a/build/versionhack/versionhack_go116.go b/build/versionhack/versionhack_go116.go new file mode 100644 index 00000000..c04e2581 --- /dev/null +++ b/build/versionhack/versionhack_go116.go @@ -0,0 +1,4 @@ +//go:build !go1.17 +// +build !go1.17 + +package versionhack diff --git a/build/vfs.go b/build/vfs.go new file mode 100644 index 00000000..e3779ecf --- /dev/null +++ b/build/vfs.go @@ -0,0 +1,92 @@ +package build + +import ( + "io" + "net/http" + "os" + "path" + "path/filepath" + "strings" +) + +// vfs is a convenience wrapper around http.FileSystem that provides accessor +// methods required by go/build.Context. +type vfs struct{ http.FileSystem } + +func (fs vfs) IsDir(name string) bool { + name = filepath.ToSlash(name) + dir, err := fs.Open(name) + if err != nil { + return false + } + defer dir.Close() + info, err := dir.Stat() + if err != nil { + return false + } + return info.IsDir() +} + +func (fs vfs) ReadDir(name string) (fi []os.FileInfo, err error) { + name = filepath.ToSlash(name) + dir, err := fs.Open(name) + if err != nil { + return nil, err + } + defer dir.Close() + return dir.Readdir(0) +} + +func (fs vfs) OpenFile(name string) (r io.ReadCloser, err error) { + name = filepath.ToSlash(name) + return fs.Open(name) +} + +func splitPathList(list string) []string { + if list == "" { + return nil + } + const pathListSeparator = ":" // UNIX style + return strings.Split(list, pathListSeparator) +} + +// hasSubdir reports whether dir is lexically a subdirectory of +// root, perhaps multiple levels below. It does not try to check +// whether dir exists. +// If so, hasSubdir sets rel to a slash-separated path that +// can be joined to root to produce a path equivalent to dir. +func hasSubdir(root, dir string) (rel string, ok bool) { + // Implementation based on golang.org/x/tools/go/buildutil. + const sep = "/" // UNIX style + root = path.Clean(root) + if !strings.HasSuffix(root, sep) { + root += sep + } + + dir = path.Clean(dir) + if !strings.HasPrefix(dir, root) { + return "", false + } + + return dir[len(root):], true +} + +// withPrefix implements http.FileSystem, which places the underlying FS under +// the given prefix path. +type withPrefix struct { + fs http.FileSystem + prefix string +} + +func (wp *withPrefix) Open(name string) (http.File, error) { + name = filepath.ToSlash(name) + prefix := filepath.ToSlash(wp.prefix) + if !strings.HasPrefix(name, prefix) { + return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist} + } + f, err := wp.fs.Open(strings.TrimPrefix(name, prefix)) + if err != nil { + return nil, &os.PathError{Op: "open", Path: name, Err: err} + } + return f, nil +} diff --git a/circle.yml b/circle.yml index a0944f47..102c7994 100644 --- a/circle.yml +++ b/circle.yml @@ -1,31 +1,292 @@ -version: 2 +# CircleCI configuration for GopherJS. +# +# This configuration has one build_and_test workflow designed to run on all commits +# and pull requests. It consists of three jobs: +# +# - build: Builds and runs GopherJS unit tests, as well as lints, smoke tests, etc. +# This job is designed to provide quickest feedback on the most important +# functionality. It should not include anything heavyweight and should be kept +# under 2-3 minutes of runtime. +# +# - gopherjs_tests: Runs standard library and GopherJS package tests using GopherJS +# *itself*. This is the most comprehensive test suite we have, and it is sharded +# into 4 parallel instances for faster execution. +# +# - gorepo_tests: Runs language tests from the Go compiler test suite. The objective +# of these tests is to ensure conformance of GopherJS to the upstream Go to the +# highest degree possible, considering differences in the runtime. +# +# If all tests passed, it is reasonably to assume that the version is more or less +# bug-free (although as of summer 2021 our test coverage is not ideal). +# +# For convenience of upgrades, NVM, Node.js and Go versions are specified as +# parameters at the top of the config. Increasing the version and ensuring that the +# workflow passes is sufficient to verify GopherJS compatibility with that version. +# +# Versions of Node modules GopherJS depends on are specified in package.json and can +# be changed there (remember to run `npm install` to update the lock file). + +version: 2.1 +executors: + gopherjs: + docker: + - image: cimg/base:stable + working_directory: ~/gopherjs + +workflows: + version: 2 + build_and_test: + jobs: + - build + - gopherjs_tests: + requires: + - build + - gorepo_tests: + requires: + - build + - darwin_smoke: + requires: + - build + - windows_smoke: + requires: + - build + +parameters: + go_version: + type: string + default: "1.18.5" + nvm_version: + type: string + default: "0.38.0" + node_version: + type: string + default: "12" + +orbs: + win: circleci/windows@4.0.0 + go: circleci/go@1.7.1 + node: circleci/node@5.0.1 + jobs: build: - docker: - - image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37 - command: /sbin/init - environment: - SOURCE_MAP_SUPPORT: false - working_directory: ~/go/src/github.com/gopherjs/gopherjs + executor: gopherjs + steps: + - setup_and_install_gopherjs + - run: + name: Check minified prelude + command: | + set -e + go generate github.com/gopherjs/gopherjs/compiler/prelude + diff -u <(echo -n) <(git status --porcelain) + - run: + name: Check gofmt + command: diff -u <(echo -n) <(gofmt -d .) + - run: + name: Check go vet + command: | + set -e + # Go package in root directory. + go vet . + # All subdirectories except "doc", "tests", "node*". + for d in */; do echo ./$d...; done | grep -v ./doc | grep -v ./tests | grep -v ./node | xargs go vet + - run: + name: Check natives build tags + command: diff -u <(echo -n) <(go list ./compiler/natives/src/...) # All those packages should have // +build js. + - run: + name: Smoke tests + command: | + gopherjs build -v net/http # Should build successfully. + gopherjs test -v fmt log # Should catch problems with test execution and source maps. + - run: + name: go test ... + command: | + set +e + # Run all tests except gorepo, which will be run separately in parallel. + go test -v -race $(go list ./... | grep -v github.com/gopherjs/gopherjs/tests/gorepo) | tee /tmp/test-go.txt + status="$?" + # Convert test output into junit format for CircleCI. + mkdir -p ~/test-reports/ + go-junit-report --full-class-name < /tmp/test-go.txt > ~/test-reports/go.xml + exit "$status" + - store_test_results: + path: ~/test-reports/ + - run: + name: TodoMVC in GOPATH mode + command: | + set -e + export GO111MODULE=off + export GOPATH=/tmp/gopath + mkdir -p $GOPATH/src + go get -v github.com/gopherjs/todomvc + gopherjs build -v -o /tmp/todomvc_gopath.js github.com/gopherjs/todomvc + gopherjs test -v github.com/gopherjs/todomvc/... + find $GOPATH + - run: + name: TodoMVC in Go Modules mode + command: | + set -e + export GO111MODULE=on + export GOPATH=/tmp/gomod + mkdir -p $GOPATH/src + cd /tmp + git clone --depth=1 https://github.com/gopherjs/todomvc.git + cd /tmp/todomvc + gopherjs build -v -o /tmp/todomvc_gomod.js github.com/gopherjs/todomvc + gopherjs test -v github.com/gopherjs/todomvc/... + find $GOPATH + - run: + name: Compare GOPATH and Go Modules output + command: diff -u <(sed 's/todomvc_gomod.js.map/todomvc_ignored.js.map/' /tmp/todomvc_gomod.js) <(sed 's/todomvc_gopath.js.map/todomvc_ignored.js.map/' /tmp/todomvc_gopath.js) + + gopherjs_tests: + executor: gopherjs + parallelism: 4 + steps: + - setup_and_install_gopherjs + - run: + name: gopherjs test ... + command: | + set +e + ulimit -s 10000 + PACKAGE_NAMES=$( \ + GOOS=js GOARCH=wasm go list std github.com/gopherjs/gopherjs/js/... github.com/gopherjs/gopherjs/tests/... \ + | grep -v -x -f .std_test_pkg_exclusions \ + | circleci tests split --split-by=timings --timings-type=classname \ + ) + gopherjs test -p 2 --minify -v --short $PACKAGE_NAMES \ + | tee /tmp/test-gopherjs.txt + status="$?" + set -e + # Convert test output into junit format for CircleCI. + mkdir -p ~/test-reports/ + go-junit-report --full-class-name < /tmp/test-gopherjs.txt > ~/test-reports/gopherjs-${CIRCLE_NODE_INDEX}.xml + exit "$status" + no_output_timeout: "1h" # Packages like math/big take a while to run all tests. + - store_test_results: + path: ~/test-reports/ + + gorepo_tests: + executor: gopherjs + parallelism: 4 + steps: + - setup_environment + - checkout + - install_deps + - install_gopherjs + - run: + name: Go Repository tests + command: | + go test -v github.com/gopherjs/gopherjs/tests/gorepo + + windows_smoke: + executor: + name: win/default + shell: powershell.exe + steps: + - checkout + - run: + name: Install Go + command: | + choco install golang --version="<< pipeline.parameters.go_version >>" -my + go version + (Get-Command go).Path + [Environment]::SetEnvironmentVariable( + "Path", + [Environment]::GetEnvironmentVariable("Path", [EnvironmentVariableTarget]::Machine) + ";C:\Users\circleci\go\bin", + [EnvironmentVariableTarget]::Machine) + + - install_deps: + optional: false + - run: + name: Install GopherJS + command: + go install -v . + (Get-Command gopherjs).Path + - run: + name: Test GopherJS + command: go test -v -short ./... + - run: + name: Smoke tests + command: | + $env:NODE_PATH=$(npm root) + $env:SOURCE_MAP_SUPPORT=false + gopherjs build -v net/http + gopherjs test -v --short fmt sort ./tests + + darwin_smoke: + macos: + xcode: 13.3.0 # Mac OS 12.2.1 + steps: + - checkout + - setup_environment + - install_deps: + optional: false + - run: + name: Install GopherJS + command: go install -v . + - run: + name: Test GopherJS + command: go test -v -short ./... + - run: + name: Smoke tests + command: | + gopherjs build -v net/http + gopherjs test -v --short fmt log os ./tests + +commands: + setup_environment: + description: Set up Go, NVM and Node.js + steps: + - go/install: + version: << pipeline.parameters.go_version >> + - node/install: + node-version: << pipeline.parameters.node_version >> + - run: + name: Set up environment + command: | + echo 'export PATH="$PATH:$HOME/go/bin"' >> $BASH_ENV + echo 'export GO111MODULE=on' >> $BASH_ENV + echo 'export SOURCE_MAP_SUPPORT=true' >> $BASH_ENV + # Make nodejs able to require installed modules from any working path. + echo "export NODE_PATH=$(npm root)" >> $BASH_ENV + go version + node -v + go install -v github.com/nevkontakte/go-junit-report@forked # For CircleCI test reports. + install_deps: + description: Install Go and Node dependency packages + parameters: + optional: + default: true + type: boolean + description: Install node-syscall module and its dependencies. + steps: + - when: + condition: + not: << parameters.optional >> + steps: + - run: + name: Install required Node.js packages + command: | + # Extra flags to avoid installing node-syscall. + npm install --no-optional --no-package-lock + - when: + condition: << parameters.optional >> + steps: + - run: + name: Install required Node.js packages (including optional) + command: | + npm ci # Install our dependencies from package.json. + - go/mod-download + install_gopherjs: + description: Install GopherJS + steps: + - run: + name: Install GopherJS + command: go install -v && gopherjs version + setup_and_install_gopherjs: + description: A shorthand for setting up GopherJS environment and building the binary. steps: + - setup_environment - checkout - - run: git clone https://github.com/creationix/nvm $HOME/.nvm && cd $HOME/.nvm && git checkout v0.33.9 && echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV && echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV - - run: nvm install 10.0.0 && nvm alias default 10.0.0 - - run: cd /usr/local && sudo rm -rf go && curl https://storage.googleapis.com/golang/go1.12.linux-amd64.tar.gz | sudo tar -xz - - run: echo 'export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"' >> $BASH_ENV - - run: go get -t -d -v ./... - - run: go install -v - - run: npm install # Install our (dev) dependencies from package.json. - - run: npm install --global node-gyp - - run: cd node-syscall && node-gyp rebuild && mkdir -p ~/.node_libraries && cp build/Release/syscall.node ~/.node_libraries/syscall.node - - - run: go generate github.com/gopherjs/gopherjs/compiler/prelude - - run: diff -u <(echo -n) <(git status --porcelain) - - run: diff -u <(echo -n) <(gofmt -d .) - - run: go vet . # Go package in root directory. - - run: for d in */; do echo ./$d...; done | grep -v ./doc | grep -v ./tests | grep -v ./node | xargs go vet # All subdirectories except "doc", "tests", "node*". - - run: diff -u <(echo -n) <(go list ./compiler/natives/src/...) # All those packages should have // +build js. - - run: gopherjs install -v net/http # Should build successfully (can't run tests, since only client is supported). - - run: ulimit -s 10000 && gopherjs test --minify -v --short github.com/gopherjs/gopherjs/tests/... $(go list std | grep -v -x -f .std_test_pkg_exclusions) - - run: go test -v -race ./... - - run: gopherjs test -v fmt # No minification should work. + - install_deps + - install_gopherjs diff --git a/compiler/analysis/info.go b/compiler/analysis/info.go index a8181615..c984b726 100644 --- a/compiler/analysis/info.go +++ b/compiler/analysis/info.go @@ -1,9 +1,11 @@ package analysis import ( + "fmt" "go/ast" "go/token" "go/types" + "strings" "github.com/gopherjs/gopherjs/compiler/astutil" "github.com/gopherjs/gopherjs/compiler/typesutil" @@ -11,72 +13,146 @@ import ( type continueStmt struct { forStmt *ast.ForStmt - analyzeStack []ast.Node + analyzeStack astPath +} + +func newContinueStmt(forStmt *ast.ForStmt, stack astPath) continueStmt { + cs := continueStmt{ + forStmt: forStmt, + analyzeStack: stack.copy(), + } + return cs +} + +// astPath is a list of AST nodes where each previous node is a parent of the +// next node. +type astPath []ast.Node + +func (src astPath) copy() astPath { + dst := make(astPath, len(src)) + copy(dst, src) + return dst +} + +func (ap astPath) String() string { + s := &strings.Builder{} + s.WriteString("[") + for i, n := range ap { + if i > 0 { + s.WriteString(", ") + } + fmt.Fprintf(s, "%T(%p)", n, n) + } + s.WriteString("]") + return s.String() } type Info struct { *types.Info Pkg *types.Package - IsBlocking func(*types.Func) bool HasPointer map[*types.Var]bool FuncDeclInfos map[*types.Func]*FuncInfo FuncLitInfos map[*ast.FuncLit]*FuncInfo - InitFuncInfo *FuncInfo - allInfos []*FuncInfo - comments ast.CommentMap -} + InitFuncInfo *FuncInfo // Context for package variable initialization. -type FuncInfo struct { - HasDefer bool - Flattened map[ast.Node]bool - Blocking map[ast.Node]bool - GotoLabel map[*types.Label]bool - LocalCalls map[*types.Func][][]ast.Node - ContinueStmts []continueStmt - p *Info - analyzeStack []ast.Node + isImportedBlocking func(*types.Func) bool // For functions from other packages. + allInfos []*FuncInfo } -func (info *Info) newFuncInfo() *FuncInfo { +func (info *Info) newFuncInfo(n ast.Node) *FuncInfo { funcInfo := &FuncInfo{ - p: info, - Flattened: make(map[ast.Node]bool), - Blocking: make(map[ast.Node]bool), - GotoLabel: make(map[*types.Label]bool), - LocalCalls: make(map[*types.Func][][]ast.Node), + pkgInfo: info, + Flattened: make(map[ast.Node]bool), + Blocking: make(map[ast.Node]bool), + GotoLabel: make(map[*types.Label]bool), + localNamedCallees: make(map[*types.Func][]astPath), + literalFuncCallees: make(map[*ast.FuncLit][]astPath), + } + + // Register the function in the appropriate map. + switch n := n.(type) { + case *ast.FuncDecl: + if n.Body == nil { + // Function body comes from elsewhere (for example, from a go:linkname + // directive), conservatively assume that it may be blocking. + // TODO(nevkontakte): It is possible to improve accuracy of this detection. + // Since GopherJS supports inly "import-style" go:linkname, at this stage + // the compiler already determined whether the implementation function is + // blocking, and we could check that. + funcInfo.Blocking[n] = true + } + info.FuncDeclInfos[info.Defs[n.Name].(*types.Func)] = funcInfo + case *ast.FuncLit: + info.FuncLitInfos[n] = funcInfo } + + // And add it to the list of all functions. info.allInfos = append(info.allInfos, funcInfo) + return funcInfo } +func (info *Info) IsBlocking(fun *types.Func) bool { + return len(info.FuncDeclInfos[fun].Blocking) > 0 +} + func AnalyzePkg(files []*ast.File, fileSet *token.FileSet, typesInfo *types.Info, typesPkg *types.Package, isBlocking func(*types.Func) bool) *Info { info := &Info{ - Info: typesInfo, - Pkg: typesPkg, - HasPointer: make(map[*types.Var]bool), - comments: make(ast.CommentMap), - IsBlocking: isBlocking, - FuncDeclInfos: make(map[*types.Func]*FuncInfo), - FuncLitInfos: make(map[*ast.FuncLit]*FuncInfo), + Info: typesInfo, + Pkg: typesPkg, + HasPointer: make(map[*types.Var]bool), + isImportedBlocking: isBlocking, + FuncDeclInfos: make(map[*types.Func]*FuncInfo), + FuncLitInfos: make(map[*ast.FuncLit]*FuncInfo), } - info.InitFuncInfo = info.newFuncInfo() + info.InitFuncInfo = info.newFuncInfo(nil) + // Traverse the full AST of the package and collect information about existing + // functions. for _, file := range files { - for k, v := range ast.NewCommentMap(fileSet, file, file.Comments) { - info.comments[k] = v - } ast.Walk(info.InitFuncInfo, file) } + for _, funcInfo := range info.allInfos { + if !funcInfo.HasDefer { + continue + } + // Conservatively assume that if a function has a deferred call, it might be + // blocking, and therefore all return statements need to be treated as + // blocking. + // TODO(nevkontakte): This could be improved by detecting whether a deferred + // call is actually blocking. Doing so might reduce generated code size a + // bit. + for _, returnStmt := range funcInfo.returnStmts { + funcInfo.markBlocking(returnStmt) + } + } + + // Propagate information about blocking calls to the caller functions. + // For each function we check all other functions it may call and if any of + // them are blocking, we mark the caller blocking as well. The process is + // repeated until no new blocking functions is detected. for { done := true - for _, funcInfo := range info.allInfos { - for obj, calls := range funcInfo.LocalCalls { - if len(info.FuncDeclInfos[obj].Blocking) != 0 { - for _, call := range calls { - funcInfo.markBlocking(call) + for _, caller := range info.allInfos { + // Check calls to named functions and function-typed variables. + for callee, callSites := range caller.localNamedCallees { + if info.IsBlocking(callee) { + for _, callSite := range callSites { + caller.markBlocking(callSite) } - delete(funcInfo.LocalCalls, obj) + delete(caller.localNamedCallees, callee) + done = false + } + } + + // Check direct calls to function literals. + for callee, callSites := range caller.literalFuncCallees { + if len(info.FuncLitInfos[callee].Blocking) > 0 { + for _, callSite := range callSites { + caller.markBlocking(callSite) + } + delete(caller.literalFuncCallees, callee) done = false } } @@ -86,9 +162,13 @@ func AnalyzePkg(files []*ast.File, fileSet *token.FileSet, typesInfo *types.Info } } + // After all function blocking information was propagated, mark flow control + // statements as blocking whenever they may lead to a blocking function call. for _, funcInfo := range info.allInfos { - for _, continueStmt := range funcInfo.ContinueStmts { + for _, continueStmt := range funcInfo.continueStmts { if funcInfo.Blocking[continueStmt.forStmt.Post] { + // If a for-loop post-expression is blocking, the continue statement + // that leads to it must be treated as blocking. funcInfo.markBlocking(continueStmt.analyzeStack) } } @@ -97,158 +177,214 @@ func AnalyzePkg(files []*ast.File, fileSet *token.FileSet, typesInfo *types.Info return info } -func (c *FuncInfo) Visit(node ast.Node) ast.Visitor { +type FuncInfo struct { + HasDefer bool + // Nodes are "flattened" into a switch-case statement when we need to be able + // to jump into an arbitrary position in the code with a GOTO statement, or + // resume a goroutine after a blocking call unblocks. + Flattened map[ast.Node]bool + // Blocking indicates that either the AST node itself or its descendant may + // block goroutine execution (for example, a channel operation). + Blocking map[ast.Node]bool + // GotoLavel indicates a label referenced by a goto statement, rather than a + // named loop. + GotoLabel map[*types.Label]bool + // List of continue statements in the function. + continueStmts []continueStmt + // List of return statements in the function. + returnStmts []astPath + // List of other named functions from the current package this function calls. + // If any of them are blocking, this function will become blocking too. + localNamedCallees map[*types.Func][]astPath + // List of function literals directly called from this function (for example: + // `func() { /* do stuff */ }()`). This is distinct from function literals + // assigned to named variables (for example: `doStuff := func() {}; + // doStuff()`), which are handled by localNamedCallees. If any of them are + // identified as blocking, this function will become blocking too. + literalFuncCallees map[*ast.FuncLit][]astPath + + pkgInfo *Info // Function's parent package. + visitorStack astPath +} + +func (fi *FuncInfo) Visit(node ast.Node) ast.Visitor { if node == nil { - if len(c.analyzeStack) != 0 { - c.analyzeStack = c.analyzeStack[:len(c.analyzeStack)-1] + if len(fi.visitorStack) != 0 { + fi.visitorStack = fi.visitorStack[:len(fi.visitorStack)-1] } return nil } - c.analyzeStack = append(c.analyzeStack, node) + fi.visitorStack = append(fi.visitorStack, node) switch n := node.(type) { - case *ast.FuncDecl: - newInfo := c.p.newFuncInfo() - c.p.FuncDeclInfos[c.p.Defs[n.Name].(*types.Func)] = newInfo - return newInfo - case *ast.FuncLit: - newInfo := c.p.newFuncInfo() - c.p.FuncLitInfos[n] = newInfo - return newInfo + case *ast.FuncDecl, *ast.FuncLit: + // Analyze the function in its own context. + return fi.pkgInfo.newFuncInfo(n) case *ast.BranchStmt: switch n.Tok { case token.GOTO: - for _, n2 := range c.analyzeStack { - c.Flattened[n2] = true - } - c.GotoLabel[c.p.Uses[n.Label].(*types.Label)] = true + // Emulating GOTO in JavaScript requires the code to be flattened into a + // switch-statement. + fi.markFlattened(fi.visitorStack) + fi.GotoLabel[fi.pkgInfo.Uses[n.Label].(*types.Label)] = true case token.CONTINUE: - if n.Label != nil { - label := c.p.Uses[n.Label].(*types.Label) - for i := len(c.analyzeStack) - 1; i >= 0; i-- { - if labelStmt, ok := c.analyzeStack[i].(*ast.LabeledStmt); ok && c.p.Defs[labelStmt.Label] == label { - if _, ok := labelStmt.Stmt.(*ast.RangeStmt); ok { - return nil - } - stack := make([]ast.Node, len(c.analyzeStack)) - copy(stack, c.analyzeStack) - c.ContinueStmts = append(c.ContinueStmts, continueStmt{labelStmt.Stmt.(*ast.ForStmt), stack}) - return nil - } - } - return nil - } - for i := len(c.analyzeStack) - 1; i >= 0; i-- { - if _, ok := c.analyzeStack[i].(*ast.RangeStmt); ok { - return nil - } - if forStmt, ok := c.analyzeStack[i].(*ast.ForStmt); ok { - stack := make([]ast.Node, len(c.analyzeStack)) - copy(stack, c.analyzeStack) - c.ContinueStmts = append(c.ContinueStmts, continueStmt{forStmt, stack}) - return nil - } + loopStmt := astutil.FindLoopStmt(fi.visitorStack, n, fi.pkgInfo.Info) + if forStmt, ok := (loopStmt).(*ast.ForStmt); ok { + // In `for x; y; z { ... }` loops `z` may be potentially blocking + // and therefore continue expression that triggers it would have to + // be treated as blocking. + fi.continueStmts = append(fi.continueStmts, newContinueStmt(forStmt, fi.visitorStack)) } } + return fi case *ast.CallExpr: - callTo := func(obj types.Object) { - switch o := obj.(type) { - case *types.Func: - if recv := o.Type().(*types.Signature).Recv(); recv != nil { - if _, ok := recv.Type().Underlying().(*types.Interface); ok { - c.markBlocking(c.analyzeStack) - return - } - } - if o.Pkg() != c.p.Pkg { - if c.p.IsBlocking(o) { - c.markBlocking(c.analyzeStack) - } - return - } - stack := make([]ast.Node, len(c.analyzeStack)) - copy(stack, c.analyzeStack) - c.LocalCalls[o] = append(c.LocalCalls[o], stack) - case *types.Var: - c.markBlocking(c.analyzeStack) - } - } - switch f := astutil.RemoveParens(n.Fun).(type) { - case *ast.Ident: - callTo(c.p.Uses[f]) - case *ast.SelectorExpr: - if sel := c.p.Selections[f]; sel != nil && typesutil.IsJsObject(sel.Recv()) { - break - } - callTo(c.p.Uses[f.Sel]) - case *ast.FuncLit: - ast.Walk(c, n.Fun) - for _, arg := range n.Args { - ast.Walk(c, arg) - } - if len(c.p.FuncLitInfos[f].Blocking) != 0 { - c.markBlocking(c.analyzeStack) - } - return nil - default: - if !astutil.IsTypeExpr(f, c.p.Info) { - c.markBlocking(c.analyzeStack) - } - } + return fi.visitCallExpr(n) case *ast.SendStmt: - c.markBlocking(c.analyzeStack) + // Sending into a channel is blocking. + fi.markBlocking(fi.visitorStack) + return fi case *ast.UnaryExpr: switch n.Op { case token.AND: if id, ok := astutil.RemoveParens(n.X).(*ast.Ident); ok { - c.p.HasPointer[c.p.Uses[id].(*types.Var)] = true + fi.pkgInfo.HasPointer[fi.pkgInfo.Uses[id].(*types.Var)] = true } case token.ARROW: - c.markBlocking(c.analyzeStack) + // Receiving from a channel is blocking. + fi.markBlocking(fi.visitorStack) } + return fi case *ast.RangeStmt: - if _, ok := c.p.TypeOf(n.X).Underlying().(*types.Chan); ok { - c.markBlocking(c.analyzeStack) + if _, ok := fi.pkgInfo.TypeOf(n.X).Underlying().(*types.Chan); ok { + // for-range loop over a channel is blocking. + fi.markBlocking(fi.visitorStack) } + return fi case *ast.SelectStmt: for _, s := range n.Body.List { if s.(*ast.CommClause).Comm == nil { // default clause - return c + return fi } } - c.markBlocking(c.analyzeStack) + // Select statements without a default case are blocking. + fi.markBlocking(fi.visitorStack) + return fi case *ast.CommClause: + // FIXME(nevkontakte): Does this need to be manually spelled out? Presumably + // ast.Walk would visit all those nodes anyway, and we are not creating any + // new contexts here. + // https://github.com/gopherjs/gopherjs/issues/230 seems to be relevant? switch comm := n.Comm.(type) { case *ast.SendStmt: - ast.Walk(c, comm.Chan) - ast.Walk(c, comm.Value) + ast.Walk(fi, comm.Chan) + ast.Walk(fi, comm.Value) case *ast.ExprStmt: - ast.Walk(c, comm.X.(*ast.UnaryExpr).X) + ast.Walk(fi, comm.X.(*ast.UnaryExpr).X) case *ast.AssignStmt: - ast.Walk(c, comm.Rhs[0].(*ast.UnaryExpr).X) + ast.Walk(fi, comm.Rhs[0].(*ast.UnaryExpr).X) } for _, s := range n.Body { - ast.Walk(c, s) + ast.Walk(fi, s) } - return nil + return nil // The subtree was manually checked, no need to visit it again. case *ast.GoStmt: - ast.Walk(c, n.Call.Fun) + // Unlike a regular call, the function in a go statement doesn't block the + // caller goroutine, but the expression that determines the function and its + // arguments still need to be checked. + ast.Walk(fi, n.Call.Fun) for _, arg := range n.Call.Args { - ast.Walk(c, arg) + ast.Walk(fi, arg) } - return nil + return nil // The subtree was manually checked, no need to visit it again. case *ast.DeferStmt: - c.HasDefer = true + fi.HasDefer = true if funcLit, ok := n.Call.Fun.(*ast.FuncLit); ok { - ast.Walk(c, funcLit.Body) + ast.Walk(fi, funcLit.Body) + } + return fi + case *ast.ReturnStmt: + // Capture all return statements in the function. They could become blocking + // if the function has a blocking deferred call. + fi.returnStmts = append(fi.returnStmts, fi.visitorStack.copy()) + return fi + default: + return fi + } + // Deliberately no return here to make sure that each of the cases above is + // self-sufficient and explicitly decides in which context the its AST subtree + // needs to be analyzed. +} + +func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor { + switch f := astutil.RemoveParens(n.Fun).(type) { + case *ast.Ident: + fi.callToNamedFunc(fi.pkgInfo.Uses[f]) + case *ast.SelectorExpr: + if sel := fi.pkgInfo.Selections[f]; sel != nil && typesutil.IsJsObject(sel.Recv()) { + // js.Object methods are known to be non-blocking, but we still must + // check its arguments. + } else { + fi.callToNamedFunc(fi.pkgInfo.Uses[f.Sel]) + } + case *ast.FuncLit: + // Collect info about the function literal itself. + ast.Walk(fi, n.Fun) + + // Check all argument expressions. + for _, arg := range n.Args { + ast.Walk(fi, arg) + } + // Register literal function call site in case it is identified as blocking. + fi.literalFuncCallees[f] = append(fi.literalFuncCallees[f], fi.visitorStack.copy()) + return nil // No need to walk under this CallExpr, we already did it manually. + default: + if astutil.IsTypeExpr(f, fi.pkgInfo.Info) { + // This is a type assertion, not a call. Type assertion itself is not + // blocking, but we will visit the expression itself. + } else { + // The function is returned by a non-trivial expression. We have to be + // conservative and assume that function might be blocking. + fi.markBlocking(fi.visitorStack) } } - return c + + return fi +} + +func (fi *FuncInfo) callToNamedFunc(callee types.Object) { + switch o := callee.(type) { + case *types.Func: + if recv := o.Type().(*types.Signature).Recv(); recv != nil { + if _, ok := recv.Type().Underlying().(*types.Interface); ok { + // Conservatively assume that an interface implementation may be blocking. + fi.markBlocking(fi.visitorStack) + return + } + } + if o.Pkg() != fi.pkgInfo.Pkg { + if fi.pkgInfo.isImportedBlocking(o) { + fi.markBlocking(fi.visitorStack) + } + return + } + // We probably don't know yet whether the callee function is blocking. + // Record the calls site for the later stage. + fi.localNamedCallees[o] = append(fi.localNamedCallees[o], fi.visitorStack.copy()) + case *types.Var: + // Conservatively assume that a function in a variable might be blocking. + fi.markBlocking(fi.visitorStack) + } +} + +func (fi *FuncInfo) markBlocking(stack astPath) { + for _, n := range stack { + fi.Blocking[n] = true + fi.Flattened[n] = true + } } -func (c *FuncInfo) markBlocking(stack []ast.Node) { +func (fi *FuncInfo) markFlattened(stack astPath) { for _, n := range stack { - c.Blocking[n] = true - c.Flattened[n] = true + fi.Flattened[n] = true } } diff --git a/compiler/analysis/info_test.go b/compiler/analysis/info_test.go new file mode 100644 index 00000000..72320825 --- /dev/null +++ b/compiler/analysis/info_test.go @@ -0,0 +1,79 @@ +package analysis + +import ( + "go/ast" + "go/token" + "go/types" + "testing" + + "github.com/gopherjs/gopherjs/internal/srctesting" +) + +// See: https://github.com/gopherjs/gopherjs/issues/955. +func TestBlockingFunctionLiteral(t *testing.T) { + src := ` +package test + +func blocking() { + c := make(chan bool) + <-c +} + +func indirectlyBlocking() { + func() { blocking() }() +} + +func directlyBlocking() { + func() { + c := make(chan bool) + <-c + }() +} + +func notBlocking() { + func() { println() } () +} +` + fset := token.NewFileSet() + file := srctesting.Parse(t, fset, src) + typesInfo, typesPkg := srctesting.Check(t, fset, file) + + pkgInfo := AnalyzePkg([]*ast.File{file}, fset, typesInfo, typesPkg, func(f *types.Func) bool { + panic("isBlocking() should be never called for imported functions in this test.") + }) + + assertBlocking(t, file, pkgInfo, "blocking") + assertBlocking(t, file, pkgInfo, "indirectlyBlocking") + assertBlocking(t, file, pkgInfo, "directlyBlocking") + assertNotBlocking(t, file, pkgInfo, "notBlocking") +} + +func assertBlocking(t *testing.T, file *ast.File, pkgInfo *Info, funcName string) { + typesFunc := getTypesFunc(t, file, pkgInfo, funcName) + if !pkgInfo.IsBlocking(typesFunc) { + t.Errorf("Got: %q is not blocking. Want: %q is blocking.", typesFunc, typesFunc) + } +} + +func assertNotBlocking(t *testing.T, file *ast.File, pkgInfo *Info, funcName string) { + typesFunc := getTypesFunc(t, file, pkgInfo, funcName) + if pkgInfo.IsBlocking(typesFunc) { + t.Errorf("Got: %q is blocking. Want: %q is not blocking.", typesFunc, typesFunc) + } +} + +func getTypesFunc(t *testing.T, file *ast.File, pkgInfo *Info, funcName string) *types.Func { + obj := file.Scope.Lookup(funcName) + if obj == nil { + t.Fatalf("Declaration of %q is not found in the AST.", funcName) + } + decl, ok := obj.Decl.(*ast.FuncDecl) + if !ok { + t.Fatalf("Got: %q is %v. Want: a function declaration.", funcName, obj.Kind) + } + blockingType, ok := pkgInfo.Defs[decl.Name] + if !ok { + t.Fatalf("No type information is found for %v.", decl.Name) + } + return blockingType.(*types.Func) +} diff --git a/compiler/astutil/astutil.go b/compiler/astutil/astutil.go index 7cd93b3d..b9c4b54c 100644 --- a/compiler/astutil/astutil.go +++ b/compiler/astutil/astutil.go @@ -1,8 +1,11 @@ package astutil import ( + "fmt" "go/ast" + "go/token" "go/types" + "strings" ) func RemoveParens(e ast.Expr) ast.Expr { @@ -46,3 +49,101 @@ func IsTypeExpr(expr ast.Expr, info *types.Info) bool { return false } } + +func ImportsUnsafe(file *ast.File) bool { + for _, imp := range file.Imports { + if imp.Path.Value == `"unsafe"` { + return true + } + } + return false +} + +// FuncKey returns a string, which uniquely identifies a top-level function or +// method in a package. +func FuncKey(d *ast.FuncDecl) string { + if d.Recv == nil || len(d.Recv.List) == 0 { + return d.Name.Name + } + recv := d.Recv.List[0].Type + if star, ok := recv.(*ast.StarExpr); ok { + recv = star.X + } + return recv.(*ast.Ident).Name + "." + d.Name.Name +} + +// PruneOriginal returns true if gopherjs:prune-original directive is present +// before a function decl. +// +// `//gopherjs:prune-original` is a GopherJS-specific directive, which can be +// applied to functions in native overlays and will instruct the augmentation +// logic to delete the body of a standard library function that was replaced. +// This directive can be used to remove code that would be invalid in GopherJS, +// such as code expecting ints to be 64-bit. It should be used with caution +// since it may create unused imports in the original source file. +func PruneOriginal(d *ast.FuncDecl) bool { + if d.Doc == nil { + return false + } + for _, c := range d.Doc.List { + if strings.HasPrefix(c.Text, "//gopherjs:prune-original") { + return true + } + } + return false +} + +// FindLoopStmt tries to find the loop statement among the AST nodes in the +// |stack| that corresponds to the break/continue statement represented by +// branch. +// +// This function is label-aware and assumes the code was successfully +// type-checked. +func FindLoopStmt(stack []ast.Node, branch *ast.BranchStmt, typeInfo *types.Info) ast.Stmt { + if branch.Tok != token.CONTINUE && branch.Tok != token.BREAK { + panic(fmt.Errorf("FindLoopStmt() must be used with a break or continue statement only, got: %v", branch)) + } + + for i := len(stack) - 1; i >= 0; i-- { + n := stack[i] + + if branch.Label != nil { + // For a labelled continue the loop will always be in a labelled statement. + referencedLabel := typeInfo.Uses[branch.Label].(*types.Label) + labelStmt, ok := n.(*ast.LabeledStmt) + if !ok { + continue + } + if definedLabel := typeInfo.Defs[labelStmt.Label]; definedLabel != referencedLabel { + continue + } + n = labelStmt.Stmt + } + + switch s := n.(type) { + case *ast.RangeStmt, *ast.ForStmt: + return s.(ast.Stmt) + } + } + + // This should never happen in a source that passed type checking. + panic(fmt.Errorf("continue/break statement %v doesn't have a matching loop statement among ancestors", branch)) +} + +// EndsWithReturn returns true if the last effective statement is a "return". +func EndsWithReturn(stmts []ast.Stmt) bool { + if len(stmts) == 0 { + return false + } + last := stmts[len(stmts)-1] + switch l := last.(type) { + case *ast.ReturnStmt: + return true + case *ast.LabeledStmt: + return EndsWithReturn([]ast.Stmt{l.Stmt}) + case *ast.BlockStmt: + return EndsWithReturn(l.List) + default: + return false + } +} diff --git a/compiler/astutil/astutil_test.go b/compiler/astutil/astutil_test.go new file mode 100644 index 00000000..a996ae73 --- /dev/null +++ b/compiler/astutil/astutil_test.go @@ -0,0 +1,183 @@ +package astutil + +import ( + "go/token" + "testing" + + "github.com/gopherjs/gopherjs/internal/srctesting" +) + +func TestImportsUnsafe(t *testing.T) { + tests := []struct { + desc string + imports string + want bool + }{ + { + desc: "no imports", + imports: "", + want: false, + }, { + desc: "other imports", + imports: `import "some/other/package"`, + want: false, + }, { + desc: "only unsafe", + imports: `import "unsafe"`, + want: true, + }, { + desc: "multi-import decl", + imports: `import ( + "some/other/package" + "unsafe" + )`, + want: true, + }, { + desc: "two import decls", + imports: `import "some/other/package" + import "unsafe"`, + want: true, + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + src := "package testpackage\n\n" + test.imports + fset := token.NewFileSet() + file := srctesting.Parse(t, fset, src) + got := ImportsUnsafe(file) + if got != test.want { + t.Fatalf("ImportsUnsafe() returned %t, want %t", got, test.want) + } + }) + } +} + +func TestFuncKey(t *testing.T) { + tests := []struct { + desc string + src string + want string + }{ + { + desc: "top-level function", + src: `package testpackage; func foo() {}`, + want: "foo", + }, { + desc: "top-level exported function", + src: `package testpackage; func Foo() {}`, + want: "Foo", + }, { + desc: "method", + src: `package testpackage; func (_ myType) bar() {}`, + want: "myType.bar", + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + fdecl := srctesting.ParseFuncDecl(t, test.src) + if got := FuncKey(fdecl); got != test.want { + t.Errorf("Got %q, want %q", got, test.want) + } + }) + } +} + +func TestPruneOriginal(t *testing.T) { + tests := []struct { + desc string + src string + want bool + }{ + { + desc: "no comment", + src: `package testpackage; + func foo() {}`, + want: false, + }, { + desc: "regular godoc", + src: `package testpackage; + // foo does something + func foo() {}`, + want: false, + }, { + desc: "only directive", + src: `package testpackage; + //gopherjs:prune-original + func foo() {}`, + want: true, + }, { + desc: "directive with explanation", + src: `package testpackage; + //gopherjs:prune-original because reasons + func foo() {}`, + want: true, + }, { + desc: "directive in godoc", + src: `package testpackage; + // foo does something + //gopherjs:prune-original + func foo() {}`, + want: true, + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + fdecl := srctesting.ParseFuncDecl(t, test.src) + if got := PruneOriginal(fdecl); got != test.want { + t.Errorf("PruneOriginal() returned %t, want %t", got, test.want) + } + }) + } +} + +func TestEndsWithReturn(t *testing.T) { + tests := []struct { + desc string + src string + want bool + }{ + { + desc: "empty function", + src: `func foo() {}`, + want: false, + }, { + desc: "implicit return", + src: `func foo() { a() }`, + want: false, + }, { + desc: "explicit return", + src: `func foo() { a(); return }`, + want: true, + }, { + desc: "labelled return", + src: `func foo() { Label: return }`, + want: true, + }, { + desc: "labelled call", + src: `func foo() { Label: a() }`, + want: false, + }, { + desc: "return in a block", + src: `func foo() { a(); { b(); return; } }`, + want: true, + }, { + desc: "a block without return", + src: `func foo() { a(); { b(); c(); } }`, + want: false, + }, { + desc: "conditional block", + src: `func foo() { a(); if x { b(); return; } }`, + want: false, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + fdecl := srctesting.ParseFuncDecl(t, "package testpackage\n"+test.src) + got := EndsWithReturn(fdecl.Body.List) + if got != test.want { + t.Errorf("EndsWithReturn() returned %t, want %t", got, test.want) + } + }) + } +} diff --git a/compiler/compiler.go b/compiler/compiler.go index 36ec91a8..e660b1be 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1,3 +1,8 @@ +// Package compiler implements GopherJS compiler logic. +// +// WARNING: This package's API is treated as internal and currently doesn't +// provide any API stability guarantee, use it at your own risk. If you need a +// stable interface, prefer invoking the gopherjs CLI tool as a subprocess. package compiler import ( @@ -10,6 +15,7 @@ import ( "go/types" "io" "strings" + "time" "github.com/gopherjs/gopherjs/compiler/prelude" "golang.org/x/tools/go/gcexportdata" @@ -17,7 +23,6 @@ import ( var sizes32 = &types.StdSizes{WordSize: 4, MaxAlign: 8} var reservedKeywords = make(map[string]bool) -var _ = ___GOPHERJS_REQUIRES_GO_VERSION_1_12___ // Compile error on other Go versions, because they're not supported. func init() { for _, keyword := range []string{"abstract", "arguments", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "eval", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof", "undefined", "var", "void", "volatile", "while", "with", "yield"} { @@ -28,31 +33,115 @@ func init() { type ErrorList []error func (err ErrorList) Error() string { - return err[0].Error() + if len(err) == 0 { + return "" + } + return fmt.Sprintf("%s (and %d more errors)", err[0].Error(), len(err[1:])) +} + +func (err ErrorList) Normalize() error { + if len(err) == 0 { + return nil + } + return err } +// Archive contains intermediate build outputs of a single package. +// +// This is a logical equivalent of an object file in traditional compilers. type Archive struct { - ImportPath string - Name string - Imports []string - ExportData []byte + // Package's full import path, e.g. "some/package/name". + ImportPath string + // Package's name as per "package" statement at the top of a source file. + // Usually matches the last component of import path, but may differ in + // certain cases (e.g. main or test packages). + Name string + // A list of full package import paths that the current package imports across + // all source files. See go/types.Package.Imports(). + Imports []string + // Serialized contents of go/types.Package in a binary format. This information + // is used by the compiler to type-check packages that import this one. See + // gcexportdata.Write(). + // + // TODO(nevkontakte): It would be more convenient to store go/types.Package + // itself and only serialize it when writing the archive onto disk. + ExportData []byte + // Compiled package-level symbols. Declarations []*Decl - IncJSCode []byte - FileSet []byte - Minified bool + // Concatenated contents of all raw .inc.js of the package. + IncJSCode []byte + // JSON-serialized contents of go/token.FileSet. This is used to obtain source + // code locations for various symbols (e.g. for sourcemap generation). See + // token.FileSet.Write(). + // + // TODO(nevkontakte): This is also more convenient to store as the original + // object and only serialize before writing onto disk. + FileSet []byte + // Whether or not the package was compiled with minification enabled. + Minified bool + // A list of go:linkname directives encountered in the package. + GoLinknames []GoLinkname + // Time when this archive was built. + BuildTime time.Time } +func (a Archive) String() string { + return fmt.Sprintf("compiler.Archive{%s}", a.ImportPath) +} + +// RegisterTypes adds package type information from the archive into the provided map. +func (a *Archive) RegisterTypes(packages map[string]*types.Package) error { + var err error + // TODO(nevkontakte): Should this be shared throughout the build? + fset := token.NewFileSet() + packages[a.ImportPath], err = gcexportdata.Read(bytes.NewReader(a.ExportData), fset, packages, a.ImportPath) + return err +} + +// Decl represents a package-level symbol (e.g. a function, variable or type). +// +// It contains code generated by the compiler for this specific symbol, which is +// grouped by the execution stage it belongs to in the JavaScript runtime. type Decl struct { - FullName string - Vars []string - DeclCode []byte - MethodListCode []byte - TypeInitCode []byte - InitCode []byte + // The package- or receiver-type-qualified name of function or method obj. + // See go/types.Func.FullName(). + FullName string + // A logical equivalent of a symbol name in an object file in the traditional + // Go compiler/linker toolchain. Used by GopherJS to support go:linkname + // directives. Must be set for decls that are supported by go:linkname + // implementation. + LinkingName SymName + // A list of package-level JavaScript variable names this symbol needs to declare. + Vars []string + // NamedRecvType is method named recv declare. + NamedRecvType string + // JavaScript code that declares basic information about a symbol. For a type + // it configures basic information about the type and its identity. For a function + // or method it contains its compiled body. + DeclCode []byte + // JavaScript code that initializes reflection metadata about type's method list. + MethodListCode []byte + // JavaScript code that initializes the rest of reflection metadata about a type + // (e.g. struct fields, array type sizes, element types, etc.). + TypeInitCode []byte + // JavaScript code that needs to be executed during the package init phase to + // set the symbol up (e.g. initialize package-level variable value). + InitCode []byte + // Symbol's identifier used by the dead-code elimination logic, not including + // package path. If empty, the symbol is assumed to be alive and will not be + // eliminated. For methods it is the same as its receiver type identifier. DceObjectFilter string + // The second part of the identified used by dead-code elimination for methods. + // Empty for other types of symbols. DceMethodFilter string - DceDeps []string - Blocking bool + // List of fully qualified (including package path) DCE symbol identifiers the + // symbol depends on for dead code elimination purposes. + DceDeps []string + // Set to true if a function performs a blocking operation (I/O or + // synchronization). The compiler will have to generate function code such + // that it can be resumed after a blocking operation completes without + // blocking the main thread in the meantime. + Blocking bool } type Dependency struct { @@ -102,18 +191,33 @@ type dceInfo struct { methodFilter string } -func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter) error { +func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter, goVersion string) error { mainPkg := pkgs[len(pkgs)-1] minify := mainPkg.Minified + // Aggregate all go:linkname directives in the program together. + gls := goLinknameSet{} + for _, pkg := range pkgs { + gls.Add(pkg.GoLinknames) + } + byFilter := make(map[string][]*dceInfo) - var pendingDecls []*Decl + var pendingDecls []*Decl // A queue of live decls to find other live decls. for _, pkg := range pkgs { for _, d := range pkg.Declarations { if d.DceObjectFilter == "" && d.DceMethodFilter == "" { + // This is an entry point (like main() or init() functions) or a variable + // initializer which has a side effect, consider it live. pendingDecls = append(pendingDecls, d) continue } + if gls.IsImplementation(d.LinkingName) { + // If a decl is referenced by a go:linkname directive, we just assume + // it's not dead. + // TODO(nevkontakte): This is a safe, but imprecise assumption. We should + // try and trace whether the referencing functions are actually live. + pendingDecls = append(pendingDecls, d) + } info := &dceInfo{decl: d} if d.DceObjectFilter != "" { info.objectFilter = pkg.ImportPath + "." + d.DceObjectFilter @@ -126,13 +230,15 @@ func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter) error { } } - dceSelection := make(map[*Decl]struct{}) + dceSelection := make(map[*Decl]struct{}) // Known live decls. for len(pendingDecls) != 0 { d := pendingDecls[len(pendingDecls)-1] pendingDecls = pendingDecls[:len(pendingDecls)-1] - dceSelection[d] = struct{}{} + dceSelection[d] = struct{}{} // Mark the decl as live. + // Consider all decls the current one is known to depend on and possible add + // them to the live queue. for _, dep := range d.DceDeps { if infos, ok := byFilter[dep]; ok { delete(byFilter, dep) @@ -154,6 +260,10 @@ func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter) error { if _, err := w.Write([]byte("\"use strict\";\n(function() {\n\n")); err != nil { return err } + if _, err := w.Write([]byte(fmt.Sprintf("var $goVersion = %q;\n", goVersion))); err != nil { + return err + } + preludeJS := prelude.Prelude if minify { preludeJS = prelude.Minified @@ -167,19 +277,18 @@ func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter) error { // write packages for _, pkg := range pkgs { - if err := WritePkgCode(pkg, dceSelection, minify, w); err != nil { + if err := WritePkgCode(pkg, dceSelection, gls, minify, w); err != nil { return err } } - if _, err := w.Write([]byte("$synthesizeMethods();\nvar $mainPkg = $packages[\"" + string(mainPkg.ImportPath) + "\"];\n$packages[\"runtime\"].$init();\n$go($mainPkg.$init, []);\n$flushConsole();\n\n}).call(this);\n")); err != nil { + if _, err := w.Write([]byte("$synthesizeMethods();\n$initAllLinknames();\nvar $mainPkg = $packages[\"" + string(mainPkg.ImportPath) + "\"];\n$packages[\"runtime\"].$init();\n$go($mainPkg.$init, []);\n$flushConsole();\n\n}).call(this);\n")); err != nil { return err } - return nil } -func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, minify bool, w *SourceMapFilter) error { +func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, gls goLinknameSet, minify bool, w *SourceMapFilter) error { if w.MappingCallback != nil && pkg.FileSet != nil { w.fileSet = token.NewFileSet() if err := w.fileSet.Read(json.NewDecoder(bytes.NewReader(pkg.FileSet)).Decode); err != nil { @@ -207,6 +316,20 @@ func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, minify bool, w if _, err := w.Write(d.DeclCode); err != nil { return err } + if gls.IsImplementation(d.LinkingName) { + // This decl is referenced by a go:linkname directive, expose it to external + // callers via $linkname object (declared in prelude). We are not using + // $pkg to avoid clashes with exported symbols. + var code string + if recv, method, ok := d.LinkingName.IsMethod(); ok { + code = fmt.Sprintf("\t$linknames[%q] = $unsafeMethodToFunction(%v,%q,%t);\n", d.LinkingName.String(), d.NamedRecvType, method, strings.HasPrefix(recv, "*")) + } else { + code = fmt.Sprintf("\t$linknames[%q] = %s;\n", d.LinkingName.String(), d.Vars[0]) + } + if _, err := w.Write(removeWhitespace([]byte(code), minify)); err != nil { + return err + } + } } for _, d := range filteredDecls { if _, err := w.Write(d.MethodListCode); err != nil { @@ -219,6 +342,29 @@ func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, minify bool, w } } + { + // Set up all functions which package declares, but which implementation + // comes from elsewhere via a go:linkname compiler directive. This code + // needs to be executed after all $packages entries were defined, since such + // reference may go in a direction opposite of the import graph. It also + // needs to run before any initializer code runs, since that code may invoke + // linknamed function. + lines := []string{} + for _, d := range filteredDecls { + impl, found := gls.FindImplementation(d.LinkingName) + if !found { + continue // The symbol is not affected by a go:linkname directive. + } + lines = append(lines, fmt.Sprintf("\t\t%s = $linknames[%q];\n", d.Vars[0], impl.String())) + } + if len(lines) > 0 { + code := fmt.Sprintf("\t$pkg.$initLinknames = function() {\n%s};\n", strings.Join(lines, "")) + if _, err := w.Write(removeWhitespace([]byte(code), minify)); err != nil { + return err + } + } + } + if _, err := w.Write(removeWhitespace([]byte("\t$init = function() {\n\t\t$pkg.$init = function() {};\n\t\t/* */ var $f, $c = false, $s = 0, $r; if (this !== undefined && this.$blk !== undefined) { $f = this; $c = true; $s = $f.$s; $r = $f.$r; } s: while (true) { switch ($s) { case 0:\n"), minify)); err != nil { return err } @@ -236,21 +382,17 @@ func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, minify bool, w return nil } -func ReadArchive(filename, path string, r io.Reader, packages map[string]*types.Package) (*Archive, error) { +// ReadArchive reads serialized compiled archive of the importPath package. +func ReadArchive(path string, r io.Reader) (*Archive, error) { var a Archive if err := gob.NewDecoder(r).Decode(&a); err != nil { return nil, err } - var err error - packages[path], err = gcexportdata.Read(bytes.NewReader(a.ExportData), token.NewFileSet(), packages, path) - if err != nil { - return nil, err - } - return &a, nil } +// WriteArchive writes compiled package archive on disk for later reuse. func WriteArchive(a *Archive, w io.Writer) error { return gob.NewEncoder(w).Encode(a) } diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go new file mode 100644 index 00000000..377f09d9 --- /dev/null +++ b/compiler/compiler_test.go @@ -0,0 +1,146 @@ +package compiler + +import ( + "bytes" + "go/ast" + "go/build" + "go/parser" + "go/token" + "go/types" + "testing" + + "github.com/google/go-cmp/cmp" + "golang.org/x/tools/go/loader" +) + +func TestOrder(t *testing.T) { + fileA := ` +package foo + +var Avar = "a" + +type Atype struct{} + +func Afunc() int { + var varA = 1 + var varB = 2 + return varA+varB +} +` + + fileB := ` +package foo + +var Bvar = "b" + +type Btype struct{} + +func Bfunc() int { + var varA = 1 + var varB = 2 + return varA+varB +} +` + files := []source{{"fileA.go", []byte(fileA)}, {"fileB.go", []byte(fileB)}} + + compare(t, "foo", files, false) + compare(t, "foo", files, true) +} + +func compare(t *testing.T, path string, sourceFiles []source, minify bool) { + outputNormal, err := compile(path, sourceFiles, minify) + if err != nil { + t.Fatal(err) + } + + // reverse the array + for i, j := 0, len(sourceFiles)-1; i < j; i, j = i+1, j-1 { + sourceFiles[i], sourceFiles[j] = sourceFiles[j], sourceFiles[i] + } + + outputReversed, err := compile(path, sourceFiles, minify) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(string(outputNormal), string(outputReversed)); diff != "" { + t.Errorf("files in different order produce different JS:\n%s", diff) + } +} + +type source struct { + name string + contents []byte +} + +func compile(path string, sourceFiles []source, minify bool) ([]byte, error) { + conf := loader.Config{} + conf.Fset = token.NewFileSet() + conf.ParserMode = parser.ParseComments + + context := build.Default // make a copy of build.Default + conf.Build = &context + conf.Build.BuildTags = []string{"js"} + + var astFiles []*ast.File + for _, sourceFile := range sourceFiles { + astFile, err := parser.ParseFile(conf.Fset, sourceFile.name, sourceFile.contents, parser.ParseComments) + if err != nil { + return nil, err + } + astFiles = append(astFiles, astFile) + } + conf.CreateFromFiles(path, astFiles...) + prog, err := conf.Load() + if err != nil { + return nil, err + } + + archiveCache := map[string]*Archive{} + var importContext *ImportContext + importContext = &ImportContext{ + Packages: make(map[string]*types.Package), + Import: func(path string) (*Archive, error) { + // find in local cache + if a, ok := archiveCache[path]; ok { + return a, nil + } + + pi := prog.Package(path) + importContext.Packages[path] = pi.Pkg + + // compile package + a, err := Compile(path, pi.Files, prog.Fset, importContext, minify) + if err != nil { + return nil, err + } + archiveCache[path] = a + return a, nil + }, + } + + a, err := importContext.Import(path) + if err != nil { + return nil, err + } + b, err := renderPackage(a) + if err != nil { + return nil, err + } + return b, nil +} + +func renderPackage(archive *Archive) ([]byte, error) { + selection := make(map[*Decl]struct{}) + for _, d := range archive.Declarations { + selection[d] = struct{}{} + } + + buf := &bytes.Buffer{} + + if err := WritePkgCode(archive, selection, goLinknameSet{}, false, &SourceMapFilter{Writer: buf}); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/compiler/expressions.go b/compiler/expressions.go index 42fe624b..5d9e37cc 100644 --- a/compiler/expressions.go +++ b/compiler/expressions.go @@ -32,13 +32,13 @@ func (e *expression) StringWithParens() string { return e.str } -func (c *funcContext) translateExpr(expr ast.Expr) *expression { - exprType := c.p.TypeOf(expr) - if value := c.p.Types[expr].Value; value != nil { +func (fc *funcContext) translateExpr(expr ast.Expr) *expression { + exprType := fc.pkgCtx.TypeOf(expr) + if value := fc.pkgCtx.Types[expr].Value; value != nil { basic := exprType.Underlying().(*types.Basic) switch { case isBoolean(basic): - return c.formatExpr("%s", strconv.FormatBool(constant.BoolVal(value))) + return fc.formatExpr("%s", strconv.FormatBool(constant.BoolVal(value))) case isInteger(basic): if is64Bit(basic) { if basic.Kind() == types.Int64 { @@ -46,31 +46,31 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { if !ok { panic("could not get exact uint") } - return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatInt(d>>32, 10), strconv.FormatUint(uint64(d)&(1<<32-1), 10)) + return fc.formatExpr("new %s(%s, %s)", fc.typeName(exprType), strconv.FormatInt(d>>32, 10), strconv.FormatUint(uint64(d)&(1<<32-1), 10)) } d, ok := constant.Uint64Val(constant.ToInt(value)) if !ok { panic("could not get exact uint") } - return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) + return fc.formatExpr("new %s(%s, %s)", fc.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } d, ok := constant.Int64Val(constant.ToInt(value)) if !ok { panic("could not get exact int") } - return c.formatExpr("%s", strconv.FormatInt(d, 10)) + return fc.formatExpr("%s", strconv.FormatInt(d, 10)) case isFloat(basic): f, _ := constant.Float64Val(value) - return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) + return fc.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case isComplex(basic): r, _ := constant.Float64Val(constant.Real(value)) i, _ := constant.Float64Val(constant.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } - return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) + return fc.formatExpr("new %s(%s, %s)", fc.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case isString(basic): - return c.formatExpr("%s", encodeString(constant.StringVal(value))) + return fc.formatExpr("%s", encodeString(constant.StringVal(value))) default: panic("Unhandled constant type: " + basic.String()) } @@ -79,22 +79,22 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { var obj types.Object switch e := expr.(type) { case *ast.SelectorExpr: - obj = c.p.Uses[e.Sel] + obj = fc.pkgCtx.Uses[e.Sel] case *ast.Ident: - obj = c.p.Defs[e] + obj = fc.pkgCtx.Defs[e] if obj == nil { - obj = c.p.Uses[e] + obj = fc.pkgCtx.Uses[e] } } if obj != nil && typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Global": - return c.formatExpr("$global") + return fc.formatExpr("$global") case "Module": - return c.formatExpr("$module") + return fc.formatExpr("$module") case "Undefined": - return c.formatExpr("undefined") + return fc.formatExpr("undefined") } } @@ -107,10 +107,10 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { collectIndexedElements := func(elementType types.Type) []string { var elements []string i := 0 - zero := c.translateExpr(c.zeroValue(elementType)).String() + zero := fc.translateExpr(fc.zeroValue(elementType)).String() for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { - key, ok := constant.Int64Val(constant.ToInt(c.p.Types[kve.Key].Value)) + key, ok := constant.Int64Val(constant.ToInt(fc.pkgCtx.Types[kve.Key].Value)) if !ok { panic("could not get exact int") } @@ -120,7 +120,7 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { for len(elements) <= i { elements = append(elements, zero) } - elements[i] = c.translateImplicitConversionWithCloning(element, elementType).String() + elements[i] = fc.translateImplicitConversionWithCloning(element, elementType).String() i++ } return elements @@ -130,22 +130,22 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) == 0 { - return c.formatExpr("%s.zero()", c.typeName(t)) + return fc.formatExpr("%s.zero()", fc.typeName(t)) } - zero := c.translateExpr(c.zeroValue(t.Elem())).String() + zero := fc.translateExpr(fc.zeroValue(t.Elem())).String() for len(elements) < int(t.Len()) { elements = append(elements, zero) } - return c.formatExpr(`$toNativeArray(%s, [%s])`, typeKind(t.Elem()), strings.Join(elements, ", ")) + return fc.formatExpr(`$toNativeArray(%s, [%s])`, typeKind(t.Elem()), strings.Join(elements, ", ")) case *types.Slice: - return c.formatExpr("new %s([%s])", c.typeName(exprType), strings.Join(collectIndexedElements(t.Elem()), ", ")) + return fc.formatExpr("new %s([%s])", fc.typeName(exprType), strings.Join(collectIndexedElements(t.Elem()), ", ")) case *types.Map: entries := make([]string, len(e.Elts)) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) - entries[i] = fmt.Sprintf("{ k: %s, v: %s }", c.translateImplicitConversionWithCloning(kve.Key, t.Key()), c.translateImplicitConversionWithCloning(kve.Value, t.Elem())) + entries[i] = fmt.Sprintf("{ k: %s, v: %s }", fc.translateImplicitConversionWithCloning(kve.Key, t.Key()), fc.translateImplicitConversionWithCloning(kve.Value, t.Elem())) } - return c.formatExpr("$makeMap(%s.keyFor, [%s])", c.typeName(t.Key()), strings.Join(entries, ", ")) + return fc.formatExpr("$makeMap(%s.keyFor, [%s])", fc.typeName(t.Key()), strings.Join(entries, ", ")) case *types.Struct: elements := make([]string, t.NumFields()) isKeyValue := true @@ -154,134 +154,138 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { } if !isKeyValue { for i, element := range e.Elts { - elements[i] = c.translateImplicitConversionWithCloning(element, t.Field(i).Type()).String() + elements[i] = fc.translateImplicitConversionWithCloning(element, t.Field(i).Type()).String() } } if isKeyValue { for i := range elements { - elements[i] = c.translateExpr(c.zeroValue(t.Field(i).Type())).String() + elements[i] = fc.translateExpr(fc.zeroValue(t.Field(i).Type())).String() } for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) for j := range elements { if kve.Key.(*ast.Ident).Name == t.Field(j).Name() { - elements[j] = c.translateImplicitConversionWithCloning(kve.Value, t.Field(j).Type()).String() + elements[j] = fc.translateImplicitConversionWithCloning(kve.Value, t.Field(j).Type()).String() break } } } } - return c.formatExpr("new %s.ptr(%s)", c.typeName(exprType), strings.Join(elements, ", ")) + return fc.formatExpr("new %s.ptr(%s)", fc.typeName(exprType), strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: - _, fun := translateFunction(e.Type, nil, e.Body, c, exprType.(*types.Signature), c.p.FuncLitInfos[e], "") - if len(c.p.escapingVars) != 0 { - names := make([]string, 0, len(c.p.escapingVars)) - for obj := range c.p.escapingVars { - names = append(names, c.p.objectNames[obj]) + _, fun := translateFunction(e.Type, nil, e.Body, fc, exprType.(*types.Signature), fc.pkgCtx.FuncLitInfos[e], "") + if len(fc.pkgCtx.escapingVars) != 0 { + names := make([]string, 0, len(fc.pkgCtx.escapingVars)) + for obj := range fc.pkgCtx.escapingVars { + names = append(names, fc.pkgCtx.objectNames[obj]) } sort.Strings(names) list := strings.Join(names, ", ") - return c.formatExpr("(function(%s) { return %s; })(%s)", list, fun, list) + return fc.formatExpr("(function(%s) { return %s; })(%s)", list, fun, list) } - return c.formatExpr("(%s)", fun) + return fc.formatExpr("(%s)", fun) case *ast.UnaryExpr: - t := c.p.TypeOf(e.X) + t := fc.pkgCtx.TypeOf(e.X) switch e.Op { case token.AND: if typesutil.IsJsObject(exprType) { - return c.formatExpr("%e.object", e.X) + return fc.formatExpr("%e.object", e.X) } switch t.Underlying().(type) { case *types.Struct, *types.Array: - return c.translateExpr(e.X) + // JavaScript's pass-by-reference semantics makes passing array's or + // struct's object semantically equivalent to passing a pointer + // TODO(nevkontakte): Evaluate if performance gain justifies complexity + // introduced by the special case. + return fc.translateExpr(e.X) } switch x := astutil.RemoveParens(e.X).(type) { case *ast.CompositeLit: - return c.formatExpr("$newDataPointer(%e, %s)", x, c.typeName(c.p.TypeOf(e))) + return fc.formatExpr("$newDataPointer(%e, %s)", x, fc.typeName(fc.pkgCtx.TypeOf(e))) case *ast.Ident: - obj := c.p.Uses[x].(*types.Var) - if c.p.escapingVars[obj] { - return c.formatExpr("(%1s.$ptr || (%1s.$ptr = new %2s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %1s)))", c.p.objectNames[obj], c.typeName(exprType)) + obj := fc.pkgCtx.Uses[x].(*types.Var) + if fc.pkgCtx.escapingVars[obj] { + return fc.formatExpr("(%1s.$ptr || (%1s.$ptr = new %2s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %1s)))", fc.pkgCtx.objectNames[obj], fc.typeName(exprType)) } - return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) + return fc.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, fc.varPtrName(obj), fc.typeName(exprType), fc.objectName(obj), fc.translateAssign(x, fc.newIdent("$v", exprType), false)) case *ast.SelectorExpr: - sel, ok := c.p.SelectionOf(x) + sel, ok := fc.pkgCtx.SelectionOf(x) if !ok { // qualified identifier - obj := c.p.Uses[x.Sel].(*types.Var) - return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) + obj := fc.pkgCtx.Uses[x.Sel].(*types.Var) + return fc.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, fc.varPtrName(obj), fc.typeName(exprType), fc.objectName(obj), fc.translateAssign(x, fc.newIdent("$v", exprType), false)) } - newSel := &ast.SelectorExpr{X: c.newIdent("this.$target", c.p.TypeOf(x.X)), Sel: x.Sel} - c.setType(newSel, exprType) - c.p.additionalSelections[newSel] = sel - return c.formatExpr("(%1e.$ptr_%2s || (%1e.$ptr_%2s = new %3s(function() { return %4e; }, function($v) { %5s }, %1e)))", x.X, x.Sel.Name, c.typeName(exprType), newSel, c.translateAssign(newSel, c.newIdent("$v", exprType), false)) + newSel := &ast.SelectorExpr{X: fc.newIdent("this.$target", fc.pkgCtx.TypeOf(x.X)), Sel: x.Sel} + fc.setType(newSel, exprType) + fc.pkgCtx.additionalSelections[newSel] = sel + return fc.formatExpr("(%1e.$ptr_%2s || (%1e.$ptr_%2s = new %3s(function() { return %4e; }, function($v) { %5s }, %1e)))", x.X, x.Sel.Name, fc.typeName(exprType), newSel, fc.translateAssign(newSel, fc.newIdent("$v", exprType), false)) case *ast.IndexExpr: - if _, ok := c.p.TypeOf(x.X).Underlying().(*types.Slice); ok { - return c.formatExpr("$indexPtr(%1e.$array, %1e.$offset + %2e, %3s)", x.X, x.Index, c.typeName(exprType)) + if _, ok := fc.pkgCtx.TypeOf(x.X).Underlying().(*types.Slice); ok { + return fc.formatExpr("$indexPtr(%1e.$array, %1e.$offset + %2e, %3s)", x.X, x.Index, fc.typeName(exprType)) } - return c.formatExpr("$indexPtr(%e, %e, %s)", x.X, x.Index, c.typeName(exprType)) + return fc.formatExpr("$indexPtr(%e, %e, %s)", x.X, x.Index, fc.typeName(exprType)) case *ast.StarExpr: - return c.translateExpr(x.X) + return fc.translateExpr(x.X) default: panic(fmt.Sprintf("Unhandled: %T\n", x)) } case token.ARROW: call := &ast.CallExpr{ - Fun: c.newIdent("$recv", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", t)), types.NewTuple(types.NewVar(0, nil, "", exprType), types.NewVar(0, nil, "", types.Typ[types.Bool])), false)), + Fun: fc.newIdent("$recv", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", t)), types.NewTuple(types.NewVar(0, nil, "", exprType), types.NewVar(0, nil, "", types.Typ[types.Bool])), false)), Args: []ast.Expr{e.X}, } - c.Blocking[call] = true + fc.Blocking[call] = true if _, isTuple := exprType.(*types.Tuple); isTuple { - return c.formatExpr("%e", call) + return fc.formatExpr("%e", call) } - return c.formatExpr("%e[0]", call) + return fc.formatExpr("%e[0]", call) } basic := t.Underlying().(*types.Basic) switch e.Op { case token.ADD: - return c.translateExpr(e.X) + return fc.translateExpr(e.X) case token.SUB: switch { case is64Bit(basic): - return c.formatExpr("new %1s(-%2h, -%2l)", c.typeName(t), e.X) + return fc.formatExpr("new %1s(-%2h, -%2l)", fc.typeName(t), e.X) case isComplex(basic): - return c.formatExpr("new %1s(-%2r, -%2i)", c.typeName(t), e.X) + return fc.formatExpr("new %1s(-%2r, -%2i)", fc.typeName(t), e.X) case isUnsigned(basic): - return c.fixNumber(c.formatExpr("-%e", e.X), basic) + return fc.fixNumber(fc.formatExpr("-%e", e.X), basic) default: - return c.formatExpr("-%e", e.X) + return fc.formatExpr("-%e", e.X) } case token.XOR: if is64Bit(basic) { - return c.formatExpr("new %1s(~%2h, ~%2l >>> 0)", c.typeName(t), e.X) + return fc.formatExpr("new %1s(~%2h, ~%2l >>> 0)", fc.typeName(t), e.X) } - return c.fixNumber(c.formatExpr("~%e", e.X), basic) + return fc.fixNumber(fc.formatExpr("~%e", e.X), basic) case token.NOT: - return c.formatExpr("!%e", e.X) + return fc.formatExpr("!%e", e.X) default: panic(e.Op) } case *ast.BinaryExpr: if e.Op == token.NEQ { - return c.formatExpr("!(%s)", c.translateExpr(&ast.BinaryExpr{ + return fc.formatExpr("!(%s)", fc.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } - t := c.p.TypeOf(e.X) - t2 := c.p.TypeOf(e.Y) + t := fc.pkgCtx.TypeOf(e.X) + t2 := fc.pkgCtx.TypeOf(e.Y) _, isInterface := t2.Underlying().(*types.Interface) if isInterface || types.Identical(t, types.Typ[types.UntypedNil]) { t = t2 @@ -291,31 +295,31 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { if is64Bit(basic) { switch e.Op { case token.MUL: - return c.formatExpr("$mul64(%e, %e)", e.X, e.Y) + return fc.formatExpr("$mul64(%e, %e)", e.X, e.Y) case token.QUO: - return c.formatExpr("$div64(%e, %e, false)", e.X, e.Y) + return fc.formatExpr("$div64(%e, %e, false)", e.X, e.Y) case token.REM: - return c.formatExpr("$div64(%e, %e, true)", e.X, e.Y) + return fc.formatExpr("$div64(%e, %e, true)", e.X, e.Y) case token.SHL: - return c.formatExpr("$shiftLeft64(%e, %f)", e.X, e.Y) + return fc.formatExpr("$shiftLeft64(%e, %f)", e.X, e.Y) case token.SHR: - return c.formatExpr("$shiftRight%s(%e, %f)", toJavaScriptType(basic), e.X, e.Y) + return fc.formatExpr("$shiftRight%s(%e, %f)", toJavaScriptType(basic), e.X, e.Y) case token.EQL: - return c.formatExpr("(%1h === %2h && %1l === %2l)", e.X, e.Y) + return fc.formatExpr("(%1h === %2h && %1l === %2l)", e.X, e.Y) case token.LSS: - return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l < %2l))", e.X, e.Y) + return fc.formatExpr("(%1h < %2h || (%1h === %2h && %1l < %2l))", e.X, e.Y) case token.LEQ: - return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l <= %2l))", e.X, e.Y) + return fc.formatExpr("(%1h < %2h || (%1h === %2h && %1l <= %2l))", e.X, e.Y) case token.GTR: - return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l > %2l))", e.X, e.Y) + return fc.formatExpr("(%1h > %2h || (%1h === %2h && %1l > %2l))", e.X, e.Y) case token.GEQ: - return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l >= %2l))", e.X, e.Y) + return fc.formatExpr("(%1h > %2h || (%1h === %2h && %1l >= %2l))", e.X, e.Y) case token.ADD, token.SUB: - return c.formatExpr("new %3s(%1h %4t %2h, %1l %4t %2l)", e.X, e.Y, c.typeName(t), e.Op) + return fc.formatExpr("new %3s(%1h %4t %2h, %1l %4t %2l)", e.X, e.Y, fc.typeName(t), e.Op) case token.AND, token.OR, token.XOR: - return c.formatExpr("new %3s(%1h %4t %2h, (%1l %4t %2l) >>> 0)", e.X, e.Y, c.typeName(t), e.Op) + return fc.formatExpr("new %3s(%1h %4t %2h, (%1l %4t %2l) >>> 0)", e.X, e.Y, fc.typeName(t), e.Op) case token.AND_NOT: - return c.formatExpr("new %3s(%1h & ~%2h, (%1l & ~%2l) >>> 0)", e.X, e.Y, c.typeName(t)) + return fc.formatExpr("new %3s(%1h & ~%2h, (%1l & ~%2l) >>> 0)", e.X, e.Y, fc.typeName(t)) default: panic(e.Op) } @@ -324,13 +328,13 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { if isComplex(basic) { switch e.Op { case token.EQL: - return c.formatExpr("(%1r === %2r && %1i === %2i)", e.X, e.Y) + return fc.formatExpr("(%1r === %2r && %1i === %2i)", e.X, e.Y) case token.ADD, token.SUB: - return c.formatExpr("new %3s(%1r %4t %2r, %1i %4t %2i)", e.X, e.Y, c.typeName(t), e.Op) + return fc.formatExpr("new %3s(%1r %4t %2r, %1i %4t %2i)", e.X, e.Y, fc.typeName(t), e.Op) case token.MUL: - return c.formatExpr("new %3s(%1r * %2r - %1i * %2i, %1r * %2i + %1i * %2r)", e.X, e.Y, c.typeName(t)) + return fc.formatExpr("new %3s(%1r * %2r - %1i * %2i, %1r * %2i + %1i * %2r)", e.X, e.Y, fc.typeName(t)) case token.QUO: - return c.formatExpr("$divComplex(%e, %e)", e.X, e.Y) + return fc.formatExpr("$divComplex(%e, %e)", e.X, e.Y) default: panic(e.Op) } @@ -338,19 +342,19 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { switch e.Op { case token.EQL: - return c.formatParenExpr("%e === %e", e.X, e.Y) + return fc.formatParenExpr("%e === %e", e.X, e.Y) case token.LSS, token.LEQ, token.GTR, token.GEQ: - return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) + return fc.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.ADD, token.SUB: - return c.fixNumber(c.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) + return fc.fixNumber(fc.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) case token.MUL: switch basic.Kind() { case types.Int32, types.Int: - return c.formatParenExpr("$imul(%e, %e)", e.X, e.Y) + return fc.formatParenExpr("$imul(%e, %e)", e.X, e.Y) case types.Uint32, types.Uintptr: - return c.formatParenExpr("$imul(%e, %e) >>> 0", e.X, e.Y) + return fc.formatParenExpr("$imul(%e, %e) >>> 0", e.X, e.Y) } - return c.fixNumber(c.formatExpr("%e * %e", e.X, e.Y), basic) + return fc.fixNumber(fc.formatExpr("%e * %e", e.X, e.Y), basic) case token.QUO: if isInteger(basic) { // cut off decimals @@ -358,40 +362,40 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { if isUnsigned(basic) { shift = ">>>" } - return c.formatExpr(`(%1s = %2e / %3e, (%1s === %1s && %1s !== 1/0 && %1s !== -1/0) ? %1s %4s 0 : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_q"), e.X, e.Y, shift) + return fc.formatExpr(`(%1s = %2e / %3e, (%1s === %1s && %1s !== 1/0 && %1s !== -1/0) ? %1s %4s 0 : $throwRuntimeError("integer divide by zero"))`, fc.newVariable("_q"), e.X, e.Y, shift) } if basic.Kind() == types.Float32 { - return c.fixNumber(c.formatExpr("%e / %e", e.X, e.Y), basic) + return fc.fixNumber(fc.formatExpr("%e / %e", e.X, e.Y), basic) } - return c.formatExpr("%e / %e", e.X, e.Y) + return fc.formatExpr("%e / %e", e.X, e.Y) case token.REM: - return c.formatExpr(`(%1s = %2e %% %3e, %1s === %1s ? %1s : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_r"), e.X, e.Y) + return fc.formatExpr(`(%1s = %2e %% %3e, %1s === %1s ? %1s : $throwRuntimeError("integer divide by zero"))`, fc.newVariable("_r"), e.X, e.Y) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && isUnsigned(basic) { op = ">>>" } - if v := c.p.Types[e.Y].Value; v != nil { + if v := fc.pkgCtx.Types[e.Y].Value; v != nil { i, _ := constant.Uint64Val(constant.ToInt(v)) if i >= 32 { - return c.formatExpr("0") + return fc.formatExpr("0") } - return c.fixNumber(c.formatExpr("%e %s %s", e.X, op, strconv.FormatUint(i, 10)), basic) + return fc.fixNumber(fc.formatExpr("%e %s %s", e.X, op, strconv.FormatUint(i, 10)), basic) } if e.Op == token.SHR && !isUnsigned(basic) { - return c.fixNumber(c.formatParenExpr("%e >> $min(%f, 31)", e.X, e.Y), basic) + return fc.fixNumber(fc.formatParenExpr("%e >> $min(%f, 31)", e.X, e.Y), basic) } - y := c.newVariable("y") - return c.fixNumber(c.formatExpr("(%s = %f, %s < 32 ? (%e %s %s) : 0)", y, e.Y, y, e.X, op, y), basic) + y := fc.newVariable("y") + return fc.fixNumber(fc.formatExpr("(%s = %f, %s < 32 ? (%e %s %s) : 0)", y, e.Y, y, e.X, op, y), basic) case token.AND, token.OR: if isUnsigned(basic) { - return c.formatParenExpr("(%e %t %e) >>> 0", e.X, e.Op, e.Y) + return fc.formatParenExpr("(%e %t %e) >>> 0", e.X, e.Op, e.Y) } - return c.formatParenExpr("%e %t %e", e.X, e.Op, e.Y) + return fc.formatParenExpr("%e %t %e", e.X, e.Op, e.Y) case token.AND_NOT: - return c.fixNumber(c.formatParenExpr("%e & ~%e", e.X, e.Y), basic) + return fc.fixNumber(fc.formatParenExpr("%e & ~%e", e.X, e.Y), basic) case token.XOR: - return c.fixNumber(c.formatParenExpr("%e ^ %e", e.X, e.Y), basic) + return fc.fixNumber(fc.formatParenExpr("%e ^ %e", e.X, e.Y), basic) default: panic(e.Op) } @@ -399,138 +403,157 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ: - return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) + return fc.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.LAND: - if c.Blocking[e.Y] { - skipCase := c.caseCounter - c.caseCounter++ - resultVar := c.newVariable("_v") - c.Printf("if (!(%s)) { %s = false; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) - c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) - return c.formatExpr("%s", resultVar) - } - return c.formatExpr("%e && %e", e.X, e.Y) + if fc.Blocking[e.Y] { + skipCase := fc.caseCounter + fc.caseCounter++ + resultVar := fc.newVariable("_v") + fc.Printf("if (!(%s)) { %s = false; $s = %d; continue s; }", fc.translateExpr(e.X), resultVar, skipCase) + fc.Printf("%s = %s; case %d:", resultVar, fc.translateExpr(e.Y), skipCase) + return fc.formatExpr("%s", resultVar) + } + return fc.formatExpr("%e && %e", e.X, e.Y) case token.LOR: - if c.Blocking[e.Y] { - skipCase := c.caseCounter - c.caseCounter++ - resultVar := c.newVariable("_v") - c.Printf("if (%s) { %s = true; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) - c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) - return c.formatExpr("%s", resultVar) - } - return c.formatExpr("%e || %e", e.X, e.Y) + if fc.Blocking[e.Y] { + skipCase := fc.caseCounter + fc.caseCounter++ + resultVar := fc.newVariable("_v") + fc.Printf("if (%s) { %s = true; $s = %d; continue s; }", fc.translateExpr(e.X), resultVar, skipCase) + fc.Printf("%s = %s; case %d:", resultVar, fc.translateExpr(e.Y), skipCase) + return fc.formatExpr("%s", resultVar) + } + return fc.formatExpr("%e || %e", e.X, e.Y) case token.EQL: switch u := t.Underlying().(type) { case *types.Array, *types.Struct: - return c.formatExpr("$equal(%e, %e, %s)", e.X, e.Y, c.typeName(t)) + return fc.formatExpr("$equal(%e, %e, %s)", e.X, e.Y, fc.typeName(t)) case *types.Interface: - return c.formatExpr("$interfaceIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) - case *types.Pointer: - if _, ok := u.Elem().Underlying().(*types.Array); ok { - return c.formatExpr("$equal(%s, %s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t), c.typeName(u.Elem())) - } + return fc.formatExpr("$interfaceIsEqual(%s, %s)", fc.translateImplicitConversion(e.X, t), fc.translateImplicitConversion(e.Y, t)) case *types.Basic: if isBoolean(u) { - if b, ok := analysis.BoolValue(e.X, c.p.Info.Info); ok && b { - return c.translateExpr(e.Y) + if b, ok := analysis.BoolValue(e.X, fc.pkgCtx.Info.Info); ok && b { + return fc.translateExpr(e.Y) } - if b, ok := analysis.BoolValue(e.Y, c.p.Info.Info); ok && b { - return c.translateExpr(e.X) + if b, ok := analysis.BoolValue(e.Y, fc.pkgCtx.Info.Info); ok && b { + return fc.translateExpr(e.X) } } } - return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) + return fc.formatExpr("%s === %s", fc.translateImplicitConversion(e.X, t), fc.translateImplicitConversion(e.Y, t)) default: panic(e.Op) } case *ast.ParenExpr: - return c.formatParenExpr("%e", e.X) + return fc.formatParenExpr("%e", e.X) case *ast.IndexExpr: - switch t := c.p.TypeOf(e.X).Underlying().(type) { - case *types.Array, *types.Pointer: - pattern := rangeCheck("%1e[%2f]", c.p.Types[e.Index].Value != nil, true) - if _, ok := t.(*types.Pointer); ok { // check pointer for nix (attribute getter causes a panic) - pattern = `(%1e.nilCheck, ` + pattern + `)` - } - return c.formatExpr(pattern, e.X, e.Index) + switch t := fc.pkgCtx.TypeOf(e.X).Underlying().(type) { + case *types.Pointer: + if _, ok := t.Elem().Underlying().(*types.Array); !ok { + // Should never happen in type-checked code. + panic(fmt.Errorf("non-array pointers can't be used with index expression")) + } + // Rewrite arrPtr[i] → (*arrPtr)[i] to concentrate array dereferencing + // logic in one place. + x := &ast.StarExpr{ + Star: e.X.Pos(), + X: e.X, + } + astutil.SetType(fc.pkgCtx.Info.Info, t.Elem(), x) + e.X = x + return fc.translateExpr(e) + case *types.Array: + pattern := rangeCheck("%1e[%2f]", fc.pkgCtx.Types[e.Index].Value != nil, true) + return fc.formatExpr(pattern, e.X, e.Index) case *types.Slice: - return c.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f]", c.p.Types[e.Index].Value != nil, false), e.X, e.Index) + return fc.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f]", fc.pkgCtx.Types[e.Index].Value != nil, false), e.X, e.Index) case *types.Map: - if typesutil.IsJsObject(c.p.TypeOf(e.Index)) { - c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: e.Index.Pos(), Msg: "cannot use js.Object as map key"}) + if typesutil.IsJsObject(fc.pkgCtx.TypeOf(e.Index)) { + fc.pkgCtx.errList = append(fc.pkgCtx.errList, types.Error{Fset: fc.pkgCtx.fileSet, Pos: e.Index.Pos(), Msg: "cannot use js.Object as map key"}) } - key := fmt.Sprintf("%s.keyFor(%s)", c.typeName(t.Key()), c.translateImplicitConversion(e.Index, t.Key())) + key := fmt.Sprintf("%s.keyFor(%s)", fc.typeName(t.Key()), fc.translateImplicitConversion(e.Index, t.Key())) if _, isTuple := exprType.(*types.Tuple); isTuple { - return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? [%1s.v, true] : [%4e, false])`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) - } - return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? %1s.v : %4e)`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) + return fc.formatExpr( + `(%1s = $mapIndex(%2e,%3s), %1s !== undefined ? [%1s.v, true] : [%4e, false])`, + fc.newVariable("_entry"), + e.X, + key, + fc.zeroValue(t.Elem()), + ) + } + return fc.formatExpr( + `(%1s = $mapIndex(%2e,%3s), %1s !== undefined ? %1s.v : %4e)`, + fc.newVariable("_entry"), + e.X, + key, + fc.zeroValue(t.Elem()), + ) case *types.Basic: - return c.formatExpr("%e.charCodeAt(%f)", e.X, e.Index) + return fc.formatExpr("%e.charCodeAt(%f)", e.X, e.Index) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: - if b, isBasic := c.p.TypeOf(e.X).Underlying().(*types.Basic); isBasic && isString(b) { + if b, isBasic := fc.pkgCtx.TypeOf(e.X).Underlying().(*types.Basic); isBasic && isString(b) { switch { case e.Low == nil && e.High == nil: - return c.translateExpr(e.X) + return fc.translateExpr(e.X) case e.Low == nil: - return c.formatExpr("$substring(%e, 0, %f)", e.X, e.High) + return fc.formatExpr("$substring(%e, 0, %f)", e.X, e.High) case e.High == nil: - return c.formatExpr("$substring(%e, %f)", e.X, e.Low) + return fc.formatExpr("$substring(%e, %f)", e.X, e.Low) default: - return c.formatExpr("$substring(%e, %f, %f)", e.X, e.Low, e.High) + return fc.formatExpr("$substring(%e, %f, %f)", e.X, e.Low, e.High) } } - slice := c.translateConversionToSlice(e.X, exprType) + slice := fc.translateConversionToSlice(e.X, exprType) switch { case e.Low == nil && e.High == nil: - return c.formatExpr("%s", slice) + return fc.formatExpr("%s", slice) case e.Low == nil: if e.Max != nil { - return c.formatExpr("$subslice(%s, 0, %f, %f)", slice, e.High, e.Max) + return fc.formatExpr("$subslice(%s, 0, %f, %f)", slice, e.High, e.Max) } - return c.formatExpr("$subslice(%s, 0, %f)", slice, e.High) + return fc.formatExpr("$subslice(%s, 0, %f)", slice, e.High) case e.High == nil: - return c.formatExpr("$subslice(%s, %f)", slice, e.Low) + return fc.formatExpr("$subslice(%s, %f)", slice, e.Low) default: if e.Max != nil { - return c.formatExpr("$subslice(%s, %f, %f, %f)", slice, e.Low, e.High, e.Max) + return fc.formatExpr("$subslice(%s, %f, %f, %f)", slice, e.Low, e.High, e.Max) } - return c.formatExpr("$subslice(%s, %f, %f)", slice, e.Low, e.High) + return fc.formatExpr("$subslice(%s, %f, %f)", slice, e.Low, e.High) } case *ast.SelectorExpr: - sel, ok := c.p.SelectionOf(e) + sel, ok := fc.pkgCtx.SelectionOf(e) if !ok { // qualified identifier - return c.formatExpr("%s", c.objectName(obj)) + return fc.formatExpr("%s", fc.objectName(obj)) } switch sel.Kind() { case types.FieldVal: - fields, jsTag := c.translateSelection(sel, e.Pos()) + fields, jsTag := fc.translateSelection(sel, e.Pos()) if jsTag != "" { if _, ok := sel.Type().(*types.Signature); ok { - return c.formatExpr("$internalize(%1e.%2s%3s, %4s, %1e.%2s)", e.X, strings.Join(fields, "."), formatJSStructTagVal(jsTag), c.typeName(sel.Type())) + return fc.formatExpr("$internalize(%1e.%2s%3s, %4s, %1e.%2s)", e.X, strings.Join(fields, "."), formatJSStructTagVal(jsTag), fc.typeName(sel.Type())) } - return c.internalize(c.formatExpr("%e.%s%s", e.X, strings.Join(fields, "."), formatJSStructTagVal(jsTag)), sel.Type()) + return fc.internalize(fc.formatExpr("%e.%s%s", e.X, strings.Join(fields, "."), formatJSStructTagVal(jsTag)), sel.Type()) } - return c.formatExpr("%e.%s", e.X, strings.Join(fields, ".")) + return fc.formatExpr("%e.%s", e.X, strings.Join(fields, ".")) case types.MethodVal: - return c.formatExpr(`$methodVal(%s, "%s")`, c.makeReceiver(e), sel.Obj().(*types.Func).Name()) + return fc.formatExpr(`$methodVal(%s, "%s")`, fc.makeReceiver(e), sel.Obj().(*types.Func).Name()) case types.MethodExpr: if !sel.Obj().Exported() { - c.p.dependencies[sel.Obj()] = true + fc.pkgCtx.dependencies[sel.Obj()] = true } if _, ok := sel.Recv().Underlying().(*types.Interface); ok { - return c.formatExpr(`$ifaceMethodExpr("%s")`, sel.Obj().(*types.Func).Name()) + return fc.formatExpr(`$ifaceMethodExpr("%s")`, sel.Obj().(*types.Func).Name()) } - return c.formatExpr(`$methodExpr(%s, "%s")`, c.typeName(sel.Recv()), sel.Obj().(*types.Func).Name()) + return fc.formatExpr(`$methodExpr(%s, "%s")`, fc.typeName(sel.Recv()), sel.Obj().(*types.Func).Name()) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } @@ -538,45 +561,45 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { case *ast.CallExpr: plainFun := astutil.RemoveParens(e.Fun) - if astutil.IsTypeExpr(plainFun, c.p.Info.Info) { - return c.formatExpr("(%s)", c.translateConversion(e.Args[0], c.p.TypeOf(plainFun))) + if astutil.IsTypeExpr(plainFun, fc.pkgCtx.Info.Info) { + return fc.formatExpr("(%s)", fc.translateConversion(e.Args[0], fc.pkgCtx.TypeOf(plainFun))) } - sig := c.p.TypeOf(plainFun).Underlying().(*types.Signature) + sig := fc.pkgCtx.TypeOf(plainFun).Underlying().(*types.Signature) switch f := plainFun.(type) { case *ast.Ident: - obj := c.p.Uses[f] + obj := fc.pkgCtx.Uses[f] if o, ok := obj.(*types.Builtin); ok { - return c.translateBuiltin(o.Name(), sig, e.Args, e.Ellipsis.IsValid()) + return fc.translateBuiltin(o.Name(), sig, e.Args, e.Ellipsis.IsValid()) } if typesutil.IsJsPackage(obj.Pkg()) && obj.Name() == "InternalObject" { - return c.translateExpr(e.Args[0]) + return fc.translateExpr(e.Args[0]) } - return c.translateCall(e, sig, c.translateExpr(f)) + return fc.translateCall(e, sig, fc.translateExpr(f)) case *ast.SelectorExpr: - sel, ok := c.p.SelectionOf(f) + sel, ok := fc.pkgCtx.SelectionOf(f) if !ok { // qualified identifier - obj := c.p.Uses[f.Sel] + obj := fc.pkgCtx.Uses[f.Sel] if typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Debugger": - return c.formatExpr("debugger") + return fc.formatExpr("debugger") case "InternalObject": - return c.translateExpr(e.Args[0]) + return fc.translateExpr(e.Args[0]) } } - return c.translateCall(e, sig, c.translateExpr(f)) + return fc.translateCall(e, sig, fc.translateExpr(f)) } externalizeExpr := func(e ast.Expr) string { - t := c.p.TypeOf(e) + t := fc.pkgCtx.TypeOf(e) if types.Identical(t, types.Typ[types.UntypedNil]) { return "null" } - return c.externalize(c.translateExpr(e).String(), t) + return fc.externalize(fc.translateExpr(e).String(), t) } externalizeArgs := func(args []ast.Expr) string { s := make([]string, len(args)) @@ -588,7 +611,7 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { switch sel.Kind() { case types.MethodVal: - recv := c.makeReceiver(f) + recv := fc.makeReceiver(f) declaredFuncRecv := sel.Obj().(*types.Func).Type().(*types.Signature).Recv().Type() if typesutil.IsJsObject(declaredFuncRecv) { globalRef := func(id string) string { @@ -599,60 +622,60 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { } switch sel.Obj().Name() { case "Get": - if id, ok := c.identifierConstant(e.Args[0]); ok { - return c.formatExpr("%s", globalRef(id)) + if id, ok := fc.identifierConstant(e.Args[0]); ok { + return fc.formatExpr("%s", globalRef(id)) } - return c.formatExpr("%s[$externalize(%e, $String)]", recv, e.Args[0]) + return fc.formatExpr("%s[$externalize(%e, $String)]", recv, e.Args[0]) case "Set": - if id, ok := c.identifierConstant(e.Args[0]); ok { - return c.formatExpr("%s = %s", globalRef(id), externalizeExpr(e.Args[1])) + if id, ok := fc.identifierConstant(e.Args[0]); ok { + return fc.formatExpr("%s = %s", globalRef(id), externalizeExpr(e.Args[1])) } - return c.formatExpr("%s[$externalize(%e, $String)] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) + return fc.formatExpr("%s[$externalize(%e, $String)] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Delete": - return c.formatExpr("delete %s[$externalize(%e, $String)]", recv, e.Args[0]) + return fc.formatExpr("delete %s[$externalize(%e, $String)]", recv, e.Args[0]) case "Length": - return c.formatExpr("$parseInt(%s.length)", recv) + return fc.formatExpr("$parseInt(%s.length)", recv) case "Index": - return c.formatExpr("%s[%e]", recv, e.Args[0]) + return fc.formatExpr("%s[%e]", recv, e.Args[0]) case "SetIndex": - return c.formatExpr("%s[%e] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) + return fc.formatExpr("%s[%e] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Call": - if id, ok := c.identifierConstant(e.Args[0]); ok { + if id, ok := fc.identifierConstant(e.Args[0]); ok { if e.Ellipsis.IsValid() { - objVar := c.newVariable("obj") - return c.formatExpr("(%s = %s, %s.%s.apply(%s, %s))", objVar, recv, objVar, id, objVar, externalizeExpr(e.Args[1])) + objVar := fc.newVariable("obj") + return fc.formatExpr("(%s = %s, %s.%s.apply(%s, %s))", objVar, recv, objVar, id, objVar, externalizeExpr(e.Args[1])) } - return c.formatExpr("%s(%s)", globalRef(id), externalizeArgs(e.Args[1:])) + return fc.formatExpr("%s(%s)", globalRef(id), externalizeArgs(e.Args[1:])) } if e.Ellipsis.IsValid() { - objVar := c.newVariable("obj") - return c.formatExpr("(%s = %s, %s[$externalize(%e, $String)].apply(%s, %s))", objVar, recv, objVar, e.Args[0], objVar, externalizeExpr(e.Args[1])) + objVar := fc.newVariable("obj") + return fc.formatExpr("(%s = %s, %s[$externalize(%e, $String)].apply(%s, %s))", objVar, recv, objVar, e.Args[0], objVar, externalizeExpr(e.Args[1])) } - return c.formatExpr("%s[$externalize(%e, $String)](%s)", recv, e.Args[0], externalizeArgs(e.Args[1:])) + return fc.formatExpr("%s[$externalize(%e, $String)](%s)", recv, e.Args[0], externalizeArgs(e.Args[1:])) case "Invoke": if e.Ellipsis.IsValid() { - return c.formatExpr("%s.apply(undefined, %s)", recv, externalizeExpr(e.Args[0])) + return fc.formatExpr("%s.apply(undefined, %s)", recv, externalizeExpr(e.Args[0])) } - return c.formatExpr("%s(%s)", recv, externalizeArgs(e.Args)) + return fc.formatExpr("%s(%s)", recv, externalizeArgs(e.Args)) case "New": if e.Ellipsis.IsValid() { - return c.formatExpr("new ($global.Function.prototype.bind.apply(%s, [undefined].concat(%s)))", recv, externalizeExpr(e.Args[0])) + return fc.formatExpr("new ($global.Function.prototype.bind.apply(%s, [undefined].concat(%s)))", recv, externalizeExpr(e.Args[0])) } - return c.formatExpr("new (%s)(%s)", recv, externalizeArgs(e.Args)) + return fc.formatExpr("new (%s)(%s)", recv, externalizeArgs(e.Args)) case "Bool": - return c.internalize(recv, types.Typ[types.Bool]) + return fc.internalize(recv, types.Typ[types.Bool]) case "String": - return c.internalize(recv, types.Typ[types.String]) + return fc.internalize(recv, types.Typ[types.String]) case "Int": - return c.internalize(recv, types.Typ[types.Int]) + return fc.internalize(recv, types.Typ[types.Int]) case "Int64": - return c.internalize(recv, types.Typ[types.Int64]) + return fc.internalize(recv, types.Typ[types.Int64]) case "Uint64": - return c.internalize(recv, types.Typ[types.Uint64]) + return fc.internalize(recv, types.Typ[types.Uint64]) case "Float": - return c.internalize(recv, types.Typ[types.Float64]) + return fc.internalize(recv, types.Typ[types.Float64]) case "Interface": - return c.internalize(recv, types.NewInterface(nil, nil)) + return fc.internalize(recv, types.NewInterface(nil, nil)) case "Unsafe": return recv default: @@ -664,59 +687,59 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { if reservedKeywords[methodName] { methodName += "$" } - return c.translateCall(e, sig, c.formatExpr("%s.%s", recv, methodName)) + return fc.translateCall(e, sig, fc.formatExpr("%s.%s", recv, methodName)) case types.FieldVal: - fields, jsTag := c.translateSelection(sel, f.Pos()) + fields, jsTag := fc.translateSelection(sel, f.Pos()) if jsTag != "" { - call := c.formatExpr("%e.%s%s(%s)", f.X, strings.Join(fields, "."), formatJSStructTagVal(jsTag), externalizeArgs(e.Args)) + call := fc.formatExpr("%e.%s%s(%s)", f.X, strings.Join(fields, "."), formatJSStructTagVal(jsTag), externalizeArgs(e.Args)) switch sig.Results().Len() { case 0: return call case 1: - return c.internalize(call, sig.Results().At(0).Type()) + return fc.internalize(call, sig.Results().At(0).Type()) default: - c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: f.Pos(), Msg: "field with js tag can not have func type with multiple results"}) + fc.pkgCtx.errList = append(fc.pkgCtx.errList, types.Error{Fset: fc.pkgCtx.fileSet, Pos: f.Pos(), Msg: "field with js tag can not have func type with multiple results"}) } } - return c.translateCall(e, sig, c.formatExpr("%e.%s", f.X, strings.Join(fields, "."))) + return fc.translateCall(e, sig, fc.formatExpr("%e.%s", f.X, strings.Join(fields, "."))) case types.MethodExpr: - return c.translateCall(e, sig, c.translateExpr(f)) + return fc.translateCall(e, sig, fc.translateExpr(f)) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } default: - return c.translateCall(e, sig, c.translateExpr(plainFun)) + return fc.translateCall(e, sig, fc.translateExpr(plainFun)) } case *ast.StarExpr: - if typesutil.IsJsObject(c.p.TypeOf(e.X)) { - return c.formatExpr("new $jsObjectPtr(%e)", e.X) + if typesutil.IsJsObject(fc.pkgCtx.TypeOf(e.X)) { + return fc.formatExpr("new $jsObjectPtr(%e)", e.X) } if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 { - if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.Identical(c.p.TypeOf(c2.Fun), types.Typ[types.UnsafePointer]) { + if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.Identical(fc.pkgCtx.TypeOf(c2.Fun), types.Typ[types.UnsafePointer]) { if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND { - return c.translateExpr(unary.X) // unsafe conversion + return fc.translateExpr(unary.X) // unsafe conversion } } } switch exprType.Underlying().(type) { case *types.Struct, *types.Array: - return c.translateExpr(e.X) + return fc.translateExpr(e.X) } - return c.formatExpr("%e.$get()", e.X) + return fc.formatExpr("%e.$get()", e.X) case *ast.TypeAssertExpr: if e.Type == nil { - return c.translateExpr(e.X) + return fc.translateExpr(e.X) } - t := c.p.TypeOf(e.Type) + t := fc.pkgCtx.TypeOf(e.Type) if _, isTuple := exprType.(*types.Tuple); isTuple { - return c.formatExpr("$assertType(%e, %s, true)", e.X, c.typeName(t)) + return fc.formatExpr("$assertType(%e, %s, true)", e.X, fc.typeName(t)) } - return c.formatExpr("$assertType(%e, %s)", e.X, c.typeName(t)) + return fc.formatExpr("$assertType(%e, %s)", e.X, fc.typeName(t)) case *ast.Ident: if e.Name == "_" { @@ -724,31 +747,31 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { } switch o := obj.(type) { case *types.Var, *types.Const: - return c.formatExpr("%s", c.objectName(o)) + return fc.formatExpr("%s", fc.objectName(o)) case *types.Func: - return c.formatExpr("%s", c.objectName(o)) + return fc.formatExpr("%s", fc.objectName(o)) case *types.TypeName: - return c.formatExpr("%s", c.typeName(o.Type())) + return fc.formatExpr("%s", fc.typeName(o.Type())) case *types.Nil: if typesutil.IsJsObject(exprType) { - return c.formatExpr("null") + return fc.formatExpr("null") } switch t := exprType.Underlying().(type) { case *types.Basic: if t.Kind() != types.UnsafePointer { panic("unexpected basic type") } - return c.formatExpr("0") + return fc.formatExpr("0") case *types.Slice, *types.Pointer: - return c.formatExpr("%s.nil", c.typeName(exprType)) + return fc.formatExpr("%s.nil", fc.typeName(exprType)) case *types.Chan: - return c.formatExpr("$chanNil") + return fc.formatExpr("$chanNil") case *types.Map: - return c.formatExpr("false") + return fc.formatExpr("false") case *types.Interface: - return c.formatExpr("$ifaceNil") + return fc.formatExpr("$ifaceNil") case *types.Signature: - return c.formatExpr("$throwNilPointerError") + return fc.formatExpr("$throwNilPointerError") default: panic(fmt.Sprintf("unexpected type: %T", t)) } @@ -757,7 +780,7 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { } case nil: - return c.formatExpr("") + return fc.formatExpr("") default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) @@ -765,28 +788,84 @@ func (c *funcContext) translateExpr(expr ast.Expr) *expression { } } -func (c *funcContext) translateCall(e *ast.CallExpr, sig *types.Signature, fun *expression) *expression { - args := c.translateArgs(sig, e.Args, e.Ellipsis.IsValid()) - if c.Blocking[e] { - resumeCase := c.caseCounter - c.caseCounter++ +func (fc *funcContext) translateCall(e *ast.CallExpr, sig *types.Signature, fun *expression) *expression { + args := fc.translateArgs(sig, e.Args, e.Ellipsis.IsValid()) + if fc.Blocking[e] { + resumeCase := fc.caseCounter + fc.caseCounter++ returnVar := "$r" if sig.Results().Len() != 0 { - returnVar = c.newVariable("_r") + returnVar = fc.newVariable("_r") } - c.Printf("%[1]s = %[2]s(%[3]s); /* */ $s = %[4]d; case %[4]d: if($c) { $c = false; %[1]s = %[1]s.$blk(); } if (%[1]s && %[1]s.$blk !== undefined) { break s; }", returnVar, fun, strings.Join(args, ", "), resumeCase) + fc.Printf("%[1]s = %[2]s(%[3]s); /* */ $s = %[4]d; case %[4]d: if($c) { $c = false; %[1]s = %[1]s.$blk(); } if (%[1]s && %[1]s.$blk !== undefined) { break s; }", returnVar, fun, strings.Join(args, ", "), resumeCase) if sig.Results().Len() != 0 { - return c.formatExpr("%s", returnVar) + return fc.formatExpr("%s", returnVar) } - return c.formatExpr("") + return fc.formatExpr("") } - return c.formatExpr("%s(%s)", fun, strings.Join(args, ", ")) + return fc.formatExpr("%s(%s)", fun, strings.Join(args, ", ")) } -func (c *funcContext) makeReceiver(e *ast.SelectorExpr) *expression { - sel, _ := c.p.SelectionOf(e) +// delegatedCall returns a pair of JS expressions representing a callable function +// and its arguments to be invoked elsewhere. +// +// This function is necessary in conjunction with keywords such as `go` and `defer`, +// where we need to compute function and its arguments at the the keyword site, +// but the call itself will happen elsewhere (hence "delegated"). +// +// Built-in functions and cetrain `js.Object` methods don't translate into JS +// function calls, and need to be wrapped before they can be delegated, which +// this function handles and returns JS expressions that are safe to delegate +// and behave like a regular JS function and a list of its argument values. +func (fc *funcContext) delegatedCall(expr *ast.CallExpr) (callable *expression, arglist *expression) { + isBuiltin := false + isJs := false + switch fun := expr.Fun.(type) { + case *ast.Ident: + _, isBuiltin = fc.pkgCtx.Uses[fun].(*types.Builtin) + case *ast.SelectorExpr: + isJs = typesutil.IsJsPackage(fc.pkgCtx.Uses[fun.Sel].Pkg()) + } + sig := fc.pkgCtx.TypeOf(expr.Fun).Underlying().(*types.Signature) + sigTypes := signatureTypes{Sig: sig} + args := fc.translateArgs(sig, expr.Args, expr.Ellipsis.IsValid()) + + if !isBuiltin && !isJs { + // Normal function calls don't require wrappers. + callable = fc.translateExpr(expr.Fun) + arglist = fc.formatExpr("[%s]", strings.Join(args, ", ")) + return callable, arglist + } + + // Since some builtins or js.Object methods may not transpile into + // callable expressions, we need to wrap then in a proxy lambda in order + // to push them onto the deferral stack. + vars := make([]string, len(expr.Args)) + callArgs := make([]ast.Expr, len(expr.Args)) + ellipsis := expr.Ellipsis + + for i := range expr.Args { + v := fc.newVariable("_arg") + vars[i] = v + // Subtle: the proxy lambda argument needs to be assigned with the type + // that the original function expects, and not with the argument + // expression result type, or we may do implicit type conversion twice. + callArgs[i] = fc.newIdent(v, sigTypes.Param(i, ellipsis.IsValid())) + } + wrapper := &ast.CallExpr{ + Fun: expr.Fun, + Args: callArgs, + Ellipsis: expr.Ellipsis, + } + callable = fc.formatExpr("function(%s) { %e; }", strings.Join(vars, ", "), wrapper) + arglist = fc.formatExpr("[%s]", strings.Join(args, ", ")) + return callable, arglist +} + +func (fc *funcContext) makeReceiver(e *ast.SelectorExpr) *expression { + sel, _ := fc.pkgCtx.SelectionOf(e) if !sel.Obj().Exported() { - c.p.dependencies[sel.Obj()] = true + fc.pkgCtx.dependencies[sel.Obj()] = true } x := e.X @@ -801,13 +880,13 @@ func (c *funcContext) makeReceiver(e *ast.SelectorExpr) *expression { } fakeSel := &ast.SelectorExpr{X: x, Sel: ast.NewIdent("o")} - c.p.additionalSelections[fakeSel] = &fakeSelection{ + fc.pkgCtx.additionalSelections[fakeSel] = &fakeSelection{ kind: types.FieldVal, recv: sel.Recv(), index: sel.Index()[:len(sel.Index())-1], typ: recvType, } - x = c.setType(fakeSel, recvType) + x = fc.setType(fakeSel, recvType) } _, isPointer := recvType.Underlying().(*types.Pointer) @@ -815,117 +894,129 @@ func (c *funcContext) makeReceiver(e *ast.SelectorExpr) *expression { _, pointerExpected := methodsRecvType.(*types.Pointer) if !isPointer && pointerExpected { recvType = types.NewPointer(recvType) - x = c.setType(&ast.UnaryExpr{Op: token.AND, X: x}, recvType) + x = fc.setType(&ast.UnaryExpr{Op: token.AND, X: x}, recvType) } if isPointer && !pointerExpected { - x = c.setType(x, methodsRecvType) + x = fc.setType(x, methodsRecvType) } - recv := c.translateImplicitConversionWithCloning(x, methodsRecvType) + recv := fc.translateImplicitConversionWithCloning(x, methodsRecvType) if isWrapped(recvType) { - recv = c.formatExpr("new %s(%s)", c.typeName(methodsRecvType), recv) + // Wrap JS-native value to have access to the Go type's methods. + recv = fc.formatExpr("new %s(%s)", fc.typeName(methodsRecvType), recv) } return recv } -func (c *funcContext) translateBuiltin(name string, sig *types.Signature, args []ast.Expr, ellipsis bool) *expression { +func (fc *funcContext) translateBuiltin(name string, sig *types.Signature, args []ast.Expr, ellipsis bool) *expression { switch name { case "new": t := sig.Results().At(0).Type().(*types.Pointer) - if c.p.Pkg.Path() == "syscall" && types.Identical(t.Elem().Underlying(), types.Typ[types.Uintptr]) { - return c.formatExpr("new Uint8Array(8)") + if fc.pkgCtx.Pkg.Path() == "syscall" && types.Identical(t.Elem().Underlying(), types.Typ[types.Uintptr]) { + return fc.formatExpr("new Uint8Array(8)") } switch t.Elem().Underlying().(type) { case *types.Struct, *types.Array: - return c.formatExpr("%e", c.zeroValue(t.Elem())) + return fc.formatExpr("%e", fc.zeroValue(t.Elem())) default: - return c.formatExpr("$newDataPointer(%e, %s)", c.zeroValue(t.Elem()), c.typeName(t)) + return fc.formatExpr("$newDataPointer(%e, %s)", fc.zeroValue(t.Elem()), fc.typeName(t)) } case "make": - switch argType := c.p.TypeOf(args[0]).Underlying().(type) { + switch argType := fc.pkgCtx.TypeOf(args[0]).Underlying().(type) { case *types.Slice: - t := c.typeName(c.p.TypeOf(args[0])) + t := fc.typeName(fc.pkgCtx.TypeOf(args[0])) if len(args) == 3 { - return c.formatExpr("$makeSlice(%s, %f, %f)", t, args[1], args[2]) + return fc.formatExpr("$makeSlice(%s, %f, %f)", t, args[1], args[2]) } - return c.formatExpr("$makeSlice(%s, %f)", t, args[1]) + return fc.formatExpr("$makeSlice(%s, %f)", t, args[1]) case *types.Map: - if len(args) == 2 && c.p.Types[args[1]].Value == nil { - return c.formatExpr(`((%1f < 0 || %1f > 2147483647) ? $throwRuntimeError("makemap: size out of range") : {})`, args[1]) + if len(args) == 2 && fc.pkgCtx.Types[args[1]].Value == nil { + return fc.formatExpr(`((%1f < 0 || %1f > 2147483647) ? $throwRuntimeError("makemap: size out of range") : new $global.Map())`, args[1]) } - return c.formatExpr("{}") + return fc.formatExpr("new $global.Map()") case *types.Chan: length := "0" if len(args) == 2 { - length = c.formatExpr("%f", args[1]).String() + length = fc.formatExpr("%f", args[1]).String() } - return c.formatExpr("new $Chan(%s, %s)", c.typeName(c.p.TypeOf(args[0]).Underlying().(*types.Chan).Elem()), length) + return fc.formatExpr("new $Chan(%s, %s)", fc.typeName(fc.pkgCtx.TypeOf(args[0]).Underlying().(*types.Chan).Elem()), length) default: panic(fmt.Sprintf("Unhandled make type: %T\n", argType)) } case "len": - switch argType := c.p.TypeOf(args[0]).Underlying().(type) { + switch argType := fc.pkgCtx.TypeOf(args[0]).Underlying().(type) { case *types.Basic: - return c.formatExpr("%e.length", args[0]) + return fc.formatExpr("%e.length", args[0]) case *types.Slice: - return c.formatExpr("%e.$length", args[0]) + return fc.formatExpr("%e.$length", args[0]) case *types.Pointer: - return c.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len()) + return fc.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len()) case *types.Map: - return c.formatExpr("$keys(%e).length", args[0]) + return fc.formatExpr("(%e ? %e.size : 0)", args[0], args[0]) case *types.Chan: - return c.formatExpr("%e.$buffer.length", args[0]) + return fc.formatExpr("%e.$buffer.length", args[0]) // length of array is constant default: panic(fmt.Sprintf("Unhandled len type: %T\n", argType)) } case "cap": - switch argType := c.p.TypeOf(args[0]).Underlying().(type) { + switch argType := fc.pkgCtx.TypeOf(args[0]).Underlying().(type) { case *types.Slice, *types.Chan: - return c.formatExpr("%e.$capacity", args[0]) + return fc.formatExpr("%e.$capacity", args[0]) case *types.Pointer: - return c.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len()) + return fc.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len()) // capacity of array is constant default: panic(fmt.Sprintf("Unhandled cap type: %T\n", argType)) } case "panic": - return c.formatExpr("$panic(%s)", c.translateImplicitConversion(args[0], types.NewInterface(nil, nil))) + return fc.formatExpr("$panic(%s)", fc.translateImplicitConversion(args[0], types.NewInterface(nil, nil))) case "append": if ellipsis || len(args) == 1 { - argStr := c.translateArgs(sig, args, ellipsis) - return c.formatExpr("$appendSlice(%s, %s)", argStr[0], argStr[1]) + argStr := fc.translateArgs(sig, args, ellipsis) + return fc.formatExpr("$appendSlice(%s, %s)", argStr[0], argStr[1]) } sliceType := sig.Results().At(0).Type().Underlying().(*types.Slice) - return c.formatExpr("$append(%e, %s)", args[0], strings.Join(c.translateExprSlice(args[1:], sliceType.Elem()), ", ")) + return fc.formatExpr("$append(%e, %s)", args[0], strings.Join(fc.translateExprSlice(args[1:], sliceType.Elem()), ", ")) case "delete": - keyType := c.p.TypeOf(args[0]).Underlying().(*types.Map).Key() - return c.formatExpr(`delete %e[%s.keyFor(%s)]`, args[0], c.typeName(keyType), c.translateImplicitConversion(args[1], keyType)) + args = fc.expandTupleArgs(args) + keyType := fc.pkgCtx.TypeOf(args[0]).Underlying().(*types.Map).Key() + return fc.formatExpr( + `$mapDelete(%1e, %2s.keyFor(%3s))`, + args[0], + fc.typeName(keyType), + fc.translateImplicitConversion(args[1], keyType), + ) case "copy": - if basic, isBasic := c.p.TypeOf(args[1]).Underlying().(*types.Basic); isBasic && isString(basic) { - return c.formatExpr("$copyString(%e, %e)", args[0], args[1]) + args = fc.expandTupleArgs(args) + if basic, isBasic := fc.pkgCtx.TypeOf(args[1]).Underlying().(*types.Basic); isBasic && isString(basic) { + return fc.formatExpr("$copyString(%e, %e)", args[0], args[1]) } - return c.formatExpr("$copySlice(%e, %e)", args[0], args[1]) - case "print", "println": - return c.formatExpr("console.log(%s)", strings.Join(c.translateExprSlice(args, nil), ", ")) + return fc.formatExpr("$copySlice(%e, %e)", args[0], args[1]) + case "print": + args = fc.expandTupleArgs(args) + return fc.formatExpr("$print(%s)", strings.Join(fc.translateExprSlice(args, nil), ", ")) + case "println": + args = fc.expandTupleArgs(args) + return fc.formatExpr("console.log(%s)", strings.Join(fc.translateExprSlice(args, nil), ", ")) case "complex": - argStr := c.translateArgs(sig, args, ellipsis) - return c.formatExpr("new %s(%s, %s)", c.typeName(sig.Results().At(0).Type()), argStr[0], argStr[1]) + argStr := fc.translateArgs(sig, args, ellipsis) + return fc.formatExpr("new %s(%s, %s)", fc.typeName(sig.Results().At(0).Type()), argStr[0], argStr[1]) case "real": - return c.formatExpr("%e.$real", args[0]) + return fc.formatExpr("%e.$real", args[0]) case "imag": - return c.formatExpr("%e.$imag", args[0]) + return fc.formatExpr("%e.$imag", args[0]) case "recover": - return c.formatExpr("$recover()") + return fc.formatExpr("$recover()") case "close": - return c.formatExpr(`$close(%e)`, args[0]) + return fc.formatExpr(`$close(%e)`, args[0]) default: panic(fmt.Sprintf("Unhandled builtin: %s\n", name)) } } -func (c *funcContext) identifierConstant(expr ast.Expr) (string, bool) { - val := c.p.Types[expr].Value +func (fc *funcContext) identifierConstant(expr ast.Expr) (string, bool) { + val := fc.pkgCtx.Types[expr].Value if val == nil { return "", false } @@ -941,29 +1032,29 @@ func (c *funcContext) identifierConstant(expr ast.Expr) (string, bool) { return s, true } -func (c *funcContext) translateExprSlice(exprs []ast.Expr, desiredType types.Type) []string { +func (fc *funcContext) translateExprSlice(exprs []ast.Expr, desiredType types.Type) []string { parts := make([]string, len(exprs)) for i, expr := range exprs { - parts[i] = c.translateImplicitConversion(expr, desiredType).String() + parts[i] = fc.translateImplicitConversion(expr, desiredType).String() } return parts } -func (c *funcContext) translateConversion(expr ast.Expr, desiredType types.Type) *expression { - exprType := c.p.TypeOf(expr) +func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type) *expression { + exprType := fc.pkgCtx.TypeOf(expr) if types.Identical(exprType, desiredType) { - return c.translateExpr(expr) + return fc.translateExpr(expr) } - if c.p.Pkg.Path() == "reflect" { - if call, isCall := expr.(*ast.CallExpr); isCall && types.Identical(c.p.TypeOf(call.Fun), types.Typ[types.UnsafePointer]) { + if fc.pkgCtx.Pkg.Path() == "reflect" || fc.pkgCtx.Pkg.Path() == "internal/reflectlite" { + if call, isCall := expr.(*ast.CallExpr); isCall && types.Identical(fc.pkgCtx.TypeOf(call.Fun), types.Typ[types.UnsafePointer]) { if ptr, isPtr := desiredType.(*types.Pointer); isPtr { if named, isNamed := ptr.Elem().(*types.Named); isNamed { switch named.Obj().Name() { case "arrayType", "chanType", "funcType", "interfaceType", "mapType", "ptrType", "sliceType", "structType": - return c.formatExpr("%e.kindType", call.Args[0]) // unsafe conversion + return fc.formatExpr("%e.kindType", call.Args[0]) // unsafe conversion default: - return c.translateExpr(expr) + return fc.translateExpr(expr) } } } @@ -979,72 +1070,72 @@ func (c *funcContext) translateConversion(expr ast.Expr, desiredType types.Type) case is64Bit(t): if !is64Bit(basicExprType) { if basicExprType.Kind() == types.Uintptr { // this might be an Object returned from reflect.Value.Pointer() - return c.formatExpr("new %1s(0, %2e.constructor === Number ? %2e : 1)", c.typeName(desiredType), expr) + return fc.formatExpr("new %1s(0, %2e.constructor === Number ? %2e : 1)", fc.typeName(desiredType), expr) } - return c.formatExpr("new %s(0, %e)", c.typeName(desiredType), expr) + return fc.formatExpr("new %s(0, %e)", fc.typeName(desiredType), expr) } - return c.formatExpr("new %1s(%2h, %2l)", c.typeName(desiredType), expr) + return fc.formatExpr("new %1s(%2h, %2l)", fc.typeName(desiredType), expr) case is64Bit(basicExprType): if !isUnsigned(t) && !isUnsigned(basicExprType) { - return c.fixNumber(c.formatParenExpr("%1l + ((%1h >> 31) * 4294967296)", expr), t) + return fc.fixNumber(fc.formatParenExpr("%1l + ((%1h >> 31) * 4294967296)", expr), t) } - return c.fixNumber(c.formatExpr("%s.$low", c.translateExpr(expr)), t) + return fc.fixNumber(fc.formatExpr("%s.$low", fc.translateExpr(expr)), t) case isFloat(basicExprType): - return c.formatParenExpr("%e >> 0", expr) + return fc.formatParenExpr("%e >> 0", expr) case types.Identical(exprType, types.Typ[types.UnsafePointer]): - return c.translateExpr(expr) + return fc.translateExpr(expr) default: - return c.fixNumber(c.translateExpr(expr), t) + return fc.fixNumber(fc.translateExpr(expr), t) } case isFloat(t): if t.Kind() == types.Float32 && exprType.Underlying().(*types.Basic).Kind() == types.Float64 { - return c.formatExpr("$fround(%e)", expr) + return fc.formatExpr("$fround(%e)", expr) } - return c.formatExpr("%f", expr) + return fc.formatExpr("%f", expr) case isComplex(t): - return c.formatExpr("new %1s(%2r, %2i)", c.typeName(desiredType), expr) + return fc.formatExpr("new %1s(%2r, %2i)", fc.typeName(desiredType), expr) case isString(t): - value := c.translateExpr(expr) + value := fc.translateExpr(expr) switch et := exprType.Underlying().(type) { case *types.Basic: if is64Bit(et) { - value = c.formatExpr("%s.$low", value) + value = fc.formatExpr("%s.$low", value) } if isNumeric(et) { - return c.formatExpr("$encodeRune(%s)", value) + return fc.formatExpr("$encodeRune(%s)", value) } return value case *types.Slice: if types.Identical(et.Elem().Underlying(), types.Typ[types.Rune]) { - return c.formatExpr("$runesToString(%s)", value) + return fc.formatExpr("$runesToString(%s)", value) } - return c.formatExpr("$bytesToString(%s)", value) + return fc.formatExpr("$bytesToString(%s)", value) default: panic(fmt.Sprintf("Unhandled conversion: %v\n", et)) } case t.Kind() == types.UnsafePointer: if unary, isUnary := expr.(*ast.UnaryExpr); isUnary && unary.Op == token.AND { if indexExpr, isIndexExpr := unary.X.(*ast.IndexExpr); isIndexExpr { - return c.formatExpr("$sliceToArray(%s)", c.translateConversionToSlice(indexExpr.X, types.NewSlice(types.Typ[types.Uint8]))) + return fc.formatExpr("$sliceToNativeArray(%s)", fc.translateConversionToSlice(indexExpr.X, types.NewSlice(types.Typ[types.Uint8]))) } if ident, isIdent := unary.X.(*ast.Ident); isIdent && ident.Name == "_zero" { - return c.formatExpr("new Uint8Array(0)") + return fc.formatExpr("new Uint8Array(0)") } } - if ptr, isPtr := c.p.TypeOf(expr).(*types.Pointer); c.p.Pkg.Path() == "syscall" && isPtr { + if ptr, isPtr := fc.pkgCtx.TypeOf(expr).(*types.Pointer); fc.pkgCtx.Pkg.Path() == "syscall" && isPtr { if s, isStruct := ptr.Elem().Underlying().(*types.Struct); isStruct { - array := c.newVariable("_array") - target := c.newVariable("_struct") - c.Printf("%s = new Uint8Array(%d);", array, sizes32.Sizeof(s)) - c.Delayed(func() { - c.Printf("%s = %s, %s;", target, c.translateExpr(expr), c.loadStruct(array, target, s)) + array := fc.newVariable("_array") + target := fc.newVariable("_struct") + fc.Printf("%s = new Uint8Array(%d);", array, sizes32.Sizeof(s)) + fc.Delayed(func() { + fc.Printf("%s = %s, %s;", target, fc.translateExpr(expr), fc.loadStruct(array, target, s)) }) - return c.formatExpr("%s", array) + return fc.formatExpr("%s", array) } } if call, ok := expr.(*ast.CallExpr); ok { if id, ok := call.Fun.(*ast.Ident); ok && id.Name == "new" { - return c.formatExpr("new Uint8Array(%d)", int(sizes32.Sizeof(c.p.TypeOf(call.Args[0])))) + return fc.formatExpr("new Uint8Array(%d)", int(sizes32.Sizeof(fc.pkgCtx.TypeOf(call.Args[0])))) } } } @@ -1054,103 +1145,130 @@ func (c *funcContext) translateConversion(expr ast.Expr, desiredType types.Type) case *types.Basic: if isString(et) { if types.Identical(t.Elem().Underlying(), types.Typ[types.Rune]) { - return c.formatExpr("new %s($stringToRunes(%e))", c.typeName(desiredType), expr) + return fc.formatExpr("new %s($stringToRunes(%e))", fc.typeName(desiredType), expr) } - return c.formatExpr("new %s($stringToBytes(%e))", c.typeName(desiredType), expr) + return fc.formatExpr("new %s($stringToBytes(%e))", fc.typeName(desiredType), expr) } case *types.Array, *types.Pointer: - return c.formatExpr("new %s(%e)", c.typeName(desiredType), expr) + return fc.formatExpr("new %s(%e)", fc.typeName(desiredType), expr) } case *types.Pointer: - switch u := t.Elem().Underlying().(type) { - case *types.Array: - return c.translateExpr(expr) - case *types.Struct: - if c.p.Pkg.Path() == "syscall" && types.Identical(exprType, types.Typ[types.UnsafePointer]) { - array := c.newVariable("_array") - target := c.newVariable("_struct") - return c.formatExpr("(%s = %e, %s = %e, %s, %s)", array, expr, target, c.zeroValue(t.Elem()), c.loadStruct(array, target, u), target) - } - return c.formatExpr("$pointerOfStructConversion(%e, %s)", expr, c.typeName(t)) + if types.Identical(exprType, types.Typ[types.UntypedNil]) { + // Fall through to the fc.translateImplicitConversionWithCloning(), which + // handles conversion from untyped nil to a pointer type. + break } - if !types.Identical(exprType, types.Typ[types.UnsafePointer]) { - exprTypeElem := exprType.Underlying().(*types.Pointer).Elem() - ptrVar := c.newVariable("_ptr") - getterConv := c.translateConversion(c.setType(&ast.StarExpr{X: c.newIdent(ptrVar, exprType)}, exprTypeElem), t.Elem()) - setterConv := c.translateConversion(c.newIdent("$v", t.Elem()), exprTypeElem) - return c.formatExpr("(%1s = %2e, new %3s(function() { return %4s; }, function($v) { %1s.$set(%5s); }, %1s.$target))", ptrVar, expr, c.typeName(desiredType), getterConv, setterConv) + switch ptrElType := t.Elem().Underlying().(type) { + case *types.Array: // (*[N]T)(expr) — converting expr to a pointer to an array. + if _, ok := exprType.Underlying().(*types.Slice); ok { + return fc.formatExpr("$sliceToGoArray(%e, %s)", expr, fc.typeName(desiredType)) + } + // TODO(nevkontakte): Is this just for aliased types (e.g. `type a [4]byte`)? + return fc.translateExpr(expr) + case *types.Struct: // (*StructT)(expr) — converting expr to a pointer to a struct. + if fc.pkgCtx.Pkg.Path() == "syscall" && types.Identical(exprType, types.Typ[types.UnsafePointer]) { + // Special case: converting an unsafe pointer to a byte array into a + // struct pointer when handling syscalls. + // TODO(nevkontakte): Add a runtime assertion that the unsafe.Pointer is + // indeed pointing at a byte array. + array := fc.newVariable("_array") + target := fc.newVariable("_struct") + return fc.formatExpr("(%s = %e, %s = %e, %s, %s)", array, expr, target, fc.zeroValue(t.Elem()), fc.loadStruct(array, target, ptrElType), target) + } + // Convert between structs of different types but identical layouts, + // for example: + // type A struct { foo int }; type B A; var a *A = &A{42}; var b *B = (*B)(a) + // + // TODO(nevkontakte): Should this only apply when exprType is a pointer to a + // struct as well? + return fc.formatExpr("$pointerOfStructConversion(%e, %s)", expr, fc.typeName(t)) + } + + if types.Identical(exprType, types.Typ[types.UnsafePointer]) { + // TODO(nevkontakte): Why do we fall through to the implicit conversion here? + // Conversion from unsafe.Pointer() requires explicit type conversion: https://play.golang.org/p/IQxtmpn1wgc. + // Possibly related to https://github.com/gopherjs/gopherjs/issues/1001. + break // Fall through to fc.translateImplicitConversionWithCloning() below. } + // Handle remaining cases, for example: + // type iPtr *int; var c int = 42; println((iPtr)(&c)); + // TODO(nevkontakte): Are there any other cases that fall into this case? + exprTypeElem := exprType.Underlying().(*types.Pointer).Elem() + ptrVar := fc.newVariable("_ptr") + getterConv := fc.translateConversion(fc.setType(&ast.StarExpr{X: fc.newIdent(ptrVar, exprType)}, exprTypeElem), t.Elem()) + setterConv := fc.translateConversion(fc.newIdent("$v", t.Elem()), exprTypeElem) + return fc.formatExpr("(%1s = %2e, new %3s(function() { return %4s; }, function($v) { %1s.$set(%5s); }, %1s.$target))", ptrVar, expr, fc.typeName(desiredType), getterConv, setterConv) case *types.Interface: if types.Identical(exprType, types.Typ[types.UnsafePointer]) { - return c.translateExpr(expr) + return fc.translateExpr(expr) } } - return c.translateImplicitConversionWithCloning(expr, desiredType) + return fc.translateImplicitConversionWithCloning(expr, desiredType) } -func (c *funcContext) translateImplicitConversionWithCloning(expr ast.Expr, desiredType types.Type) *expression { +func (fc *funcContext) translateImplicitConversionWithCloning(expr ast.Expr, desiredType types.Type) *expression { switch desiredType.Underlying().(type) { case *types.Struct, *types.Array: switch expr.(type) { case nil, *ast.CompositeLit: // nothing default: - return c.formatExpr("$clone(%e, %s)", expr, c.typeName(desiredType)) + return fc.formatExpr("$clone(%e, %s)", expr, fc.typeName(desiredType)) } } - return c.translateImplicitConversion(expr, desiredType) + return fc.translateImplicitConversion(expr, desiredType) } -func (c *funcContext) translateImplicitConversion(expr ast.Expr, desiredType types.Type) *expression { +func (fc *funcContext) translateImplicitConversion(expr ast.Expr, desiredType types.Type) *expression { if desiredType == nil { - return c.translateExpr(expr) + return fc.translateExpr(expr) } - exprType := c.p.TypeOf(expr) + exprType := fc.pkgCtx.TypeOf(expr) if types.Identical(exprType, desiredType) { - return c.translateExpr(expr) + return fc.translateExpr(expr) } basicExprType, isBasicExpr := exprType.Underlying().(*types.Basic) if isBasicExpr && basicExprType.Kind() == types.UntypedNil { - return c.formatExpr("%e", c.zeroValue(desiredType)) + return fc.formatExpr("%e", fc.zeroValue(desiredType)) } switch desiredType.Underlying().(type) { case *types.Slice: - return c.formatExpr("$subslice(new %1s(%2e.$array), %2e.$offset, %2e.$offset + %2e.$length)", c.typeName(desiredType), expr) + return fc.formatExpr("$convertSliceType(%1e, %2s)", expr, fc.typeName(desiredType)) case *types.Interface: if typesutil.IsJsObject(exprType) { // wrap JS object into js.Object struct when converting to interface - return c.formatExpr("new $jsObjectPtr(%e)", expr) + return fc.formatExpr("new $jsObjectPtr(%e)", expr) } if isWrapped(exprType) { - return c.formatExpr("new %s(%e)", c.typeName(exprType), expr) + return fc.formatExpr("new %s(%e)", fc.typeName(exprType), expr) } if _, isStruct := exprType.Underlying().(*types.Struct); isStruct { - return c.formatExpr("new %1e.constructor.elem(%1e)", expr) + return fc.formatExpr("new %1e.constructor.elem(%1e)", expr) } } - return c.translateExpr(expr) + return fc.translateExpr(expr) } -func (c *funcContext) translateConversionToSlice(expr ast.Expr, desiredType types.Type) *expression { - switch c.p.TypeOf(expr).Underlying().(type) { +func (fc *funcContext) translateConversionToSlice(expr ast.Expr, desiredType types.Type) *expression { + switch fc.pkgCtx.TypeOf(expr).Underlying().(type) { case *types.Array, *types.Pointer: - return c.formatExpr("new %s(%e)", c.typeName(desiredType), expr) + return fc.formatExpr("new %s(%e)", fc.typeName(desiredType), expr) } - return c.translateExpr(expr) + return fc.translateExpr(expr) } -func (c *funcContext) loadStruct(array, target string, s *types.Struct) string { - view := c.newVariable("_view") +func (fc *funcContext) loadStruct(array, target string, s *types.Struct) string { + view := fc.newVariable("_view") code := fmt.Sprintf("%s = new DataView(%s.buffer, %s.byteOffset)", view, array, array) var fields []*types.Var var collectFields func(s *types.Struct, path string) @@ -1171,7 +1289,7 @@ func (c *funcContext) loadStruct(array, target string, s *types.Struct) string { case *types.Basic: if isNumeric(t) { if is64Bit(t) { - code += fmt.Sprintf(", %s = new %s(%s.getUint32(%d, true), %s.getUint32(%d, true))", field.Name(), c.typeName(field.Type()), view, offsets[i]+4, view, offsets[i]) + code += fmt.Sprintf(", %s = new %s(%s.getUint32(%d, true), %s.getUint32(%d, true))", field.Name(), fc.typeName(field.Type()), view, offsets[i]+4, view, offsets[i]) break } code += fmt.Sprintf(", %s = %s.get%s(%d, true)", field.Name(), view, toJavaScriptType(t), offsets[i]) @@ -1179,26 +1297,27 @@ func (c *funcContext) loadStruct(array, target string, s *types.Struct) string { case *types.Array: code += fmt.Sprintf(`, %s = new ($nativeArray(%s))(%s.buffer, $min(%s.byteOffset + %d, %s.buffer.byteLength))`, field.Name(), typeKind(t.Elem()), array, array, offsets[i], array) } + // TODO(nevkontakte): Explicitly panic if unsupported field type is encountered? } return code } -func (c *funcContext) fixNumber(value *expression, basic *types.Basic) *expression { +func (fc *funcContext) fixNumber(value *expression, basic *types.Basic) *expression { switch basic.Kind() { case types.Int8: - return c.formatParenExpr("%s << 24 >> 24", value) + return fc.formatParenExpr("%s << 24 >> 24", value) case types.Uint8: - return c.formatParenExpr("%s << 24 >>> 24", value) + return fc.formatParenExpr("%s << 24 >>> 24", value) case types.Int16: - return c.formatParenExpr("%s << 16 >> 16", value) + return fc.formatParenExpr("%s << 16 >> 16", value) case types.Uint16: - return c.formatParenExpr("%s << 16 >>> 16", value) + return fc.formatParenExpr("%s << 16 >>> 16", value) case types.Int32, types.Int, types.UntypedInt: - return c.formatParenExpr("%s >> 0", value) + return fc.formatParenExpr("%s >> 0", value) case types.Uint32, types.Uint, types.Uintptr: - return c.formatParenExpr("%s >>> 0", value) + return fc.formatParenExpr("%s >>> 0", value) case types.Float32: - return c.formatExpr("$fround(%s)", value) + return fc.formatExpr("$fround(%s)", value) case types.Float64: return value default: @@ -1206,7 +1325,7 @@ func (c *funcContext) fixNumber(value *expression, basic *types.Basic) *expressi } } -func (c *funcContext) internalize(s *expression, t types.Type) *expression { +func (fc *funcContext) internalize(s *expression, t types.Type) *expression { if typesutil.IsJsObject(t) { return s } @@ -1214,25 +1333,25 @@ func (c *funcContext) internalize(s *expression, t types.Type) *expression { case *types.Basic: switch { case isBoolean(u): - return c.formatExpr("!!(%s)", s) + return fc.formatExpr("!!(%s)", s) case isInteger(u) && !is64Bit(u): - return c.fixNumber(c.formatExpr("$parseInt(%s)", s), u) + return fc.fixNumber(fc.formatExpr("$parseInt(%s)", s), u) case isFloat(u): - return c.formatExpr("$parseFloat(%s)", s) + return fc.formatExpr("$parseFloat(%s)", s) } } - return c.formatExpr("$internalize(%s, %s)", s, c.typeName(t)) + return fc.formatExpr("$internalize(%s, %s)", s, fc.typeName(t)) } -func (c *funcContext) formatExpr(format string, a ...interface{}) *expression { - return c.formatExprInternal(format, a, false) +func (fc *funcContext) formatExpr(format string, a ...interface{}) *expression { + return fc.formatExprInternal(format, a, false) } -func (c *funcContext) formatParenExpr(format string, a ...interface{}) *expression { - return c.formatExprInternal(format, a, true) +func (fc *funcContext) formatParenExpr(format string, a ...interface{}) *expression { + return fc.formatExprInternal(format, a, true) } -func (c *funcContext) formatExprInternal(format string, a []interface{}, parens bool) *expression { +func (fc *funcContext) formatExprInternal(format string, a []interface{}, parens bool) *expression { processFormat := func(f func(uint8, uint8, int)) { n := 0 for i := 0; i < len(format); i++ { @@ -1271,7 +1390,7 @@ func (c *funcContext) formatExprInternal(format string, a []interface{}, parens if _, isIdent := e.(*ast.Ident); isIdent { continue } - if val := c.p.Types[e.(ast.Expr)].Value; val != nil { + if val := fc.pkgCtx.Types[e.(ast.Expr)].Value; val != nil { continue } if !hasAssignments { @@ -1279,8 +1398,8 @@ func (c *funcContext) formatExprInternal(format string, a []interface{}, parens out.WriteByte('(') parens = false } - v := c.newVariable("x") - out.WriteString(v + " = " + c.translateExpr(e.(ast.Expr)).String() + ", ") + v := fc.newVariable("x") + out.WriteString(v + " = " + fc.translateExpr(e.(ast.Expr)).String() + ", ") vars[i] = v } @@ -1290,7 +1409,7 @@ func (c *funcContext) formatExprInternal(format string, a []interface{}, parens out.WriteString(vars[n] + suffix) return } - out.WriteString(c.translateExpr(a[n].(ast.Expr)).StringWithParens() + suffix) + out.WriteString(fc.translateExpr(a[n].(ast.Expr)).StringWithParens() + suffix) } switch k { case 0: @@ -1307,19 +1426,19 @@ func (c *funcContext) formatExprInternal(format string, a []interface{}, parens out.WriteString(a[n].(token.Token).String()) case 'e': e := a[n].(ast.Expr) - if val := c.p.Types[e].Value; val != nil { - out.WriteString(c.translateExpr(e).String()) + if val := fc.pkgCtx.Types[e].Value; val != nil { + out.WriteString(fc.translateExpr(e).String()) return } writeExpr("") case 'f': e := a[n].(ast.Expr) - if val := c.p.Types[e].Value; val != nil { + if val := fc.pkgCtx.Types[e].Value; val != nil { d, _ := constant.Int64Val(constant.ToInt(val)) out.WriteString(strconv.FormatInt(d, 10)) return } - if is64Bit(c.p.TypeOf(e).Underlying().(*types.Basic)) { + if is64Bit(fc.pkgCtx.TypeOf(e).Underlying().(*types.Basic)) { out.WriteString("$flatten64(") writeExpr("") out.WriteString(")") @@ -1328,9 +1447,9 @@ func (c *funcContext) formatExprInternal(format string, a []interface{}, parens writeExpr("") case 'h': e := a[n].(ast.Expr) - if val := c.p.Types[e].Value; val != nil { + if val := fc.pkgCtx.Types[e].Value; val != nil { d, _ := constant.Uint64Val(constant.ToInt(val)) - if c.p.TypeOf(e).Underlying().(*types.Basic).Kind() == types.Int64 { + if fc.pkgCtx.TypeOf(e).Underlying().(*types.Basic).Kind() == types.Int64 { out.WriteString(strconv.FormatInt(int64(d)>>32, 10)) return } @@ -1339,21 +1458,21 @@ func (c *funcContext) formatExprInternal(format string, a []interface{}, parens } writeExpr(".$high") case 'l': - if val := c.p.Types[a[n].(ast.Expr)].Value; val != nil { + if val := fc.pkgCtx.Types[a[n].(ast.Expr)].Value; val != nil { d, _ := constant.Uint64Val(constant.ToInt(val)) out.WriteString(strconv.FormatUint(d&(1<<32-1), 10)) return } writeExpr(".$low") case 'r': - if val := c.p.Types[a[n].(ast.Expr)].Value; val != nil { + if val := fc.pkgCtx.Types[a[n].(ast.Expr)].Value; val != nil { r, _ := constant.Float64Val(constant.Real(val)) out.WriteString(strconv.FormatFloat(r, 'g', -1, 64)) return } writeExpr(".$real") case 'i': - if val := c.p.Types[a[n].(ast.Expr)].Value; val != nil { + if val := fc.pkgCtx.Types[a[n].(ast.Expr)].Value; val != nil { i, _ := constant.Float64Val(constant.Imag(val)) out.WriteString(strconv.FormatFloat(i, 'g', -1, 64)) return diff --git a/compiler/gopherjspkg/doc.go b/compiler/gopherjspkg/doc.go index f57e84f7..e772ae19 100644 --- a/compiler/gopherjspkg/doc.go +++ b/compiler/gopherjspkg/doc.go @@ -4,9 +4,6 @@ // operation. They are needed to build the Go standard library with GopherJS. // Currently, they include: // -// github.com/gopherjs/gopherjs/js -// github.com/gopherjs/gopherjs/nosync -// +// github.com/gopherjs/gopherjs/js +// github.com/gopherjs/gopherjs/nosync package gopherjspkg - -//go:generate vfsgendev -source="github.com/gopherjs/gopherjs/compiler/gopherjspkg".FS -tag=gopherjsdev diff --git a/compiler/gopherjspkg/fs.go b/compiler/gopherjspkg/fs.go index f6fb2622..d8905714 100644 --- a/compiler/gopherjspkg/fs.go +++ b/compiler/gopherjspkg/fs.go @@ -1,15 +1,16 @@ +//go:build gopherjsdev // +build gopherjsdev package gopherjspkg import ( "go/build" - "log" "net/http" "os" pathpkg "path" "github.com/shurcooL/httpfs/filter" + log "github.com/sirupsen/logrus" ) // FS is a virtual filesystem that contains core GopherJS packages. diff --git a/compiler/gopherjspkg/fs_vfsdata.go b/compiler/gopherjspkg/fs_vfsdata.go index b21d6e83..d3329f8e 100644 --- a/compiler/gopherjspkg/fs_vfsdata.go +++ b/compiler/gopherjspkg/fs_vfsdata.go @@ -1,5 +1,6 @@ // Code generated by vfsgen; DO NOT EDIT. +//go:build !gopherjsdev // +build !gopherjsdev package gopherjspkg @@ -21,50 +22,57 @@ var FS = func() http.FileSystem { fs := vfsgen۰FS{ "/": &vfsgen۰DirInfo{ name: "/", - modTime: time.Date(2019, 4, 25, 16, 19, 34, 225618757, time.UTC), + modTime: time.Date(2022, 8, 24, 18, 44, 1, 872191253, time.UTC), }, "/js": &vfsgen۰DirInfo{ name: "js", - modTime: time.Date(2019, 3, 10, 16, 38, 53, 764271817, time.UTC), + modTime: time.Date(2022, 8, 22, 20, 46, 23, 572374682, time.UTC), }, "/js/js.go": &vfsgen۰CompressedFileInfo{ name: "js.go", - modTime: time.Date(2019, 3, 10, 16, 38, 53, 764987009, time.UTC), - uncompressedSize: 8002, + modTime: time.Date(2022, 8, 22, 20, 46, 23, 572728212, time.UTC), + uncompressedSize: 11230, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x59\x5f\x6f\xdc\x36\x12\x7f\x5e\x7d\x8a\x39\xa1\x40\x56\xcd\x56\xbe\xb6\x86\x51\x38\xe7\x87\xa4\xb9\xfa\xdc\x4b\xdc\x00\x6e\xd0\x07\x23\x30\xb8\xd2\x68\x97\xb1\x44\xea\x48\x6a\x37\x7b\xb6\xbf\xfb\x61\xf8\x47\x2b\xad\xa4\xc4\xbe\x24\x2f\x75\xc5\xe1\x6f\x7e\x9c\x19\xce\x1f\xee\xd1\x11\xbc\x63\xd9\x2d\x5b\x21\x7c\xd4\x50\x2b\xb9\xe1\x39\x6a\x28\x1a\x91\x19\x2e\x85\x86\x42\x2a\xe0\xc2\xa0\x62\x99\xe1\x62\x05\x5b\x6e\xd6\x20\x98\xe1\x1b\x84\xdf\xd9\x86\x5d\x65\x8a\xd7\x06\x5e\xbe\xbb\xd0\x29\xfc\xca\xca\x52\x83\x91\x60\xd6\xa8\xb1\x83\xc2\x14\x82\x51\xc8\x0c\xe6\xa0\x6b\xcc\x38\x2b\xcb\x1d\x2c\x77\x70\x2e\xeb\x35\xaa\xdf\xaf\x80\x89\x1c\x8c\x62\x42\x97\x56\x28\xe7\x0a\x33\x53\xee\x3c\x18\x57\x90\x49\xa5\x50\xd7\x52\xe4\x44\xa3\xa3\x5a\xef\x84\x61\x9f\xd2\xe8\xe8\x28\x3a\x3a\x82\xf7\x1a\xe1\x2d\xbb\xc5\xbf\x14\xab\x6b\x54\xb4\x1f\x3f\xd5\x52\x23\x54\x68\xd6\x32\xb7\xf4\xf6\xbb\x53\xf8\x6b\x8d\x02\x6a\xa6\x35\xc1\x6e\x58\xd9\xa0\x6e\xb5\x2f\x48\x37\x14\xb2\x2c\xe5\x96\x96\xcd\xae\x46\xc8\xa4\xd8\xa0\xd2\xed\xb9\x6a\x54\x85\x54\x15\xe6\xa7\x9e\x02\xdc\xc3\xb9\x74\xb2\xfd\x7f\xf7\x5d\xda\x9d\xf5\x7b\xf8\xb5\x83\xb9\x64\xd9\x2d\x91\xb4\x56\x2f\x58\x86\x77\x0f\x70\xef\x71\x7f\x18\xfb\xf7\xd4\xef\x5d\x09\x8f\xbb\x94\xb2\x84\xc1\xbf\x7b\x78\x25\x65\x89\x4c\x0c\xbe\x8f\xcb\x77\x24\x3c\x2e\x9d\x61\x85\x4a\x5b\xf7\x16\xa5\x64\x46\xdb\xfd\x97\x4d\xb5\x44\x35\xd4\x67\x45\x4e\x8e\xbf\x88\xab\x8d\x22\x7f\x0c\xf6\x5f\x4d\x7c\x1f\x97\x1f\xe2\x5e\x7f\xe0\xc2\xfc\x32\xdc\x7f\x21\xcc\x2f\x2f\x95\x62\xbb\x83\xef\xe3\xf2\x13\xb8\x3f\x9e\x8c\xe1\xfe\x78\x32\x00\x9e\x92\x9f\xc0\xfd\xf9\xa7\x85\xfb\xa3\x87\xfb\xf3\x4f\x53\xb8\xd3\x74\x3b\xb8\xcd\xc8\xc1\xee\xe1\x3d\x1f\x33\xc4\x94\xfc\x14\xee\xe1\xc1\x1c\xee\xd0\x10\x53\xf2\x53\xb8\xce\x10\x4d\x7b\x44\x87\x3b\x34\xc4\x7d\x4f\xea\xf3\xb8\x36\x22\x7f\xfe\xe9\x80\xef\x6f\xee\xeb\x01\xf0\x94\xfc\x24\xee\x41\xa4\x7b\xdc\x93\xe3\x29\xdc\xc9\x9b\x11\x70\x59\x59\x82\x34\x6b\x54\xa0\x4b\x9e\xa1\x0e\xfb\x87\xb1\x0b\xfb\x78\x68\xb3\xcc\x67\x70\x69\xbf\x1e\xee\xd7\x88\x4e\x53\x2f\xdd\x4d\x7d\x1f\xe2\xee\x2b\xc4\x81\x1d\xfc\xf7\x43\x7d\x24\x3f\x4f\xd3\xb4\xc3\x3a\x81\xef\x3f\xea\xf4\x8f\xe5\x47\xcc\x4c\x8b\x6b\x78\x85\xe9\x9f\xbc\xc2\x83\xfd\xaf\x99\x19\x63\x33\x21\x3f\xe4\xfb\xc3\xf8\x2a\x70\xa1\x0d\x13\x19\xca\x02\x2e\x65\xbe\xcf\xeb\x1d\x6a\x9f\xc5\xad\x58\xad\x17\x94\xa5\x9a\xcc\xe8\x71\xdc\x0e\x8c\x95\xbf\x76\x39\x6d\xdc\x81\xf7\xbe\x14\xbd\xcc\x73\x4e\x76\xa4\x72\xbb\xb0\xb5\x9c\x79\x2d\x54\xc6\x0c\xe3\x82\xd2\x22\xeb\xf2\x2c\x38\x96\xf9\x02\xa4\xa0\xe2\xbb\xb6\xe5\xce\xa0\x30\x20\x0b\x57\x0c\x69\x19\xb6\xbc\x2c\x61\x89\xb6\x6e\x62\xde\x2f\xa9\x36\xd7\x6f\xc8\xf7\x54\xd2\x58\x1a\xd5\x6d\x83\x11\x11\x27\xaf\x87\x6b\x60\x81\x04\x2a\xcf\x6d\xd8\x58\x48\x2b\xdd\x69\x2d\xb8\xd1\x6d\x29\xff\x06\x6d\xc5\xb0\x91\x80\x97\x20\x78\x09\xb5\xb4\x96\x25\xc9\x3d\x63\xfc\x4f\xc3\xca\xfe\x71\x9f\x69\x88\x45\x53\x96\x71\x1a\xe4\x32\x26\x40\x48\x43\xf6\x69\xc8\x3a\x8c\x4e\x5a\xb1\x1a\x6e\x71\x97\x46\xf6\x42\x78\x49\xe7\x8a\x3b\x7f\x48\xf8\xde\x7f\x7e\xb0\x76\x3a\x47\x03\x0a\x4d\xa3\x84\xb6\x96\x77\x42\xcf\x6c\x97\x56\xa3\x32\x3b\xd7\x8b\xd1\xd2\x8a\x6f\x50\x38\x78\xba\x21\x30\x97\x01\x2b\x21\x98\xf9\x2d\xee\x7c\x09\x4c\x5a\x25\x77\x1e\x1c\x64\xea\x6d\xec\x25\x13\xaf\xff\x0a\x0d\x50\x5b\xb4\xf2\xfa\x6d\x6f\xe4\x0d\xf7\xff\x92\xb9\xea\x91\x59\x78\xcc\xde\x6d\xbe\xdb\x13\xf2\xd2\x5e\x2c\xf0\x7a\x8d\x25\x1a\x04\x85\x95\xdc\xe0\x57\x99\xc6\x21\xf5\xac\xd3\xd1\xbe\x5f\x0d\x9a\xdf\xa0\x58\x99\xf5\xb8\x53\xe2\xd2\x2e\xc6\x2d\x85\x85\x6f\x14\x8d\xbb\x1f\x5c\x98\x11\x06\x0e\x71\x9e\xd0\xf2\x88\x47\xda\x65\xa7\xff\x42\xe4\xf8\xa9\xa7\x9e\x3f\x33\x6b\xc0\x12\x2b\x7f\x43\x99\x70\xa9\x7a\x44\x95\xdd\x3c\xe7\xa4\xe9\x73\x41\xe0\xc5\x3a\x41\xe0\xb4\x6a\x34\x4f\x56\x19\x36\x3b\xad\x8f\xf0\xb6\x97\x3e\x70\x38\x5d\x7d\xc8\xdc\xfd\xef\x9a\xdc\x65\x81\x43\x57\x0b\x56\xe1\x08\x17\x02\x99\xd3\x5a\x1b\x7b\x4c\xad\x34\x0c\x6a\xc9\xa4\x61\x5a\x00\xb7\x33\x4d\xd3\xbd\x5b\x36\xf2\x16\x07\x0c\x29\x53\x61\x59\xa4\xf0\xe7\x9a\x6b\x97\x31\x0b\xc6\x4b\xe0\x05\x70\x9b\x4c\x28\x47\xb0\xb6\x04\x8e\xba\x8c\x80\xe7\x4f\x24\xda\xd9\xd5\x21\x79\x89\x5b\xc8\x6c\xaa\xa4\x6c\x24\x70\xdb\xd6\x16\x97\xd9\xb9\x76\xa5\x3a\xe4\xdb\x51\xd2\x7d\xc6\x30\xcf\xa4\x70\x29\x4c\xaa\x64\x84\xff\x25\x6e\x9f\x4a\x3e\x6c\xe9\x30\xa7\x19\x64\xe4\xce\xf5\xaf\x97\x1d\x48\x58\x96\x49\x65\xc7\xc3\x7e\x41\x3a\x1c\xdb\x46\xa8\x92\x92\x79\xe2\x60\x86\xac\xfc\xaa\xbf\x12\x6e\x96\xf8\x12\x23\x3f\x72\x7c\x05\x27\xa7\x68\x9e\x04\xa8\x21\xaf\x56\x22\x04\xe2\x58\xc5\x18\xe4\xa1\x47\x73\x82\x79\xcd\x94\xc6\x0b\x61\xc6\xbc\x7b\x21\xcc\x64\xe2\x72\x6b\x2d\xab\x93\xe3\xc7\xf0\x3a\x39\xfe\x76\xcc\x4e\x8e\x1d\xb7\x93\xe3\x71\x76\x76\xdd\xf1\x7b\xcf\x1f\x45\xb0\xf9\x96\x0c\x9d\xce\x79\x12\x50\x87\x1c\x5b\x09\x47\xd2\x0e\x06\x5f\xe4\x18\x86\x84\x27\x92\xb4\xe0\x63\x34\xed\xc2\x3c\x69\x71\x87\x34\x83\x44\xeb\x6a\x77\xc9\x1f\xe3\xee\x90\x0e\x52\xb8\x42\x04\xc3\x96\x25\xd5\x06\x08\xdd\x62\x26\x2b\x5b\x62\xa8\x31\xcc\xd1\x30\x5e\x8e\xdd\x91\x56\xa3\x73\x77\xdb\x09\x8f\x3a\xbd\x95\xf4\x8e\x17\x9a\x15\xa3\x54\xa9\x63\x13\xd6\x37\xb5\x51\x0b\xd8\xae\x79\xb6\xb6\x6d\xdd\x12\x3b\xc7\xd8\x70\x06\x8d\xc5\x48\xdf\xb9\x66\x31\x85\x4b\x69\x2c\x0f\x91\x63\x6e\xa9\xd7\xcd\xb2\xe4\x19\x35\x82\x63\x61\x60\x77\xfb\x30\xa8\x8d\x1a\x8b\x83\x20\xe2\x38\xff\x53\x29\xa9\x00\x45\xc6\x6a\xdd\x94\x36\x9b\x77\xfc\x8b\xb4\xaa\x29\x79\x4b\x8d\xae\x3b\x6e\x94\xc0\x9c\x28\x49\x60\x70\x2e\xa1\x66\x82\x67\xb6\x2d\xae\xd8\x8e\xce\xa3\x30\x93\x1b\x54\x98\x2f\xa8\x80\xda\x94\x25\xe0\x7b\xa7\xc7\xac\x99\x81\xb5\x2c\x73\x67\x9d\x43\x4d\xa1\x58\xb8\x9e\xd6\x6d\xf1\xd3\xc5\x5d\x34\xf3\xa7\x8c\xba\xc4\xbb\xb6\xae\x50\x6b\x72\xb4\x1f\x2c\x3a\x67\xca\xa7\x35\x39\x13\xa2\x52\x9e\x62\xe2\x80\x3b\x49\x32\x9a\x79\x13\xc6\x87\x20\xa7\x10\xc3\x73\xfa\xd3\x76\xba\xb1\xd7\x1f\x27\x6d\x1a\x8d\x42\x82\x67\xd9\x6d\x8f\xaa\xb6\x5f\xda\xe6\xf2\x2b\x19\x5b\xfc\x31\xc6\x2d\x35\xab\x6f\x48\xec\xbc\x94\x4b\x56\xda\x3e\x47\xf7\x27\x90\x95\x5b\xf1\xe1\x3b\x8f\xb7\x5c\xe4\x72\x1b\xdb\x08\x5c\x2a\xb9\xd5\xe1\x0d\x2e\x3e\x7f\xf3\xc7\xab\x97\x6f\xdc\x0a\x8d\xaa\xe9\x47\x9d\xa4\xd1\x86\xa9\x80\x1e\xdc\x46\x0a\xdf\xca\xbc\x29\xd1\x2b\xdc\xcf\x00\xfe\xfc\x71\x65\x97\x63\xd8\x30\xc5\xed\xf5\xd5\x68\x68\xfa\xf2\xb8\x29\xfc\x8b\x0b\x73\xea\x06\x09\x70\xc2\xf6\x31\x56\x19\xd7\xb4\x3d\xfb\xa8\x53\xa7\xc2\x1d\xdb\xad\x69\x3a\xf8\xfe\x7f\x2f\x59\x85\xf1\x82\x5a\x88\xe4\x99\x23\xea\x59\x75\x89\xbe\x17\x39\x16\x9c\x22\x7d\xcf\xb5\xe3\x11\x47\x3b\x6e\x82\x54\xec\x80\xf6\xbb\xba\x58\xaf\x71\xd9\xac\x56\xa8\x60\x45\x2d\x6f\x26\xab\x9a\x97\x87\x33\x2e\x35\xfc\xb9\x97\x7b\x11\x53\x7c\x18\xdb\x10\x7b\x77\x07\x88\x79\x02\x77\x9d\xcc\x28\x58\xe9\x1b\x9f\x5e\x0f\xef\x97\x86\x53\xaf\xbb\x7f\x0a\x6b\x85\x1a\x85\xd1\xc0\x1f\x93\x60\xfa\xaa\x5c\xef\x3d\xd2\x7a\xb5\x51\x27\x78\xe9\xe3\xeb\x2d\xbb\xc5\xdf\x08\x62\xab\x58\xad\xbb\x9d\x1e\x85\x8e\xb3\x2c\xcb\x32\xd4\xe1\x8d\x3f\xbc\x97\xcb\xe2\xc0\x36\xd4\x4f\xc6\x2e\xe0\x98\x5a\x35\x64\x1a\x1d\xd3\x14\xb6\x95\x2a\x0f\x79\x3c\xa8\x9b\x17\xc2\x3d\xec\xd8\x2e\xd4\x13\xb4\x5d\xb6\xdb\x08\xd7\x1f\xda\x8c\xf9\x85\xb3\xb8\x18\x76\xbd\x7a\xfc\x5d\xe5\x15\xc4\x8b\x43\xa3\x14\x22\x09\x97\xea\xdf\xb8\xd3\x3d\x7f\xdc\xd2\x07\x1f\xe2\x6e\xa4\x18\x3e\x47\xb8\x03\xd0\xd6\x6e\x3a\xbf\xfe\xb0\xbf\xd2\xbc\x00\x09\x67\x67\xf6\x29\xe1\xfe\xde\xfd\xbd\x8f\xb7\xbb\x68\xd6\x35\xff\xec\x21\x9a\x31\x38\x3d\x0b\xfc\xed\x6d\x70\xa8\x71\xe2\x4f\x43\xb4\xe2\x05\xc8\x24\x9a\x69\x12\xa5\xc3\xcd\x83\xc6\x05\xb0\x76\x58\x4c\xa2\x99\xfd\xd1\x86\x84\xfe\xfe\x02\x38\xfc\xa3\xb3\xf8\x02\xf8\xf3\xe7\x56\xbd\xbe\xe6\x1f\xe0\x0c\x58\x3b\xf1\xed\xb3\x0d\xd1\xf1\xec\x74\x27\x34\xc2\x4f\x2a\xfb\x31\x62\x18\xb1\xae\x54\xae\x99\xb6\x31\x54\x53\xda\x29\x6c\x21\x09\x37\x1f\xf3\xf6\xf5\x46\x16\x14\xd0\xef\xb5\x5d\x2a\x79\xc6\x0d\x5d\x39\x83\xca\x06\x8e\x76\x7f\x76\x7e\xb5\xf1\xbf\xe3\xf8\x0a\x63\x1f\xa2\x0e\x7f\xcd\xd9\x07\x96\x27\xfb\x99\xf0\xdf\x90\x81\x0e\x2f\x4b\x12\xcd\xe4\xa4\x23\x68\x38\x21\x01\x97\x9e\x6e\x6e\xc2\xcd\xbd\x71\x87\xbf\xb9\x89\x17\xb0\x49\xa2\x59\xe0\x7c\x7a\x06\x1b\x07\xd1\x19\x94\xe2\x24\x94\x1f\x2b\x14\x8f\xb8\xcb\x2f\x8d\x38\xad\xb2\x9e\xf7\xcb\xc1\x71\xd1\x8c\xa2\xad\x72\xb0\xf5\xed\xaa\x53\x38\xe0\x6f\x67\x10\xc7\x70\x07\x47\x47\x76\x78\x0b\x3e\x88\x66\xb3\x59\x26\x85\xe1\xa2\xc1\x68\x46\xfe\xf6\xa7\xf2\x28\x34\xe7\x76\x60\x16\xee\x7e\x86\x59\xae\x0d\xf8\x8e\x35\x67\xe3\x57\x10\x3f\x39\x13\xf1\xff\x62\x78\xd3\x25\x23\x59\x2d\x81\xb1\x92\x75\x47\x57\xb2\x08\x47\x31\xbb\x3a\x4e\x16\x60\x54\x83\xe1\x12\xb0\xba\x2e\x77\x04\xe0\x86\x70\x3a\xfa\x43\x2f\x5e\x65\xd4\x8e\xbb\xf6\xcd\xfb\x55\x53\x14\x53\x21\xdb\x15\x28\x94\xac\x80\xc1\x72\x67\xfc\xc3\xb5\x0f\xa5\x3e\xce\x7c\x09\xd7\x1f\x48\xa6\x77\x74\xf7\xd0\x3d\x0c\xa6\x25\xc5\x4a\x51\x50\x51\x3c\x3d\xf3\xa8\xf6\x60\xdf\xb9\xaf\x71\xe2\xe6\xa4\x68\xe6\xde\x8e\x0e\xa5\xfc\x8b\x52\x2b\x15\xae\x64\x47\xc4\xbe\xbc\x84\x88\x5a\x5a\x8e\x6d\xc2\xb0\x72\x94\x31\xac\xb2\xf0\xdf\xe7\x0e\x35\x64\xbf\xb7\xee\x1d\x56\xf3\xaa\x2e\xd1\x3e\x52\x52\x2f\x97\xc2\x85\x7d\xa1\x68\x0b\x8d\x7d\xc2\xd4\x6b\xa9\xcc\xda\xfe\x92\x27\xd5\xf0\xee\x6b\x98\x2f\xb1\x90\xaa\x3b\x61\x24\xbe\x37\x7c\x3b\xf1\x62\xed\xfa\xad\x1e\x87\xfd\xcf\x06\x4f\x64\xe1\x7f\xa3\x98\x26\x71\xd5\xff\xb9\x23\x72\x1e\xe6\x82\xd3\x00\x73\x17\xcd\x8e\x8e\x80\x6d\x24\xcf\x21\x47\x96\x43\x26\x73\x04\x2c\x79\xc5\x05\xa3\xb0\x8d\x66\xd6\xc7\xb6\x87\xbb\x7b\x88\x66\x37\x70\x06\x18\x3d\x44\xff\x0b\x00\x00\xff\xff\x72\x0d\xcb\x80\x42\x1f\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xcc\x5a\x5f\x93\xdb\x36\x92\x7f\x26\x3f\x45\x87\x95\x2a\x8b\xb6\x22\x6d\x12\x97\x6b\x6b\x72\xf3\xe0\x24\xb7\x3e\xe7\x62\x6f\x6a\x27\xbe\x3c\xb8\x5c\x2e\x88\x6c\x4a\xf0\x50\x00\x17\x00\x25\x6b\x67\xe6\xbb\x5f\x35\x1a\x20\x29\x91\x1a\x7b\xd6\x79\x58\xbf\x58\x43\x34\xba\x7f\xfd\x07\xfd\x07\xe4\x72\x09\xbf\x89\xe2\x5a\xac\x11\x3e\x58\x68\x8c\xde\xc9\x12\x2d\x54\xad\x2a\x9c\xd4\xca\x42\xa5\x0d\x48\xe5\xd0\x88\xc2\x49\xb5\x86\xbd\x74\x1b\x50\xc2\xc9\x1d\xc2\x2f\x62\x27\xae\x0a\x23\x1b\x07\xcf\x7f\x7b\x69\x17\xf0\x93\xa8\x6b\x0b\x4e\x83\xdb\xa0\xc5\x01\x17\x61\x10\x9c\x41\xe1\xb0\x04\xdb\x60\x21\x45\x5d\x1f\x60\x75\x80\x17\xba\xd9\xa0\xf9\xe5\x0a\x84\x2a\xc1\x19\xa1\x6c\xed\x89\x4a\x69\xb0\x70\xf5\x21\x30\x93\x06\x0a\x6d\x0c\xda\x46\xab\x92\x60\x0c\x44\xdb\x83\x72\xe2\xe3\x22\x5d\x2e\xd3\xe5\x12\xde\x58\x84\x57\xe2\x1a\xff\x30\xa2\x69\xd0\xd0\x7e\xfc\xd8\x68\x8b\xb0\x45\xb7\xd1\xa5\x87\xd7\xef\x5e\x74\x1b\xfe\xd6\xd6\xf5\xf9\x4d\xcf\x5f\xff\x0c\x95\xc4\x7a\xbc\xff\x8f\x0d\x2a\x68\x84\xb5\x04\x6b\x27\xea\x16\x6d\x87\x7e\x4e\xd8\xa1\xd2\x75\xad\xf7\xb4\xec\x0e\x0d\x42\xa1\xd5\x0e\x8d\xed\xec\xd2\xa0\xa9\xb4\xd9\x62\x79\x11\x54\x80\x5b\x78\xa1\x99\xf6\xf8\xdf\xed\x50\xed\xc1\xfa\x2d\xfc\x34\xe0\xb9\x12\xc5\x35\x81\xf4\x5e\xab\x44\x81\x37\x77\x70\x1b\xf8\x7e\x33\xf5\xef\xa1\xcf\x87\x14\x81\xef\x4a\xeb\x1a\x46\xff\x6e\xe1\x47\xad\x6b\x14\x6a\xf4\x7c\x9a\x7e\x40\x11\xf8\x92\x0e\x6b\x34\xd6\x87\x47\x55\x6b\xe1\xac\xdf\xff\xba\xdd\xae\xd0\x8c\xe5\x79\x92\x67\x4f\x3f\xc9\xd7\x3a\x43\xfe\x18\xed\xbf\x3a\xf3\x7c\x9a\x7e\xcc\xf7\xed\x3b\xa9\xdc\x5f\xc7\xfb\x5f\x2a\xf7\xd7\xe7\xc6\x88\xc3\xc9\xf3\x69\xfa\x33\x7c\xbf\x7d\x36\xc5\xf7\xdb\x67\x23\xc6\xe7\xe8\xcf\xf0\xfd\xfe\xbb\x39\xff\x38\xe2\xfb\xfd\x77\xe7\xf8\xc2\xe7\xe0\x6d\x27\x14\xbb\x85\x37\x72\xca\x10\xe7\xe8\xcf\xf1\x3d\x55\x8c\xf9\x8e\x0d\x71\x8e\xfe\x1c\x5f\x36\x44\xdb\xa9\xc8\x7c\xc7\x86\xb8\x3d\xa2\xba\x9f\xaf\x8f\xc8\xef\xbf\x3b\xc1\xfb\x37\x7e\x7a\xc2\xf8\x1c\xfd\x59\xbe\x27\x91\x1e\xf8\x3e\x7b\x7a\x8e\xef\xd9\x93\x11\xf9\x8a\xba\x06\xed\x36\x68\xc0\xd6\xb2\x40\x1b\xf7\x8f\x63\x77\x10\x0f\x5d\x96\xb9\x87\x2f\xed\xb7\x13\xe7\x0a\x91\x25\x1d\xa5\xbb\x73\xcf\xc7\x7c\xfb\x0a\x73\x62\x87\xf0\x7c\x94\x1f\x5a\x55\xcc\x16\x8b\xc5\x00\x75\x0e\x8f\x3f\xd8\xc5\xdf\x57\x1f\xb0\x70\x1d\x5f\x27\xb7\xb8\xf8\x5d\x6e\xf1\x64\xff\xcf\xc2\x4d\xa1\x39\x43\x3f\xc6\xfb\xcd\xf4\x2a\x48\x65\x9d\x50\x05\xea\x0a\x5e\xeb\xb2\xcf\xeb\x03\x68\xf7\xf2\xdd\x8a\xc6\xce\x29\x4b\xb5\x85\xb3\xd3\x7c\x07\x6c\x3c\xfd\x5b\xce\x69\xd3\x0e\xbc\x0d\xa5\xe8\x79\x59\x4a\xb2\x23\x95\xeb\xb9\xef\x05\x44\x90\x42\x65\xcc\x09\xa9\x28\x2d\x8a\x21\x4e\x5f\x25\xe7\xa0\x15\x15\xef\x8d\x2f\x77\x0e\x95\x03\x5d\x71\x31\xa4\x65\xd8\xcb\xba\x86\x15\xfa\xba\x89\xe5\x71\x49\xf5\xb9\x7e\x47\xbe\xa7\x92\x26\x16\x69\xd3\x35\x28\x29\x61\x0a\x72\xa4\x05\x11\x41\xa0\x09\xd8\xc6\x8d\x89\xf6\xd4\x83\xd6\x44\x3a\xdb\x55\xf5\x3f\xa1\x2d\x19\x37\x22\xf0\x1c\x94\xac\xa1\xd1\xde\xb2\x44\xd9\x23\xc6\x7f\xb6\xa2\x3e\x56\xf7\x91\x85\x4c\xb5\x75\x9d\x2d\x22\x5d\x21\x14\x28\xed\xc8\x3e\x2d\x59\x47\x90\xa6\x5b\xd1\xc0\x35\x1e\x16\xa9\x3f\x10\x81\x92\x5d\x71\x13\x94\x84\xc7\xe1\xf1\x9d\xb7\xd3\x0b\x74\x60\xd0\xb5\x46\x59\x6f\x79\x26\x7a\xe4\xbb\xbc\x06\x8d\x3b\x70\x2f\x47\x4b\x6b\xb9\x43\xc5\xec\xe9\x84\xc0\x4c\x47\x5e\x39\xb1\x99\x5d\xe3\x21\x94\xc0\xbc\x13\x72\x13\x98\x83\x5e\x04\x1b\x07\xca\x3c\xc8\xbf\x42\x07\xd4\x16\xad\x83\x7c\xdf\x1b\x05\xc3\xfd\xbb\x60\xae\x8e\xc0\xcc\x03\xcf\xa3\xd3\x7c\xd3\x03\x0a\xd4\x81\x2c\xe2\xfa\x19\x6b\x74\x08\x06\xb7\x7a\x87\x5f\x64\x1a\xe6\x74\x64\x9d\x81\xf4\x7e\x35\x4a\xfe\x15\xd5\xda\x6d\xa6\x9d\x92\xd5\x7e\x31\xeb\x20\xcc\x43\xa3\xe8\xf8\x7c\x48\xe5\x26\x10\x30\xc7\x59\x4e\xcb\x13\x1e\xe9\x96\x59\xfe\x4b\x55\xe2\xc7\x23\xf1\xf2\x91\xdb\x00\xd6\xb8\x0d\x27\x54\x28\x4e\xd5\x13\xa2\xfc\xe6\x99\x24\x49\xf7\x05\x41\x20\x1b\x04\x01\x4b\xb5\xe8\x1e\x2c\x32\x6e\x66\xa9\x9f\xe1\xed\x40\x7d\xe2\x70\x3a\xfa\x50\xf0\xf9\x1f\x9a\x9c\xb3\xc0\xa9\xab\x95\xd8\xe2\x04\x16\x62\x32\xa3\xb5\x2e\xf6\x84\x59\x5b\x18\xd5\x92\xb3\x86\xe9\x18\xf0\xce\xc5\x62\xd1\xbb\x65\xa7\xaf\x71\x84\x90\x32\x15\xd6\xd5\x02\x7e\xdf\x48\xcb\x19\xb3\x12\xb2\x06\x59\x81\xf4\xc9\x84\x72\x84\xe8\x4a\xe0\xa4\xcb\x88\xf1\xec\x81\x40\x07\xbb\x06\x20\x5f\xe3\x1e\x0a\x9f\x2a\x29\x1b\x29\xdc\x77\xb5\x85\x33\xbb\xb4\x5c\xaa\x63\xbe\x9d\x04\x7d\x8c\x18\x66\x85\x56\x9c\xc2\xb4\xc9\x27\xf0\xbf\xc6\xfd\x43\xc1\xc7\x2d\x03\xe4\x34\x83\x4c\x9c\xb9\xe3\xe3\xe5\x07\x12\x51\x14\xda\xf8\xf1\xf2\xb8\x20\x9d\x8e\x6d\x13\x50\x49\xc8\x2c\x67\x36\x63\x54\x61\x35\x1c\x09\x9e\x25\x3e\x85\x28\x8c\x1c\x5f\x80\x89\x05\xcd\xf2\xc8\x6a\x8c\xab\xa3\x88\x81\xe8\x3e\x09\x8b\x12\xcd\xe7\x62\x82\x59\x23\x8c\xc5\x97\xca\xe5\x93\xd1\xe9\xce\x26\x2e\x5e\xeb\x50\x3d\x7b\xfa\x39\xb8\x9e\x3d\xfd\xf3\x90\x3d\x7b\xca\xd8\x9e\x3d\x9d\x46\xe7\xd7\x19\xdf\x1b\xf9\x59\x00\xdb\x3f\x13\x21\xcb\x9c\xe5\x91\xeb\x18\x63\x47\xc1\x20\xfd\x60\xf0\x49\x8c\x71\x48\x78\x20\x48\xcf\x7c\x0a\xa6\x5f\x98\xe5\x1d\xdf\x31\xcc\x48\xd1\xb9\x9a\x0f\xf9\xe7\xb8\x3b\xa6\x83\x05\x5c\x21\x82\x13\xab\x9a\x6a\x03\xc4\x6e\xb1\xd0\x5b\x5f\x62\xa8\x31\x2c\xd1\x09\x59\xdb\x69\x57\x33\x1f\x76\x77\xd7\x09\x4f\x3a\xbd\xa3\x0c\x8e\x57\x56\x54\x93\x50\xa9\x63\x53\xde\x37\x8d\x33\x73\xd8\x6f\x64\xb1\xf1\x6d\xdd\x0a\x07\x6a\xec\xa4\x80\xd6\xf3\x58\xfc\xc6\xcd\xe2\x02\x5e\x6b\xe7\x71\xa8\x12\x4b\x0f\xbd\x69\x57\xb5\x2c\xa8\x11\x9c\x0a\x03\xbf\x3b\x84\x41\xe3\xcc\x54\x1c\x44\x12\xc6\xfc\xdf\xc6\x68\x03\xa8\x0a\xd1\xd8\xb6\xf6\xd9\x7c\xe0\x5f\xa4\x55\x4b\xc9\x5b\x5b\xe4\xee\xb8\x35\x0a\x4b\x82\xa4\x41\xc0\x0b\x0d\x8d\x50\xb2\xf0\x6d\xf1\x56\x1c\x48\x1f\x83\x85\xde\xa1\xc1\x72\x4e\x05\xd4\xa7\x2c\x05\x8f\x59\x8e\xdb\x08\x07\x1b\xed\x6f\xcd\x36\x38\x92\x14\x8b\x05\xf7\xb4\xbc\x25\x4c\x17\x37\x69\x12\xb4\x4c\x87\xc0\x87\xb6\xde\xa2\xb5\xe4\xe8\x30\x58\x0c\x74\x2a\xcf\x4b\x62\x13\xa2\x31\x01\x62\xce\x8c\x07\x49\x32\x4d\x82\x09\xb3\x53\x26\x17\x90\xc1\x13\xfa\xe9\x3b\xdd\x2c\xc8\xcf\xf2\x2e\x8d\xa6\x31\xc1\x8b\xe2\xfa\x08\xaa\xf5\x4f\xba\xe6\xf2\x0b\x11\x7b\xfe\x53\x88\x3b\x68\x5e\xde\x18\xd8\x8b\x5a\xaf\x44\xed\xfb\x1c\x7b\x3c\x81\xac\x79\x25\x84\xef\x2c\xdb\x4b\x55\xea\x7d\xe6\x23\x70\x65\xf4\xde\xc6\x3b\xb8\xec\xc5\xaf\x7f\xff\xf1\xf9\xaf\xbc\x42\xa3\xea\xe2\x83\xcd\x17\xe9\x4e\x98\xc8\x3d\xba\x8d\x04\xbe\xd2\x65\x5b\x63\x10\xd8\xcf\x00\x41\xff\x6c\xeb\x97\x33\xd8\x09\x23\xfd\xf1\xb5\xe8\x68\xfa\x0a\x7c\x17\xf0\x3f\x52\xb9\x0b\x1e\x24\x88\x1d\xd3\xfb\xab\x59\xe3\xb8\x6f\x7b\xf4\xc1\x2e\x58\x0a\x6b\xce\x6b\x96\x74\xef\xff\x7c\x2d\xb6\x98\xcd\xa9\x8b\xc8\x1f\xc5\x7b\xe2\xd7\xda\x21\xc7\x67\xc7\x81\x7a\x2a\x3f\xb6\x96\x58\x49\x8e\x7a\x30\xad\xa2\xd9\xde\x86\x33\x6c\xdb\xc6\xcb\xfe\x49\x6f\xb7\x5a\xfd\x72\xd5\xa3\xb2\x30\xdb\x38\xd7\xd8\x8b\xe5\x52\xe9\x12\x3f\xd8\x85\x36\xeb\xa5\x68\xe4\x32\xac\x2f\x36\x6e\x5b\xe7\x0b\xaf\xdc\x2f\x57\x91\x93\xf5\x6d\x91\x9f\x5a\xeb\xc3\x9c\xd8\xad\x5a\xca\x00\xbd\xd5\x25\x0f\x84\x1e\x58\x9c\x08\x65\xd5\x4f\xa8\xba\x75\x4d\xeb\xfb\xc1\x38\x4c\x6f\x8c\x6e\xd7\x1b\x36\xd9\xaa\x55\x65\x8d\x26\xc0\x97\xdb\x86\x1b\x6f\xdb\x69\x00\x33\xf2\x24\x7e\x14\xb4\x34\x87\x3d\xae\x28\x81\x02\x3d\xb3\xab\x56\xd6\x65\xf0\x6e\x30\xd1\xd0\xbb\x6f\x54\x34\x54\xef\xe0\x41\x18\xb3\xaf\xb3\x36\x52\x65\xcc\xa8\xdf\x35\xe4\xf5\x33\xae\xda\xf5\x1a\x0d\xac\x69\x4e\x28\xf4\xb6\x91\xf5\xe9\xc5\x00\x4d\x49\x65\xa0\xfb\x21\xa3\x43\xe5\xbc\x32\xe1\x8c\x44\x16\xb3\x1c\x6e\x06\xe5\x44\x89\x3a\x74\x8b\x47\x83\x4f\x58\x1a\x5f\x15\x70\x50\x18\x6c\x0c\x5a\x6f\x29\xf9\x39\x59\xf9\x58\x14\x0f\x2c\x13\xfd\x6a\x77\x54\x95\xac\xc3\xa1\xe4\x77\x0f\xaa\x80\xbd\x11\x8d\x1d\xb6\xc7\x74\xde\xd8\xb2\xa2\x28\xd0\xc6\x17\x2b\xf1\x25\x83\xae\x4e\x6c\x43\x4d\x78\xc6\xa7\x54\x98\x75\xeb\xfd\x9c\xd1\xe8\xba\xd7\xa6\x8c\xc5\x2f\x8a\x9b\x55\x8a\x6f\xc3\x7c\xeb\x1e\x00\xfa\xd1\x84\x37\xc2\xdb\x77\x5d\x99\xf9\x84\x2e\x7c\xf0\x79\xc0\xc9\xbe\xde\x06\x01\xd9\xfc\xd4\x28\x95\xca\x63\x26\xfa\x5f\x3c\xd8\x23\x7f\x5c\xd3\x83\x90\x17\x78\x0e\x1b\xdf\xe1\xb0\x02\xb4\x75\x58\x03\xdf\xbe\xeb\xf3\xa0\xac\x40\xc3\xe5\xa5\xbf\x7f\xb9\xbd\xe5\xdf\x7d\xbc\xdd\xa4\xc9\xd0\xfc\xc9\x5d\x9a\x08\xb8\xb8\x8c\xf8\x7d\xfe\x60\xae\x59\x1e\xb4\x21\x58\xd9\x1c\x74\x9e\x26\x96\x48\x49\xb9\x59\x94\x38\x07\xd1\x4d\xd8\x79\x9a\xf8\x37\x65\x44\xf4\x97\x1f\x40\xc2\x7f\x0d\x16\x7f\x00\xf9\xe4\x89\x17\x6f\xdf\xca\x77\x70\x09\xa2\x1b\x93\xfb\x14\x4d\x70\x02\x3a\x3b\x08\x8d\xf8\x4a\xaa\x9f\xbd\xc6\x11\xcb\x87\x7b\x23\xac\x8f\xa1\x86\xb2\x46\xe5\xab\x6f\xcc\x95\x58\x76\x57\x5e\xba\xa2\x80\x7e\x63\xfd\x52\x2d\x0b\xe9\xe8\xc8\x39\x34\x3e\x70\x2c\xff\x1c\xbc\x2a\x0b\xef\xc1\x42\x59\x9e\x7c\x05\xd6\x07\x56\x00\x7b\x4f\xf8\xef\xc8\x40\xa7\x87\x25\x4f\x13\x7d\xd6\x11\x34\xd1\x11\x01\x27\xf4\xf7\xef\xe3\xc9\x7d\xcf\xca\xbf\x7f\x9f\xcd\x61\x97\xa7\x49\xc4\x7c\x71\x09\x3b\x66\x31\x98\x2e\xb3\x3c\xd6\x6c\x4f\x94\x4d\xb8\x2b\x2c\x4d\x38\x6d\xeb\x3d\x1f\x96\xa3\xe3\xd2\x84\xa2\x6d\xcb\x6c\x9b\xeb\xf5\xa0\xda\xc2\x57\x97\x90\x65\x70\x03\xcb\xa5\x9f\x78\xa3\x0f\xd2\x24\x49\x0a\xad\x9c\x54\x2d\xa6\x09\xf9\x3b\x68\x15\xb8\x28\x2a\x53\x3d\x9b\x39\x9f\xcf\x38\x00\x77\x01\x3f\xb0\x66\x32\x7d\x04\xf1\x23\x9b\x48\xfe\x0b\xe3\x45\x38\x19\xc9\x4b\x89\x88\x8d\x6e\x06\xb2\xf2\x79\x54\xc5\x1d\x9a\x2c\x9f\x83\x33\x2d\xc6\x43\x20\x9a\xa6\x3e\x10\x03\xbe\xb9\x20\xd5\xef\x8e\xe2\x55\x1f\xa5\xb2\xfe\x35\xea\x17\xc6\xac\x2f\xae\x83\xb0\x9d\x53\x88\x52\x37\x8d\x06\x41\xf2\x05\xf0\x6c\x70\xcd\x2a\xf2\x18\xa6\x3e\x43\xce\x03\xe7\x32\x04\xb8\x25\x7e\x7d\x90\xfb\x3f\xbb\x9a\x5d\xe2\x0e\x6b\x6a\xcf\x16\x5b\xfd\x2f\x59\xd7\xc2\x97\x6f\x54\xdf\xbc\xb9\x5a\x96\xba\xb0\xcb\x3f\x70\xb5\xec\xb5\x58\xfe\x03\x2b\x34\xa8\x0a\x5c\xb2\xe9\xdf\xb3\x53\xec\x92\xff\x5f\x72\xce\xf9\x2d\x74\x7c\x39\xc9\x8a\xea\x29\xad\xbe\xc1\xed\x0a\x4b\x2a\x26\xdd\xf9\x0c\x27\x8b\x8f\xe7\xff\x71\x86\xe7\xb4\x1f\x26\x05\x7e\xa5\x1e\xec\x11\x55\x09\x9a\x71\xab\xbe\xc1\xad\xc5\x9a\xea\x45\x54\x7c\xbf\x41\xd5\x71\x99\xfb\xd6\x42\x28\xea\x02\xb4\x71\x42\x39\xbe\xa3\x06\xa7\x53\x8e\x54\xdf\x01\xf9\xf2\xc7\x37\x3c\x91\x4d\xb8\x77\xb3\xc1\xa1\x25\x68\x05\x28\x8a\x4d\x60\x7d\x54\x59\x3a\xef\xdf\x93\x04\x64\x7f\xfe\xcf\xa4\x83\xc1\xd1\x25\x8a\xc1\x86\x89\xa3\x9d\xa6\x49\x88\xa1\xc0\xf0\x9e\x3c\x92\x26\xc7\x9e\x21\x72\x7f\xcc\x86\xb7\xca\x25\x5a\xef\x65\x6d\xe0\x55\xee\xcf\xd9\x3d\x25\xe2\x98\x5f\x16\xa3\x8e\xb0\xcc\xc1\xdf\x3e\xf7\xec\xfc\xa9\x39\x85\x70\x2e\xa9\xbd\x22\xc1\x99\xb7\x7d\x76\x31\x34\xc1\x3c\xa5\xf3\x97\x26\xb4\xce\xd7\x9b\x85\x1f\x22\x40\x80\x45\x65\x25\x75\xd2\x7e\xa2\x62\x7d\x16\x29\xd3\xfd\x81\x50\x6a\xf5\xc8\xc1\x5e\x78\xa7\x87\x38\x00\xa1\x0e\x71\x68\xb6\xd4\x79\xfa\x86\x20\x3c\x98\xf3\x56\xab\x61\x4f\xbb\xc1\xea\xee\x02\x14\x08\xbd\xe0\x97\x6f\xab\x03\x6c\x84\x2a\xbd\x24\x77\x68\xc8\xa8\x03\x0f\xc5\x99\x84\x76\x0d\x87\x92\x24\x69\xae\xd7\x93\xb4\xc7\xf9\x94\xb8\xd2\x70\x7b\x41\x69\x95\xf3\xae\x3b\x34\x6f\xff\xf2\x8e\xca\xfb\xa3\xc7\x8f\x38\x13\x12\xc5\x25\x64\x8f\x33\x9f\x5a\xd3\x64\x94\xe0\x6b\x54\x33\x77\x68\x06\x89\x3d\x72\x92\xcc\x69\x11\x38\x79\x15\x2e\x79\xe5\xc9\xb7\x17\xef\xfc\xb3\x95\x41\x71\x4d\xbf\xee\x22\xff\xe6\x7a\xfd\x3b\xeb\x4a\x6a\x3c\x81\x6c\x41\xe3\x21\xc1\x78\x42\x7b\xd3\x64\xe4\xe7\xaf\xc9\x2b\xd1\xb3\xbd\x6b\x99\xd1\xbc\x4b\xab\x69\x42\x7d\x72\x48\x08\xb1\x49\x1e\xd6\xb7\x61\x34\xfa\x37\xb3\x7d\x99\xa4\x9a\x64\x27\x6d\xda\x95\xbe\x1f\x88\xe2\xab\xd3\xc6\x28\xb2\xef\x2b\x1d\x87\x77\xa1\x55\x21\x5c\x36\x87\xad\xe5\x9c\x4f\x7d\x75\x45\xe1\x40\x39\x47\x74\xaf\xb9\xc2\xdb\x1d\x9f\x70\xca\x2e\x9d\x55\x46\x6f\xe3\x65\xff\xdc\xef\xc5\xda\x62\x7c\x2f\xd8\x1d\x71\xbe\xe9\xe6\xeb\xe2\x8d\xd8\x71\x2e\x5b\x78\x6d\x70\x52\x19\x62\x49\x9a\xe0\x58\x91\x20\xf9\x12\xc2\x44\xc8\x7f\x53\xc5\xff\xb4\x8e\x78\x62\x2a\xd2\x98\x11\x1f\x71\x1e\xc1\xe9\x65\xdc\xfd\x87\x34\x16\x27\xa1\x37\x5d\xf5\x47\x91\xf8\x19\x0d\xc7\x43\x3a\x8e\xd3\xb4\xfd\x80\xde\x63\x34\x3c\x9c\x54\x97\xfc\xb4\x39\x19\xe6\xc7\xae\x4d\x49\xee\xfa\x53\x45\x56\x0d\x1e\x1c\xc7\xcc\x89\xcb\x98\x6e\xc2\x63\x49\xe5\x0b\x06\x2f\x0f\x3c\x46\xcc\xbf\xaa\x86\x57\x10\x58\x66\x79\xbc\xf7\x67\xc3\x0d\x3c\xe4\x5d\x74\xea\xa3\xea\x5e\x1f\x25\xd9\x1a\x5d\x74\xd1\xa9\x4f\x92\x5d\x31\xc8\x0b\xc1\x27\x85\x6e\x0e\x2f\xab\x7f\xe0\x3f\x5b\x69\xb0\x9c\x70\x47\xf6\xf5\x4e\xd4\xa1\x33\xae\xce\xb9\xa6\x1a\xb8\x26\x67\x61\x9f\x8a\x00\x6a\x15\x8b\xe3\x9d\x9f\x76\xa7\x67\xed\xdd\x95\x24\x99\xed\x55\xfd\xb0\xeb\x47\xbd\xa0\xec\x7a\x37\x56\x36\xea\xc6\xe2\x3f\xec\xfe\x2d\xf1\xc9\x39\x0b\x5d\x9d\xb5\xd0\x1c\xd6\xbb\x21\xf6\xbb\x9c\x0f\x60\xdf\x1c\xf7\xed\x40\xda\xbd\x49\xf3\x49\xfb\xc7\xb6\xaa\xce\x35\xc9\x43\x02\x9f\x43\x05\xac\x0e\x2e\x7c\x13\x13\xfa\xad\x63\x3e\xb3\x15\xbc\x7d\x47\x34\x47\xb1\xc1\xdf\xd0\x8c\x7b\xac\x15\x4d\x54\x55\x65\xd1\xd1\x22\x73\x65\x85\xf9\x69\x96\xf3\x2b\x98\x34\xe1\xd7\xd2\xa7\x54\xe1\x65\x75\x47\x15\x07\xd7\x01\x89\x08\x85\xc9\xff\xb5\xf2\x18\xbb\x9e\xc9\xd3\xd1\x5c\xed\x85\xc5\xff\x9f\x30\xd7\x78\x47\xf0\x8a\x3b\x7c\xeb\x2f\xad\xfc\xf7\x0f\x54\x3e\x17\xf0\xd2\x5f\x76\x75\xd7\x31\xfe\xeb\x08\xbb\xd1\xc6\x6d\xfc\x47\x82\xda\x8c\xa7\x0d\x0b\xb3\x15\x56\xda\x0c\x5f\x5e\xe4\xe1\xda\xf9\xd5\x99\x8f\x61\xf8\x2a\xf7\x08\x43\xff\x45\xd2\x03\x51\x84\xcf\x9f\xce\x83\xb8\x3a\xfe\x92\x2a\x65\x0f\x4b\x25\x1d\xa7\x8f\xe5\x12\x9e\xef\xb4\x2c\xa1\x44\x51\x42\xa1\x4b\x04\xac\xe5\x56\x2a\xc1\xaf\x7e\x13\xef\x64\x7f\x3f\x7c\x73\x97\x26\xef\xa9\xfc\xa5\x77\xe9\xff\x07\x00\x00\xff\xff\xac\x6f\xee\xde\xde\x2b\x00\x00"), + }, + "/js/js_test.go": &vfsgen۰CompressedFileInfo{ + name: "js_test.go", + modTime: time.Date(2022, 7, 28, 18, 48, 56, 845071266, time.UTC), + uncompressedSize: 371, + + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x7c\xcf\x31\x6f\xfa\x30\x10\x05\xf0\xd9\xf7\x29\x4e\x5e\x48\xfe\xff\xca\xde\x10\x04\x31\x31\xa0\xae\x2d\x3b\x5c\xdc\x4b\xe2\xd4\x24\x91\xcf\x61\x28\xe2\xbb\x57\x41\x55\xa2\x2e\xdd\xfc\x9e\x7e\xf2\xd3\x59\x5b\xf7\x45\x39\xfa\xf0\x81\xad\x80\xb5\xf8\x7f\x0e\x30\x90\xfb\xa4\x9a\xb1\x95\x73\x62\x49\x00\xfe\x3a\xf4\x31\x61\x06\x4a\x4f\x85\xef\x6a\x0d\xa0\x74\xed\x53\x33\x96\xc6\xf5\x57\x5b\xf7\x43\xc3\xb1\x95\xe5\xd1\x8a\x86\x1c\xa0\x1a\x3b\x87\x27\x96\xf4\xda\x25\x8e\x1d\x05\xff\xc5\x07\x1f\xdd\x18\x28\xbe\x71\xc5\x91\x3b\xc7\x59\xc2\x7f\x3f\x1f\x9b\x53\x8e\x77\x50\xd6\xe2\x3b\x33\x36\x29\x0d\x52\x58\xfb\xe7\x92\x17\x19\x59\xec\x76\xbd\x31\xa0\x5a\x31\xc7\xd0\x97\x14\xcc\x81\x42\xc8\x34\xdf\x28\xe8\x17\xbc\x80\xba\x51\xc4\x27\xdd\xae\x37\x84\x7b\xbc\x3f\x76\xbf\xcb\x72\x2a\x57\xb4\x2a\x16\x36\x91\x39\x98\x09\xcc\x78\x77\xc9\x41\x9d\x71\x8f\xcb\xe2\x91\x53\xa6\x67\xae\x73\xf3\xbc\xb9\x22\xc7\x59\x0e\x0f\xf8\x0e\x00\x00\xff\xff\x8a\xd5\xbd\x72\x73\x01\x00\x00"), }, "/nosync": &vfsgen۰DirInfo{ name: "nosync", - modTime: time.Date(2019, 3, 5, 13, 38, 20, 257702305, time.UTC), + modTime: time.Date(2020, 2, 20, 21, 0, 24, 522622677, time.UTC), }, "/nosync/map.go": &vfsgen۰CompressedFileInfo{ name: "map.go", - modTime: time.Date(2019, 1, 3, 14, 55, 7, 233338323, time.UTC), + modTime: time.Date(2020, 2, 20, 21, 0, 24, 522253990, time.UTC), uncompressedSize: 1958, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x55\x4d\x8f\xdb\x46\x0c\x3d\x5b\xbf\x82\x3d\x55\x2e\x14\xe7\x9e\x62\x0f\x05\x7a\x29\xd0\x34\x40\xdb\x5b\x90\x03\x2d\x71\xac\x81\xe7\x43\x1d\x52\xeb\x2a\x8b\xfd\xef\x05\x39\xb2\x57\xde\x24\x45\x0f\xbd\xd9\x23\x0e\xf9\xf8\xde\x23\x67\xc2\xfe\x8c\x27\x82\x94\x79\x49\x7d\xd3\xbc\x7d\x0b\xef\x71\x02\xcf\x80\xd0\xe7\xd4\xcf\xa5\x50\x12\x88\x38\xc1\xc5\xcb\x08\x18\x73\x11\xff\x99\x86\x37\x7d\x4e\x2c\x98\xe4\x8d\xf8\x48\x10\x32\x0e\xdc\x01\x4b\x2e\xc4\x1d\x60\x1a\x60\xa0\x40\x42\x7c\xd0\x9c\xbf\x88\xa6\x64\x74\x04\x2e\x17\x88\x73\x10\x3f\x05\x82\x53\x2e\x79\x16\x9f\x88\x41\x32\xf4\x18\x02\xa0\x02\xf8\x9e\x21\x92\x8c\x79\xe0\x0d\x8a\xb0\x68\x2e\x4d\xf7\xe7\x48\xf0\x99\x4a\xbe\x62\x7d\xc4\xe0\x07\x2b\x4a\x71\x92\x5b\xd8\x4f\xf6\x3d\xce\x2c\x90\xb2\xc0\x91\xa0\xcf\x93\xa7\x01\xd0\x09\x15\x70\xbe\xb0\xc0\xcc\x74\x68\x64\x99\xc8\x82\x59\xca\xdc\x0b\x3c\x35\xbb\xa8\x4d\x7f\xf4\x49\xa8\x38\xec\xe9\xe9\xf9\xd3\xe6\x77\xf3\x6c\x54\xfd\x9a\x71\x80\x42\x32\x97\xc4\x20\x23\x29\x90\x99\x2a\x0b\x03\xf8\x64\x67\xca\x9d\x36\x8d\x70\xa6\xa5\x83\x5c\x20\xf9\x00\xde\x41\xca\x9a\xa3\x5e\xf1\x0c\x53\x21\xa6\x24\x87\x6b\x83\xf9\x0c\x85\x78\x0e\x02\x3e\x0d\xbe\x47\x21\x86\xcb\x48\x32\x52\x59\x2f\x5d\x90\xc1\xe5\x39\x6d\x4b\x1d\x1a\x37\xa7\x1e\xda\x08\x3f\xbc\xc7\x69\x6f\x10\xdb\x33\x2d\xb0\x41\xbf\x87\x76\xad\xfa\x72\xd6\x69\xbd\x63\xce\x61\xaf\xcd\xdb\x67\x3b\x7a\x80\x78\x88\x1f\xcf\xb4\x7c\x6a\x76\xb5\x53\xb8\x7d\x5c\x59\xf8\x43\xdb\x05\x26\xd9\x72\x70\xeb\xf8\x35\x20\x8b\x6e\x8d\x8a\x2f\x40\x58\x6d\xef\xb4\x24\x3c\x3c\x18\x4f\x4f\xcd\x6e\x67\x7f\x21\xe2\x99\xda\x7f\xd1\x64\xdf\xec\x9e\x9b\xdd\x15\x2d\x3c\xd4\xf4\x1b\xa5\x3e\x94\x8a\x74\x2b\x18\xfd\xed\x59\x7c\x3a\x6d\x50\xeb\xb1\x11\xe6\xee\x24\xf9\xa0\xc4\x5f\x3c\x53\x07\x5e\x56\xa3\x9b\xe5\xb6\xe9\x4e\xfe\x91\x56\x82\x6e\x3a\xea\x68\xd0\x70\xd3\x92\x41\x8a\x76\xed\x36\x64\xa9\x90\x35\xac\x03\x87\x81\xed\x73\x75\xd1\xd7\xf4\x5c\x1b\xf9\x26\x89\x2d\xf6\x32\x63\xb8\x97\x77\x85\x71\x93\xd8\xbb\x17\x21\xe1\xdd\x8b\xcc\x3f\xea\x7f\x65\xfd\x5e\x6d\x05\x6d\x04\xff\xcf\xf2\xbc\x2a\x63\xdd\xaf\x9a\xfd\x6c\x0b\xe4\xba\x47\xfe\x8b\xb7\xea\x8d\x2f\xed\xfe\x55\x57\xd5\xc2\x86\xaa\x96\x68\xe3\x21\x76\x9a\x76\xbf\x02\xf8\x1d\xd3\x89\x6c\x2b\x31\x38\x60\xfa\x6b\xa6\x24\x1e\x43\x58\x0c\x02\x61\x3f\x9a\x53\xd4\x05\x15\xd9\x6a\x98\xbb\x79\xd4\xf5\xe7\xc0\xdd\x7c\x62\x2d\x76\x50\x2c\x39\x4b\x9e\x6a\x6b\x5e\xa8\xa0\xf8\x9c\xae\xdb\xab\x56\x1f\x32\xb1\x6d\xaf\x44\x3d\x31\x63\xf1\x61\x81\x3e\x97\x42\x3c\xe5\x34\xe8\xda\xc4\xa4\x27\x89\x3d\x8b\xd6\xe6\x84\x13\x8f\x59\x20\x57\x8b\xd9\x3a\xd5\x84\x7d\x4e\x1a\xc0\xef\x20\x65\xc3\x7d\xf1\x21\xe8\x56\x7c\xf4\xec\x85\x06\x88\x3a\x1d\x32\x62\x82\x9c\x7a\xea\xe0\x38\xcb\xbd\x4f\x8d\xf8\xb4\xe8\x65\x4d\xa8\x2b\xbd\xae\xba\x5c\x56\x99\x86\xbb\x7d\xdd\xad\x4d\x44\x5c\xa0\x90\x0b\xd4\x8b\xdd\x8f\x38\x4d\x3a\x74\x75\xdc\x50\xae\x09\x5d\xc9\xd1\x02\xa6\xec\x93\xc0\x30\x17\x8d\xd2\xfa\x2f\x52\xdc\xd3\xa3\x99\x8f\x04\x1f\xda\xdf\xf6\xf5\x81\xd2\xe0\x34\xc7\x23\x15\xed\x9f\x02\x45\x6d\x79\xbb\x8b\x49\x47\xd4\x6f\x14\xb1\xca\x36\x75\xf5\x5d\xb0\x97\xcf\xde\xb6\x4d\x26\x73\xc1\x6b\xbf\x19\x86\xd6\x81\x9e\x7e\x73\x1a\x6f\x13\xa7\xdd\x9e\x3b\x78\xd4\x69\xab\xea\xab\x23\xd5\x8a\xde\xc1\x77\xae\xd5\x6f\x16\xb8\xdb\x1d\x0b\xe1\xb9\xd9\xa9\x37\xf5\xad\xf9\x27\x00\x00\xff\xff\xe8\x19\x65\x16\xa6\x07\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xac\x55\x4d\x8f\xdb\x46\x0c\x3d\x5b\xbf\x82\x3d\x55\x2e\x14\xe7\x9e\x62\x0f\x05\x7a\x29\xd0\x34\x40\xdb\x5b\x90\x03\x2d\x71\xac\x81\xe7\x43\x1d\x52\xeb\x2a\x8b\xfd\xef\x05\x39\xb2\x57\xde\x24\x45\x0f\xbd\xd9\x23\x0e\xf9\xf8\xde\x23\x67\xc2\xfe\x8c\x27\x82\x94\x79\x49\x7d\xd3\xbc\x7d\x0b\xef\x71\x02\xcf\x80\xd0\xe7\xd4\xcf\xa5\x50\x12\x88\x38\xc1\xc5\xcb\x08\x18\x73\x11\xff\x99\x86\x37\x7d\x4e\x2c\x98\xe4\x8d\xf8\x48\x10\x32\x0e\xdc\x01\x4b\x2e\xc4\x1d\x60\x1a\x60\xa0\x40\x42\x7c\xd0\x9c\xbf\x88\xa6\x64\x74\x04\x2e\x17\x88\x73\x10\x3f\x05\x82\x53\x2e\x79\x16\x9f\x88\x41\x32\xf4\x18\x02\xa0\x02\xf8\x9e\x21\x92\x8c\x79\xe0\x0d\x8a\xb0\x68\x2e\x4d\xf7\xe7\x48\xf0\x99\x4a\xbe\x62\x7d\xc4\xe0\x07\x2b\x4a\x71\x92\x5b\xd8\x4f\xf6\x3d\xce\x2c\x90\xb2\xc0\x91\xa0\xcf\x93\xa7\x01\xd0\x09\x15\x70\xbe\xb0\xc0\xcc\x74\x68\x64\x99\xc8\x82\x59\xca\xdc\x0b\x3c\x35\xbb\xa8\x4d\x7f\xf4\x49\xa8\x38\xec\xe9\xe9\xf9\xd3\xe6\x77\xf3\x6c\x54\xfd\x9a\x71\x80\x42\x32\x97\xc4\x20\x23\x29\x90\x99\x2a\x0b\x03\xf8\x64\x67\xca\x9d\x36\x8d\x70\xa6\xa5\x83\x5c\x20\xf9\x00\xde\x41\xca\x9a\xa3\x5e\xf1\x0c\x53\x21\xa6\x24\x87\x6b\x83\xf9\x0c\x85\x78\x0e\x02\x3e\x0d\xbe\x47\x21\x86\xcb\x48\x32\x52\x59\x2f\x5d\x90\xc1\xe5\x39\x6d\x4b\x1d\x1a\x37\xa7\x1e\xda\x08\x3f\xbc\xc7\x69\x6f\x10\xdb\x33\x2d\xb0\x41\xbf\x87\x76\xad\xfa\x72\xd6\x69\xbd\x63\xce\x61\xaf\xcd\xdb\x67\x3b\x7a\x80\x78\x88\x1f\xcf\xb4\x7c\x6a\x76\xb5\x53\xb8\x7d\x5c\x59\xf8\x43\xdb\x05\x26\xd9\x72\x70\xeb\xf8\x35\x20\x8b\x6e\x8d\x8a\x2f\x40\x58\x6d\xef\xb4\x24\x3c\x3c\x18\x4f\x4f\xcd\x6e\x67\x7f\x21\xe2\x99\xda\x7f\xd1\x64\xdf\xec\x9e\x9b\xdd\x15\x2d\x3c\xd4\xf4\x1b\xa5\x3e\x94\x8a\x74\x2b\x18\xfd\xed\x59\x7c\x3a\x6d\x50\xeb\xb1\x11\xe6\xee\x24\xf9\xa0\xc4\x5f\x3c\x53\x07\x5e\x56\xa3\x9b\xe5\xb6\xe9\x4e\xfe\x91\x56\x82\x6e\x3a\xea\x68\xd0\x70\xd3\x92\x41\x8a\x76\xed\x36\x64\xa9\x90\x35\xac\x03\x87\x81\xed\x73\x75\xd1\xd7\xf4\x5c\x1b\xf9\x26\x89\x2d\xf6\x32\x63\xb8\x97\x77\x85\x71\x93\xd8\xbb\x17\x21\xe1\xdd\x8b\xcc\x3f\xea\x7f\x65\xfd\x5e\x6d\x05\x6d\x04\xff\xcf\xf2\xbc\x2a\x63\xdd\xaf\x9a\xfd\x6c\x0b\xe4\xba\x47\xfe\x8b\xb7\xea\x8d\x2f\xed\xfe\x55\x57\xd5\xc2\x86\xaa\x96\x68\xe3\x21\x76\x9a\x76\xbf\x02\xf8\x1d\xd3\x89\x6c\x2b\x31\x38\x60\xfa\x6b\xa6\x24\x1e\x43\x58\x0c\x02\x61\x3f\x9a\x53\xd4\x05\x15\xd9\x6a\x98\xbb\x79\xd4\xf5\xe7\xc0\xdd\x7c\x62\x2d\x76\x50\x2c\x39\x4b\x9e\x6a\x6b\x5e\xa8\xa0\xf8\x9c\xae\xdb\xab\x56\x1f\x32\xb1\x6d\xaf\x44\x3d\x31\x63\xf1\x61\x81\x3e\x97\x42\x3c\xe5\x34\xe8\xda\xc4\xa4\x27\x89\x3d\x8b\xd6\xe6\x84\x13\x8f\x59\x20\x57\x8b\xd9\x3a\xd5\x84\x7d\x4e\x1a\xc0\xef\x20\x65\xc3\x7d\xf1\x21\xe8\x56\x7c\xf4\xec\x85\x06\x88\x3a\x1d\x32\x62\x82\x9c\x7a\xea\xe0\x38\xcb\xbd\x4f\x8d\xf8\xb4\xe8\x65\x4d\xa8\x2b\xbd\xae\xba\x5c\x56\x99\x86\xbb\x7d\xdd\xad\x4d\x44\x5c\xa0\x90\x0b\xd4\x8b\xdd\x8f\x38\x4d\x3a\x74\x75\xdc\x50\xae\x09\x5d\xc9\xd1\x02\xa6\xec\x93\xc0\x30\x17\x8d\xd2\xfa\x2f\x52\xdc\xd3\xa3\x99\x8f\x04\x1f\xda\xdf\xf6\xf5\x81\xd2\xe0\x34\xc7\x23\x15\xed\x9f\x02\x45\x6d\x79\xbb\x8b\x49\x47\xd4\x6f\x14\xb1\xca\x36\x75\xf5\x5d\xb0\x97\xcf\xde\xb6\x4d\x26\x73\xc1\x6b\xbf\x19\x86\xd6\x81\x9e\x7e\x73\x1a\x6f\x13\xa7\xdd\x9e\x3b\x78\xd4\x69\xab\xea\xab\x23\xd5\x8a\xde\xc1\x77\xae\xd5\x6f\x16\xb8\xdb\x1d\x0b\xe1\xb9\xd9\xa9\x37\xf5\xad\xf9\x27\x00\x00\xff\xff\xe8\x19\x65\x16\xa6\x07\x00\x00"), }, "/nosync/mutex.go": &vfsgen۰CompressedFileInfo{ name: "mutex.go", - modTime: time.Date(2019, 3, 5, 13, 38, 20, 257752198, time.UTC), + modTime: time.Date(2020, 2, 20, 21, 0, 24, 522431020, time.UTC), uncompressedSize: 2073, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x54\xcb\x6e\xdb\x30\x10\x3c\x4b\x5f\xb1\xc9\xc9\x4e\x62\xa5\xbd\xb6\xf5\xa1\x68\x81\x22\x40\x7a\x09\x50\xe4\x4c\x53\x2b\x99\xb0\x44\x1a\x24\x55\xd5\x4d\xf2\xef\xc5\xf2\x21\xcb\x92\xec\xc4\x2d\xaa\x93\xb0\xe4\xce\xce\xec\x0c\xb8\x65\x7c\xc3\x4a\x04\xa9\xcc\x4e\xf2\x34\xbd\xbd\x85\xef\x8d\xc5\x5f\x20\x0c\x30\xc8\x9b\xba\xde\x41\xbb\x16\x7c\x4d\x05\xa9\xe4\x62\x55\x29\xbe\x11\xb2\xcc\x52\xbb\xdb\x62\xb8\x6c\xac\x6e\xb8\x85\xa7\x34\xa1\x53\xcc\x61\xa5\x54\x95\xbe\x38\xb8\x7b\xc5\x37\x40\x65\x03\x75\x06\x77\xd6\x23\xeb\x46\x2e\xac\xa8\x11\x50\x6b\xa5\x41\x14\x50\xbb\x83\x4a\x23\xcb\x77\xe0\x61\xb2\xb4\x68\x24\x87\x59\x0d\x57\x6e\xce\xdc\x81\xcd\xe6\x34\x88\x3a\xb2\x30\xed\x29\x4d\x92\x2d\x93\x82\xcf\x2e\xbd\x8e\x0f\x50\x77\x22\x0e\x10\x2f\xe7\x69\xf2\x92\x26\x5d\xe7\x12\xac\x6e\x30\x30\xfd\x21\xa9\x0a\x8d\x7c\x2b\x5b\xa9\xec\x51\xa6\x1e\xac\xe3\x7a\x71\x8a\xac\x9f\x08\xaa\x08\x7f\x98\x7b\xfe\x63\xb6\x05\xab\x4c\xa4\xfb\xf0\x78\x96\x53\xf1\xfa\xde\xab\x56\x0b\x8b\xf7\x1e\x9a\x3e\x67\x5a\x42\xeb\xa2\xe2\x17\xd5\x48\x8b\x1a\x84\xb4\x13\x4e\x42\xa1\x34\x10\x00\x0d\x38\xb1\x27\xdd\x8e\x4d\x70\xbd\x54\x10\xb2\x84\x1e\x4c\xd8\xa1\x6e\xe1\x2a\x90\x1d\x18\xae\xdb\x6c\xc8\xee\x62\x09\xef\xe0\xf9\x99\x8e\xfa\x72\xce\x4e\xc4\xa0\xff\x54\x2e\x74\x7b\x9e\xf8\x7d\x4a\x0e\xfa\xa6\xd4\x0e\x43\xf3\xba\xaa\x57\xa2\x33\x92\x75\x10\xa0\x91\xa1\xc1\x94\xff\x69\xe8\xc3\xd0\xd1\x7f\xb5\x6d\x90\x88\xeb\xeb\xa8\xae\xb3\x2d\x57\x48\x5a\x8c\x90\x65\x85\x41\x35\x67\x55\xf5\x11\x84\x05\x77\x48\x16\xb1\xa2\x40\x6e\x41\xd9\x35\x6a\x30\xa2\x6e\x2a\xcb\x24\xaa\xc6\x38\x65\xa8\xcd\xd9\x4e\xc7\x6d\x4e\xae\x61\x60\xf5\x44\xb4\x97\x14\xed\xbf\xb2\x7c\x80\xb4\x58\x84\x95\x3c\x32\x61\xbf\x69\xd5\x6c\xdf\xfa\x66\xec\x1b\xf6\xaf\x06\x1f\xbd\x0b\x9f\xf3\x1c\x58\x9e\x1b\xc8\xb1\xb2\xec\x26\x20\xd6\x6c\x07\x2b\x04\x89\x25\xb3\xe2\x27\xde\x80\x55\x60\xd7\x7d\xcc\xbb\xc2\x15\x22\x60\xe9\x9c\xe8\xae\x13\xaa\x53\x6e\xe2\x02\xdb\x12\xae\xba\xee\x39\x5d\x98\xb9\x89\x44\xc5\xed\xb1\x2d\xb3\x08\x76\xbd\xf4\x6c\xdc\x72\x7b\xf5\x4f\x87\x3b\xf5\x1b\x8d\x43\x7b\xdc\xc2\x7d\xbf\x53\x2f\xf3\xab\x92\x08\x39\x72\x8d\x35\x4a\x6b\x06\x62\x42\xc3\x11\xae\xd4\x3b\x8b\x1c\x89\xf8\xe2\xfd\xbc\x67\x4a\x10\x4a\x49\x9a\x44\x8d\xe1\xfa\x8d\x5a\x1d\x99\x40\xbf\x5d\x9a\x7a\x82\x2f\x96\x53\x8a\xc7\x13\x22\x7c\x54\xfc\x27\x00\x00\xff\xff\xec\x95\x29\x83\x19\x08\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xb4\x54\xcb\x6e\xdb\x30\x10\x3c\x4b\x5f\xb1\xc9\xc9\x4e\x62\xa5\xbd\xb6\xf5\xa1\x68\x81\x22\x40\x7a\x09\x50\xe4\x4c\x53\x2b\x99\xb0\x44\x1a\x24\x55\xd5\x4d\xf2\xef\xc5\xf2\x21\xcb\x92\xec\xc4\x2d\xaa\x93\xb0\xe4\xce\xce\xec\x0c\xb8\x65\x7c\xc3\x4a\x04\xa9\xcc\x4e\xf2\x34\xbd\xbd\x85\xef\x8d\xc5\x5f\x20\x0c\x30\xc8\x9b\xba\xde\x41\xbb\x16\x7c\x4d\x05\xa9\xe4\x62\x55\x29\xbe\x11\xb2\xcc\x52\xbb\xdb\x62\xb8\x6c\xac\x6e\xb8\x85\xa7\x34\xa1\x53\xcc\x61\xa5\x54\x95\xbe\x38\xb8\x7b\xc5\x37\x40\x65\x03\x75\x06\x77\xd6\x23\xeb\x46\x2e\xac\xa8\x11\x50\x6b\xa5\x41\x14\x50\xbb\x83\x4a\x23\xcb\x77\xe0\x61\xb2\xb4\x68\x24\x87\x59\x0d\x57\x6e\xce\xdc\x81\xcd\xe6\x34\x88\x3a\xb2\x30\xed\x29\x4d\x92\x2d\x93\x82\xcf\x2e\xbd\x8e\x0f\x50\x77\x22\x0e\x10\x2f\xe7\x69\xf2\x92\x26\x5d\xe7\x12\xac\x6e\x30\x30\xfd\x21\xa9\x0a\x8d\x7c\x2b\x5b\xa9\xec\x51\xa6\x1e\xac\xe3\x7a\x71\x8a\xac\x9f\x08\xaa\x08\x7f\x98\x7b\xfe\x63\xb6\x05\xab\x4c\xa4\xfb\xf0\x78\x96\x53\xf1\xfa\xde\xab\x56\x0b\x8b\xf7\x1e\x9a\x3e\x67\x5a\x42\xeb\xa2\xe2\x17\xd5\x48\x8b\x1a\x84\xb4\x13\x4e\x42\xa1\x34\x10\x00\x0d\x38\xb1\x27\xdd\x8e\x4d\x70\xbd\x54\x10\xb2\x84\x1e\x4c\xd8\xa1\x6e\xe1\x2a\x90\x1d\x18\xae\xdb\x6c\xc8\xee\x62\x09\xef\xe0\xf9\x99\x8e\xfa\x72\xce\x4e\xc4\xa0\xff\x54\x2e\x74\x7b\x9e\xf8\x7d\x4a\x0e\xfa\xa6\xd4\x0e\x43\xf3\xba\xaa\x57\xa2\x33\x92\x75\x10\xa0\x91\xa1\xc1\x94\xff\x69\xe8\xc3\xd0\xd1\x7f\xb5\x6d\x90\x88\xeb\xeb\xa8\xae\xb3\x2d\x57\x48\x5a\x8c\x90\x65\x85\x41\x35\x67\x55\xf5\x11\x84\x05\x77\x48\x16\xb1\xa2\x40\x6e\x41\xd9\x35\x6a\x30\xa2\x6e\x2a\xcb\x24\xaa\xc6\x38\x65\xa8\xcd\xd9\x4e\xc7\x6d\x4e\xae\x61\x60\xf5\x44\xb4\x97\x14\xed\xbf\xb2\x7c\x80\xb4\x58\x84\x95\x3c\x32\x61\xbf\x69\xd5\x6c\xdf\xfa\x66\xec\x1b\xf6\xaf\x06\x1f\xbd\x0b\x9f\xf3\x1c\x58\x9e\x1b\xc8\xb1\xb2\xec\x26\x20\xd6\x6c\x07\x2b\x04\x89\x25\xb3\xe2\x27\xde\x80\x55\x60\xd7\x7d\xcc\xbb\xc2\x15\x22\x60\xe9\x9c\xe8\xae\x13\xaa\x53\x6e\xe2\x02\xdb\x12\xae\xba\xee\x39\x5d\x98\xb9\x89\x44\xc5\xed\xb1\x2d\xb3\x08\x76\xbd\xf4\x6c\xdc\x72\x7b\xf5\x4f\x87\x3b\xf5\x1b\x8d\x43\x7b\xdc\xc2\x7d\xbf\x53\x2f\xf3\xab\x92\x08\x39\x72\x8d\x35\x4a\x6b\x06\x62\x42\xc3\x11\xae\xd4\x3b\x8b\x1c\x89\xf8\xe2\xfd\xbc\x67\x4a\x10\x4a\x49\x9a\x44\x8d\xe1\xfa\x8d\x5a\x1d\x99\x40\xbf\x5d\x9a\x7a\x82\x2f\x96\x53\x8a\xc7\x13\x22\x7c\x54\xfc\x27\x00\x00\xff\xff\xec\x95\x29\x83\x19\x08\x00\x00"), }, "/nosync/once.go": &vfsgen۰CompressedFileInfo{ name: "once.go", - modTime: time.Date(2019, 1, 3, 14, 55, 7, 233609287, time.UTC), + modTime: time.Date(2020, 2, 20, 21, 0, 24, 522558512, time.UTC), uncompressedSize: 1072, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x53\xcb\x92\xda\x40\x0c\x3c\xdb\x5f\xd1\xb5\x27\x9c\xa2\xe0\xbe\xa9\x1c\x52\xc5\x65\x4f\x39\xe4\x0b\xc4\x58\x03\xca\x0e\x1a\x32\x0f\x58\x67\x8b\x7f\x4f\x69\x6c\x08\xb9\xd9\x92\xba\xd5\x6a\x69\xce\xe4\xde\xe9\xc0\xd0\x98\x27\x75\x7d\xbf\xdd\xe2\x87\x3a\x86\x64\x90\x22\xee\x7f\xb1\x2b\x28\x47\x2a\xb8\x4a\x08\x38\x73\xf2\x31\x9d\xc0\x1f\xe4\x4a\x98\x10\x95\x41\xae\x48\xd4\x4d\x5f\xa6\x33\xcf\xe0\x5c\x52\x75\x05\x9f\x7d\x37\x46\xd1\x03\xf6\x31\x06\xfb\x56\xc6\xfc\x7d\x6b\x8d\x76\x11\x8e\x42\xc8\x28\x47\x86\xaf\xda\x78\xe0\x21\x1e\xa4\x23\xa2\x86\xc9\xbe\x77\xd1\xd4\xec\xd9\x98\xac\x9e\x47\xf8\x98\x0c\x64\x24\x5e\x52\x2e\x28\x72\xe2\x25\x2a\x19\xa2\xb9\x90\x09\x89\xbe\x09\xda\xe0\x4d\x11\xcb\x91\x13\xae\x31\x8d\x79\x8d\x83\x5c\x58\x0d\xde\x5d\x28\x21\x5a\xad\x15\x5a\x44\x7c\xfb\xdf\xec\xe2\xca\x0f\xd6\x79\xe9\x79\xaa\xa1\xc8\x39\x70\xeb\x95\xd7\xb3\xbc\xa6\xbc\x29\xb0\xaa\xd9\x23\xd1\x4b\x7c\x67\xf8\xb5\xb1\xf1\x85\xd5\x28\x3d\x8e\x94\x41\x18\xc5\x7b\x4e\xac\x05\x17\x0a\x95\x21\x0a\x26\x77\x6c\x20\x47\xcd\x48\xe0\x3b\x94\xaf\xcf\x53\x3c\xaf\x25\xf1\xef\x2a\x69\x31\xa1\x61\x1f\xd6\x95\x08\xfe\x60\x57\x0b\x6f\xfa\xed\x76\xb1\xb8\xf9\x51\x58\xc7\x05\x22\x2a\x45\x28\xc8\x1f\x9a\x31\xb6\xdb\x53\xcd\x05\x7b\x46\xaa\xfa\xb4\x5a\x33\x0e\x3f\xc5\xfa\x36\x05\x92\xa1\x12\x68\x14\xb7\x86\x14\x9c\x68\x32\x8c\xb2\xe3\x9c\x29\x4d\xd6\xbe\x66\x06\xfd\x13\x14\xa4\x70\xa2\x60\x19\x47\xe7\x52\x13\xdf\xd7\x46\xe9\x50\x4f\xac\x25\x5b\x8e\xfe\x1b\x61\xcf\x8b\x85\x23\xf6\x13\x76\xf1\xb5\xed\xc9\x45\xf5\x72\xd8\x3c\x56\x53\xd5\xad\x06\x7c\x62\x89\xdb\x54\x2b\x2f\x81\x95\x4e\x3c\xe0\x36\x2c\x06\xbc\x99\xf5\x8e\x6a\xe6\x6c\x66\xcc\xf4\xf3\x46\xdb\x10\xf3\x55\x93\x8a\xdb\x3c\x23\x5a\x24\xaf\xdb\x89\x46\xcd\x32\x72\xca\x56\x5e\x22\x8e\x74\x61\x24\x2e\x35\x29\x8f\x5f\xe1\x6b\x1b\x6b\x3e\xe4\xd8\xae\x75\x4e\x1a\xd7\x55\xca\x31\xd6\xf9\x38\xec\x7c\x7d\x6b\x62\xda\xb1\x8a\xf8\x62\x2b\x1d\x60\xd3\x60\x9e\x67\xb0\x37\x63\x07\xb8\x69\x8f\xe5\xb3\xef\xba\x85\xac\xbb\x3d\x12\x46\x64\x99\xa6\x71\xf5\x32\xbf\xdc\xd7\xfb\x6b\xe2\xb1\x75\x15\x85\x7f\x19\x1a\xec\x8e\xf9\x86\x92\x2a\xf7\xdd\xc8\x9e\x13\xee\x06\xf6\xdd\x53\x81\xa7\x90\x79\x89\x28\x3f\x10\xb7\xd5\xd0\x77\x7e\x35\xf4\xb7\xfe\x6f\x00\x00\x00\xff\xff\xf9\x72\xbe\xa9\x30\x04\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x54\x53\xcb\x92\xda\x40\x0c\x3c\xdb\x5f\xd1\xb5\x27\x9c\xa2\xe0\xbe\xa9\x1c\x52\xc5\x65\x4f\x39\xe4\x0b\xc4\x58\x03\xca\x0e\x1a\x32\x0f\x58\x67\x8b\x7f\x4f\x69\x6c\x08\xb9\xd9\x92\xba\xd5\x6a\x69\xce\xe4\xde\xe9\xc0\xd0\x98\x27\x75\x7d\xbf\xdd\xe2\x87\x3a\x86\x64\x90\x22\xee\x7f\xb1\x2b\x28\x47\x2a\xb8\x4a\x08\x38\x73\xf2\x31\x9d\xc0\x1f\xe4\x4a\x98\x10\x95\x41\xae\x48\xd4\x4d\x5f\xa6\x33\xcf\xe0\x5c\x52\x75\x05\x9f\x7d\x37\x46\xd1\x03\xf6\x31\x06\xfb\x56\xc6\xfc\x7d\x6b\x8d\x76\x11\x8e\x42\xc8\x28\x47\x86\xaf\xda\x78\xe0\x21\x1e\xa4\x23\xa2\x86\xc9\xbe\x77\xd1\xd4\xec\xd9\x98\xac\x9e\x47\xf8\x98\x0c\x64\x24\x5e\x52\x2e\x28\x72\xe2\x25\x2a\x19\xa2\xb9\x90\x09\x89\xbe\x09\xda\xe0\x4d\x11\xcb\x91\x13\xae\x31\x8d\x79\x8d\x83\x5c\x58\x0d\xde\x5d\x28\x21\x5a\xad\x15\x5a\x44\x7c\xfb\xdf\xec\xe2\xca\x0f\xd6\x79\xe9\x79\xaa\xa1\xc8\x39\x70\xeb\x95\xd7\xb3\xbc\xa6\xbc\x29\xb0\xaa\xd9\x23\xd1\x4b\x7c\x67\xf8\xb5\xb1\xf1\x85\xd5\x28\x3d\x8e\x94\x41\x18\xc5\x7b\x4e\xac\x05\x17\x0a\x95\x21\x0a\x26\x77\x6c\x20\x47\xcd\x48\xe0\x3b\x94\xaf\xcf\x53\x3c\xaf\x25\xf1\xef\x2a\x69\x31\xa1\x61\x1f\xd6\x95\x08\xfe\x60\x57\x0b\x6f\xfa\xed\x76\xb1\xb8\xf9\x51\x58\xc7\x05\x22\x2a\x45\x28\xc8\x1f\x9a\x31\xb6\xdb\x53\xcd\x05\x7b\x46\xaa\xfa\xb4\x5a\x33\x0e\x3f\xc5\xfa\x36\x05\x92\xa1\x12\x68\x14\xb7\x86\x14\x9c\x68\x32\x8c\xb2\xe3\x9c\x29\x4d\xd6\xbe\x66\x06\xfd\x13\x14\xa4\x70\xa2\x60\x19\x47\xe7\x52\x13\xdf\xd7\x46\xe9\x50\x4f\xac\x25\x5b\x8e\xfe\x1b\x61\xcf\x8b\x85\x23\xf6\x13\x76\xf1\xb5\xed\xc9\x45\xf5\x72\xd8\x3c\x56\x53\xd5\xad\x06\x7c\x62\x89\xdb\x54\x2b\x2f\x81\x95\x4e\x3c\xe0\x36\x2c\x06\xbc\x99\xf5\x8e\x6a\xe6\x6c\x66\xcc\xf4\xf3\x46\xdb\x10\xf3\x55\x93\x8a\xdb\x3c\x23\x5a\x24\xaf\xdb\x89\x46\xcd\x32\x72\xca\x56\x5e\x22\x8e\x74\x61\x24\x2e\x35\x29\x8f\x5f\xe1\x6b\x1b\x6b\x3e\xe4\xd8\xae\x75\x4e\x1a\xd7\x55\xca\x31\xd6\xf9\x38\xec\x7c\x7d\x6b\x62\xda\xb1\x8a\xf8\x62\x2b\x1d\x60\xd3\x60\x9e\x67\xb0\x37\x63\x07\xb8\x69\x8f\xe5\xb3\xef\xba\x85\xac\xbb\x3d\x12\x46\x64\x99\xa6\x71\xf5\x32\xbf\xdc\xd7\xfb\x6b\xe2\xb1\x75\x15\x85\x7f\x19\x1a\xec\x8e\xf9\x86\x92\x2a\xf7\xdd\xc8\x9e\x13\xee\x06\xf6\xdd\x53\x81\xa7\x90\x79\x89\x28\x3f\x10\xb7\xd5\xd0\x77\x7e\x35\xf4\xb7\xfe\x6f\x00\x00\x00\xff\xff\xf9\x72\xbe\xa9\x30\x04\x00\x00"), }, "/nosync/pool.go": &vfsgen۰CompressedFileInfo{ name: "pool.go", - modTime: time.Date(2019, 1, 3, 14, 55, 7, 233714234, time.UTC), + modTime: time.Date(2020, 2, 20, 21, 0, 24, 522648878, time.UTC), uncompressedSize: 2130, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\x55\x3f\x93\xdb\xc6\x0f\xad\x4f\x9f\x02\xbf\xea\x77\xca\xe8\x74\x49\xeb\x99\x2b\x32\x29\x1c\x37\x89\x8b\x74\x1e\x17\x10\x09\x8a\x88\x97\x0b\x06\xc0\x4a\xa2\x3d\xf7\xdd\x33\x58\xfe\x39\x39\xee\x44\xee\xf2\xe1\xe1\xbd\x07\x68\xc4\xe6\x0b\x9e\x09\xb2\xd8\x94\x9b\xdd\xee\xf9\x19\x7e\x85\x8f\x22\x09\xd8\x00\xc1\xc8\x41\x3a\x70\x1a\x46\x51\xd4\x09\xe4\xf4\x37\x35\x6e\xe0\x3d\x3a\x0c\x38\xc1\x89\x80\x73\xcb\x17\x6e\x0b\xa6\x34\x81\xe1\x85\x5a\xc0\xdc\x06\x94\x92\x2b\xd3\x85\xda\xe3\xee\xf9\xb9\x62\xe7\x09\xd8\x69\x00\x73\x51\x6a\x81\x33\x78\x4f\x73\xc1\x05\x4d\x69\x90\x0a\x51\x5c\x06\x74\x6e\x2a\x2c\x3a\x60\x9e\xc0\x79\x20\xb8\xb2\xf7\x52\x3c\xf0\xb2\x38\x77\xdc\xa0\xb3\xe4\x23\x7c\xe8\xde\xd0\x7a\x49\xad\xd5\x47\xc9\x69\x02\xa5\x8e\x94\x72\x43\x70\xed\x29\x8a\xb2\x41\x8f\xe3\x48\xd9\x0e\x71\x2b\xc0\x2a\xb1\x81\xcf\xbd\x07\x8f\x96\x30\x25\x69\xd0\xef\xd8\x6f\xca\x18\x76\x04\x9d\x28\x14\x23\x38\x4d\x30\x94\xe4\x3c\x26\x82\xb3\xa8\x14\xe7\x4c\x06\xc6\xf1\x16\x33\x49\xb1\x34\xad\x18\x81\xf0\x7f\x83\xb1\xe8\x28\x46\x81\xe5\x02\x0d\x36\x3d\xc1\x56\x0f\x4e\xc5\xa1\xe4\x62\xa1\x90\xd3\x60\xb5\x54\x42\x27\x05\xa5\x62\x74\x98\xc5\x4d\x4c\x17\xce\x67\x18\x95\xcc\x8a\x46\xab\xb5\xe3\x33\xea\x29\x4c\x6d\x24\x25\x6a\x5c\xf4\x08\x7f\x85\x5f\x6c\x07\xe0\xb0\xed\x0b\x59\xfc\x20\xb4\x09\x5c\x02\xec\x54\x38\xb5\x40\x5d\xc7\x0d\x53\xf6\xd0\x44\x09\xdb\xa7\xb9\x51\x25\x82\xc4\xe6\x76\x84\xdf\xe5\x4a\x17\xd2\x0a\xc4\x16\x06\x80\x15\x76\x3c\xa5\x59\x10\x4c\x29\xf0\xee\x3e\xd9\xac\x07\x1c\x47\x95\x51\x19\x9d\xaa\x70\xd2\x01\x6e\x92\xba\xc0\x80\x39\x68\x23\x9c\x55\xca\xf8\x7d\xf0\xaa\x0e\x81\x63\x9c\x28\x7b\x24\xad\xc7\x88\x10\x0e\x92\xcf\x11\x38\x18\xc5\x29\x3b\xd7\xbc\x54\x99\xda\xb0\xa6\x91\xdc\x14\x55\xca\x1e\x41\xa5\x91\x72\x4b\xb9\x86\xa7\x49\xd1\xaa\xcd\x34\x96\x41\x38\xce\x7c\x46\x95\x0b\xb7\x14\x23\x70\xc5\xd0\x28\xca\xa8\xf3\xd7\xcd\x25\x96\x0c\x72\x21\xed\x09\x6b\xd4\xb1\x51\x31\x8b\x16\xa6\x15\xf8\xae\x73\xba\xe1\x10\xf1\x90\x0e\xce\x22\xed\x8f\xdd\x2f\x83\xd0\x0d\xbe\x32\x39\xc0\xb5\xe7\xa6\x87\x01\x39\x3b\x72\x36\xc0\x00\x6b\xa7\x8c\xc3\x3c\x14\x4f\xc6\x5f\xa9\x9d\x47\xe9\x3f\x53\x5a\x7c\x2c\x0e\xa7\xd2\x75\xa4\x16\xee\xd3\x72\xcd\x1a\x4c\x64\x50\x72\x4b\x1a\x70\x49\xb0\x85\xc7\x3a\x13\x95\xfa\x5d\x7e\x51\x09\xb0\x71\xbe\x50\x9a\x60\x54\xce\xce\xf9\xbc\xaf\x4a\x5b\xaf\x9c\xbf\x58\x9d\xa5\x40\xf9\xa7\x30\x59\x43\xd9\xd7\x96\xff\x9c\xdb\x11\xef\x49\xa1\xc7\xdc\x1e\x00\xdf\x32\xb1\xf5\x14\xf6\x19\x8c\xa8\x3e\xab\x61\xbd\xa8\x3f\x25\x8e\xf9\x9f\x37\x0d\xb0\x2d\x73\x1e\xc7\x6b\xd0\x42\xbe\x1a\xb6\xaa\xdf\x01\x8c\x63\xb2\x6b\xc5\xc5\x12\x68\x85\xe6\x74\x6e\xc6\x5d\x29\x25\xe0\xca\xb7\x6e\xaf\x20\x8c\xca\x72\x84\x0f\x35\xca\x43\xe8\xb3\x4d\x40\x78\xde\xe3\x85\xc0\x4a\xd3\x6f\x6b\x8f\xc3\xc5\xa1\x1e\xf7\xc4\x0a\x72\xcd\xdf\xa5\xbd\xf6\xef\xd3\xb8\x2c\x21\x73\x2d\x8d\xc3\xb7\xdd\xc3\xac\xfe\xa7\xcf\x9c\x9d\xb4\xc3\x86\xbe\xbd\xee\x1e\xfe\xa0\x2b\x00\x74\x25\x37\x8f\x7b\xb8\x3f\x79\xad\x8b\xf8\x3d\x39\x18\xa5\x5a\x18\x33\xa0\x9e\xd8\xb7\x59\x80\x4e\x65\xd8\xd6\xdd\x61\x59\x9b\x75\xac\xd7\x93\x75\xdd\x1c\xaa\x67\x4a\x5e\x34\xd7\x0b\x2e\xf5\xc3\x08\x11\xe9\x71\x2d\x15\xfb\xb7\xe9\x25\xb6\x92\x0b\xf0\x39\x07\xe3\xb8\x37\x46\x2b\x01\xe1\x4a\xb1\x45\x3c\x4c\xa3\x61\xf4\xba\xd4\xe0\xb7\x0a\x63\x61\x5e\x49\xed\xac\xb9\x59\x19\xa8\x6e\x6c\xa5\x34\x0f\xcb\x89\xfc\x4a\x94\xe1\x82\xa9\x50\x98\x6e\x31\xa0\x2e\xf0\xb1\xf8\xfa\x7f\x11\xd5\x96\xf3\x99\xee\x3c\xc2\xef\x69\x0b\xd6\x87\xae\x72\xbd\xd6\x52\x35\x5e\x57\x36\x5a\x6e\x43\xe6\x99\xe8\x78\x0c\x69\xeb\x7a\xca\x4f\x99\xd3\xa1\x7e\xb4\x28\xb0\x16\x52\xb2\x92\x6a\xf0\x42\x88\xba\x47\xe3\xb3\xe3\x2e\x0c\x81\xc7\x11\x7e\x0a\xf1\xf6\xf1\xe9\xf7\xf6\x84\x9f\xdc\x41\xa2\xfc\x38\x1e\xab\xb1\x7b\x78\x79\x81\x9f\xe3\x7d\x1c\xcc\xd5\xff\xf7\x52\xe9\xc4\xbb\x87\x85\x5e\x3d\x78\xdc\xef\x1e\x1e\x5e\x77\xdb\xcb\xcc\x69\x17\xcf\x37\x78\xf7\x02\x0b\xde\xa7\x7b\xec\xa7\x5f\x3e\xef\x1e\x96\x07\x78\xbb\xf2\xee\x87\x3b\x0b\xe0\x6d\x89\x4f\xd5\xb5\x6d\x0d\x6e\xab\xe1\x61\xe4\x0f\xed\x7d\x2c\xfe\x78\xbb\x6f\x6f\xbf\xf4\x77\x8b\xa6\xd6\x16\x66\xec\x4a\xf4\x8d\x4a\xfd\xff\x6c\x57\x12\x07\xb8\xed\x77\xaf\xbb\x7f\x03\x00\x00\xff\xff\x07\xba\x3e\x57\x52\x08\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\x64\x55\x3f\x93\xdb\xc6\x0f\xad\x4f\x9f\x02\xbf\xea\x77\xca\xe8\x74\x49\xeb\x99\x2b\x32\x29\x1c\x37\x89\x8b\x74\x1e\x17\x10\x09\x8a\x88\x97\x0b\x06\xc0\x4a\xa2\x3d\xf7\xdd\x33\x58\xfe\x39\x39\xee\x44\xee\xf2\xe1\xe1\xbd\x07\x68\xc4\xe6\x0b\x9e\x09\xb2\xd8\x94\x9b\xdd\xee\xf9\x19\x7e\x85\x8f\x22\x09\xd8\x00\xc1\xc8\x41\x3a\x70\x1a\x46\x51\xd4\x09\xe4\xf4\x37\x35\x6e\xe0\x3d\x3a\x0c\x38\xc1\x89\x80\x73\xcb\x17\x6e\x0b\xa6\x34\x81\xe1\x85\x5a\xc0\xdc\x06\x94\x92\x2b\xd3\x85\xda\xe3\xee\xf9\xb9\x62\xe7\x09\xd8\x69\x00\x73\x51\x6a\x81\x33\x78\x4f\x73\xc1\x05\x4d\x69\x90\x0a\x51\x5c\x06\x74\x6e\x2a\x2c\x3a\x60\x9e\xc0\x79\x20\xb8\xb2\xf7\x52\x3c\xf0\xb2\x38\x77\xdc\xa0\xb3\xe4\x23\x7c\xe8\xde\xd0\x7a\x49\xad\xd5\x47\xc9\x69\x02\xa5\x8e\x94\x72\x43\x70\xed\x29\x8a\xb2\x41\x8f\xe3\x48\xd9\x0e\x71\x2b\xc0\x2a\xb1\x81\xcf\xbd\x07\x8f\x96\x30\x25\x69\xd0\xef\xd8\x6f\xca\x18\x76\x04\x9d\x28\x14\x23\x38\x4d\x30\x94\xe4\x3c\x26\x82\xb3\xa8\x14\xe7\x4c\x06\xc6\xf1\x16\x33\x49\xb1\x34\xad\x18\x81\xf0\x7f\x83\xb1\xe8\x28\x46\x81\xe5\x02\x0d\x36\x3d\xc1\x56\x0f\x4e\xc5\xa1\xe4\x62\xa1\x90\xd3\x60\xb5\x54\x42\x27\x05\xa5\x62\x74\x98\xc5\x4d\x4c\x17\xce\x67\x18\x95\xcc\x8a\x46\xab\xb5\xe3\x33\xea\x29\x4c\x6d\x24\x25\x6a\x5c\xf4\x08\x7f\x85\x5f\x6c\x07\xe0\xb0\xed\x0b\x59\xfc\x20\xb4\x09\x5c\x02\xec\x54\x38\xb5\x40\x5d\xc7\x0d\x53\xf6\xd0\x44\x09\xdb\xa7\xb9\x51\x25\x82\xc4\xe6\x76\x84\xdf\xe5\x4a\x17\xd2\x0a\xc4\x16\x06\x80\x15\x76\x3c\xa5\x59\x10\x4c\x29\xf0\xee\x3e\xd9\xac\x07\x1c\x47\x95\x51\x19\x9d\xaa\x70\xd2\x01\x6e\x92\xba\xc0\x80\x39\x68\x23\x9c\x55\xca\xf8\x7d\xf0\xaa\x0e\x81\x63\x9c\x28\x7b\x24\xad\xc7\x88\x10\x0e\x92\xcf\x11\x38\x18\xc5\x29\x3b\xd7\xbc\x54\x99\xda\xb0\xa6\x91\xdc\x14\x55\xca\x1e\x41\xa5\x91\x72\x4b\xb9\x86\xa7\x49\xd1\xaa\xcd\x34\x96\x41\x38\xce\x7c\x46\x95\x0b\xb7\x14\x23\x70\xc5\xd0\x28\xca\xa8\xf3\xd7\xcd\x25\x96\x0c\x72\x21\xed\x09\x6b\xd4\xb1\x51\x31\x8b\x16\xa6\x15\xf8\xae\x73\xba\xe1\x10\xf1\x90\x0e\xce\x22\xed\x8f\xdd\x2f\x83\xd0\x0d\xbe\x32\x39\xc0\xb5\xe7\xa6\x87\x01\x39\x3b\x72\x36\xc0\x00\x6b\xa7\x8c\xc3\x3c\x14\x4f\xc6\x5f\xa9\x9d\x47\xe9\x3f\x53\x5a\x7c\x2c\x0e\xa7\xd2\x75\xa4\x16\xee\xd3\x72\xcd\x1a\x4c\x64\x50\x72\x4b\x1a\x70\x49\xb0\x85\xc7\x3a\x13\x95\xfa\x5d\x7e\x51\x09\xb0\x71\xbe\x50\x9a\x60\x54\xce\xce\xf9\xbc\xaf\x4a\x5b\xaf\x9c\xbf\x58\x9d\xa5\x40\xf9\xa7\x30\x59\x43\xd9\xd7\x96\xff\x9c\xdb\x11\xef\x49\xa1\xc7\xdc\x1e\x00\xdf\x32\xb1\xf5\x14\xf6\x19\x8c\xa8\x3e\xab\x61\xbd\xa8\x3f\x25\x8e\xf9\x9f\x37\x0d\xb0\x2d\x73\x1e\xc7\x6b\xd0\x42\xbe\x1a\xb6\xaa\xdf\x01\x8c\x63\xb2\x6b\xc5\xc5\x12\x68\x85\xe6\x74\x6e\xc6\x5d\x29\x25\xe0\xca\xb7\x6e\xaf\x20\x8c\xca\x72\x84\x0f\x35\xca\x43\xe8\xb3\x4d\x40\x78\xde\xe3\x85\xc0\x4a\xd3\x6f\x6b\x8f\xc3\xc5\xa1\x1e\xf7\xc4\x0a\x72\xcd\xdf\xa5\xbd\xf6\xef\xd3\xb8\x2c\x21\x73\x2d\x8d\xc3\xb7\xdd\xc3\xac\xfe\xa7\xcf\x9c\x9d\xb4\xc3\x86\xbe\xbd\xee\x1e\xfe\xa0\x2b\x00\x74\x25\x37\x8f\x7b\xb8\x3f\x79\xad\x8b\xf8\x3d\x39\x18\xa5\x5a\x18\x33\xa0\x9e\xd8\xb7\x59\x80\x4e\x65\xd8\xd6\xdd\x61\x59\x9b\x75\xac\xd7\x93\x75\xdd\x1c\xaa\x67\x4a\x5e\x34\xd7\x0b\x2e\xf5\xc3\x08\x11\xe9\x71\x2d\x15\xfb\xb7\xe9\x25\xb6\x92\x0b\xf0\x39\x07\xe3\xb8\x37\x46\x2b\x01\xe1\x4a\xb1\x45\x3c\x4c\xa3\x61\xf4\xba\xd4\xe0\xb7\x0a\x63\x61\x5e\x49\xed\xac\xb9\x59\x19\xa8\x6e\x6c\xa5\x34\x0f\xcb\x89\xfc\x4a\x94\xe1\x82\xa9\x50\x98\x6e\x31\xa0\x2e\xf0\xb1\xf8\xfa\x7f\x11\xd5\x96\xf3\x99\xee\x3c\xc2\xef\x69\x0b\xd6\x87\xae\x72\xbd\xd6\x52\x35\x5e\x57\x36\x5a\x6e\x43\xe6\x99\xe8\x78\x0c\x69\xeb\x7a\xca\x4f\x99\xd3\xa1\x7e\xb4\x28\xb0\x16\x52\xb2\x92\x6a\xf0\x42\x88\xba\x47\xe3\xb3\xe3\x2e\x0c\x81\xc7\x11\x7e\x0a\xf1\xf6\xf1\xe9\xf7\xf6\x84\x9f\xdc\x41\xa2\xfc\x38\x1e\xab\xb1\x7b\x78\x79\x81\x9f\xe3\x7d\x1c\xcc\xd5\xff\xf7\x52\xe9\xc4\xbb\x87\x85\x5e\x3d\x78\xdc\xef\x1e\x1e\x5e\x77\xdb\xcb\xcc\x69\x17\xcf\x37\x78\xf7\x02\x0b\xde\xa7\x7b\xec\xa7\x5f\x3e\xef\x1e\x96\x07\x78\xbb\xf2\xee\x87\x3b\x0b\xe0\x6d\x89\x4f\xd5\xb5\x6d\x0d\x6e\xab\xe1\x61\xe4\x0f\xed\x7d\x2c\xfe\x78\xbb\x6f\x6f\xbf\xf4\x77\x8b\xa6\xd6\x16\x66\xec\x4a\xf4\x8d\x4a\xfd\xff\x6c\x57\x12\x07\xb8\xed\x77\xaf\xbb\x7f\x03\x00\x00\xff\xff\x07\xba\x3e\x57\x52\x08\x00\x00"), }, } fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ @@ -73,6 +81,7 @@ var FS = func() http.FileSystem { } fs["/js"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ fs["/js/js.go"].(os.FileInfo), + fs["/js/js_test.go"].(os.FileInfo), } fs["/nosync"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ fs["/nosync/map.go"].(os.FileInfo), diff --git a/compiler/gopherjspkg/gen.go b/compiler/gopherjspkg/gen.go new file mode 100644 index 00000000..2a03d19c --- /dev/null +++ b/compiler/gopherjspkg/gen.go @@ -0,0 +1,11 @@ +//go:build generate +// +build generate + +package gopherjspkg + +import ( + _ "github.com/shurcooL/vfsgen" // Force go.mod to require this package. +) + +//go:generate vfsgendev -source="github.com/gopherjs/gopherjs/compiler/gopherjspkg".FS -tag=gopherjsdev +//go:generate gofmt -w -s fs_vfsdata.go diff --git a/compiler/linkname.go b/compiler/linkname.go new file mode 100644 index 00000000..5e178246 --- /dev/null +++ b/compiler/linkname.go @@ -0,0 +1,211 @@ +package compiler + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "strings" + + "github.com/gopherjs/gopherjs/compiler/astutil" +) + +// GoLinkname describes a go:linkname compiler directive found in the source code. +// +// GopherJS treats these directives in a way that resembles a symbolic link, +// where for a single given symbol implementation there may be zero or more +// symbols referencing it. This is subtly different from the upstream Go +// implementation, which simply overrides symbol name the linker will use. +type GoLinkname struct { + Implementation SymName + Reference SymName +} + +// SymName uniquely identifies a named submol within a program. +// +// This is a logical equivalent of a symbol name used by traditional linkers. +// The following properties should hold true: +// +// - Each named symbol within a program has a unique SymName. +// - Similarly named methods of different types will have different symbol names. +// - The string representation is opaque and should not be attempted to reversed +// to a struct form. +type SymName struct { + PkgPath string // Full package import path. + Name string // Symbol name. +} + +// newSymName constructs SymName for a given named symbol. +func newSymName(o types.Object) SymName { + if fun, ok := o.(*types.Func); ok { + sig := fun.Type().(*types.Signature) + if recv := sig.Recv(); recv != nil { + // Special case: disambiguate names for different types' methods. + typ := recv.Type() + if ptr, ok := typ.(*types.Pointer); ok { + return SymName{ + PkgPath: o.Pkg().Path(), + Name: "(*" + ptr.Elem().(*types.Named).Obj().Name() + ")." + o.Name(), + } + } + return SymName{ + PkgPath: o.Pkg().Path(), + Name: typ.(*types.Named).Obj().Name() + "." + o.Name(), + } + } + } + return SymName{ + PkgPath: o.Pkg().Path(), + Name: o.Name(), + } +} + +func (n SymName) String() string { return n.PkgPath + "." + n.Name } + +func (n SymName) IsMethod() (recv string, method string, ok bool) { + pos := strings.IndexByte(n.Name, '.') + if pos == -1 { + return + } + recv, method, ok = n.Name[:pos], n.Name[pos+1:], true + size := len(recv) + if size > 2 && recv[0] == '(' && recv[size-1] == ')' { + recv = recv[1 : size-1] + } + return +} + +// parseGoLinknames processed comments in a source file and extracts //go:linkname +// compiler directive from the comments. +// +// The following directive format is supported: +// //go:linkname . +// //go:linkname .. +// //go:linkname .<(*type)>. +// +// GopherJS directive support has the following limitations: +// +// - External linkname must be specified. +// - The directive must be applied to a package-level function or method (variables +// are not supported). +// - The local function referenced by the directive must have no body (in other +// words, it can only "import" an external function implementation into the +// local scope). +func parseGoLinknames(fset *token.FileSet, pkgPath string, file *ast.File) ([]GoLinkname, error) { + var errs ErrorList = nil + var directives []GoLinkname + + isUnsafe := astutil.ImportsUnsafe(file) + + processComment := func(comment *ast.Comment) error { + if !strings.HasPrefix(comment.Text, "//go:linkname ") { + return nil // Not a linkname compiler directive. + } + + // TODO(nevkontakte): Ideally we should check that the directive comment + // is on a line by itself, line Go compiler does, but ast.Comment doesn't + // provide an easy way to find that out. + + if !isUnsafe { + return fmt.Errorf(`//go:linkname is only allowed in Go files that import "unsafe"`) + } + + fields := strings.Fields(comment.Text) + if len(fields) != 3 { + return fmt.Errorf(`usage (all fields required): //go:linkname localname importpath.extname`) + } + + localPkg, localName := pkgPath, fields[1] + extPkg, extName := "", fields[2] + if pos := strings.LastIndexByte(extName, '/'); pos != -1 { + if idx := strings.IndexByte(extName[pos+1:], '.'); idx != -1 { + extPkg, extName = extName[0:pos+idx+1], extName[pos+idx+2:] + } + } else if idx := strings.IndexByte(extName, '.'); idx != -1 { + extPkg, extName = extName[0:idx], extName[idx+1:] + } + + obj := file.Scope.Lookup(localName) + if obj == nil { + return fmt.Errorf("//go:linkname local symbol %q is not found in the current source file", localName) + } + + if obj.Kind != ast.Fun { + if pkgPath == "math/bits" || pkgPath == "reflect" { + // These standard library packages are known to use go:linkname with + // variables, which GopherJS doesn't support. We silently ignore such + // directives, since it doesn't seem to cause any problems. + return nil + } + return fmt.Errorf("gopherjs: //go:linkname is only supported for functions, got %q", obj.Kind) + } + + decl := obj.Decl.(*ast.FuncDecl) + if decl.Body != nil { + if pkgPath == "runtime" || pkgPath == "internal/bytealg" || pkgPath == "internal/fuzz" { + // These standard library packages are known to use unsupported + // "insert"-style go:linkname directives, which we ignore here and handle + // case-by-case in native overrides. + return nil + } + return fmt.Errorf("gopherjs: //go:linkname can not insert local implementation into an external package %q", extPkg) + } + // Local function has no body, treat it as a reference to an external implementation. + directives = append(directives, GoLinkname{ + Reference: SymName{PkgPath: localPkg, Name: localName}, + Implementation: SymName{PkgPath: extPkg, Name: extName}, + }) + return nil + } + + for _, cg := range file.Comments { + for _, c := range cg.List { + if err := processComment(c); err != nil { + errs = append(errs, ErrorAt(err, fset, c.Pos())) + } + } + } + + return directives, errs.Normalize() +} + +// goLinknameSet is a utility that enables quick lookup of whether a decl is +// affected by any go:linkname directive in the program. +type goLinknameSet struct { + byImplementation map[SymName][]GoLinkname + byReference map[SymName]GoLinkname +} + +// Add more GoLinkname directives into the set. +func (gls *goLinknameSet) Add(entries []GoLinkname) error { + if gls.byImplementation == nil { + gls.byImplementation = map[SymName][]GoLinkname{} + } + if gls.byReference == nil { + gls.byReference = map[SymName]GoLinkname{} + } + for _, e := range entries { + gls.byImplementation[e.Implementation] = append(gls.byImplementation[e.Implementation], e) + if prev, found := gls.byReference[e.Reference]; found { + return fmt.Errorf("conflicting go:linkname directives: two implementations for %q: %q and %q", + e.Reference, prev.Implementation, e.Implementation) + } + gls.byReference[e.Reference] = e + } + return nil +} + +// IsImplementation returns true if there is a directive referencing this symbol +// as an implementation. +func (gls *goLinknameSet) IsImplementation(sym SymName) bool { + _, found := gls.byImplementation[sym] + return found +} + +// FindImplementation returns a symbol name, which provides the implementation +// for the given symbol. The second value indicates whether the implementation +// was found. +func (gls *goLinknameSet) FindImplementation(sym SymName) (SymName, bool) { + directive, found := gls.byReference[sym] + return directive.Implementation, found +} diff --git a/compiler/linkname_test.go b/compiler/linkname_test.go new file mode 100644 index 00000000..d0ce9c54 --- /dev/null +++ b/compiler/linkname_test.go @@ -0,0 +1,216 @@ +package compiler + +import ( + "go/ast" + "go/importer" + "go/parser" + "go/token" + "go/types" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +func parseSource(t *testing.T, src string) (*ast.File, *token.FileSet) { + t.Helper() + + const filename = "" + fset := token.NewFileSet() + + file, err := parser.ParseFile(fset, filename, src, parser.ParseComments) + if err != nil { + t.Log(src) + t.Fatalf("Failed to parse source code: %s", err) + } + return file, fset +} + +func makePackage(t *testing.T, src string) *types.Package { + t.Helper() + + file, fset := parseSource(t, src) + conf := types.Config{Importer: importer.Default()} + pkg, err := conf.Check(file.Name.Name, fset, []*ast.File{file}, nil) + if err != nil { + t.Log(src) + t.Fatalf("Failed to type check source code: %s", err) + } + + return pkg +} + +func TestSymName(t *testing.T) { + pkg := makePackage(t, + `package testcase + + func AFunction() {} + type AType struct {} + func (AType) AMethod() {} + func (*AType) APointerMethod() {} + var AVariable int32 + `) + + tests := []struct { + obj types.Object + want SymName + }{ + { + obj: pkg.Scope().Lookup("AFunction"), + want: SymName{PkgPath: "testcase", Name: "AFunction"}, + }, { + obj: pkg.Scope().Lookup("AType"), + want: SymName{PkgPath: "testcase", Name: "AType"}, + }, { + obj: types.NewMethodSet(pkg.Scope().Lookup("AType").Type()).Lookup(pkg, "AMethod").Obj(), + want: SymName{PkgPath: "testcase", Name: "AType.AMethod"}, + }, { + obj: types.NewMethodSet(types.NewPointer(pkg.Scope().Lookup("AType").Type())).Lookup(pkg, "APointerMethod").Obj(), + want: SymName{PkgPath: "testcase", Name: "(*AType).APointerMethod"}, + }, { + obj: pkg.Scope().Lookup("AVariable"), + want: SymName{PkgPath: "testcase", Name: "AVariable"}, + }, + } + + for _, test := range tests { + t.Run(test.obj.Name(), func(t *testing.T) { + got := newSymName(test.obj) + if got != test.want { + t.Errorf("NewSymName(%q) returned %#v, want: %#v", test.obj.Name(), got, test.want) + } + }) + } +} + +func TestParseGoLinknames(t *testing.T) { + tests := []struct { + desc string + src string + wantError string + wantDirectives []GoLinkname + }{ + { + desc: "no directives", + src: `package testcase + + // This comment doesn't start with go:linkname + func a() {} + // go:linkname directive must have no space between the slash and the directive. + func b() {} + // An example in the middle of a comment is also not a directive: //go:linkname foo bar.baz + func c() {} + `, + wantDirectives: []GoLinkname{}, + }, { + desc: "normal use case", + src: `package testcase + + import _ "unsafe" + + //go:linkname a other/package.testcase_a + func a() + `, + wantDirectives: []GoLinkname{ + { + Reference: SymName{PkgPath: "testcase", Name: "a"}, + Implementation: SymName{PkgPath: "other/package", Name: "testcase_a"}, + }, + }, + }, { + desc: "multiple directives in one comment group", + src: `package testcase + import _ "unsafe" + + // The following functions are implemented elsewhere: + //go:linkname a other/package.a + //go:linkname b other/package.b + + func a() + func b() + `, + wantDirectives: []GoLinkname{ + { + Reference: SymName{PkgPath: "testcase", Name: "a"}, + Implementation: SymName{PkgPath: "other/package", Name: "a"}, + }, { + Reference: SymName{PkgPath: "testcase", Name: "b"}, + Implementation: SymName{PkgPath: "other/package", Name: "b"}, + }, + }, + }, { + desc: "unsafe not imported", + src: `package testcase + + //go:linkname a other/package.a + func a() + `, + wantError: `import "unsafe"`, + }, { + desc: "gopherjs: both parameters are required", + src: `package testcase + + import _ "unsafe" + + //go:linkname a + func a() + `, + wantError: "usage", + }, { + desc: "referenced function doesn't exist", + src: `package testcase + + import _ "unsafe" + + //go:linkname b other/package.b + func a() + `, + wantError: `"b" is not found`, + }, { + desc: "gopherjs: referenced a variable, not a function", + src: `package testcase + + import _ "unsafe" + + //go:linkname a other/package.a + var a string = "foo" + `, + wantError: `is only supported for functions`, + }, { + desc: "gopherjs: can not insert local implementation", + src: `package testcase + + import _ "unsafe" + + //go:linkname a other/package.a + func a() { println("do a") } + `, + wantError: `can not insert local implementation`, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + file, fset := parseSource(t, test.src) + directives, err := parseGoLinknames(fset, "testcase", file) + + if test.wantError != "" { + if err == nil { + t.Fatalf("ParseGoLinknames() returned no error, want: %s.", test.wantError) + } else if !strings.Contains(err.Error(), test.wantError) { + t.Fatalf("ParseGoLinknames() returned error: %s. Want an error containing %q.", err, test.wantError) + } + return + } + + if err != nil { + t.Fatalf("ParseGoLinkanmes() returned error: %s. Want: no error.", err) + } + + if diff := cmp.Diff(test.wantDirectives, directives, cmpopts.EquateEmpty()); diff != "" { + t.Fatalf("ParseGoLinknames() returned diff (-want,+got):\n%s", diff) + } + }) + } +} diff --git a/compiler/natives/fs.go b/compiler/natives/fs.go deleted file mode 100644 index 13bbd3b5..00000000 --- a/compiler/natives/fs.go +++ /dev/null @@ -1,29 +0,0 @@ -// +build gopherjsdev - -package natives - -import ( - "go/build" - "log" - "net/http" - "os" - "strings" - - "github.com/shurcooL/httpfs/filter" -) - -// FS is a virtual filesystem that contains native packages. -var FS = filter.Keep( - http.Dir(importPathToDir("github.com/gopherjs/gopherjs/compiler/natives")), - func(path string, fi os.FileInfo) bool { - return path == "/" || path == "/src" || strings.HasPrefix(path, "/src/") - }, -) - -func importPathToDir(importPath string) string { - p, err := build.Import(importPath, "", build.FindOnly) - if err != nil { - log.Fatalln(err) - } - return p.Dir -} diff --git a/compiler/natives/fs_vfsdata.go b/compiler/natives/fs_vfsdata.go deleted file mode 100644 index 0a8d2edc..00000000 --- a/compiler/natives/fs_vfsdata.go +++ /dev/null @@ -1,1055 +0,0 @@ -// Code generated by vfsgen; DO NOT EDIT. - -// +build !gopherjsdev - -package natives - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - pathpkg "path" - "time" -) - -// FS is a virtual filesystem that contains native packages. -var FS = func() http.FileSystem { - fs := vfsgen۰FS{ - "/": &vfsgen۰DirInfo{ - name: "/", - modTime: time.Date(2019, 5, 1, 6, 1, 9, 582016136, time.UTC), - }, - "/src": &vfsgen۰DirInfo{ - name: "src", - modTime: time.Date(2018, 4, 20, 9, 8, 7, 919304753, time.UTC), - }, - "/src/bytes": &vfsgen۰DirInfo{ - name: "bytes", - modTime: time.Date(2018, 4, 20, 9, 33, 19, 948206308, time.UTC), - }, - "/src/bytes/bytes.go": &vfsgen۰CompressedFileInfo{ - name: "bytes.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 508, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x90\xcd\x4e\xc3\x30\x10\x84\xcf\xde\xa7\x18\x6e\x8d\x68\x55\x72\x45\x4d\x0f\x20\x0e\x3c\x43\xd5\xc3\xda\xdd\x54\x86\xe0\x14\x27\x91\xa8\x50\xde\x1d\xd9\x71\x1a\x19\x55\xca\x21\xde\x9f\x99\x6f\x67\xbb\xc5\xa3\x1e\x6c\x73\xc2\x47\x47\x74\x61\xf3\xc9\x67\x81\xbe\xf6\xd2\x11\xd5\x83\x33\x78\x77\x27\xf9\x79\xb9\xf6\xb2\xea\x70\x38\x86\xce\x1a\x26\x4e\x14\xb0\xae\xc7\x2f\xa9\xba\xf5\xb0\x6b\x68\x3c\x57\xf0\xec\xce\x82\x2e\x94\x95\xad\xa1\x51\x55\x30\xf1\xa5\xbc\xf4\x83\x77\xb0\xa4\xd4\x48\xe1\x4b\x85\x4d\x49\x63\x32\x7b\xfb\x1e\xb8\x59\x71\xd0\x9a\xbc\x0a\xe8\xb6\x6d\xc2\xbe\xad\xd1\x88\x5b\x71\x81\x87\x2a\xfe\xe9\x22\xca\x26\x91\x9a\x9b\x4e\xa2\x6a\xa2\x31\x0b\x0d\xcf\x34\x26\xec\xea\x83\x3d\x66\x40\x69\x35\x87\xea\xfd\x20\x37\xac\xd7\xf6\xeb\xc2\x5e\x72\xb0\xfc\x78\xc3\x77\xfc\x2c\xf6\x19\xeb\x2c\x5e\x4e\x6e\xca\xc4\xc8\x02\x50\xe2\x63\xec\x60\x74\x36\xbb\x99\x87\xa7\xfe\xfe\x7f\xbf\xbc\x91\x2f\x09\xed\xee\x04\x14\x74\x96\xf3\x9e\x68\xa4\xbf\x00\x00\x00\xff\xff\x23\x2d\xfc\x5d\xfc\x01\x00\x00"), - }, - "/src/bytes/bytes_test.go": &vfsgen۰CompressedFileInfo{ - name: "bytes_test.go", - modTime: time.Date(2018, 2, 27, 18, 59, 8, 0, time.UTC), - uncompressedSize: 215, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\xcc\xc1\x4a\xc4\x30\x10\x87\xf1\x73\xe7\x29\x86\x5c\x6c\x55\xba\x8f\xb1\xe0\xb5\xde\x44\x24\x4d\xff\xb6\xe3\xa6\x93\x90\x99\x22\xab\xf8\xee\xb2\xe0\xc5\xeb\xc7\x8f\xef\x74\xe2\x87\xf9\x90\xbc\xf0\x87\x11\xd5\x98\x2e\x71\x05\xcf\x57\x87\xbd\x39\xcc\x89\x64\xaf\xa5\x39\xf7\xd4\x85\x5b\x10\x5d\x03\x0d\x44\xef\x87\x26\x5e\xa2\xae\x68\xe5\xb0\x29\x4b\x42\xef\x7c\xff\x47\xc6\xe7\x81\x5f\x5e\x6f\x1b\xfe\xa6\xce\xc7\xe9\x22\xb5\x0f\xff\x39\x37\x64\x81\x71\x51\xb6\xab\xa5\x98\xf3\x78\x86\xd7\xb8\xc2\xe4\x0b\x8f\xfc\xb9\x49\xda\xf8\x5c\xea\x86\xf6\x34\xf1\x52\x60\x7a\xe7\x2c\x7b\xcd\xd8\xa1\x1e\x06\xa2\xae\x46\x95\xd4\x87\x43\x1b\x62\xda\xe2\x9c\x11\x06\xfa\xa1\xdf\x00\x00\x00\xff\xff\x25\x40\x6e\x83\xd7\x00\x00\x00"), - }, - "/src/crypto": &vfsgen۰DirInfo{ - name: "crypto", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 552154706, time.UTC), - }, - "/src/crypto/internal": &vfsgen۰DirInfo{ - name: "internal", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 552270972, time.UTC), - }, - "/src/crypto/internal/subtle": &vfsgen۰DirInfo{ - name: "subtle", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 552422592, time.UTC), - }, - "/src/crypto/internal/subtle/aliasing.go": &vfsgen۰CompressedFileInfo{ - name: "aliasing.go", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 552642511, time.UTC), - uncompressedSize: 654, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x90\x4d\x6f\xd4\x3c\x14\x85\xd7\xe3\x5f\x71\x14\xbd\xea\x9b\x68\xda\x84\x6e\x11\x45\x62\x55\xc1\xa6\x0b\x90\x58\x20\x16\x8e\x73\x27\x76\x70\xae\xa3\xeb\x1b\x88\x85\xf8\xef\x68\xa6\xa5\x7c\x0e\xec\x72\xa5\xe7\x39\xe7\xc4\x5d\x87\x7d\xbf\x86\x38\x60\xca\xc6\x2c\xd6\x7d\xb0\x23\x21\xaf\xbd\x46\x32\x26\xcc\x4b\x12\x45\x35\x06\xf5\x6b\xdf\xba\x34\x77\x63\x5a\x3c\xc9\x94\xbf\x7f\x4c\xb9\x32\xa6\xeb\xf0\x82\xcb\xdd\x47\x92\x68\x17\x08\x1d\xbd\x8c\x4f\x9e\xd4\x93\x60\x83\xe5\x01\x05\xd9\x5b\x21\xcc\x34\x27\x29\xb0\x0a\xcb\x05\x35\x27\x05\x93\xa3\x9c\xad\x84\x58\x8e\x51\x2e\x89\x50\x5e\x12\x0f\x81\xc7\x06\x81\x07\xda\x5a\xbc\xf1\x8f\x6e\x4f\x25\xf1\x00\xf5\x84\x1c\x83\x23\x44\xe2\x51\x3d\x42\x46\x18\x39\x09\x0d\xad\x39\xac\xec\x7e\x18\x55\x6f\x97\x28\x78\xf7\xbe\x2f\x4a\x0d\xfa\x94\x22\x3e\x9b\x5d\xd7\xe1\xf6\xf4\x23\xaf\x5e\x3f\xc5\x5b\x82\xb3\xfc\xbf\x42\x28\x16\x24\xc6\x92\x02\x2b\x09\xac\x04\xf5\x33\x69\x70\x97\xc8\x09\x6b\xa6\x47\xeb\xa1\xff\xc4\xb1\x8d\xb9\x35\x3b\x21\x5d\x85\x8f\x93\xea\xad\xc1\x73\x3c\xc1\xc5\xc5\xe9\x2a\xdf\x2e\xb3\xdb\x4d\xb9\x7d\xf9\xe0\xdc\xf5\x13\x39\xad\xb7\xa6\xbd\x25\xad\xab\xff\xac\x88\x2d\x55\x83\x9b\x1b\xfc\x4e\x95\x5f\xa9\x7f\xa5\xa5\xc3\x21\x93\x56\xcd\x11\xa8\x1b\x3c\xfb\x6b\xe8\xcf\xf0\xfe\x7e\xf4\xd5\xf5\xb9\x92\x33\xde\x9f\x4b\xce\x2c\xda\xdf\xbf\xd3\xd5\xb5\xf9\x62\xbe\x06\x00\x00\xff\xff\xb2\x4c\x59\x2e\x8e\x02\x00\x00"), - }, - "/src/crypto/rand": &vfsgen۰DirInfo{ - name: "rand", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 431816802, time.UTC), - }, - "/src/crypto/rand/rand.go": &vfsgen۰CompressedFileInfo{ - name: "rand.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 431904120, time.UTC), - uncompressedSize: 1415, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x54\x41\x4f\xeb\x38\x10\x3e\xc7\xbf\x62\xc8\xae\x56\xf1\x12\x12\x24\x04\x87\xae\x8a\xc4\x22\x84\x38\x2c\xbb\x8b\x76\xdf\x3b\x20\x0e\x76\x32\x69\x5c\x52\xbb\x6f\xec\x34\x54\xa5\xff\xfd\xc9\x71\x52\x0a\xf4\xe9\x5d\xda\x38\xdf\x37\xdf\x37\x33\x9e\x49\x9e\xc3\xb1\x6c\x55\x53\xc2\xdc\x32\xb6\x14\xc5\xb3\x98\x21\x90\xd0\x25\x63\x6a\xb1\x34\xe4\x20\x61\x51\x8c\x44\x86\x6c\xcc\x58\x14\xcf\x94\xab\x5b\x99\x15\x66\x91\xcf\xcc\xb2\x46\x9a\xdb\xb7\x87\xb9\x8d\x19\x67\xac\x6a\x75\x01\x4a\x2b\x97\x70\xd8\xb0\xe8\x01\x45\x89\x04\x53\xf8\x8d\xf4\x2c\x1c\x36\x5b\xb6\x65\xcc\xad\x97\x08\xbb\x77\x60\x1d\xb5\x85\xdb\x6c\x07\x81\x84\xe0\xf7\x1d\xc8\xc1\xff\x27\x12\x1e\x9f\xe4\xda\x21\x87\x44\x83\xd2\x2e\x05\x24\x82\x3e\xbd\xde\x4a\x10\x89\x35\x4c\xa6\x30\xb7\xd9\x9d\x76\x48\x5a\x34\x7f\xcb\x39\x16\x2e\x91\x3c\xbb\x45\x97\xc4\xbf\xf6\x9c\x98\xb3\xc8\x54\x95\x45\xf7\x13\x76\x20\xc5\xdc\x13\x12\xce\x58\x94\xe7\x20\xc9\x74\x16\x89\x45\x05\xad\x97\xce\x0c\x0a\xb7\x8d\x91\xa2\x09\x61\x01\xf0\x26\xaa\x82\x81\x35\xed\x59\xff\xeb\x12\x2b\xa5\xb1\xf4\xe9\x8e\x02\x9f\xe2\x17\xf6\x7a\xa7\xb0\xdd\x17\x39\x3a\x20\xb2\x43\x43\xec\x0c\xdd\x83\xd0\xa5\x59\x7c\x11\x4d\x8b\x36\xe6\x07\x83\x22\x0d\x53\x68\x50\x27\x92\xfb\x93\xaa\x40\xc3\x25\x5c\x9c\x9f\x9f\x5d\x04\xdc\x17\x7a\xb5\x32\xaa\x84\x7f\x5b\xe3\xc4\xcd\x4b\x81\x58\x62\x79\xe3\x7b\x0d\xae\x26\xd3\x69\x90\x6b\xf8\xe0\x36\x46\x76\x35\x6a\x2f\x3f\x73\x35\x28\x0b\x0b\x43\x08\xae\x16\x3a\x38\xa4\x20\x2c\xd8\x25\x16\xaa\x52\x58\x82\xd2\x63\x58\xed\xdc\x72\x92\xe7\x5d\xd7\x65\xdd\x59\x66\x68\x96\xff\xf7\x90\x7f\x45\x19\xba\x71\xf5\xcf\x5d\xfe\x4b\x78\x3c\x59\xa0\xab\x4d\x79\x72\xc8\xde\x57\xd6\xdb\xf8\xd3\xd6\xff\x0c\xed\xb9\x16\x4d\xf3\xb9\x3f\x29\xf4\x13\x31\xa0\xb6\x95\x61\x40\x52\x08\x57\x3f\xfe\x1f\x6b\xde\x77\x8a\xd0\xb5\xa4\x41\xa7\xa0\x55\xc3\x7a\x83\x6d\x18\x8b\x7b\x53\x62\x36\xb7\xfd\x75\x11\x7e\x6b\x15\xe1\x81\xd1\x18\x90\x98\xff\xb1\x23\xfd\xe0\x52\xa9\xcf\xf2\xcf\xb5\x43\xeb\x75\x06\x76\x76\xa7\x57\xe6\x19\xdf\x66\x6c\x90\x7d\x23\xf7\xd2\x7b\xb1\x07\xaf\xff\x5d\xcd\xe8\xe2\x74\x3f\x64\xf4\x08\xf3\xc1\xc7\x16\xec\xd7\x1f\xa0\x0f\x4d\x18\xb0\xd3\x34\xac\xa4\xcd\xee\xb1\x1b\x13\xcd\xbd\x3e\x68\xe3\x40\xac\x84\x6a\x84\x6c\x10\x94\x06\x57\x2b\x0b\xa8\x57\x8a\x8c\x5e\xa0\x76\x31\x67\xe3\x07\x40\x0a\x57\xd4\x58\x26\x15\xf8\x63\x32\x6e\xbe\x34\xa6\x49\x81\x50\x94\x7f\x89\x17\xff\x11\xe0\x9f\x71\x5f\xe3\x90\x4c\x8f\xc9\xb6\x82\x8f\x78\x54\x19\x0a\x65\xb4\x15\x87\xcb\x9d\xe2\x66\xd8\x87\xa3\xca\x23\x8f\x93\xe1\xfd\x13\x1f\xf6\x62\xd4\x15\x8d\xc5\xdd\x84\x79\x83\x29\x78\xfe\x40\x9f\x3c\x85\xb6\xbc\xeb\x97\x37\x9a\x4e\xe1\x14\x5e\x5f\xa1\x57\xef\xd7\x7b\xcb\xbe\x07\x00\x00\xff\xff\x4b\xf2\x65\x42\x87\x05\x00\x00"), - }, - "/src/crypto/x509": &vfsgen۰DirInfo{ - name: "x509", - modTime: time.Date(2018, 4, 20, 10, 26, 30, 238700007, time.UTC), - }, - "/src/crypto/x509/x509.go": &vfsgen۰CompressedFileInfo{ - name: "x509.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 177, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8d\xb1\x6e\xc2\x40\x10\x05\xeb\xec\x57\x3c\x5d\x65\x27\x51\x9c\x26\x45\xd2\xa6\x88\x94\x02\x21\xfc\x05\x67\x7b\x81\x83\xf3\xed\x69\x6f\x0d\x58\x88\x7f\x47\x58\xa2\x1d\x8d\x66\x9a\x06\x6f\xdd\x14\xe2\x80\x43\x21\xca\xbe\x3f\xfa\x1d\xe3\xf2\xf5\xf9\x4d\x14\xc6\x2c\x6a\x70\xac\x2a\x5a\x1c\xd1\x76\x4a\x3d\xa2\xf8\xa1\x9d\x8b\xf1\xb8\x11\xb1\x52\xd5\xa8\x5e\x7f\x59\x6d\x2d\x12\xdf\xb1\xb8\x35\xae\xf4\xa2\x6c\x93\x26\xa4\xf0\xa4\xe5\x63\xc5\xe7\xca\xf5\x3a\x67\x93\xe6\xb1\xf8\x41\x59\x42\x50\x11\x43\x16\x89\x08\x05\x49\x0c\xfe\xe4\x43\xf4\x5d\x64\x84\x84\x3f\xc9\x7b\xd6\xff\xd6\xd5\x74\xa3\x7b\x00\x00\x00\xff\xff\xa1\x8b\x91\x39\xb1\x00\x00\x00"), - }, - "/src/crypto/x509/x509_test.go": &vfsgen۰CompressedFileInfo{ - name: "x509_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 364, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x90\xb1\x0e\x82\x40\x0c\x40\x67\xfb\x15\xcd\x4d\xa0\x09\xb8\x38\x38\x1b\x07\x37\x23\x84\x1d\xb1\x90\x13\xb8\x92\x6b\x31\x12\xe3\xbf\x1b\xd1\x49\x17\xc2\xdc\xf7\x5e\x9b\xc6\x31\xae\xce\xbd\x6d\x2e\x78\x15\x80\x2e\x2f\xea\xbc\x22\xbc\x6f\xd6\x5b\x00\xdb\x76\xec\x15\x8d\x92\xa8\x75\x95\x01\x28\x7b\x57\x60\x4a\xa2\xc9\x20\x4a\xed\x8e\xbc\x1e\x99\x9b\x40\x71\xf9\x85\xa2\x34\xc4\x07\x2c\x34\x4a\x6a\xdb\x05\xc6\x31\xca\x88\xa2\x67\x56\x31\x21\x3c\xff\x2a\xa7\xf7\x64\x6e\x62\xef\x6e\x59\xee\x67\xeb\x9f\x0b\x32\xf2\xb6\x1c\x26\x34\x7e\xec\xc3\xf8\xa0\x29\xcb\x47\xf1\x15\x00\x00\xff\xff\xa4\x46\xbd\x49\x6c\x01\x00\x00"), - }, - "/src/database": &vfsgen۰DirInfo{ - name: "database", - modTime: time.Date(2018, 4, 20, 9, 16, 25, 459988033, time.UTC), - }, - "/src/database/sql": &vfsgen۰DirInfo{ - name: "sql", - modTime: time.Date(2018, 4, 20, 9, 32, 51, 261527036, time.UTC), - }, - "/src/database/sql/driver": &vfsgen۰DirInfo{ - name: "driver", - modTime: time.Date(2018, 4, 20, 12, 39, 47, 342057645, time.UTC), - }, - "/src/database/sql/driver/driver_test.go": &vfsgen۰CompressedFileInfo{ - name: "driver_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 1185, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x53\x4d\x8f\xd3\x30\x10\x3d\x93\x5f\x31\x9a\x03\x38\x60\x35\xc9\x0a\xad\x44\x24\x2e\xb0\xe2\xba\x1c\x7a\xdb\xf6\xe0\x24\x0e\x32\x18\x3b\xf8\x23\xa5\xaa\xfa\xdf\x91\xe3\x06\xa4\xd6\x6d\xc3\x25\x9e\xcc\x9b\x79\xf3\xe4\x79\x2e\x0a\x78\xd7\x78\x21\x3b\xf8\x6e\xb3\x6c\x60\xed\x0f\xf6\x8d\x43\x67\xc4\xc8\x4d\x96\x8d\xcc\xc0\xc8\xa4\xe7\x9f\xb5\x1a\xb9\x71\xdc\xac\xb9\x75\x16\x3e\xc2\xcb\xf6\x32\x7f\xc8\x5e\x1d\x3e\x69\x2d\x29\xa0\x33\x9e\x23\x85\x70\x50\x40\x3c\xd2\x7f\xd0\xfa\x2a\xf4\xb2\x6d\xf6\x8e\x13\x74\x98\x27\xf1\x98\x4a\x71\x56\x69\xc2\x2a\x99\x15\xca\x3d\xbe\x27\x55\x7a\x86\x17\xca\x55\x8f\xd7\x50\xec\x99\xb4\x41\xfd\x74\x9e\x81\xa7\x5c\x0a\xc2\xf2\x4a\x4f\x99\x4e\x47\x89\x65\x9e\x46\x4f\x1a\x2f\xe1\xb6\x86\xb9\xbf\x06\xec\xb5\x46\x0a\xdc\x98\x1a\xd0\xfe\x92\x45\x5c\x6a\x0d\xad\xf6\xb2\x53\x6f\x1c\xb4\x71\x79\xb0\x09\xa5\x1b\x0c\x53\x35\xb8\xfd\xc0\xa1\xd1\x5a\x26\x28\x1f\x16\xd1\x3d\x24\x89\x9e\x78\xcf\xbc\x74\x5f\x99\x61\x3f\xb9\xe3\xe6\xaf\x73\x28\x28\xbd\x3b\x7d\xf0\x6e\x2d\x79\x3b\xdd\x4d\x4e\x94\x90\x39\x05\x25\xe4\x92\xae\xd7\x4c\xd9\x5d\x08\xe6\x73\x41\xcb\xb9\xaa\xa2\xb8\x55\x2e\xc8\x87\x7c\xde\x5b\x88\x42\x0f\x14\x05\xac\x9f\x9f\x9e\x6b\xf8\x22\x7e\xaf\x6e\x8f\xeb\x49\xb9\x0a\xa6\xeb\xa5\x66\xd3\xee\xa7\xbf\xfb\x32\x1b\x12\x6c\x7a\xe6\xd6\xdb\x52\x1b\x7b\xa8\x8e\xf3\x6b\x9b\xc2\xff\x15\x6b\x09\xb2\xf0\x46\x91\xe1\x12\x8d\x22\x0e\x8c\xbb\xf2\xca\xfa\x61\xd0\xc6\xf1\x2e\x5a\x24\xfa\x68\x25\x2c\x05\x06\x56\x8a\x96\x83\xee\xc3\x4d\x06\xde\x63\xf6\x27\x00\x00\xff\xff\x8d\xf2\x41\x9a\xa1\x04\x00\x00"), - }, - "/src/debug": &vfsgen۰DirInfo{ - name: "debug", - modTime: time.Date(2018, 4, 20, 9, 10, 26, 815054147, time.UTC), - }, - "/src/debug/elf": &vfsgen۰DirInfo{ - name: "elf", - modTime: time.Date(2018, 4, 20, 9, 40, 48, 430335834, time.UTC), - }, - "/src/debug/elf/elf_test.go": &vfsgen۰FileInfo{ - name: "elf_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x65\x6c\x66\x0a\x0a\x69\x6d\x70\x6f\x72\x74\x20\x22\x74\x65\x73\x74\x69\x6e\x67\x22\x0a\x0a\x66\x75\x6e\x63\x20\x54\x65\x73\x74\x4e\x6f\x53\x65\x63\x74\x69\x6f\x6e\x4f\x76\x65\x72\x6c\x61\x70\x73\x28\x74\x20\x2a\x74\x65\x73\x74\x69\x6e\x67\x2e\x54\x29\x20\x7b\x0a\x09\x74\x2e\x53\x6b\x69\x70\x28\x22\x6e\x6f\x74\x20\x36\x6c\x22\x29\x0a\x7d\x0a"), - }, - "/src/encoding": &vfsgen۰DirInfo{ - name: "encoding", - modTime: time.Date(2018, 4, 20, 9, 17, 51, 678431000, time.UTC), - }, - "/src/encoding/gob": &vfsgen۰DirInfo{ - name: "gob", - modTime: time.Date(2018, 4, 20, 10, 28, 37, 407632207, time.UTC), - }, - "/src/encoding/gob/gob_test.go": &vfsgen۰CompressedFileInfo{ - name: "gob_test.go", - modTime: time.Date(2018, 2, 27, 18, 42, 13, 0, time.UTC), - uncompressedSize: 2598, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x56\x51\x6f\xdb\x3e\x0e\x7f\xb6\x3e\x05\x67\xdc\x0a\xa7\xe7\x39\x95\x93\xae\x9d\x81\x3e\xac\x5b\x77\xd8\x43\x3b\x60\x33\x70\xdb\x8a\x62\x70\x6c\x26\xd1\xea\x48\x3e\x49\x6e\x1a\x04\xf9\xee\x07\x4a\x76\x9c\xae\xff\x0d\x2b\xd0\x56\xa4\x7e\xfc\x91\xa2\x48\xca\xe3\x31\xfc\x7b\xd6\x8a\xba\x82\x9f\x86\xb1\xa6\x28\xef\x8b\x05\xc2\x42\xcd\x18\x13\xab\x46\x69\x0b\x11\x0b\xc2\xd9\xc6\xa2\x09\x59\x10\x6a\x9c\xd7\x58\x5a\x5a\x5a\x34\x56\xc8\x45\xc8\x46\x8c\x8d\xc7\x90\x7f\x7a\xff\x29\x83\x1c\x8d\xbd\x92\x55\xae\xae\x64\x05\xea\x01\xb5\x16\x15\x42\x59\x48\x98\x21\x68\x5c\xa9\x07\xac\x40\xc9\x12\xc1\x2e\x11\x66\xed\x02\xd6\xc2\x2e\xe1\xba\xd0\x1a\xe6\x02\xeb\x0a\x84\x81\xb9\x78\xc4\x2a\x61\xf3\x56\x96\x4f\x08\x23\x0b\xc7\x9d\xd7\x24\x1f\xc1\x96\x05\x76\xd3\x20\xe4\x29\x18\xab\xdb\xd2\x92\x26\xc8\x49\x10\x72\xc1\x82\x5d\xbf\x3f\x39\xdc\xff\x0a\xf3\x5a\x15\xf6\xf5\x94\x05\xc1\x77\x38\x16\xd2\x1e\x20\xf9\x21\xf2\x6d\x0c\x97\x31\xbc\x03\x70\x98\xe0\x1a\xba\x9f\x55\xd1\xdc\x7a\x1f\x77\xc7\x03\xd7\x75\x7a\xb0\x2d\xa4\xbd\xcb\x27\xa4\xf5\xc0\x27\x46\x7d\x7c\xc1\xb5\x90\xb6\xb1\x7a\x30\x39\xee\x3c\x95\x6a\xd5\xf4\x54\xb4\xae\xf1\x91\xa7\xe7\x77\xc3\x92\x40\x94\xb2\x1e\x74\x9b\x76\xac\x77\xb7\xe9\x61\x50\x57\xab\xc6\x6e\xae\x8b\xe6\xd0\xbd\x90\x16\xc6\x63\xb0\x0a\xca\x25\x96\xf7\x60\x97\x85\x85\x35\xdd\x4e\x89\xe2\x01\xa1\x00\xa9\xe4\x2b\x29\x6a\x32\x4a\x58\x10\xdc\xf4\x07\x3f\xbe\x9d\xdc\x0d\xdc\x5f\xac\x36\x9d\x3a\x1d\xce\xf4\x51\xda\xd7\x53\xe3\xb4\xe4\xc9\x21\x3f\x7f\xec\x08\xba\x03\x78\xf3\x9e\x75\x6f\xfa\xad\xd7\xdc\xde\x51\xbd\xb9\xbb\xec\x3d\xe7\xa9\xbb\xa5\x46\x40\x76\x01\x93\x84\x4f\xf9\xe9\x1b\x16\x20\x49\x69\x72\xc6\xcf\x29\x25\x76\xad\xbc\x7c\xc2\x82\x15\x16\x92\xf2\x9e\x5d\xc0\x34\x65\xc1\x5c\xc8\x05\x6a\x43\xe2\x29\x0b\x0c\xa7\x45\xe8\x1d\xf3\x90\x05\x26\x3d\x50\xa4\x21\x0b\x1e\x0a\xed\x82\xe5\x30\xe4\x1c\x2e\x7a\x21\xe2\xc9\x49\x0c\x3c\x39\x19\x0d\xc8\xf4\xaf\x90\x85\xd6\x1c\x0e\xd2\x45\xf2\xed\xc9\x1d\x5c\x80\xe1\x9d\xc4\x9d\x94\xee\xf1\xe9\x2f\xf8\xb4\xc3\xa7\x9d\xc4\x7b\x6b\xc2\xbb\xdb\x79\xdb\x39\x19\xea\x60\xaf\xf6\xb6\x47\x8d\x38\xd4\x39\x86\x23\x7c\xca\x90\xfe\x33\x43\xe7\x9d\xd0\x83\xca\x13\xd8\xb5\x62\x81\x75\xa9\x3d\xca\xb9\x6b\xa0\xac\xbb\x3e\x7e\x16\xb3\x20\xb8\xdc\x8b\xe7\x24\xbe\xeb\xc5\x57\xa7\x24\x5e\x67\xbf\x6f\xaf\x6d\xd8\x88\x30\xa3\xb8\x63\x08\x91\x56\xb8\x73\x36\x69\xf6\x6b\xcf\x6d\xa7\x19\xe4\x93\xed\xd7\x0c\x08\xfc\x3d\x83\xa3\xae\x14\x76\x31\xf0\x93\x7e\x0f\xfd\x56\x57\x16\x3b\x4f\xe6\x9d\x66\xcf\x5b\xb5\x73\x1f\x52\xdd\x85\x5d\x04\x21\x95\x5d\xe8\x0d\x7d\x1b\x67\x4f\xda\x78\xdb\xb9\x1d\xbc\xc4\xd0\x2d\x0e\x63\xea\xbb\x3d\xfb\x53\xb7\x6f\x5d\x29\x66\xbe\xce\x62\xff\xcf\x4b\xdc\x31\xec\xa7\xef\x07\xf1\x08\x76\x29\x0c\x34\x5a\xcd\x6a\x5c\x65\x7e\x33\xc8\x37\x0d\x5e\x69\xad\x74\x06\x95\xb1\xc9\xbf\x0c\x5a\x9a\xb3\x52\x59\x28\x80\xc6\xac\x15\x4a\x76\x58\x4a\x67\x61\x81\xe6\x61\xb5\xc2\x15\x4d\x6c\x88\xc6\x0b\x61\x97\xed\x2c\x29\xd5\x6a\xbc\x50\xcd\x12\xf5\x4f\x33\x2c\xba\x47\x21\x59\xa8\x6c\x7a\x7e\x96\x4d\x46\x8e\x8a\x06\x54\xf6\xe7\x09\xb5\xa5\x8a\xcf\x86\xaa\x8d\x5d\xc1\x0f\x8a\xd4\x1d\xaf\x1f\x62\x94\xe0\x7b\x8c\x9e\x8e\xb2\x11\x21\x6e\xfa\xda\x81\xa3\x61\x44\x6d\x79\x72\x1a\x43\x4a\x7f\x26\xc9\xa9\x63\xa2\x91\x95\x75\xb8\x3e\x9e\xad\xe1\x31\x18\xef\xc9\x0f\xaf\xcc\xed\xfb\xe9\xb5\x3d\x3b\x8b\xe1\xfc\x4d\x0c\x3c\x9d\x4c\xe9\x37\xe5\x93\xa9\xc3\x7e\xfe\x38\x54\x37\xbc\x82\x74\x22\x9c\x87\x7d\x24\xe1\x8d\x5a\x53\x92\xe9\x9d\xb3\x62\x85\x21\x6d\x7f\xcb\x9e\xce\xb8\x28\x5c\x62\x5d\xab\x18\x4c\x21\x6a\xa5\x43\x77\x9a\x7c\x38\x4d\x9e\x6e\x43\x77\xa1\xc2\x40\x9e\xba\x72\xdb\xb1\x60\x46\x3d\x26\x71\x1d\xb9\x67\x39\xb9\x6c\xe7\x73\xd4\x23\x16\xa0\xd6\xb4\x73\x83\xeb\x2b\x59\xaa\x0a\x75\x34\x1b\x25\x7e\x19\x59\x3e\x62\x81\x98\x03\x61\x5e\x5c\x00\x8d\x77\x6a\x51\x9b\xb8\xba\x88\x42\x74\xb0\x2c\x8c\x09\x31\x72\x6e\x68\x1c\xfc\xb0\x1c\x72\xee\xa9\x1d\xf3\x7b\xdc\x33\xfb\x65\x74\xf4\xe3\xb7\xdc\x1f\x0a\x5b\xd4\x51\x58\xe1\x33\x6e\x31\x87\x17\x7d\xd9\xbc\x47\x6c\xae\xfe\xd7\x16\x75\x64\x79\x0c\x8e\xee\x30\xb6\x79\x1f\x1c\xe0\x63\x83\xa5\xc5\x0a\x5e\x3e\xc0\x42\x59\x78\xf9\x10\xc6\x70\x4c\x46\x3e\x84\x1d\xa3\x0a\xbe\x44\x28\x66\x46\xd5\xad\xc5\x7a\x03\xa6\xd5\xfe\x5b\xa3\x7b\xde\x2a\xaa\x46\x5f\xfc\xee\x91\x4b\x5c\x2c\x96\x27\xfb\xa7\xf2\xe2\x59\x76\xe6\x51\xd8\x3d\x87\x60\x50\xda\x70\x7f\x84\x1f\x7f\x6d\xd7\x7b\xf7\xb6\x3b\x36\x7c\xdc\x50\x6f\x7e\x2e\x4a\x7c\xfe\x71\x33\x1e\x83\x3b\xb8\x90\x8b\xf1\x42\xcd\xa0\x6c\xb5\x46\x69\xeb\x0d\xb4\x06\xe9\x00\x66\x23\xcb\x04\x72\xaa\x0f\xb2\xf4\x6a\xa7\xfc\x6f\x21\xec\x7f\xb4\x6a\x1b\x28\x64\xe5\x98\xca\x42\x52\xbb\x9b\xb6\x2c\x11\x2b\x58\x2f\x51\x76\x0c\x94\x8c\xd6\xd0\x07\x57\x60\x93\x2f\xf7\xa2\x89\xc2\xd6\xd0\xdb\xe9\xb7\xc3\x11\xdb\xb1\xff\x07\x00\x00\xff\xff\x9b\x7c\x41\xd0\x26\x0a\x00\x00"), - }, - "/src/encoding/json": &vfsgen۰DirInfo{ - name: "json", - modTime: time.Date(2018, 4, 20, 11, 15, 14, 520460736, time.UTC), - }, - "/src/encoding/json/stream_test.go": &vfsgen۰FileInfo{ - name: "stream_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x6a\x73\x6f\x6e\x0a\x0a\x69\x6d\x70\x6f\x72\x74\x20\x22\x74\x65\x73\x74\x69\x6e\x67\x22\x0a\x0a\x66\x75\x6e\x63\x20\x54\x65\x73\x74\x48\x54\x54\x50\x44\x65\x63\x6f\x64\x69\x6e\x67\x28\x74\x20\x2a\x74\x65\x73\x74\x69\x6e\x67\x2e\x54\x29\x20\x7b\x0a\x09\x74\x2e\x53\x6b\x69\x70\x28\x22\x6e\x65\x74\x77\x6f\x72\x6b\x20\x61\x63\x63\x65\x73\x73\x20\x69\x73\x20\x6e\x6f\x74\x20\x73\x75\x70\x70\x6f\x72\x74\x65\x64\x20\x62\x79\x20\x47\x6f\x70\x68\x65\x72\x4a\x53\x22\x29\x0a\x7d\x0a"), - }, - "/src/fmt": &vfsgen۰DirInfo{ - name: "fmt", - modTime: time.Date(2018, 4, 20, 9, 26, 36, 223979708, time.UTC), - }, - "/src/fmt/fmt_test.go": &vfsgen۰FileInfo{ - name: "fmt_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x66\x6d\x74\x5f\x74\x65\x73\x74\x0a\x0a\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x43\x6f\x75\x6e\x74\x20\x3d\x20\x31\x30\x30\x0a"), - }, - "/src/go": &vfsgen۰DirInfo{ - name: "go", - modTime: time.Date(2018, 8, 20, 0, 59, 45, 382177476, time.UTC), - }, - "/src/go/token": &vfsgen۰DirInfo{ - name: "token", - modTime: time.Date(2018, 4, 20, 9, 43, 49, 174565883, time.UTC), - }, - "/src/go/token/token_test.go": &vfsgen۰FileInfo{ - name: "token_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x74\x6f\x6b\x65\x6e\x0a\x0a\x69\x6d\x70\x6f\x72\x74\x20\x28\x0a\x09\x22\x74\x65\x73\x74\x69\x6e\x67\x22\x0a\x29\x0a\x0a\x66\x75\x6e\x63\x20\x54\x65\x73\x74\x46\x69\x6c\x65\x53\x65\x74\x52\x61\x63\x65\x28\x74\x20\x2a\x74\x65\x73\x74\x69\x6e\x67\x2e\x54\x29\x20\x7b\x0a\x09\x74\x2e\x53\x6b\x69\x70\x28\x29\x0a\x7d\x0a"), - }, - "/src/internal": &vfsgen۰DirInfo{ - name: "internal", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 433051526, time.UTC), - }, - "/src/internal/bytealg": &vfsgen۰DirInfo{ - name: "bytealg", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 432112113, time.UTC), - }, - "/src/internal/bytealg/bytealg.go": &vfsgen۰CompressedFileInfo{ - name: "bytealg.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 432157623, time.UTC), - uncompressedSize: 181, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\xcb\xb1\x0a\xc2\x30\x10\xc6\xf1\x39\xf7\x14\x9f\x5b\x8b\x85\xee\x42\x47\x9f\xa2\x74\xb8\x8b\x97\x12\x3d\x52\x4d\x9b\x41\xa4\xef\x2e\x29\xb8\xb8\x1d\xff\xfb\x7e\x7d\x8f\xb3\x94\x68\x37\xdc\x57\xa2\x27\xfb\x07\xcf\x0a\x79\x6f\xca\x36\x13\x85\x92\x3c\xae\xaf\xc2\xd6\x70\x07\xc1\x38\xd5\x57\x0b\x59\x16\xc3\x87\x5c\x0c\x30\x4d\x0d\xb7\x38\x0d\xc7\x25\x6d\xcd\x2e\xeb\x56\x72\x42\x60\x5b\x95\xdc\x4e\x2e\x2c\x19\xb1\x83\xc7\x65\x40\xe6\x34\x2b\xf8\x18\xc6\x00\x5f\xad\x8c\x71\x3a\xc2\x1f\xad\x76\xa7\x5f\xdc\x72\x51\xda\xe9\x1b\x00\x00\xff\xff\x11\x57\xe4\x4d\xb5\x00\x00\x00"), - }, - "/src/internal/cpu": &vfsgen۰DirInfo{ - name: "cpu", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 432311944, time.UTC), - }, - "/src/internal/cpu/cpu.go": &vfsgen۰FileInfo{ - name: "cpu.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 432356960, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x63\x70\x75\x0a\x0a\x63\x6f\x6e\x73\x74\x20\x28\x0a\x09\x43\x61\x63\x68\x65\x4c\x69\x6e\x65\x53\x69\x7a\x65\x20\x20\x20\x20\x3d\x20\x30\x0a\x09\x43\x61\x63\x68\x65\x4c\x69\x6e\x65\x50\x61\x64\x53\x69\x7a\x65\x20\x3d\x20\x30\x0a\x29\x0a"), - }, - "/src/internal/fmtsort": &vfsgen۰DirInfo{ - name: "fmtsort", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 432624173, time.UTC), - }, - "/src/internal/fmtsort/fmtsort_test.go": &vfsgen۰CompressedFileInfo{ - name: "fmtsort_test.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 432693305, time.UTC), - uncompressedSize: 1103, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x53\x41\x8f\xd3\x3c\x10\x3d\xc7\xbf\x62\xbe\x48\xdd\x2f\x81\x90\xb6\x80\x38\x74\x29\x97\x15\x20\x40\x2a\x48\xbb\xf7\x95\xd7\x99\x34\x6e\x52\x3b\xb2\xa7\x29\x15\xdb\xff\x8e\xc6\x75\xb7\x5d\x15\xc1\xa5\xb5\x3d\x6f\x66\xde\x7b\x33\x19\x8f\xe1\xe5\xc3\x46\x77\x15\xac\xbc\x10\xbd\x54\xad\x5c\x22\xd4\x6b\xf2\xd6\xd1\x3d\xa1\x27\x21\xf4\xba\xb7\x8e\x20\x13\x49\xba\x96\xd4\xa4\x22\x49\x1d\xd6\x1d\x2a\xe2\x23\x63\xb4\x59\xa6\x42\x24\xa9\x36\x84\xce\xc8\x6e\x1c\x0b\xa4\x22\x17\x62\x3c\x06\x83\x58\xf9\xdb\x56\xf7\xe0\x90\x6b\x79\xd8\x36\x48\x0d\x3a\xa0\x06\xa1\xd5\xa6\x82\xca\xa2\x37\xff\x13\x6c\xad\x6b\xa1\xb6\x0e\x38\x5f\x9b\x25\x58\x03\x9f\x6d\xdf\xa0\xfb\x7a\x5b\x8a\x7a\x63\xd4\xa9\x5a\xd6\x42\x24\x52\x7e\xd3\xa6\xca\xe1\xc1\xda\x0e\x7e\x89\xc4\x6f\x35\xa9\x06\x5a\x3e\x2b\xe9\xf1\x09\xf6\x83\x5c\xf1\x74\xb9\x69\xa4\x99\x89\x24\x71\x48\x1b\x67\x80\xdc\x06\x45\xb2\x17\xc7\x7b\x2d\x3b\x8f\x62\x1f\x04\x2c\x2c\xe1\x0c\xfc\xce\x28\xd8\x6a\x6a\x02\x6d\xeb\xf4\x52\x1b\xd9\xc1\x1d\x7a\xba\xb1\xeb\x5e\x3a\x8c\x0c\xcf\x5e\x32\x82\x17\xd1\xa2\xf2\x2e\x67\x42\x2c\xee\xbe\x00\x7e\x84\xd9\x1c\x9c\x34\x4b\x04\x75\x40\x73\xa2\x67\x50\x40\xe9\x02\x86\xc9\x09\x13\x32\x38\x16\x82\xab\x02\x86\xe9\x9f\x82\x89\xae\xcf\x2c\x1a\x26\xc1\x9b\x2c\xcf\x63\x34\x51\xd6\x90\x36\xac\x35\x49\x58\xee\x45\xc6\xf4\x1f\x19\xe1\x4f\x71\xeb\x38\xe6\xf2\xa8\x75\x98\x30\xa9\x3c\x00\x06\xe9\x00\x7f\xf6\xa8\x08\xb4\xa1\xf0\x14\xc7\x72\xa8\x1a\xe6\xa2\x61\x3e\x87\xd5\xec\xd0\x26\xa2\xe7\x30\x39\xdc\xd9\x77\xb9\xf0\x20\x1d\x02\x39\xad\xda\x5d\x79\x08\xe8\x1a\x68\xd7\x33\x81\x61\x52\xde\xed\x7a\xcc\xf2\x6b\xc8\x68\xd7\x47\xe2\x5c\xf4\x38\xe4\x4f\x9d\x95\xf4\xe6\x35\x3c\x3e\xc2\x5f\x00\xef\xde\xe6\x70\x75\x05\xbc\xde\xe5\x17\xbf\x90\x0b\xf6\x2d\x44\xce\x6c\x38\x11\x7c\x35\x3d\xbc\xec\xcf\x95\xbc\xbf\x14\x12\x71\x11\xf0\xe1\x12\x30\x7d\x3e\x04\x05\xff\xcd\x8f\xa6\xc5\xa6\x54\x7e\x74\xce\xba\x3a\x4b\x47\x7e\x76\x5c\x93\x6c\x34\x14\xa3\x21\x9f\x8f\xaa\xeb\x23\x7c\x54\xa5\xc5\xc9\x0e\x3e\xf2\x28\x0a\x50\x45\x44\xe4\xa7\x56\xfc\xb3\xe7\x55\xdf\x8b\xd3\xbe\x7e\x77\x15\xba\xcb\x6d\xa5\x32\x2c\x45\xda\x1a\xbb\x35\xa0\xbd\xdf\xe0\x0c\x8c\xee\xa0\xc5\xdd\xb3\x8f\x36\xcd\xc5\x5e\xfc\x0e\x00\x00\xff\xff\xd0\xa4\x01\x39\x4f\x04\x00\x00"), - }, - "/src/internal/poll": &vfsgen۰DirInfo{ - name: "poll", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 432861736, time.UTC), - }, - "/src/internal/poll/fd_poll.go": &vfsgen۰CompressedFileInfo{ - name: "fd_poll.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 432947943, time.UTC), - uncompressedSize: 1931, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x54\x41\x6f\x2a\x37\x10\x3e\xaf\x7f\xc5\x28\x97\xec\x12\xd8\x4d\xdb\x5b\x14\x0e\x15\x69\xd2\x48\x55\xa9\x92\x48\x39\x20\x1a\x19\x7b\x80\x49\xbc\xb6\x6b\x7b\x83\x10\xca\x7f\xaf\xbc\xbb\x04\x48\xe0\x85\xf7\xa4\x77\x02\x79\x66\xbe\xf9\xbe\x6f\x67\xa6\x28\xe0\x6c\x52\x91\x92\xf0\xec\x19\xb3\x5c\xbc\xf0\x19\x82\x35\x4a\x31\x46\xa5\x35\x2e\xc0\x49\xa0\x12\x4f\x18\x2b\x8a\xfa\xfd\x0a\xbd\x00\xf2\xc0\x41\x9b\x9e\xb1\x40\xa5\x55\x58\xa2\x0e\x3c\x90\xd1\x60\xa6\xc0\x35\xdc\x16\xc3\x3a\x19\x1d\x4c\x8d\x83\x9b\xe1\xef\x77\x83\x3f\xfb\xcf\x3e\x67\x45\x11\x81\x6e\x83\xff\x58\x48\x1e\x26\xdc\xa3\x04\xa3\xe1\x6f\x3e\xf8\x0b\x48\xc3\x4c\x80\x30\xa5\xa5\x88\x93\x7a\x44\xb8\x19\xde\x0d\x87\x0f\x85\x77\xa2\x20\x1d\xd0\x69\xae\x8a\xd8\xa7\x98\xca\xa7\xf8\xfb\xa4\xb9\x50\xf9\xcc\x64\xdd\xd8\x65\x52\x05\xa0\x00\xd2\xa0\x07\x7c\x45\x0d\x0a\xbd\xcf\x59\x58\x5a\xdc\x48\xf1\xc1\x55\x22\xc0\x8a\x25\x42\x19\x4f\x7a\x06\x13\x63\x14\x7b\x63\x6c\x5a\x69\x01\xa9\x95\xd0\x59\x27\x67\x40\x9a\x42\x3a\x95\xd0\xb9\xbe\xca\x00\x9d\x33\x0e\x56\xe0\x30\x54\x4e\x83\x26\x05\x07\xca\x22\x34\xa6\x19\xac\x0e\xc4\xf1\x95\x44\x88\x71\xb0\x32\x5f\xf3\xe8\x43\x70\x15\x1e\x82\xb4\x0e\x2d\x77\x98\x96\x46\x22\x90\x0e\x5d\x20\x7f\x4d\x0a\x6b\xfa\xef\xdc\x58\x42\xd3\x6d\xcc\x15\x4b\x92\x96\x2e\x3a\x37\x68\x5e\xd3\xa6\x32\x63\xc9\x1b\x4b\x36\x62\x0e\x79\xd0\x76\xbe\x43\x2e\xd3\x7d\x3d\xd7\x7e\x58\x99\xaf\x49\x9e\xba\xd3\x35\xbf\xec\x0b\x41\x8f\x8e\x02\x1e\x8d\xbb\xf8\x1a\x77\xc1\x29\xfc\x2c\x97\xfe\x70\xee\x81\x4a\x34\x55\x38\x64\x56\xec\x7e\x8c\x53\x35\xcb\x63\x6c\x8a\x89\x47\x79\xd4\x20\xee\x35\xe8\x03\xdc\x80\x6b\x81\x0a\xe5\xbb\x4b\xdb\x83\xba\xfd\x85\x8c\x52\x7c\xa2\xe2\x20\xc7\xa6\x9b\x6e\xbb\x73\x5a\xef\xc6\x3d\x86\x2b\xe4\x52\x91\xc6\x34\x40\x3c\x21\x79\x74\xea\xdb\x4b\xb3\xae\x8c\x86\xfd\x78\x75\xed\xce\xf7\x94\x17\x05\xfc\xd3\x8a\x74\x64\x83\x71\x6d\xdc\x43\x98\x23\xc8\xcd\xf3\x04\xe3\x74\x54\xf1\x4a\x4d\x96\x75\xb0\x39\x72\xf5\xb5\x31\x0e\xfe\xad\x48\x07\x1b\x5c\x7a\x9e\x01\x4d\x63\x82\x43\x20\xaf\x4f\x03\x18\x8d\x39\x3c\xcc\xc9\xc7\x43\x67\xb4\x5a\x36\x30\xf1\x3a\x06\xf4\x81\xf4\x2c\x6f\x64\xec\x32\x49\x33\x68\x31\xe3\x50\xb6\xb4\xb7\xda\xb0\x86\xfe\xc0\xd8\x65\x3c\xbd\x7e\xa9\x45\xee\x2a\x1d\x25\x3f\xdd\x63\xc9\xc5\x7f\x15\x39\x6c\xa1\x3f\x07\x52\x0f\x9d\x08\xf6\xdb\xaf\x59\xbb\x05\x1d\x0f\xfd\x3e\x9c\xd7\x2b\x20\xe6\x70\xd1\x87\x92\xbf\x60\x2a\xe6\x5c\x37\x93\xc6\x92\xc4\x63\xf9\xc8\x29\xa0\xf3\x23\x3f\x86\x3e\x70\x6b\x51\xcb\x74\xe7\xb9\x0b\x62\x1e\x73\x2f\x7b\x62\x5e\x6f\x4c\xc7\xf7\x7a\x5f\xb0\x75\xa8\x90\xfb\x3d\x6c\xdb\xc0\x07\xb6\x1d\x7f\x76\xc6\x58\xb2\x88\x24\x77\x7a\xd7\x42\x14\xea\x74\x91\x6d\xc4\x34\xde\x45\x2a\xac\x15\xb6\x18\x9d\x8f\x63\x79\xfc\xf7\xcb\xc5\x98\x7d\xd2\xb5\xd8\x0b\x24\x51\x61\xc0\x2d\xb5\x5d\xf0\xd9\x3b\xee\x65\xaf\xde\x86\xa8\xf4\x95\xbb\x2d\x5e\xd0\x3a\x59\x72\x3b\x6a\x55\x8c\x47\xe3\x2d\x5f\xff\x0f\x00\x00\xff\xff\x9e\x79\xbb\x91\x8b\x07\x00\x00"), - }, - "/src/internal/syscall": &vfsgen۰DirInfo{ - name: "syscall", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 433101921, time.UTC), - }, - "/src/internal/syscall/unix": &vfsgen۰DirInfo{ - name: "unix", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 433166644, time.UTC), - }, - "/src/internal/syscall/unix/unix.go": &vfsgen۰CompressedFileInfo{ - name: "unix.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 433222605, time.UTC), - uncompressedSize: 149, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x3c\xc8\x3f\xaa\xc2\x40\x10\x07\xe0\xfa\xcd\x29\x7e\x65\xc2\x0b\xc4\x0b\x78\x00\x1b\x2b\x2f\x30\xd9\x3f\x61\xcd\x3a\x13\x66\x67\x41\x10\xef\x2e\x42\xb0\xf8\x9a\x6f\x9e\xf1\xbf\xf4\x52\x23\xee\x8d\x68\xe7\xb0\xf1\x9a\xd0\xa5\x3c\x89\x82\x4a\x73\x18\x4b\xd4\xc7\xcd\x78\xc7\x19\xa7\x23\x73\x73\x76\xf6\xdf\x52\xee\x12\x70\x69\x57\x95\xa5\x6a\xd8\x86\x1c\x51\xc4\x47\x0c\x72\x4c\x91\x15\x8b\x6a\x9d\x90\xcc\xbe\xd4\x46\xbc\xe8\xcf\x92\x77\x13\x64\xae\x2d\x4d\x90\x52\xe9\x4d\x9f\x00\x00\x00\xff\xff\x38\x6c\xdd\x48\x95\x00\x00\x00"), - }, - "/src/internal/testenv": &vfsgen۰DirInfo{ - name: "testenv", - modTime: time.Date(2018, 4, 20, 11, 3, 8, 366769229, time.UTC), - }, - "/src/internal/testenv/testenv.go": &vfsgen۰CompressedFileInfo{ - name: "testenv.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 424, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x8f\xc1\x6a\xc3\x30\x0c\x86\xcf\xd1\x53\x08\x9f\x12\x36\x92\xfb\x6e\xa3\x8c\xf5\xd6\xb2\x3e\x81\xeb\x2a\x8d\xbb\x58\x2e\x92\xb2\xb4\x8c\xbe\xfb\xf0\xd6\x52\xd8\x06\x3e\xfd\x9f\xfd\xf1\xb9\xeb\xf0\x61\x3b\xc5\x71\x87\x07\x05\x38\xfa\xf0\xee\xf7\x84\x46\x6a\xc4\x1f\x00\x31\x1d\xb3\x18\xd6\x50\x39\x99\xd8\x62\x22\x07\x95\x53\x93\xc8\x7b\x75\xd0\x00\x74\x1d\x2e\xbd\xbe\x9c\x28\xa0\x50\xb9\xac\x38\x0f\x64\x03\x09\xda\x40\x18\x26\x11\x62\x43\x3d\xab\x51\xc2\xe0\x19\xd5\xbc\x18\x32\xcd\x78\x94\x1c\x48\x95\xb4\x58\x26\x8d\xbc\xc7\xac\xed\xa6\xf0\xf5\x0f\xc2\x2c\x58\xa7\x2c\x84\x21\xa7\x94\x79\x3c\x37\x48\x27\x0a\xed\x22\xa7\xe4\x79\xd7\x42\x3f\x71\xb8\x15\xd4\x0d\x6e\x73\x1e\xf1\x13\x2a\x9d\xa3\x85\x01\xaf\xd1\xed\xeb\x6a\xb5\x29\x73\xf0\x4a\xe8\xd8\x87\xd1\x3d\x41\x55\x09\xd9\x24\x8c\xbd\x1f\x95\x6e\x70\xe7\x65\x8e\xfc\x8d\x63\x8f\xd7\xaf\xb6\x4b\xaf\x6b\xa1\x3e\x9e\xea\xbb\xf2\xf9\x6d\xb1\x7c\x44\xe7\x25\xb9\xa6\xc8\x7f\xfb\xaa\x0b\x94\xf3\x27\xa5\xbc\xbb\xc7\x1c\xf4\x9f\x94\x0b\xdc\x06\x93\x89\xe0\x02\x5f\x01\x00\x00\xff\xff\xdc\xf8\xeb\x9e\xa8\x01\x00\x00"), - }, - "/src/io": &vfsgen۰DirInfo{ - name: "io", - modTime: time.Date(2018, 4, 20, 9, 18, 9, 731474926, time.UTC), - }, - "/src/io/io_test.go": &vfsgen۰CompressedFileInfo{ - name: "io_test.go", - modTime: time.Date(2018, 2, 27, 18, 42, 13, 0, time.UTC), - uncompressedSize: 574, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\xd0\x41\x4b\xfb\x40\x10\x05\xf0\x73\xf7\x53\x0c\xbd\xfc\x9b\xbf\x92\x7e\x06\x29\x46\x10\xbc\x98\x82\xc7\xb2\x26\xcf\x64\xec\x66\x76\x99\x9d\x45\x51\xfc\xee\xd2\xa6\xa7\x52\x0f\xde\x3c\x2d\x3c\x78\xcb\xef\xcd\x7a\x4d\x57\xcf\x85\x43\x4f\xaf\xd9\xb9\xe4\xbb\xbd\x1f\x40\x1c\x77\x86\x6c\xce\xf1\x94\xa2\x1a\xad\xdc\x62\x79\x08\x58\x86\xa5\xab\x9c\x7b\x29\xd2\xd1\x16\xd9\x1e\x4a\x30\x7e\x52\x36\xe8\xee\xf8\xb4\xa6\x2c\x43\xcb\x32\x04\xdc\x84\x10\xbb\x95\xd1\xff\x53\xb5\xde\x56\xf4\xe9\x16\x56\xb7\x7b\x4e\xab\xca\x7d\x9d\x7f\xf4\x08\xdf\x43\x9b\xe0\xcd\x20\x3f\x16\x8f\x12\x52\x04\x46\xa6\x28\xa4\x45\x8c\x27\xd4\x1b\x1f\x02\x34\x93\x97\xfe\x3c\x6b\xd4\x4f\xc8\xd7\xf4\x36\x72\x37\xd2\x5d\x4c\x23\xf4\xbe\xa5\x3e\x22\xcb\x3f\xa3\x5c\xd2\x61\xe6\xf2\x02\x69\xde\x36\xef\xd9\x8c\x9e\xe5\x4f\xe9\x4e\x07\x53\x20\xdf\xbe\x8f\xbe\x64\x43\x3f\x67\xf9\xd7\xc0\x16\xd6\xb0\xf8\xc0\x1f\xd0\x8b\x16\x92\x68\xc4\x53\x0a\x98\x20\x33\xe7\x3b\x00\x00\xff\xff\x75\x6f\xe1\xab\x3e\x02\x00\x00"), - }, - "/src/math": &vfsgen۰DirInfo{ - name: "math", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 433889333, time.UTC), - }, - "/src/math/big": &vfsgen۰DirInfo{ - name: "big", - modTime: time.Date(2018, 4, 20, 9, 34, 7, 314436336, time.UTC), - }, - "/src/math/big/big.go": &vfsgen۰CompressedFileInfo{ - name: "big.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 174, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x44\x8d\xbd\xaa\xc3\x30\x0c\x46\x77\x3f\x85\xf6\x0b\x11\x5c\x68\x87\xcc\xdd\x03\x25\xd0\xd9\x89\x15\xdb\xf9\x93\x91\xe4\x94\xbe\x7d\x49\x3b\xf4\x9b\xbe\xe1\x70\x0e\x22\xfc\x0d\x35\xaf\x01\x66\x75\xae\xf8\x71\xf1\x91\x60\xc8\xd1\x39\x44\xe8\xbb\x5b\xd7\x42\x9f\xb2\x42\x56\xf0\xf0\x64\x59\xbc\x70\xdd\x03\x4c\x2c\x90\xcc\x8a\xb6\x88\x31\x5b\xaa\x43\x33\xf2\x86\x91\x4b\x22\x99\xf5\x77\xb2\x6a\x25\xc5\xeb\xe5\xbf\x39\x95\xdf\xdd\x69\xe3\x83\xc0\x4f\x46\x02\x96\xbc\xc1\x07\x3b\x2b\x42\xca\xeb\x41\xa1\x71\xf6\x2a\x04\x0f\x96\x00\x35\xef\x56\x4c\xdc\x3b\x00\x00\xff\xff\x55\xc0\x14\x01\xae\x00\x00\x00"), - }, - "/src/math/big/big_test.go": &vfsgen۰CompressedFileInfo{ - name: "big_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 148, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd2\xd7\x57\xd0\x4e\x2a\xcd\xcc\x49\x51\xc8\x2a\xe6\xe2\x2a\x48\x4c\xce\x4e\x4c\x4f\x55\x48\xca\x4c\xe7\xe2\xca\xcc\x2d\xc8\x2f\x2a\x51\x50\x2a\x49\x2d\x2e\xc9\xcc\x4b\x57\xe2\xe2\x4a\x2b\xcd\x4b\x56\x08\x49\x2d\x2e\x71\xaa\x2c\x49\x2d\xd6\x28\x51\xd0\x82\xca\xe9\x85\x68\x2a\x54\x73\x71\x96\xe8\x05\x67\x67\x16\x68\x28\x25\x15\xe5\x67\xa7\xe6\x29\x69\x72\xd5\x22\xe9\xf1\xcd\x4f\x09\x2e\x2c\x2a\xc1\xad\xab\x38\x27\xbf\x1c\xac\x07\x10\x00\x00\xff\xff\x9b\x59\x2d\xf0\x94\x00\x00\x00"), - }, - "/src/math/bits": &vfsgen۰DirInfo{ - name: "bits", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 433404122, time.UTC), - }, - "/src/math/bits/bits.go": &vfsgen۰CompressedFileInfo{ - name: "bits.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 433455586, time.UTC), - uncompressedSize: 314, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x8e\xc1\x4a\xc5\x30\x10\x45\xd7\x9d\xaf\xb8\x74\x95\x20\xbc\xec\x05\x97\xfe\x80\x3f\x20\xed\xeb\xbc\x32\xda\x26\x65\x92\x54\x6a\xf1\xdf\xc5\x24\x82\xb8\x7a\x9b\x2c\xce\xcd\x39\x8c\x73\x78\x18\xb3\x2c\x13\xde\x22\xd1\x36\x5c\xdf\x87\x99\x31\x4a\x8a\x44\xe9\xd8\x18\xaf\xac\x8a\x98\x54\xfc\x4c\x74\xcb\xfe\x0a\x53\xa1\xc5\xb3\x6a\x50\x63\xdb\x8a\x93\x3a\xe5\x94\xd5\x37\x60\xd8\xd2\x17\x91\x73\x78\xc9\x3e\xc9\xca\xe5\x3f\x64\xdd\x16\x5e\xd9\xa7\x08\xad\xfc\x52\x86\xcb\xbf\xfa\x5f\xc9\x58\x9c\x3f\xad\x7d\x50\x18\xea\xc2\xce\x7a\x5b\xc2\x47\x0d\x72\x79\x9f\x8a\x66\xfa\xd6\xac\xf4\x11\xe2\x13\xcf\xac\xf8\x55\x7a\x4b\xdd\x24\xbb\x4c\xed\x1a\xdc\xa7\x57\x05\xe3\x81\x4f\xd6\xd0\x5b\xb2\xf4\x1d\x00\x00\xff\xff\x76\x78\x13\x86\x3a\x01\x00\x00"), - }, - "/src/math/math.go": &vfsgen۰CompressedFileInfo{ - name: "math.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 433707031, time.UTC), - uncompressedSize: 4581, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x57\xdd\x6e\xdb\x38\x13\xbd\xb6\x9e\x62\x3e\xe3\x43\x57\xda\x2a\xb2\xe5\x04\x41\x51\xc4\x05\xba\xc1\xa6\x5b\xa0\xed\x2e\x36\xed\xde\x04\xbe\xa0\x64\xd2\xa6\x2b\x91\x2a\x49\xc5\x72\x9b\xbe\xfb\x82\xd4\x1f\x25\x5b\xb1\xbd\x57\xb6\xc8\x33\x67\xce\x8c\x66\xc8\xd1\x64\x02\x2f\xa3\x9c\x26\x4b\xd8\x48\xc7\xc9\x50\xfc\x15\xad\x30\xa4\x48\xad\x1d\x87\xa6\x19\x17\x0a\x5c\x67\x34\x5e\x51\xb5\xce\xa3\x20\xe6\xe9\x64\xc5\xb3\x35\x16\x1b\xd9\xfe\xd9\xc8\xb1\xe3\x39\xce\x23\x12\xc6\x10\xe6\xb0\x91\xc1\xbb\x84\x47\x28\x09\xde\x61\xe5\x8e\x3f\x22\xb5\x1e\x7b\x06\xf0\x1d\x0b\x0e\x24\xe1\x48\x5d\x5f\xc1\x1c\xa6\x66\x31\xe3\xf2\x3d\x23\x30\x87\x10\x26\x06\x61\x56\x19\x5e\x95\xab\x17\xdd\x65\xc4\xb4\x61\xbd\xe4\x90\x9c\xc5\xf0\x36\xe6\xd2\x2d\x6a\x62\xaf\xf1\xf0\xc3\x19\x09\xac\x72\xc1\x8c\xb2\xe0\x16\x25\x89\x3b\x46\x31\x97\x63\x1f\x0a\x2f\xb8\xd3\x30\xd7\x73\x7e\x5a\x34\xeb\xb3\x78\xd6\x03\x44\x92\xb2\xd3\x79\x24\x65\xc3\x34\x67\xe8\xd1\xe8\x01\x22\x85\xce\xd0\xa3\xd0\x90\x1e\x85\xce\xd1\xa3\xd1\xc3\x44\x33\x77\xe7\xc3\x39\x5c\xb3\xb1\x0f\xbb\x83\x74\xb7\x91\x50\x27\xcb\x8a\x23\xa1\x0e\xab\xba\xc5\x34\x39\x9d\x06\xd3\x64\x80\x86\x67\x3b\x49\x57\xcc\x2d\x7c\xd8\x1d\x64\xa3\x04\xdc\x02\x6e\x60\x0a\x4f\x4f\x10\x4e\x0a\x98\xcf\xab\x72\xf7\xe0\x7f\x73\x70\x77\xed\xde\xce\xde\xfb\xe1\x8c\x6a\x25\x17\x85\x33\xfa\xd9\xe8\x2a\x2c\xe7\xa7\x37\xc2\x60\x1f\xdc\x9e\xd3\x06\xc3\x5d\xf0\xbb\x20\xcf\xb3\x60\x0d\xe8\xe0\xe3\xa3\x06\x71\xc7\xa2\xc8\x4e\xd6\x89\x8b\x6c\x40\x66\x91\xcd\x4e\x66\xc9\xf8\x76\xec\xc3\x6c\x88\x28\x0d\x8f\x04\x50\x42\x5a\x9b\xbb\x84\x73\x71\xb2\x77\xa2\xd1\x87\xa3\xb8\x13\xb8\xc8\x5c\xd2\x12\xb9\x44\xa0\xb8\x7e\xf4\xb5\x67\xa0\x4c\x79\x16\x31\x29\x4d\x5a\x8e\x3f\x76\x19\x57\x6e\xe6\xc3\xb7\xe7\xf4\xac\x1b\x54\x6b\xf9\x9e\x11\x57\xd7\x7c\xe9\xc2\xb2\x91\x5b\xaa\xe2\xb5\xfe\x17\x23\x89\xc1\x60\xde\xcc\x61\xfa\xba\x2d\xe5\xf2\xf8\x77\x46\x4b\x4c\x50\x9e\x28\x6b\xa7\xac\x7b\x5d\xe8\x8d\x1f\x0d\x6d\xa3\xf4\xa1\x75\x1a\x71\x9e\x54\xcd\x45\x74\xd3\x54\xb7\x8a\xd5\x33\x8d\x73\xd3\x3a\x35\xae\xba\x67\xfa\xb8\x9b\x1a\x57\x27\x0b\x25\x12\x5b\x3a\x3e\xa1\x4f\x9d\x6c\x53\x69\x14\x74\xf2\xab\x9b\x99\x34\x36\x1f\x96\x26\xdd\x87\xdf\x4a\xf7\x74\xb8\x08\xa7\xb3\x2b\xb8\x31\xdb\x2f\x5e\x98\x9f\x1b\x30\x6b\x3f\x60\x32\x81\x2f\x12\x83\xbe\x54\x83\x8c\x6f\x81\x70\x01\x32\x45\x49\x62\x60\x8f\x28\xc9\xb1\x84\xed\x1a\x0b\x0c\x54\xfd\x22\xe1\x91\xa2\x28\xc1\x01\xdc\x71\x01\x19\x16\x84\x8b\x14\xb1\x18\x07\xce\xc8\xa4\x40\xcb\x99\xeb\x0b\x55\x27\xa0\xad\x0c\x14\x3b\x23\x1d\xbd\xbd\x02\xbf\x1e\xec\x04\x5c\x64\x6d\x39\x5a\x19\x4b\x9a\x78\x4b\x4c\x9b\x08\xbe\x1a\xa8\x78\x4a\xa0\xd0\x49\x2b\xca\x38\xb7\x5c\x7c\x45\x82\xe7\x6c\x69\xa2\xe4\x99\xa2\x29\xfd\x8e\x05\x44\xf9\x0a\x28\x83\x7f\x5e\xf9\x20\x70\xca\x1f\x31\x20\x05\x92\xa7\x18\x32\x4e\x99\xb2\x2a\x08\x31\x5b\x92\x25\x3f\xe1\xab\xc3\x8d\xf4\x81\xaf\xc2\xe9\xf3\x1d\x99\x94\x90\xae\xcd\x91\x93\x28\x29\x21\x1d\x9b\x23\xc7\x4e\x62\x10\xad\xc5\x47\x54\x0c\xdf\x29\x4d\x84\x25\xc6\xb2\xa2\xcf\xdc\x44\xb5\x55\x85\xb1\xac\xf8\xf2\xa8\x55\x3b\xe6\x95\x29\xfd\x7f\xca\x97\x3a\xa7\x9a\x68\x2f\xad\x1f\xf9\x92\x74\x8f\xa7\xba\x07\x9a\xa5\xfd\xe6\x7d\x7a\x1a\xea\x51\xe2\x37\xef\x96\x12\x08\x27\xc3\x30\x73\x7e\x8c\x4c\xfd\xbe\x9e\x9b\xb8\x88\x0f\xa1\x67\x75\xe9\x05\x94\x45\x6a\xaa\xbe\xd6\xab\xfb\xfb\x50\xd0\xda\x6b\x8d\xf9\x8b\x6f\x9f\xbd\xe4\xcd\xc5\x1e\xea\x28\x5c\xf3\xf7\x22\xd4\xdd\xec\xee\xba\x11\xda\x57\x7c\xe7\x8e\x0f\x07\x4a\xb7\xec\xbc\xc3\x69\xfe\x1b\xa7\x88\xb2\x25\x16\x47\xdf\x9e\xe8\x20\x5b\x86\x7b\xba\x62\x11\xed\xcc\x53\xf5\xd1\x5a\x4f\x1b\x07\x47\x17\x8b\xe0\xf4\x59\x73\x70\xf4\xbd\x3f\x67\xf2\x1d\x1e\x7c\xef\x29\xeb\x7d\x1a\xb8\x92\x32\x1f\x62\x2e\x3b\x75\x57\x71\x1a\xe9\x9e\x5f\x4e\x51\x16\xcb\xb7\x33\xe6\x4b\xf9\x6d\x68\xbe\xfc\x7c\xc6\x10\x3e\x38\x83\x7f\x3e\x67\x04\x1f\x9e\xc0\x3f\x8b\x9c\x0d\x0d\x5b\x75\xe5\xb6\x25\x6a\xbd\xe6\xf2\xd1\x9c\xd1\xfd\x0a\xb0\x6b\xb7\x33\x9e\x36\x13\x71\xe5\xc4\xa5\x4c\xb9\x85\xe7\x69\x65\x5a\x91\xfe\xae\x8b\x72\x02\x52\x89\x3c\x56\x9a\x26\xa7\x4c\x5d\xce\x90\x10\x68\x07\xf0\x30\x5b\x94\xcf\xce\xc8\x10\xd4\x1b\x0f\xb3\x45\xf5\x5c\x6d\x5c\x5f\x55\x1b\xe1\xa2\x7a\x6e\xe2\xa5\x8c\x2a\xd7\xbc\x6a\x14\xe9\x73\xa0\xf7\x89\xfa\x56\xdb\xfd\x96\x13\x82\xc5\xd8\x0b\x3e\xe1\xad\xfb\xca\x73\x46\x1b\x19\xbc\x67\x0a\x0b\x86\x92\x3f\xa3\x0d\x8e\x95\x1b\xe5\xc4\x0b\xee\xb5\x85\xa5\x70\xec\xf7\xe9\xbe\x98\x4d\x43\x5a\xd1\xa1\xc8\x3b\x42\x68\x87\xb6\xcf\x78\x57\xee\xfe\x07\xca\x2a\x29\x03\x94\xd7\x57\x7b\x94\xd6\x68\xaa\x5d\x46\x54\xc9\xfa\xe0\xbe\x9c\x79\x50\x06\xae\x33\x19\xe5\x24\xb0\x55\x3f\x4c\x17\xa0\x07\x9e\xfa\xb5\xeb\x7d\x2b\x4d\x0f\xd3\x45\x9f\x9b\x08\x9e\x1a\xfe\xa8\xa2\xf5\x6a\x3f\x35\x7f\xd7\x1e\xe6\x10\x75\xe8\x7b\xee\xbb\xfc\xd7\x57\xb6\x76\x5d\xe4\x9a\xad\xac\xf1\xc6\xb8\x4a\x4f\x5f\x7b\x89\x74\xfb\x12\xc2\x85\x77\x73\x73\x39\x83\x97\x43\x80\xe9\xc2\xeb\x8b\xe8\x05\xd9\x6b\xb6\x83\x41\x96\x0b\x6e\xe4\xed\xef\x87\xf6\x3e\xbc\x79\x03\x97\x33\x6f\x3f\x25\x6d\x54\xce\x4f\xe7\xdf\x00\x00\x00\xff\xff\x85\x20\xa4\x35\xe5\x11\x00\x00"), - }, - "/src/math/math_test.go": &vfsgen۰CompressedFileInfo{ - name: "math_test.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 433956447, time.UTC), - uncompressedSize: 704, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\x92\x3f\x6f\xdb\x30\x10\xc5\xe7\xf0\x53\x3c\x78\x89\xdd\xca\x16\x02\xb8\x19\xba\x78\x69\x50\x64\x28\x5c\x20\xde\x8b\x93\x7c\x92\xae\xa1\x48\x95\x77\x8a\x2c\x04\xf9\xee\x85\x6c\xb7\xca\x12\x4e\xfc\x73\xf7\x7b\xef\x1e\x98\xe7\xf8\x5c\xf4\xe2\x8f\xf8\xad\xce\x75\x54\x3e\x53\xcd\x68\xc9\x9a\x5f\xc6\x6a\xce\x49\xdb\xc5\x64\x58\xba\x9b\xc5\x74\x21\xa1\x5e\xb8\x95\x73\x79\x8e\x27\x2f\x75\xe3\x47\x34\x52\x37\x9c\x60\xd1\x73\xa2\x50\xb2\xc2\x1a\x0a\xe8\x3b\xb5\xc4\xd4\x66\x88\xd6\x70\x1a\x44\x19\x07\x56\xfb\x4e\x6d\x4b\xa8\x48\xbc\x6e\x26\xcc\x61\xff\x6d\xff\x15\x8f\x53\x17\x27\x06\xa1\x60\x33\x4e\x18\x68\x84\x45\x54\x72\x9a\xdb\x76\x78\xb4\x5b\xc5\xc0\x92\x8e\x93\x8a\x21\x06\x3f\x22\x06\xc6\xd9\x6d\x9e\xe3\xb2\x12\xff\xe9\x25\xb1\x42\x42\x99\x98\x54\x42\xfd\xce\xe0\x06\x3f\x39\x35\xd4\x5d\x35\x6f\x75\x56\xad\xe4\xb4\xc3\x0f\x1a\x0b\xc6\xc0\x33\x4f\x9b\xd8\xfb\x23\xe2\x0b\xa7\x24\xc7\xf7\x83\x68\xc7\xa5\x54\x52\x92\xf7\x23\x28\x1c\x11\xa2\x4d\x58\x5c\xb3\x5c\x0f\x53\xfd\xac\x9d\xcd\xd0\x82\x4b\xea\x95\x61\x8d\x28\x06\xf1\x1e\x97\x73\x4b\x61\xbc\x84\x76\x9e\x4a\xa7\x18\x0a\x86\x67\x55\x50\x59\xf6\x89\x8c\x37\xd8\x27\xb4\x67\x9f\x53\xfb\x0c\x15\x45\x25\x81\x77\xae\xea\x43\x89\xd2\x47\xe5\x25\x65\x28\x50\xf9\x48\x76\xbf\x5d\xa1\x88\xd1\x9f\x4b\x5f\x91\xd8\xfa\x14\x66\x77\xe7\xca\x0c\x5b\x5e\xdf\x6d\x57\x78\xbb\x30\x5e\x38\x8d\x1f\x72\x3e\x64\xdc\xf3\xfa\xee\xcb\xc4\xb8\x40\xa6\x41\x1e\x4e\xdd\xd2\xf0\xe9\xfa\x8d\x36\x87\x0c\x0f\xa7\x0e\xd3\xf3\xf2\x3f\xf4\xba\xc9\x10\xa8\x65\xa8\x25\x09\xf5\x0a\xaf\xee\xc6\x36\x4f\xcf\xd2\x2d\x17\x12\xfe\x45\xb0\x58\xb9\x37\xf7\x37\x00\x00\xff\xff\x4e\x32\x53\x1a\xc0\x02\x00\x00"), - }, - "/src/math/rand": &vfsgen۰DirInfo{ - name: "rand", - modTime: time.Date(2018, 4, 20, 9, 43, 49, 187307567, time.UTC), - }, - "/src/math/rand/rand_test.go": &vfsgen۰CompressedFileInfo{ - name: "rand_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 160, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\xcb\x51\x0a\xc2\x30\x0c\x00\xd0\x6f\x73\x8a\xd0\xaf\x4d\x61\x03\x3d\x82\xe0\x05\xdc\x05\x6a\x57\x4b\x5c\x4d\x4a\x93\x22\x22\xde\x5d\x10\x3f\xfc\xd9\xf7\xe3\x8d\x23\xee\x2e\x8d\xf2\x8c\x37\x05\x28\x3e\x2c\x3e\x45\xac\x9e\x67\x00\xba\x17\xa9\x86\xce\xa2\x1a\x71\x72\x00\xd7\xc6\x01\xa7\xa8\x76\xca\xe2\xed\xb0\xef\x0c\xb7\x3f\x1d\xa6\x1e\x5f\xb0\xb1\xe1\xbc\x50\xe9\x9c\x66\x79\xb8\x1e\xde\x7f\xe7\x28\x1c\x5a\xad\x91\x6d\xbd\x35\x25\x4e\xc8\xa2\x4f\x0e\xdf\xfe\x09\x00\x00\xff\xff\x3d\xb4\x3b\xb8\xa0\x00\x00\x00"), - }, - "/src/net": &vfsgen۰DirInfo{ - name: "net", - modTime: time.Date(2018, 4, 20, 9, 12, 45, 414149374, time.UTC), - }, - "/src/net/http": &vfsgen۰DirInfo{ - name: "http", - modTime: time.Date(2018, 4, 20, 9, 31, 37, 938492727, time.UTC), - }, - "/src/net/http/cookiejar": &vfsgen۰DirInfo{ - name: "cookiejar", - modTime: time.Date(2018, 4, 20, 12, 39, 47, 414045680, time.UTC), - }, - "/src/net/http/cookiejar/example_test.go": &vfsgen۰CompressedFileInfo{ - name: "example_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 269, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\xcc\x41\x4e\xc3\x30\x10\x85\xe1\x75\xe7\x14\x4f\x5d\xb5\x02\x35\x82\x65\x77\xa8\x02\x24\x16\x05\xd1\x03\xd0\xa9\x3d\x21\x6e\x1c\xdb\x78\x26\x0d\x08\x71\x77\x14\xb1\x65\xfb\xf4\xbd\xbf\x69\x70\x75\x1a\x43\xf4\x38\x2b\x51\x61\xd7\xf3\xbb\xc0\xe5\xdc\x07\x39\x73\x7d\x33\x51\x23\x0a\x43\xc9\xd5\xb0\x6c\x07\x5b\x12\xb5\x63\x72\xb8\xff\xe4\xa1\x44\xd9\xcb\xb4\x5a\xe3\x9b\x16\x4d\x83\x24\x36\xe5\xda\x83\x9d\x13\x55\xa4\x6c\xd0\xb1\xcc\x4f\xf1\x38\x7d\xe1\x31\x97\x4e\xea\xd3\xe1\x1a\x9c\x3c\xac\x0b\x8a\x39\x0f\x2f\x45\x92\x57\xe4\x84\xce\xac\xcc\xdb\x66\x2f\xd3\x41\xea\x45\x2a\xd1\xa2\x1d\x6c\xf3\x52\x43\xb2\x98\x56\xc7\xbb\xd6\xa4\xe2\x46\x0d\x55\x3e\x46\x51\xdb\x12\xf0\x10\xf9\x92\xeb\x16\xbb\x2e\xbb\x1c\xd9\x04\xbb\x2e\x14\xfa\xb3\xb7\xc9\xff\x67\x9f\xd9\x06\xe1\x88\x57\x0e\x1a\xd2\x71\x4d\x3f\xf4\x1b\x00\x00\xff\xff\x4a\xaa\xb1\x5a\x0d\x01\x00\x00"), - }, - "/src/net/http/fetch.go": &vfsgen۰CompressedFileInfo{ - name: "fetch.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 3551, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x56\x5f\x6f\xdb\x36\x10\x7f\x16\x3f\xc5\x4d\xc3\x3a\x29\xb5\xa5\x16\x28\xfa\xe0\xc5\x0f\xa9\x9b\x76\xc1\xda\xa5\x48\xb2\xa7\x20\x18\x68\xe9\x24\x31\x91\x48\x85\xa4\x92\x18\x81\xbf\xfb\x70\xa4\x24\xcb\x49\xda\x62\x01\xea\x4a\xe2\xf1\xee\x77\x77\xbf\xfb\x93\xa6\xf0\x7a\xdd\x89\x3a\x87\x6b\xc3\x58\xcb\xb3\x1b\x5e\x22\x54\xd6\xb6\x8c\x89\xa6\x55\xda\x42\xc4\x82\x10\xb5\x56\xda\x84\x2c\x08\x8b\xc6\xd2\x7f\x42\xf9\xdf\x54\xa8\xce\x8a\x9a\x5e\x8c\xd5\x99\x92\x77\x21\x63\x41\x58\x0a\x5b\x75\xeb\x24\x53\x4d\x5a\xaa\xb6\x42\x7d\x6d\x76\x0f\xd7\x26\x64\x31\x63\x69\x0a\xc6\x6a\xe4\xcd\x19\xf2\x1c\x35\x88\xa6\xad\xb1\x41\x69\x0d\x70\x09\x42\x25\xf4\x7d\x55\x2b\x83\x1a\xee\x35\x6f\x5b\xd4\x50\x28\x0d\xf4\x99\xaf\x6b\x3c\x77\x97\x41\x15\x0e\xae\x59\xa4\x69\x81\x36\xab\x12\xd3\x62\x96\xdc\x57\xdc\xde\x97\x89\xd2\x65\x9a\x30\xbb\x69\x71\xdf\x96\xb1\xba\xcb\x2c\x3c\xb2\xa0\x45\x99\x0b\x59\xc2\xe5\xd5\x7a\x63\x91\x05\x5e\x0c\xe0\xe0\xda\x24\xa7\xeb\x6b\xcc\x2c\xdb\x32\x56\x74\x32\x83\x48\xc3\xc1\x54\x4b\xec\xa0\x44\x6d\x7f\x37\x86\x48\x82\x90\x76\x06\xa8\x35\xb8\x88\xc5\x64\x41\x14\x50\xa3\x8c\x74\xd2\x9b\x8a\x61\xb9\x84\x37\x74\x12\xdc\x71\x4d\xe1\x0d\x82\xf5\xaa\x02\x80\x25\x34\xfc\x06\xa3\xac\xe2\x72\xd0\x49\x87\xa8\xf5\xaa\xda\x3b\xf4\xca\x59\x10\xd0\x3f\x9d\x78\x50\xc9\x8a\xd7\x75\x14\x6a\xe4\x79\x18\xf7\x2f\xb6\x42\x19\xce\x48\x09\x79\x10\x69\x34\x5d\x6d\x27\xbe\x39\x80\x41\x40\x18\xfd\x59\xf2\x19\x6d\x14\xe6\x4a\x62\x18\x27\x1f\x94\xaa\xa3\x41\xa4\x87\x71\x38\xa7\xd4\x1c\x9f\x7e\xf2\x1f\x35\xda\x4e\x4b\xf7\xbc\x75\xbf\x6b\x2f\x33\xd5\x76\xc7\xeb\x8e\xd4\x9d\x48\x8b\xba\xe0\x19\x46\x71\x12\x4d\xfc\xdb\x4e\x01\x72\xa3\xe4\x0b\x00\xd3\x14\x8e\x8c\xe9\x1a\x34\x20\xec\xef\x06\x38\x7c\x3c\xfd\x7a\xfc\x90\x61\x6b\x85\x92\x09\xdb\x03\xe8\xd9\x9a\xfc\x8d\xf7\xbd\x42\x8f\xa3\x41\x63\x78\x49\x48\xce\xad\x16\xb2\x8c\xe2\x9d\x79\x7a\x32\x58\xa3\x27\x45\x90\x71\x83\xb0\x86\xc5\x12\x0e\xe7\xeb\x55\xb5\x20\xb9\x31\x81\xb0\x84\xf5\x20\x43\xa9\x76\x52\xce\xb8\x97\x73\x21\x81\x37\x8e\x07\xcc\xc5\x65\xcb\x02\x09\x4b\xc8\x54\xbb\x89\xda\x19\xec\xa8\xc0\xf6\xb4\x8e\xcf\x97\x72\x71\xc5\x06\x45\x72\x06\x52\xd4\x3f\x60\xa1\xab\x91\x28\xf6\x6e\x13\xfc\x34\x85\x8b\x4a\x18\x10\xa5\x54\x1a\xa9\x9c\x36\xfd\xa1\x57\x89\x39\x14\x5a\x35\x90\x71\x99\x61\x0d\x0d\xda\x4a\xe5\x09\x9c\x2b\x28\xb8\x9e\xc1\x09\xe4\x22\x07\xa9\x2c\xa0\xcc\x54\x47\x59\x73\x2a\x32\x25\x33\x8d\x54\x24\x54\xba\xc2\x76\x9c\x62\x0f\xf7\x15\x6a\x04\x8d\xd4\x2c\xc8\x0f\x5b\x61\x6f\x4d\x18\x68\x90\x4b\x21\xcb\xa2\xab\x13\xf8\xaa\x8c\x85\xce\xa0\x1e\x90\xf5\x62\x0e\x8b\x46\xd3\x26\x1f\x54\xbe\x49\x7a\x77\x12\x67\xe6\xa4\x20\x7d\x1a\x5d\xca\x25\x62\x0e\x56\xf5\xb6\xfa\xdb\x74\x3a\x03\x61\xc9\x1b\x58\xe3\xae\x8d\x60\x0e\x5c\xe6\x60\xd1\xd0\xe3\x7d\x85\x12\x6c\xc5\xad\xd7\x92\x29\xa2\x52\xd7\x26\xec\x69\xfd\xf8\xa0\x84\xf1\x2e\xfe\x3e\xf8\x69\x0a\xae\xbf\x5c\x68\x2e\x8d\xb3\x2f\x08\xd3\x99\xea\x64\x7e\xa1\x85\x6b\x4f\x4e\x3f\x05\x7e\x82\xa1\x33\x14\x94\x4f\x74\x15\x8e\xbe\x9d\x24\x70\x62\xc1\x74\x2d\x69\x30\x7d\x53\x12\xb2\x24\xf5\x14\x02\x25\x89\x78\x2a\x17\x68\xfa\xbe\xf5\xc4\xa8\xef\x5c\x8f\x23\x1b\x2c\x1c\xec\x4b\xc4\x3b\x48\x91\xc6\x5b\x38\x38\xc3\xdb\x0e\x8d\x8d\x21\x3a\x38\xeb\x2d\xcc\x26\xed\xa9\x72\x2c\x32\xc4\xe2\x6b\x93\x7c\xae\xd5\x9a\xd7\xbe\x5e\xfe\xf4\x27\x61\xec\x2a\x29\x66\x01\x75\xdf\x1b\xdc\xcc\xc0\x55\xb4\xbb\xa2\xb9\x2c\x29\xf9\xb7\x89\x97\x76\xd5\x43\x72\xff\xf6\x52\x3b\xa1\xfe\x92\xab\xe7\xde\x68\x1f\x72\xea\xed\x32\x0f\x67\x13\xe5\xf1\x58\x38\xaa\xb5\xa4\xa3\xe1\xed\xa5\x71\x65\x7b\x25\x86\x3e\xf2\xb8\x25\x65\xa1\xe7\x6f\xb8\x00\xf7\x47\x58\xbe\xba\x2f\x54\xd7\x61\x6f\xa9\x3f\xed\xdf\xdc\x49\xa6\x31\x47\x69\x05\xaf\xe9\x34\x34\xbc\xc1\xb9\xd2\xa2\x14\xae\x63\x6e\x99\x6f\x8a\xb7\x8e\x94\xf0\xcb\x92\x78\xe0\xc0\x53\x75\x9d\x7e\x3c\x5d\xc0\x27\x21\x73\x50\x9d\x05\x2f\x48\x41\xa6\xd4\x6d\x06\x26\xfa\xe4\x62\x4e\x43\x41\xb9\xb2\x70\x99\x1a\x65\x35\x27\x6a\x13\x69\x68\x6e\x00\xcf\xef\x88\x7a\x8e\xd0\x89\xb7\xe3\xff\xce\x11\xe1\x43\x57\x14\xa8\xcf\x55\xa7\x33\x04\x6e\x7f\x32\xf2\x7e\x25\x18\xf3\x46\x3c\x08\xd7\x1a\xe9\x6d\x36\xb4\x2a\x3f\xb0\xdd\x70\x3d\xaa\xeb\x68\xf0\x90\x02\x2e\x0a\x27\x34\xf1\x35\x18\x8e\x87\xaa\x84\x34\xdd\xf1\x0b\x9a\xce\x58\xe0\xf5\x3d\xdf\x18\xc8\x48\xc0\x79\xe9\xcd\x09\x99\xd5\x9d\x6b\x6c\x4a\x0e\x1d\x79\xd2\x1e\xa5\xa8\x27\x0d\xf2\x99\x1d\x16\x50\xe2\x2f\x43\xd2\x15\x5e\x51\xc7\x55\xf9\xc6\x65\x85\xaa\xe4\x9b\x56\x8d\x30\xb8\xcf\x59\xcf\x25\x17\x90\x70\xe6\x32\xf7\xcf\xd9\x97\xb1\xd5\xcf\x40\xb5\x36\x66\x6c\x9c\xb9\xa4\xe7\xc9\x58\x1d\xeb\x83\xcc\xfb\x69\xf2\xe2\xd8\x8d\xf7\x50\x3c\x1d\xb5\x3f\x9c\xb4\x9e\x80\x04\xdc\xd7\xcb\xe3\xd6\xc7\x64\x37\x2d\xab\xb1\xea\x7a\x87\x94\x3e\xe6\xce\x25\xa7\xd8\x55\x87\xab\x94\x17\xa6\x64\x76\x43\x9a\x57\x5c\x2a\x29\x32\x5e\x7b\x13\x7f\xe1\x26\xba\xc1\xcd\xfe\xd0\xeb\x81\x5c\x66\x37\x14\x5c\x5f\x80\xd1\xee\x5b\x5f\x85\x4f\x06\x25\x85\x2f\x08\x32\x25\x2d\x4a\xfb\x05\x65\x69\x2b\xc7\x28\x69\xdf\xbf\x8b\xe6\x6f\x9d\x90\x28\x20\xab\x47\xb2\xf5\x3b\x61\xf2\x8d\x6b\x83\x27\xd2\xf6\x26\xbc\xa7\x2b\xaf\x68\xee\x35\x85\xf1\x0c\xde\xbe\x99\xc1\xfb\x77\xf1\x1f\xee\xfa\x72\x42\xc3\x27\x46\x97\x90\xd5\x0e\x91\x03\x34\x99\xdb\x7e\x28\xf7\xa9\x3d\x9c\xc3\xab\x21\xa3\x5e\xcb\xb9\xe5\xb6\x33\x7d\xa3\x80\xbd\x25\xc5\xb8\xa3\xc9\x6e\x00\xaf\x21\x84\x10\x5e\x83\xbf\x74\x81\x0f\x36\x7a\xf1\x02\xb9\x15\xc7\xb3\x89\x81\x95\xca\x71\xf1\x5d\x03\x4e\xde\x8b\xfb\x04\x8d\x78\x7c\x70\xfc\xd1\x6a\xea\xf0\x02\xf6\xfc\xf7\x12\x54\x2e\xe3\x55\x80\x57\xd3\xa5\xe0\xd1\xbf\x2c\xf6\x10\xb8\x5a\x1a\x68\x55\xa2\xf5\xa2\x61\xec\xf7\xaf\xa0\x9f\x13\x8b\x31\x38\xb7\xee\xfb\x76\x31\xc6\xf5\x70\x4e\x55\xe5\x90\x3d\xd8\x28\x4e\x3e\x2a\x89\x51\xbc\x60\xfd\xf2\xb7\x9d\xb0\xff\xe5\x35\xee\x59\xa6\xc6\x95\xad\x68\x6c\x72\x4c\xe5\x55\x44\xa1\x44\x9b\x52\x7f\x5b\xf8\x7e\x19\xc5\x50\x70\x51\x63\xbe\x80\xdf\x8c\xab\x6c\xb7\xd2\x8d\xd4\xfc\x5f\xf8\x62\x36\x01\xf1\x93\x4b\x63\xa3\x3f\x5a\xd3\xe4\x1d\xda\xb6\x28\xa0\x55\xc6\x88\x75\x8d\xcf\x86\x3b\x7b\xd6\xdf\x86\x45\x74\xe2\xd5\xa0\xc8\x6f\x1a\x98\xd3\xae\x31\xf2\xd6\x6f\x93\x9e\xc1\x8b\x9d\x3a\xfa\xe0\xf7\xc0\xef\xed\x9d\xcf\xfa\xea\x96\x6d\xd9\x7f\x01\x00\x00\xff\xff\xcd\xea\xf8\xb6\xdf\x0d\x00\x00"), - }, - "/src/net/http/http.go": &vfsgen۰CompressedFileInfo{ - name: "http.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 2998, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x56\x61\x6f\xdb\x36\x10\xfd\x2c\xfe\x8a\xab\x06\x04\x52\xaa\xc8\x0d\x50\x74\x43\x1a\x63\xc8\xd2\xae\x09\xd0\x74\x85\x93\x02\x05\xba\xa2\xa0\xa5\x93\xc4\x84\x26\x15\x92\x8a\xe3\x15\xfe\xef\xc3\x91\xb2\x22\x3b\xe9\x86\x2d\x5f\x42\x93\xc7\xbb\x7b\x8f\xef\xee\x34\x99\xc0\xf3\x79\x27\x64\x09\xd7\x96\xb1\x96\x17\x37\xbc\x46\x68\x9c\x6b\x19\x13\x8b\x56\x1b\x07\x09\x8b\xe2\x79\x57\x09\x1d\xd3\x62\xe5\xd0\xd2\x02\x8d\xd1\xc6\xaf\x84\x9e\x08\xdd\x39\x21\xe9\x87\x42\x37\x71\x78\xef\x5a\xa3\x9d\xbf\x60\x9d\x29\xb4\xba\x8b\x19\x8b\xe2\x5a\xb8\xa6\x9b\xe7\x85\x5e\x4c\x6a\xdd\x36\x68\xae\xed\xc3\xe2\xda\xc6\x2c\x65\xec\x8e\x1b\x78\x83\x15\xef\xa4\xbb\x32\x5c\x59\x9f\xc2\x14\xaa\x4e\x15\x49\x0a\x33\xdd\xa9\xf2\xca\x88\xb6\x45\x03\xdf\x59\x64\x97\xc2\x15\x0d\xad\x0a\x6e\x11\xae\x6d\xfe\x4e\xea\x39\x97\xf9\x3b\x74\x49\x5c\xa1\x2b\x9a\x38\x85\x67\x53\x3a\xf9\xa4\x4a\xac\x84\xc2\x12\xf6\xf6\x76\x2d\x67\xc8\x4b\x3e\x97\x78\xe9\x0c\xf2\xc5\xe3\x2b\x47\x30\x99\xc0\xb6\x11\x08\x0b\x9d\xc5\x12\xb8\x05\x0e\x45\x83\xc5\x0d\x54\xda\x80\xed\x5a\x9f\xb3\xae\xc0\x7a\x43\xa1\x6a\x30\x68\x5b\xad\x2c\xc2\x5c\x97\x02\x6d\x06\x16\x03\xcb\xf6\x68\x32\xf1\x69\xe6\xb6\xc5\x22\x5f\x36\xdc\x2d\xeb\x5c\x9b\x7a\xf2\x53\xb8\x6d\x73\x16\x45\x06\x5d\x67\x14\xec\x79\xcb\x81\x96\xef\xeb\xa7\x61\x7f\xbe\x78\x7f\xe6\x5c\x3b\xc3\xdb\x0e\xad\x7b\x02\xcc\xc8\xe3\xe7\xb3\xd9\x96\xbf\x32\x50\x3f\x32\x51\x7a\xcb\x60\xcd\xd6\x49\xca\xd8\x64\x32\x3e\x18\xb8\x58\x36\xa8\x40\xa1\x70\x0d\x1a\xf8\x9d\xb2\x85\x93\x8f\xe7\xa0\xb4\x81\xed\xac\xfc\x36\x37\x08\xfc\x8e\x0b\x49\xac\xe6\x70\xee\x80\xcb\x25\x5f\x59\xa8\xb8\x90\x36\x67\x6e\xd5\xe2\x56\x18\xeb\x4c\x57\x50\x1a\x8c\xf4\x00\xc9\xe8\x6c\xa4\x8d\xc4\xe0\x2d\xec\xf7\x81\x52\x48\xf6\x67\x3d\xfb\x19\x78\xd5\xa6\xa4\x97\x0d\x3a\x21\xfb\x5d\x9b\x7f\xc0\x65\xe2\x05\x4c\x0f\x73\x34\xc0\xd0\x55\x8f\xe4\x69\x14\x96\xc0\x0f\x28\xe2\x94\xad\x59\x48\x7c\x4c\x6d\x9f\x39\x05\x16\xaa\x92\xa2\x6e\x1c\x2c\x78\xfb\x65\x93\xe5\xd7\xfd\x6b\x9b\xff\x31\xbf\xc6\xc2\xb1\x01\x9d\x83\xfd\xb1\x8f\xff\x8a\xf0\xbe\x31\x70\x34\xfd\x37\x71\x78\xd4\x29\x63\x91\xa8\xc0\xe5\x43\x72\xd3\x29\x51\x43\x6e\xa2\xf1\xee\x8f\x92\x0e\xca\x18\x99\x7e\x31\x78\xfb\x15\xa6\x70\xdf\x18\x2f\x2a\x34\x50\xa2\x44\x87\xc9\x83\x4d\x06\x06\x6f\x29\x34\x55\xc7\x69\x43\xc9\x2e\xf8\x0d\x26\x45\xc3\x15\x0c\x90\x52\x16\xa1\x31\xbb\xc7\x01\x26\xf3\x28\xf3\x4b\x02\xa6\x95\xd4\xbc\x8c\xb3\x4d\xab\xa0\xd4\x1b\xe4\x25\x9a\x0c\xbe\xd1\xe5\xa1\x2d\x11\xe4\x99\x3f\x49\x7c\x5f\x1b\xff\xa6\xf6\x36\xfa\xfd\xe5\x2b\xed\x24\x14\xe4\x94\x4b\x99\xc4\x35\xba\x13\x29\x37\xb9\x9d\x79\x2b\x1b\xa7\xf9\xa5\x33\x42\xd5\x49\x0a\xcf\x21\xfe\x53\xc5\x69\x9a\xa6\x39\xf9\xb8\x38\xbf\x78\x1b\xac\x92\x94\x45\xd1\x5c\x97\xab\x27\x1e\xe5\x93\x50\xee\x97\x13\x63\xf8\xaa\x7f\x10\x0a\xe8\x4f\x36\x8d\x23\x4e\xd3\xfc\x5c\x39\x34\x15\x2f\x30\x49\xf3\x3e\x33\x62\x20\x2a\xb4\x72\xa8\xdc\x7b\x54\xb5\xf3\x34\x09\xe5\x5e\xbd\x4c\x0e\x0e\x29\x62\xdf\x21\x0d\xde\xe6\x17\xe8\x1a\x5d\x7a\x62\x7c\xdb\x88\xcf\xde\x9e\xbc\x89\xa9\xd4\xe9\xf1\x43\x1d\xd0\xf5\xbe\x65\xe7\x1f\xb9\xb1\x78\xae\x5c\x12\x68\x0c\x09\x9d\x86\x60\x07\x21\x5a\x9c\x66\x70\xf8\x22\x83\x57\x2f\xd3\xd7\xfe\xfa\x48\x37\xbb\x89\x4d\x41\xd2\xee\x9a\x45\xe3\x2e\xf3\xc8\x28\x24\x2f\x51\x25\x44\x56\x4a\x18\xd6\xcc\xb7\x23\x2f\x92\xe3\x03\xd8\xdb\xd0\xef\xa3\x5c\x3a\xee\x3a\x7b\x04\xfd\xdf\xc0\x9c\xf5\xfb\x3b\x4f\x03\x31\x3c\xdf\x35\xb9\xc2\x7b\x37\x32\xcb\x1e\x9c\x9e\xea\x12\x8f\x9e\x76\x4a\xb4\x04\xd3\xf0\xba\x43\xfc\xfe\xb1\x03\x65\xc1\xe2\x74\x8c\xf0\x08\xb6\x00\x7b\x83\xdf\x74\xb9\x1a\x1c\x00\x84\x69\x9a\x7f\xd0\xed\xa9\xd4\xf6\x09\x55\x06\x62\xfc\xd5\xbe\x14\x37\xb7\x0d\xde\x66\x9e\xb0\x68\xbd\x53\x1c\xbe\x60\x36\xd5\x81\xf0\x50\xba\xa1\x52\x42\x89\x1d\x1f\xfc\xa0\x17\xee\xb4\x3d\xea\xcf\x58\xc6\xe9\xe3\x30\x7c\xae\x8d\xfb\xdf\x61\x4c\xef\xbf\xe0\xaa\xc0\xdd\x08\xa1\x00\x75\x8b\x2a\xce\x46\x7a\x0e\xeb\x4f\xb3\xf7\xc3\x0b\xa6\xa3\x8c\x36\xf5\x73\xb5\x6a\x31\xce\x20\xe6\x54\x64\xf3\xae\xaa\xd0\xc4\x29\x0d\xf5\x86\x5b\x70\x1a\xe6\x08\xbc\x72\x68\x20\x04\x80\x4e\x39\x21\x87\x09\x3d\xef\xea\xbf\x84\x94\x3c\x5f\xe8\xf0\x9f\x06\xb4\x6d\xf4\xf2\xdb\xbc\xab\xf3\xa2\x16\xbf\x8a\x72\x7a\x78\x78\xf8\xe2\xe7\x57\x87\x34\x0e\x0c\x5a\x2d\xef\xb0\x64\x11\x7d\x11\xdc\xe0\x2a\x83\x3b\x2e\x3b\xb4\x54\x5e\x86\xab\x1a\x7d\xd2\x41\x2b\x9e\x18\xb2\xfb\xd6\x5b\x3d\x18\xf5\x97\xbc\xce\x1f\x28\xb0\xe8\xfa\x87\x08\x0e\xe2\x6c\x14\x22\xed\x9f\xdf\x37\x74\x0a\x42\xe2\x1a\x97\xe5\xd8\x8f\x0a\x0c\x03\x4a\x8b\xfe\x90\x94\x35\xf4\x81\x5e\x87\x24\xba\x13\x29\x93\x8d\x33\x8a\x20\x2a\x6f\xf4\x6c\x54\xed\x9b\xe3\xdc\x8b\x36\xf1\xe4\x0e\x03\x0b\x16\x9d\x1d\xa6\x7b\x41\x06\xe0\x1a\xff\x35\xb4\xca\x40\xa8\x42\x76\x25\x7d\x26\x69\xb5\x11\x46\xf0\xb8\x35\xa2\x03\xb0\x47\x71\x1e\x43\xca\xbc\x5f\x02\xc6\x58\x64\x51\x62\x18\xbc\xbe\xe7\x91\x1e\x08\xdb\xf1\x41\xe8\x27\xa3\x0f\x1d\xda\xc8\x28\x5a\x6f\xda\xb3\x70\x7c\xe0\x45\x3b\xfe\x22\x1a\x12\x5a\xff\xc3\xb0\x3e\xf5\x1a\xee\x1f\x6a\x67\x60\x7f\xf7\xaf\x73\xdf\x98\x0c\xf4\x8d\x9f\x4d\xdb\x83\xf3\x35\x6d\x6f\x3f\x56\x28\xac\x34\xc4\xfc\x3b\x00\x00\xff\xff\x05\x0b\xbb\x60\xb6\x0b\x00\x00"), - }, - "/src/net/net.go": &vfsgen۰CompressedFileInfo{ - name: "net.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 1122, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x41\x6f\x1a\x3d\x10\x86\xcf\xf8\x57\xcc\xe7\x93\xfd\x75\xbb\xa8\x52\xd4\x43\x25\x0e\x0d\xad\x22\xaa\x36\x44\x42\x6a\x2b\x45\x39\x78\xbd\xb3\x1b\x83\xb1\xb7\x1e\x6f\xc3\xaa\xe2\xbf\x57\x5e\x76\x49\x02\x5c\x7b\xc2\x0c\x33\xcf\xfb\x68\x86\xe9\x14\xde\x14\xad\xb1\x25\xac\x89\xb1\x46\xe9\x8d\xaa\x11\x1c\x46\xc6\xcc\xb6\xf1\x21\x82\x60\x13\x8e\x21\xf8\x40\x9c\x4d\x38\x75\xa4\x95\xb5\x9c\xb1\x09\xaf\x4d\x7c\x6c\x8b\x5c\xfb\xed\xb4\xf6\xcd\x23\x86\x35\x3d\x3f\xd6\xc4\x99\x64\xac\x6a\x9d\x86\xaf\x86\x22\x3a\xe1\x30\x66\x60\x55\x59\x06\xa0\x18\x8c\xab\x25\x88\xc3\x4f\x18\x32\xe8\x33\x24\xfc\x61\x93\x46\x39\xa3\xc5\x21\x33\xbf\xc5\x27\xc1\x1d\xc6\x27\x1f\x36\xa0\xb4\x46\x22\x30\x04\xce\x47\xa0\xb6\x49\x86\x58\x42\xd1\xc1\x4d\x1f\xfc\x65\xc5\xa5\x64\xfb\x21\x57\x94\xf0\xff\x27\xa3\x2c\x06\x09\xe9\x53\x0c\x9c\x0c\x92\x44\x22\x1d\x3d\xe6\xde\xb9\x7f\xe2\x40\x1d\x2d\x9c\x89\x22\x51\xc7\x5a\x13\x7c\x81\x8b\xbb\xdf\x57\xab\xa8\xf4\x46\x48\x28\xbc\xb7\x29\x35\x60\x6c\x83\x83\x4a\x59\xc2\xb3\xee\xf7\x63\xb7\x18\x42\x29\x15\x33\x78\xf1\xed\x6a\xab\x9a\x1e\x26\x4f\x69\xd9\x25\xe8\x0f\xe3\x4a\xff\x44\x8b\xbb\x33\xf2\x77\x43\x51\x2d\xee\x2e\xb3\x8e\x90\xad\xda\x8d\xf7\xbb\x56\x7a\x63\x7d\x2d\x24\x18\x17\x5f\x0c\x0c\xff\x97\x7c\xb5\xfc\xf6\xf1\xe7\x7c\x79\x7b\x9b\x86\xa7\x53\x98\xfb\xa6\x03\x5f\x0d\x07\xa0\x7c\xe1\x4a\xdc\x5d\x77\x11\xf3\x03\xba\xe8\x22\xf6\x35\x31\x1e\x29\x83\x43\xf5\x34\x61\x9d\x86\x23\x06\xa7\xec\xb2\x58\xa3\x8e\x82\x64\x3e\x57\xd6\x0a\x6e\x12\x60\x59\xf1\x2c\x35\xdd\x58\x5f\x28\x9b\xdf\x60\x14\x7c\xd5\x13\xf9\xd8\x57\x05\xbf\x9d\x3f\xaa\x30\xf7\x25\xf2\x0c\xb4\x94\x09\x29\xe4\x89\x6b\x4a\xa7\xfc\xf3\xaf\x56\xd9\x17\x96\xd4\x17\xc4\x2e\x83\x0e\xee\x1f\x0e\x86\xe3\x3d\x4d\x05\x16\x9d\xd8\x49\xf8\x6f\xd6\xbf\xba\x7e\x99\xaf\xb7\x39\xd9\xb3\x49\xe5\x03\x98\x0c\x0a\xf8\x30\x83\xa0\x5c\x8d\xb0\xeb\x1b\x4d\x05\x45\x9a\xed\xee\xcd\x43\x5f\x38\x19\x4d\xb3\xfb\xe3\x2a\x62\x68\xf1\xa2\xf3\xa5\xed\xd2\xb1\x28\x68\x10\x3f\x5b\xf1\xb9\x16\x3d\x6b\xcd\x66\xa0\x5f\x39\x99\x53\x9f\xb7\xef\xd8\x9e\xfd\x0d\x00\x00\xff\xff\x93\x28\xa9\x7f\x62\x04\x00\x00"), - }, - "/src/os": &vfsgen۰DirInfo{ - name: "os", - modTime: time.Date(2018, 6, 29, 21, 3, 27, 551348355, time.UTC), - }, - "/src/os/os.go": &vfsgen۰CompressedFileInfo{ - name: "os.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 581, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x91\x4f\x6b\xdc\x30\x10\xc5\xcf\x9e\x4f\x31\xd5\x49\x62\x5b\x3b\xb9\x76\x6b\x4a\x28\x61\x5b\x28\x2d\xb4\x94\x1e\x42\x28\xfe\x33\xd6\x8e\x23\x4b\x46\x92\x9b\x85\x65\xbf\x7b\x91\xd6\x4e\x21\xe0\x83\xd1\xfc\xde\x9b\x99\x37\x55\x85\xbb\x76\x61\xd3\xe3\x18\x00\xe6\xa6\x7b\x6a\x34\xa1\x0b\x00\x3c\xcd\xce\x47\x94\x50\x08\xf2\xde\xf9\x20\x00\x0a\xa1\x39\x1e\x97\xb6\xec\xdc\x54\x69\x37\x1f\xc9\x8f\xe1\xff\xcf\x18\x04\x28\x80\x61\xb1\x1d\xfa\xc5\x46\x9e\xe8\x4f\xe3\x75\x90\x0a\x1f\x1e\x43\xf4\x6c\x35\x9e\xb1\xaa\xd0\xba\x88\x5d\x63\x0c\xf5\xe8\x2c\xfe\x66\xdb\xbb\xe7\x00\x85\xa7\xb8\x78\x8b\x77\x5e\x07\xb8\xac\x3e\x6c\x39\x4a\x85\x67\x28\x78\xc0\xd9\xbb\x8e\x42\xc0\xf7\x35\x8e\xa1\x3c\x18\xd7\x36\xa6\x3c\x50\x94\x62\xad\x08\xb5\x7f\x81\xde\x64\xe8\x97\xed\x69\x60\x4b\x7d\xb2\x28\x1a\xaf\xff\x26\xf5\xca\x5c\xb5\xe9\x51\x28\x28\x8a\xd4\x18\x6b\x9c\x9a\x27\x92\xdb\xc0\x6f\x31\x95\xcb\xaf\x64\x75\x3c\x4a\xf5\xee\x36\x81\x83\xf3\xc8\xc9\xe7\x66\x8f\x8c\x1f\x5e\x23\x7b\xe4\xdd\x2e\xf7\xcb\x96\x0f\xfc\x88\xf5\x95\xf9\x62\x7b\x3a\x49\xc6\x1d\xde\xaa\xf2\x67\x6e\x20\x93\xe1\x05\xd2\xc7\x03\x1a\xb2\x32\x69\x14\xd6\x35\xde\x64\x8f\x75\xaa\x6d\xa0\xb3\xf8\x28\x32\x7e\x79\x95\x74\x4b\x83\xf3\x74\x7f\xba\xe6\xb5\x55\xe9\x44\xdd\x12\x9b\xd6\x90\x54\x28\xb7\x9d\xf2\x45\x73\xaa\x6b\xe6\x42\xac\x8f\xa1\xfc\x46\xcf\x52\xdc\xbf\xc8\xf2\xb1\x78\x9a\x0d\x4d\x64\x23\xf5\x98\x96\x3f\x7c\xbf\xfb\xf1\xe9\x73\x3d\x06\xa1\xe0\x02\xff\x02\x00\x00\xff\xff\x55\xfc\x3a\xb3\x45\x02\x00\x00"), - }, - "/src/os/signal": &vfsgen۰DirInfo{ - name: "signal", - modTime: time.Date(2018, 4, 20, 9, 32, 51, 274055626, time.UTC), - }, - "/src/os/signal/signal.go": &vfsgen۰CompressedFileInfo{ - name: "signal.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 233, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\xce\xbf\xca\xc2\x40\x10\x04\xf0\x7e\x9f\x62\xca\x84\x0f\xbe\x13\xad\x2d\xc4\x42\x3b\xc5\x17\x90\x4b\xb2\x09\x1b\x2f\x7b\xe1\xfe\xd8\x84\xbc\xbb\xa0\x69\x22\xd8\xce\x6f\x60\xc6\x18\xfc\x55\x59\x5c\x83\x3e\x12\x8d\xb6\x7e\xd8\x8e\x11\xa5\x53\xeb\x88\x8c\xc1\x75\x15\x41\x22\xd4\x27\xc8\x30\x3a\x1e\x58\x13\x37\x68\x7d\xc0\xe9\x72\xb8\x1d\xcf\xfb\x3e\xfe\x13\xb5\x59\xeb\xa5\x7e\x6f\x24\xda\xca\x71\x91\x45\xd3\x6e\x5b\x62\x9a\x57\xcc\xba\xd2\x6f\x96\x4e\x7d\xf8\xcd\x81\xeb\x67\x51\xe2\xc3\x00\x26\x04\x4e\x39\x28\x36\x98\x97\x1b\xce\xfb\xb1\x78\xcf\xbe\x02\x00\x00\xff\xff\x29\x0b\xd3\x08\xe9\x00\x00\x00"), - }, - "/src/reflect": &vfsgen۰DirInfo{ - name: "reflect", - modTime: time.Date(2019, 8, 11, 22, 48, 34, 27211540, time.UTC), - }, - "/src/reflect/example_test.go": &vfsgen۰CompressedFileInfo{ - name: "example_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 311, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x8d\xcf\x4a\x33\x31\x14\xc5\xd7\xbd\x4f\x71\xc9\xe2\xa3\xe5\x93\xa4\x95\xba\xe8\xec\x5c\x88\xe2\xa6\x62\x1f\xc0\xa6\x93\x9b\x3f\x75\x92\x0c\xc9\x8d\x08\xa5\xef\x2e\x33\x22\x82\xbb\x03\xbf\x73\xce\x4f\x29\xfc\x7f\x6a\x61\x30\x78\xae\x00\xa3\xee\xdf\xb5\x23\x2c\x64\x07\xea\xf9\x8d\xa9\x32\x40\x88\x63\x2e\x8c\xc2\x46\x16\x00\xb6\xa5\x1e\x1f\x3e\x75\x1c\x07\x3a\x70\x69\x3d\xef\xed\x72\x85\x17\x58\x28\x85\x8f\x79\xf4\x54\x9e\x0f\x68\x32\x55\x4c\x99\x31\x4c\xbd\x48\x89\x7f\x4e\xa5\x36\xe6\xf5\x3b\xee\xad\xc5\x44\x64\xc8\xa0\xcd\x05\xd9\x87\x8a\x93\x52\xce\x5f\x07\x22\xf4\xcc\x63\xed\x94\x72\x81\x7d\x3b\xc9\x3e\x47\xe5\x66\xc5\xb9\xfe\x86\x50\x6b\xa3\xaa\xb6\xbb\x1d\xc0\xc2\x46\x96\x2f\x25\x24\x1e\xd2\xf2\xf8\xa1\x87\x46\x1d\xfe\xbb\x3c\x51\x70\x9e\xbb\xb5\xdc\xe2\xbd\xa3\xee\xf6\x0a\xe7\x9a\x53\x87\x78\x11\x7e\x46\x62\x62\x37\x42\x3b\x12\x13\xfd\x3b\xdc\xc8\xbb\x79\xb8\x59\x5f\x8f\x2b\xb8\xc2\x57\x00\x00\x00\xff\xff\x0d\x48\xa9\x1a\x37\x01\x00\x00"), - }, - "/src/reflect/reflect.go": &vfsgen۰CompressedFileInfo{ - name: "reflect.go", - modTime: time.Date(2019, 8, 11, 22, 48, 29, 448797204, time.UTC), - uncompressedSize: 39691, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\xfd\x73\x1b\x37\xb2\xe0\xcf\xe4\x5f\x01\xb3\x5e\xe9\xcd\x58\x13\xea\x23\x7b\xa9\x94\x62\xe5\xd5\xc6\x49\xf6\xb4\x1b\x5b\xa9\x38\xce\x5d\x9d\x9e\xca\x0f\x22\x31\x14\xc4\x21\x66\x76\x06\xa4\xa5\x48\xfa\xdf\xaf\xd0\x8d\xef\xc1\x90\x92\xe3\xbd\xb7\x75\xb5\xfe\xc1\x22\x67\x80\x46\xa3\xbb\xd1\xe8\x2f\x80\x07\x07\x64\xff\x6a\xcd\xab\x39\xb9\xe9\xc6\xe3\x86\xce\x96\x74\xc1\x48\xcb\xca\x8a\xcd\xe4\x78\xcc\x57\x4d\xdd\x4a\x92\x8d\x47\x13\xd6\xb6\x75\xdb\x4d\xc6\xa3\x49\x27\xdb\x59\x2d\x36\xea\xe3\x5a\x74\xb4\x64\x93\xf1\x78\x34\x59\x70\x79\xbd\xbe\x9a\xce\xea\xd5\xc1\xa2\x6e\xae\x59\x7b\xd3\xb9\x0f\x37\xdd\x64\x9c\x8f\xc7\x1b\xda\x12\x2e\xb8\xe4\xb4\xe2\xbf\xb3\x39\x39\x25\x25\xad\x3a\x36\x1e\x97\x6b\x31\x83\x37\x59\x4e\xee\xc7\xa3\x83\x03\x42\x37\x35\x9f\x93\x39\xa3\x73\x32\xab\xe7\x8c\xb0\x8a\xaf\xb8\xa0\x92\xd7\x62\x3c\x5a\x77\x6c\x4e\x4e\x4e\x89\xea\x96\x71\xc2\x85\x64\x6d\x49\x67\xec\xfe\x31\x27\xf7\x8f\xf8\x3e\x6b\xe5\x5d\xa3\x9e\xe8\xaf\x6b\x31\xab\x57\xab\x5a\xfc\x1a\x3c\x5d\x31\x79\x5d\xcf\xdd\x77\xda\xb6\xf4\x2e\x6c\x32\xbb\xa6\x51\x27\x35\x6c\xf8\xc4\x62\x10\x41\xa7\x4d\xf8\xa0\x91\x6d\xf8\xa0\xab\x78\xdc\xa9\x93\xed\x7a\x26\x23\xf8\x31\x9e\xd8\xe8\x47\xce\x2a\x78\x38\x1e\x85\x64\x95\xed\x9a\x8d\x47\x6b\x2e\xe4\xd7\x0a\x10\x39\x25\xea\xcf\x79\x99\xc1\xa3\xec\x30\xcf\xa7\xd9\x4b\x20\x50\x4e\x0e\x0e\x48\xc7\x24\x29\xeb\x96\xb4\x8c\x56\xe3\x47\xcd\x8e\x9b\x4e\xf5\xc9\xe4\x5d\x03\x9d\x73\xf2\xf2\xa6\x9b\x9e\x5f\xdd\xb0\x99\x54\x3c\x6a\x99\x5c\xb7\x82\xdc\x74\xd3\x33\x35\x79\x41\x2b\x7c\xa7\x3a\xe4\xd3\xbf\x30\x99\x4d\x10\xc2\x24\xb7\x20\xb5\x5c\x59\xb8\x0e\x62\x4e\x10\x1d\x05\x99\x97\x44\xde\x35\x08\xc2\xeb\x31\xc9\xc9\xe9\xa9\x1a\xef\xbd\x98\xb3\x92\x0b\x36\x57\x8d\x47\xad\x54\x92\xb0\x87\xdc\x1e\x8f\x46\xa3\x8e\xff\xce\x4e\x88\x9a\x68\x23\xdb\xcc\x42\x52\x8f\x27\xb9\x42\x36\xcb\xf3\x42\x35\x5c\x72\x31\xc7\x86\x5f\xbb\x66\xea\x61\xd8\xac\x93\xed\x09\x21\x82\x7d\x7c\x4b\x57\xec\xbc\x2c\x33\xfd\x11\x99\x2e\x68\xf5\x2e\x18\x46\xb6\x5c\x2c\x26\x79\x5e\x90\xc9\xa4\x70\x13\x61\xb7\x6a\x25\x31\x05\xfb\xbb\xba\xae\xb2\x1c\xa1\x3f\x8e\x47\xa3\x3e\x09\x5b\x99\x4f\xdf\x79\x14\x04\x38\xf9\x78\x34\x52\xe0\xde\xc5\x74\x29\x12\x4c\x68\x65\xae\xa4\x62\x84\x72\xf3\x8e\x01\x91\x6e\xba\xe9\x5f\xaa\xfa\x8a\x56\xd3\xd7\xb4\xaa\xb2\xc9\xbf\xd9\xb7\x6e\x04\x5e\x12\xfb\x74\xfa\x13\x13\x0b\x79\x9d\xe5\xe4\xc5\x29\x39\x24\x0f\x0f\x6e\x3a\x82\xae\xbc\xb9\x00\x23\x46\xad\x9c\xca\xb2\xa2\x0b\xf2\x70\x4a\xe0\xc3\x7b\xbd\xe4\xd4\x4b\x9f\xa9\xa9\xce\xfd\xde\x8a\xc6\x73\xf5\x4a\xd1\x68\xa4\x54\x87\x9e\xf4\x1b\xc0\xaf\x23\x17\x97\x88\xa9\x7a\xad\xa4\x97\xab\x39\x1e\x7e\x43\x38\x79\x95\x98\xc3\x37\x84\xef\xef\x93\x7b\x25\xee\x3f\x68\x5e\xe8\x56\x1d\x29\x79\xdb\xc9\x29\xa0\xb1\x52\x40\x5c\xef\x33\x31\x67\xb7\x19\xcf\xe1\x9d\xe1\xa1\x6a\xe2\x33\x7f\x85\xd3\x6a\x96\x8a\xef\x4a\x48\x27\x13\x68\xcf\x4b\xf2\xc2\xf6\xc1\x59\x8e\x66\xb5\x90\x5c\xa8\xd5\x69\x66\x36\x8a\xa6\x75\x4a\x68\xd3\x30\x31\xcf\xc2\xe7\x85\xc6\x4a\xc3\x51\x34\x3c\xd9\x25\x95\x2b\x47\x6f\x2b\x91\x06\x21\x2d\xdd\xa3\xd1\x4a\xde\x35\x00\x09\x55\x44\x99\xf9\xab\x54\x43\x90\x77\xcd\x24\x37\x3d\x1e\x73\xcb\x95\xdb\x59\xbd\x16\x20\x5b\x6a\x19\x1d\x7d\x95\x55\x4c\x44\x78\xe7\xf9\xb3\xf9\xf3\x5e\xb0\x98\x43\x1d\x9b\xd5\x62\xfe\x0f\x61\xd1\xff\xdf\x1c\x5a\xa3\x7a\x0c\x76\x3f\x68\xd3\x2c\x17\x3f\x53\x79\xfd\x0c\xd5\x86\xc4\x43\x1c\x61\xdf\x36\xc3\xad\x40\x0a\x4e\x08\x31\x52\xd0\xe7\xae\x6e\x79\x6b\x5b\xe2\x27\x7c\xfa\x41\x73\xf9\x24\x5a\xe1\x85\x9b\x85\x87\xfe\x1b\xda\x5c\xb4\xf2\x92\x9c\x92\xb5\x54\xef\xfa\xca\x6f\x3d\xa4\x3e\x1f\x95\x4a\xec\x3e\x72\x39\xbb\x26\xad\x9c\xfe\x8d\x8b\xb9\xd6\x3f\x33\xda\x31\xf2\x67\xb5\xf9\x9f\x80\xce\x67\x52\xbd\x04\x02\xb7\xb2\x20\x7b\xce\x2e\x40\x31\xab\xd8\xea\x24\xde\xce\xb4\xa2\xaf\xd8\x6a\x62\xe6\x5b\x31\x71\x42\xfa\x7b\x51\xc5\x44\xb8\xc7\x00\xc3\x00\x87\xd7\xd7\x54\x00\x0a\x73\xde\x2a\xce\x7d\x57\xcb\xeb\xef\x79\x1b\xab\xd0\x8e\x89\xf9\xb9\xa8\xee\x62\x2d\xaa\x7a\x9d\x92\x77\x4c\xcc\x75\xa7\xc7\xb8\x67\xcb\x66\x9b\xe1\x9e\xbf\xb0\xd9\xc6\xef\xd9\x23\x84\xb5\x86\x9e\x45\x87\x39\x6f\x3d\x3a\xcc\x79\x1b\x4f\xfb\xc7\xb5\x98\xc1\xb4\x1b\xda\xd2\x55\xa7\x66\xee\xe4\x0e\x1e\x4d\x40\xa6\xb9\x80\xc5\x4f\x97\x2c\xbb\xb8\x44\x93\xa1\x20\xd8\xc0\xc9\x5a\xa0\x70\x5a\x2a\x16\x8c\x70\xa1\xa7\xc9\xc5\x05\x57\xb2\xe3\xe3\xac\xfb\x1b\x45\xe2\x16\x4f\xcb\xba\x75\x25\x43\x6c\xf4\x33\x44\xa7\xc6\xe5\x15\xe1\xa3\x9b\x6c\x45\x48\xf5\x44\x8c\xea\xb5\xec\xa3\x64\x40\xf4\x71\xaa\xd7\xf2\x75\xa4\x74\x93\xe3\xf9\x3c\xdf\xd0\x96\xd3\x39\x9f\xc5\x3c\xb7\xb0\x1e\x4e\xc9\x11\x79\xf5\x8a\x1c\xfd\x8f\x61\xce\x5b\xab\x57\x6f\xd7\x77\x0d\x53\x0b\x59\x19\x6e\x85\x26\xed\x6b\xbd\xba\x35\x5e\x31\x5f\x8a\x60\xd0\x13\x62\x3e\x69\x2d\xc0\x05\xc0\x23\x84\x0b\xfd\xa4\x5e\x4b\x7c\x54\xaf\x65\x24\x30\x67\xc6\xe2\x06\xa9\x31\xdb\x84\xcf\x28\xfd\x4c\xcb\x8d\xd7\x42\x73\x4b\x3f\x32\x5a\x7b\x87\xfc\x98\xfe\xf7\xf1\x16\xd4\x85\x1b\x90\x69\x88\x2c\xe5\x9f\x67\x47\xd8\xb1\x93\xd9\x8d\x02\xf6\x89\x67\x6d\x14\xc3\xec\x0e\x5d\x9a\x90\xe7\x96\xe5\x76\x13\x79\xe6\xc6\xa1\xf7\x0d\xa3\xf6\x0d\xd1\x22\x1e\xbf\xa1\x4d\x5a\x1b\x1b\xbf\x0a\xa0\x2c\xd9\xdd\x09\x49\xeb\xa0\x25\xbb\xb3\xc4\x79\xa2\xaa\x72\xa3\xff\x2c\xdb\xf4\xe8\xc6\x89\xfb\x34\xb0\xef\x94\xc7\x97\x06\xec\x9c\xc1\x4f\x04\x0d\x4e\x21\xc0\x2e\x95\x67\x18\xae\x07\x7c\x84\xcb\x41\x03\xfd\xd1\xb6\xd2\x6b\xc2\x73\x2b\x0b\x82\x1d\xb6\x2e\x8b\x10\x0e\xa2\x5d\x82\x67\x8e\x7d\x83\xa5\x51\x97\x65\xc7\xe4\x0f\xab\x2b\x34\xcf\xcc\x6e\xc0\x73\xd0\x3c\xc6\x1c\x2b\xf5\x0c\x55\xb3\x79\xdf\x4d\x08\xa0\x28\xb5\xd5\x37\xd3\x10\x1b\x5c\x80\xbe\x9f\xec\x2f\x42\xfd\x2f\x25\xb6\x65\xb4\x00\x13\xef\x24\x45\x81\x2e\x87\x7c\xbb\x60\x3d\xea\x7f\x3e\x23\x4b\x7f\x2d\x16\xbd\x89\x9d\x10\xef\xcb\xce\x95\xea\x05\x0c\xfe\xe8\x32\x55\xad\x92\x4b\x15\xf9\xe9\xd6\x19\xd2\xd8\xc9\xdf\xe3\x18\x8c\x2b\x1d\x14\x30\xb1\x85\x0c\xe3\x43\xd3\x9f\x6b\x18\x30\x4b\xbb\xf5\xd3\xf7\xd0\x4a\xb9\xc4\x36\x52\x10\x4e\x92\x98\x9d\x75\xa9\x9f\x45\x21\x9f\xf1\x36\x1f\xda\xf4\x49\xfa\xc9\xe6\xa5\x92\xee\x2d\x6f\xb5\xd3\x2d\xb7\xba\xdb\x8f\xe3\x31\x84\x30\x7c\x63\x55\x0b\xa0\x42\x51\x93\x97\x08\x54\xfe\x63\x6d\x36\x9b\xdd\x72\x6c\x9c\x29\xfb\x7d\x55\x97\x25\xd1\x46\xf5\x97\xc7\xe3\xb1\xb5\x93\x9d\xe7\x6b\xc8\x95\x49\xf2\xd2\x1f\x36\x37\x9b\x53\x96\xdb\xc6\x5e\xd0\x46\x4e\x0d\xa8\x2d\x10\x8c\x54\xbf\x79\x1a\xa4\x8b\x13\x39\xd5\xe6\xbd\xf9\x70\xa9\xa0\x2b\xc7\x3d\x32\xdf\x89\xd6\x37\x2b\xda\x5c\x20\x67\x2f\xc3\xb1\x3d\x9c\x74\x90\xca\xbc\xce\xf2\x10\x4d\x0f\x95\xd8\x47\xc0\xe1\x81\x23\xc6\x74\xf1\xb8\x81\xd1\x26\x42\xc8\x7f\x69\x59\x3c\x99\xa8\x56\x93\xff\x1a\x1b\x3b\xc6\x31\xc2\x9a\x49\xfa\xc1\x58\xd9\x2a\x84\x18\x83\x6f\x0c\x86\x8a\xfb\xea\x93\xd4\x8c\x9c\x13\x2e\x80\x82\x2e\xcc\xe5\x28\xc8\xc5\x40\x9f\x7a\x2d\x07\x3b\xd5\x6b\x69\xe7\xa7\x44\xca\x9b\xdb\xd5\x9d\x64\x1d\x79\xa9\xfe\x04\x4d\xbe\xa7\x92\x7a\xcd\xa0\x97\xfa\x87\x31\xab\xf1\x48\xd2\x05\x09\x1e\x58\xd7\xf8\xaa\xae\x2b\xc3\x4c\xd5\x2d\x66\xa2\x1a\xea\xf2\xa5\x19\xc3\xf2\x4f\x40\xe3\x1c\xfe\xcf\x72\x92\x75\x1a\x72\x4e\xee\x89\x9e\x89\x86\x76\x21\xa6\x80\xf5\xe5\x14\xb0\x7a\x8c\x00\x48\xba\x08\xfb\x6f\x01\xa0\x66\x11\xf7\xd7\x6b\x2f\xcb\x35\x00\xaf\xff\x64\xd2\x6b\xcd\x3b\x13\x21\xca\x72\x98\xfa\x96\xd1\x2c\x89\x0c\x07\x8d\x8a\x15\x85\xc2\x5a\x8f\xe7\x9c\x7a\x80\x87\x14\x01\x56\xa9\x9d\x50\xb0\x8f\x99\x02\x97\x23\x4f\x14\xfc\x2b\xb5\x79\xed\x19\x82\x2a\xbd\xee\xf6\x2d\xb0\x8e\x25\x5d\xe8\xad\x45\xd2\x85\x7a\x60\x06\x38\xb1\x43\x15\x4a\x27\x8f\x3c\xc4\x15\x18\x40\xfb\x84\x5c\xc1\x4b\x8f\xa3\xe7\x65\xf9\x13\xef\x94\x14\xab\x6f\xfd\x05\xa8\xdb\x64\x4a\x27\xe9\xcf\x6e\x16\xde\x18\x1a\xce\x05\x17\x52\xb5\xcd\x2f\xc7\x11\x61\xc0\xee\xf5\xe4\xe2\xbc\x2c\x21\xe8\xab\x08\x51\x31\x91\x79\x40\x34\x3d\x0c\x6a\x36\xec\xe2\x3d\x2c\x88\xc8\xe3\xf1\x95\xbd\xa1\x67\x26\xd1\x0e\xd6\x33\xd3\xeb\xb3\x37\x37\xdd\x0a\xe6\xa6\x3f\xfb\xf1\x68\xb3\xe6\x1c\xac\xf4\xec\x8c\xd1\xdd\x03\x1c\xcc\xcf\x03\x93\x8f\x47\x3e\x82\x76\x7e\xde\xc3\x82\xc8\x3c\xc6\x40\xcf\x4f\xe7\x4c\xdc\x46\xde\xc9\xf6\xfc\xea\x26\x08\xaa\x6b\x69\xbf\x1f\x43\xfc\x74\xa6\x17\xff\xbd\xfa\x6b\xde\x3d\xa6\x36\xbe\x99\xde\xf1\x3a\xd9\x4e\x0a\x82\x80\x21\x53\xb0\x60\xd2\x74\xfc\xc8\xe5\xb5\xd2\x7b\x06\x05\xfe\x3b\xe8\x0c\x8d\xeb\x6c\xda\xc9\xd6\xa1\xd9\xfd\xaf\x56\x4d\x6e\xee\xa5\x13\x70\x61\x79\x89\x04\x63\xe2\xea\xec\xc1\x47\xec\x61\x8d\x2a\x0b\x6c\x56\x37\x77\x68\xea\x66\x73\x45\xa1\xae\x9d\x79\x93\x86\x60\x8f\x1e\xe2\x7e\xec\x19\xc2\xbd\x01\x9c\x41\x1c\x47\x27\x23\xcb\x57\x87\x26\xc7\xa3\x51\xd3\xd6\x4d\xc2\xbc\xd5\xf6\x53\x5b\x37\x93\x7c\xfa\x0e\xc8\x93\x29\xab\x68\xde\x49\xa0\xa3\x7a\x03\x78\x42\x43\xf5\x4d\xd9\x1b\x8f\x76\x46\x4a\x91\xfe\x46\xab\x35\xcb\x24\x60\x5e\x90\x4d\x30\xa3\xb2\x22\x65\x45\x17\x39\x81\x46\xb8\x7d\x81\x6d\x3f\x35\xbb\x22\x66\x4d\x4c\x44\xeb\xf4\x14\x63\x59\x10\xb2\xf7\x1e\x22\xd5\xe2\xa7\x3f\xcb\x16\x33\x29\xc8\x08\x18\xe3\x5e\x59\x96\x91\xf5\xb6\x71\x86\x1a\xa0\xf4\x00\x48\x65\x06\x54\xfe\xe8\xeb\x9b\x41\x28\xbd\x24\x84\x60\x1f\x95\x8e\xd3\xef\x27\x05\xd9\x14\x86\x57\xad\x9c\x2a\x67\xab\x56\xa6\xe1\x8e\xc1\xf5\x83\x33\x31\xe7\xad\x23\xec\x1b\xba\x64\xe0\x70\x59\xb9\x2b\xd4\x22\x2c\xc8\x8c\x36\x4a\x70\x3d\x8a\xea\x78\x89\x26\xcb\x8b\x53\x74\xd4\x90\xeb\x54\xf0\x99\x35\x5a\xa7\x16\x28\xa9\x4b\x22\x6a\xf1\x05\xf8\x6d\xb0\x3a\x27\xc0\x56\x05\xab\x62\x82\xbc\x22\x87\x5b\xfb\x2b\x7b\x7c\x41\x25\xdf\x30\x02\x11\x41\xd3\x57\x21\xf7\x8c\xbe\x33\xda\x84\xe3\x7e\x0b\x10\xb6\xf7\xb6\xed\xb0\xab\xe5\x9b\x27\x8a\x77\x4d\x91\x48\x19\x19\x10\x93\xc2\x5f\x51\x8e\xac\x29\xf3\x18\xf2\xb4\x61\x02\x91\xf4\x96\xfd\xf4\x87\x8a\xad\xb2\x3c\xd7\x23\xfd\xce\xda\x7a\x92\x93\x47\xc5\xef\x43\xb7\xf8\x75\x1e\x33\x4a\xfa\xfe\xea\x52\x87\x2f\xfc\x4c\x28\xa4\x13\x30\x95\x0c\xf9\x6b\xc5\x31\x9b\x15\x75\x22\xaf\xb3\x87\x8f\x86\x88\x5c\x2d\x0b\xc1\x2b\x7f\x59\x08\x5e\xf9\xf2\xed\x7b\x73\xfd\x09\x1b\x95\x30\xab\x05\xaa\xdc\xba\x9d\x78\xde\x0d\x10\xb8\x3f\x0b\x5f\x16\x53\x28\xe0\x9a\x0a\x96\x99\x63\xd7\xa7\x20\x94\xe2\x95\x69\xf9\x6f\x1b\x5a\x4d\x42\xda\x83\x4e\x39\x2f\x33\xf4\x53\xb8\x90\x05\x61\x15\x5b\x69\x65\x1b\x99\xe3\x11\x3e\xa1\x14\xd9\x70\xba\x93\x22\x05\x29\x2f\x08\xc0\xf6\x48\xf5\xfa\x9a\x8a\xf3\x32\x9b\xf3\x16\x3e\x7e\xcf\xdb\x82\xc8\x4f\x18\xd1\xc4\xad\x3d\xb1\xcd\x0b\x02\x41\x6f\x1b\x2f\xb7\xdf\x75\x14\xdc\x43\xe3\xc7\xb5\x98\x29\x86\x89\x82\xa0\xad\xaf\xd5\xb4\x0e\xac\x6a\xab\xce\x13\x43\xfb\x66\x6f\x8f\x40\x56\x8c\x0b\x50\xb6\x90\x46\xe5\xe2\x42\x3f\xfa\xe2\xe8\x32\x56\x39\x79\x6a\xe5\xe2\xf8\x27\xa4\xa2\x9d\x24\xb4\x5d\x28\x41\xb6\x43\xe0\x1e\xb2\xee\x24\xb9\x62\x04\x94\x91\x59\xd4\x37\xdd\x59\x10\x30\xf7\xf6\x14\x8d\x80\xd9\xfd\xd4\x96\x13\x47\xcb\x55\x6f\x0c\xa3\x68\x92\x6d\x50\xcd\xdc\x74\xe7\x61\xdc\x3b\x02\x5b\xaf\x65\x1a\xae\x09\x7a\x03\x80\x14\xe4\xa7\x70\xd2\xb8\x47\xc0\xc9\x33\xa1\xfe\x3f\x5f\x4b\xc7\x0b\x8f\x6b\x6f\x68\x73\x5e\x66\x4b\x76\x97\x14\x54\x9d\x08\x5a\xb2\x3b\x2f\x13\x64\xb3\x11\x85\xea\x5d\xb8\x70\x5d\x4f\x95\x36\x8a\x1f\x5c\x6c\x68\xc5\xe7\x0a\x08\x6c\x00\x64\x42\xf6\x01\xa2\xb1\x02\x42\xed\xba\x75\x62\x3a\xaa\xe9\x24\x74\xc9\xee\xf2\x70\x7d\x78\x73\xf3\xcc\x4c\xbd\x47\xf6\x4d\xd6\xad\xc3\xe9\x30\xa6\xbf\x20\x3c\xf0\x30\xef\xf3\x32\xfb\x94\xb5\x66\xe3\x98\x7d\xd8\x07\x07\x28\xad\x68\x89\x9c\x97\x99\xb6\xcf\x2e\x2e\xdf\xb9\x48\x9d\x1d\xed\xe0\x80\x8c\x6e\xba\x5e\x94\x32\x96\x37\x84\x91\xe7\xd0\xbe\xec\x98\x96\xcd\xe6\x02\x2d\x55\x1d\xd5\xbc\x7f\xbc\x7f\xc4\x16\x28\x97\xa5\x93\xcb\xd2\xc4\x2f\xd5\x6b\x0c\x42\x62\xd9\x8c\x51\xc1\xf0\x3c\x16\x01\x33\x87\x13\xec\x0f\xac\xd7\xb5\x51\xd3\x33\x59\xd3\x8c\xe7\x64\x9f\x4c\xc8\x35\xed\x88\xa8\x8d\x7d\x00\xa0\x90\x12\xe8\xd4\x81\x3d\x39\x55\xae\x91\x1d\x1e\x1e\x43\x68\xdf\x8e\x7d\x70\x40\x7e\xd0\x21\x51\x1c\x4e\x3f\xb7\xc8\xf6\x0c\x3a\x7c\x1f\x74\x7c\xf9\x92\x50\x31\x27\x2f\xbd\x5d\x87\xd0\x96\x11\x5e\x55\x6c\x41\x2b\xd3\x05\xd6\x0a\x60\x05\x80\x71\x5f\x36\x2f\x79\x49\x96\xea\xa5\x6a\xa4\xc7\xfc\x86\x2c\xcd\xb0\x0f\x0f\xf8\xd9\xa6\x67\x1c\x22\xc3\xe4\xd3\xc3\x13\x2a\x6a\x71\xb7\xaa\xd7\x9d\x26\xa8\x5d\x50\x1a\x11\xb7\xa6\x34\xc8\x47\xf3\x01\x09\x86\x38\x59\xfb\x1b\xdf\x3d\x12\x56\x75\x1e\x1a\xba\x69\x04\xd2\x34\x0e\xd9\xc3\x4b\xf2\xa1\x20\xf3\x35\xda\xfc\x1d\x93\x17\xaa\xf7\xe5\x37\xf0\x68\xa7\x54\xcc\xd7\x4d\xc5\x67\x54\x32\x4f\x3e\xc0\xef\x35\x83\xc0\x1f\x07\xd6\x86\xab\x41\x52\xf1\xed\x4d\x57\x86\x95\x3b\xb0\x37\xa3\xf0\x4f\xf2\xe9\x5b\xf6\xd1\xe0\x7e\xd3\x95\xe8\xb3\x81\x1b\x52\xf8\x23\xd9\x57\x10\xd3\x4e\xbf\xb2\x31\xec\x02\x8a\xc7\xe2\xd7\xf2\xae\x71\x8b\x19\x69\x97\xf7\xda\xd0\xc5\xa4\x50\x84\xa5\x0b\xfb\xca\x8f\xc5\xdf\x74\x25\x3c\xc6\x89\x3f\x49\x91\xd8\xc8\xf6\x04\x43\xd2\x06\x20\x8e\x6d\x74\xd5\xff\x61\x6d\xed\x39\x96\xce\x49\x1a\x30\x69\x9d\x1f\xe8\x9b\x9a\x81\xa9\x83\x4e\xcb\x07\x45\x5f\x28\x54\xb3\x61\x48\xdf\x97\xf1\x36\x11\xcf\x75\x30\x9b\x88\xcb\xc6\xd8\x00\x65\xe4\x08\x45\xfe\x68\x23\x5b\xc3\x52\xe7\xec\x8c\xa3\xd2\x84\xdd\xb0\xfc\x39\xf9\x70\xe6\xac\xa4\xeb\x6a\x2b\x42\xbb\x3c\xb3\x61\xd2\x79\x66\x7c\xc2\x63\x8b\x7d\xdd\x33\x21\xb3\x12\xfc\xb5\x82\x5c\x71\xd9\x81\x4d\xfe\xd5\x9f\x9c\x65\x67\x59\xa8\x88\x1f\x39\xba\x8d\x84\xc2\x88\x90\x43\xf9\x36\x4e\x9c\x09\xf9\xb5\x9a\xf6\xcb\x4c\x69\xbe\xaf\xf3\xac\x91\x6d\x4e\xa0\x40\xe8\xeb\x4c\x8d\x9f\xbb\x86\x47\x5f\xb9\x96\x47\x5f\xf9\x4d\x8f\xbe\x8a\xdb\x16\xea\xbf\x2f\x8f\x5d\x87\x2f\x8f\xfd\x0e\x5f\x1e\xc7\x1d\xbe\xfa\x93\x6b\xfb\xd5\x9f\xfc\xb6\x5f\xfd\x29\x68\xfb\x9e\x3b\x94\xd7\x01\xce\xeb\x1e\xd2\xef\xb9\x87\xf5\x3a\x44\x7b\xdd\xc7\xfb\x3d\xd8\xed\xef\x01\x3f\xfc\xdb\x60\xa2\x53\xf7\xf6\xe6\xb0\xee\x4f\xe2\x3d\xf7\x66\xb1\x0e\xa7\xb1\x0e\xe6\x11\x87\x02\x60\xed\x35\xb2\x55\x1b\xaf\xe7\xab\x5b\x47\xde\xb2\x2d\x0f\xdd\x77\x65\x8b\x79\xde\x7b\x29\xb0\xea\x97\xb6\x0b\x65\x35\x00\xec\x9c\x98\x12\x08\xfb\x64\x9b\x63\xaf\x20\x26\x6c\xec\x13\x32\xa3\x55\xa5\x0c\x6b\x33\x2c\x84\xb8\xc0\xc3\x87\x6f\xce\xc1\x1f\x8f\xa4\x49\xad\x3a\xb9\x2c\xb5\xac\x66\x2e\x80\xdf\xcb\x7f\x41\x51\x66\xb9\xd1\x2a\xdd\x4e\x0f\x66\x24\xaf\x79\x17\x44\x7d\x68\xbb\x58\xaf\x98\x80\x59\xf9\x41\x3d\x7f\xf7\x56\xd3\x00\x52\x38\xeb\x08\x26\x5e\x10\x85\xce\xf4\xed\x7a\x75\x26\x30\x75\x1b\x65\x6e\xa1\x13\xe4\x0b\x69\xbb\x00\x63\x47\x6d\x71\xaa\xcf\x99\x50\x3e\xa0\x9b\x17\x0e\x80\x2a\xdc\xa9\x52\xdd\xcb\xc3\xf2\x82\x5f\x82\x0a\xc5\x34\xa5\x66\x08\xc6\x49\x14\x68\x01\x2c\xcb\x5d\x01\x96\x41\xf0\x7c\x2d\xfd\x22\xac\xc3\x13\x4c\x50\x3b\xa7\x1b\x9f\x1f\xf9\xcf\x7d\xe8\x17\x87\x97\xd3\x1a\x7d\x57\x88\xb9\x39\x35\xe7\xd7\xef\x44\x3b\x28\xe8\x53\xad\x6d\x03\x44\x5c\x96\xbb\x20\xad\x9f\xe8\xf6\xa6\xa3\xd3\xac\xba\xea\xe6\x1d\x93\x3a\x0e\x58\x90\xd6\x62\xe2\x17\x11\xf9\x28\xeb\x5c\x69\x3e\x8e\x97\x47\x2f\x50\x56\x46\xf1\x36\xba\xc8\x94\xb0\x78\xcb\x43\x09\xe4\x7c\xc5\x56\xab\x7a\xc3\x32\x97\x24\xb5\x41\xd1\x10\xe0\x40\x9e\x74\xde\xc9\xdc\xee\xb7\x50\x09\xdc\x6f\xd3\xb5\x33\xdb\x66\xc1\xa4\x1f\xca\xa8\x6a\x3a\x7f\x37\xa3\x15\x6d\xb3\x26\x1a\xb0\x20\xc2\x24\xf9\x73\xf3\x61\x6b\xe5\x78\x13\x0e\x62\xa7\x1f\xec\x1d\xca\x91\xf7\xf6\xe4\x82\x74\xfc\x77\x86\xb1\xbc\x6c\x76\x9d\x9a\xf3\xcc\x2e\x4c\x13\x04\x48\x25\xa6\xf3\x7c\xbc\x73\x5f\xc4\xc0\xc8\xeb\x6b\x2a\xb4\xe8\xe8\x6d\x4f\x8d\x30\xd5\x01\x0c\x85\x8e\xbf\xf5\xf9\xb8\xaf\x68\xe3\xf1\xc9\xc6\x20\xb3\x55\x0a\xed\x27\x21\x13\x5a\x82\x89\x61\x97\xec\xee\xc7\xba\xf5\x46\x55\x9e\x6a\x3c\x5a\xe6\xab\x1d\x9b\xa2\x1b\x8f\x96\x46\x53\xc5\x79\x71\x76\x87\x11\xe7\xe5\x46\xd3\x04\x18\xa6\x94\x6b\xaf\x3e\x7f\xb9\x21\xa7\xaa\x9d\xcf\x59\xd8\x1d\x96\x7e\x50\x7e\xfa\x37\x76\xe7\x62\x7f\x88\xf4\xa4\x20\xcb\x8d\x1f\x4f\xd7\x14\x59\x6e\x0a\xb2\xf4\xe8\xda\xd0\xd9\x8c\x75\x9d\x37\xc7\x55\x7a\x9a\x7d\xeb\xed\x43\x81\xce\x8c\xa1\x12\xf4\xcb\xc7\x23\x26\x64\x7b\x97\x9e\xfb\x0a\xad\xb5\x25\x12\x00\x1b\x26\xcf\x25\x24\xc3\x86\xcf\x36\xb9\x60\x00\x5d\xc5\xe7\x19\x5a\x3f\x83\x91\x25\x4d\xcc\x34\x4f\x4b\x5c\x43\xbb\x8e\x2f\x44\x8f\x32\x05\xd9\xd0\x2a\x25\x73\x40\xda\x14\x41\x6e\xba\xdf\x68\x95\x26\xc8\x86\x56\x79\xc4\x5d\xa6\xb3\x13\xda\x73\x04\x42\x25\xf2\x10\x90\xd6\x64\x1f\x2d\x64\x8c\x73\xc8\xd0\xb6\x54\xfa\xdf\x25\x7c\xb0\xb9\x22\x03\xfc\x61\x32\x87\x70\x92\x02\x01\x79\xd4\xdf\x28\x92\xdb\x67\xe0\x16\xcf\x09\xdb\xe9\x3a\x11\x94\xb7\xe0\xd9\x66\xa2\x87\x4a\x96\x87\xac\x30\x4b\xb6\xd4\x5c\x0a\x28\x3f\x67\x15\x93\xbe\x56\x8e\xd7\x78\x5a\x44\xb7\xc8\x64\x72\xfc\xef\x71\x98\xa5\xab\x3e\x59\xd1\xe6\x4c\x49\xb7\xcb\xf3\x4b\x42\x08\xc1\x80\xf7\x0a\x0a\x36\xed\x62\x1f\x8f\x96\xec\xae\x0b\x1e\x70\x2c\xc0\x94\x63\x38\x85\x05\xe1\x46\xde\x11\x79\xcd\xf0\x33\x6e\x6f\xf0\x9d\x4b\xd6\x52\xa9\x76\x4a\x31\x07\x37\xb7\x9b\x92\xb3\x92\x80\x19\xa3\x9b\xb1\x5b\xde\xc9\xae\x80\xe6\x8a\x30\x92\xd7\x42\x01\xa3\xd2\x84\xff\xe5\x35\x83\x81\x66\xeb\xb6\x65\x42\x02\x4d\xea\x56\x89\xe7\x9a\xe9\x36\x9d\x0f\xb2\x20\x2d\x5b\xd0\x76\x5e\xb1\xae\x53\xa6\x9a\x82\x6c\xfa\x1a\x84\xa6\xe4\x0c\x90\xbe\x62\x33\xba\xee\x98\xdf\x06\xc6\xb2\x88\xaf\xf8\xe2\x1a\x63\xa6\x92\x56\x8c\xcc\xd7\x8c\xc8\x1a\x50\x00\xee\xf1\x5a\x10\x2e\x08\x25\x55\x5d\x37\xd3\xf1\x08\x08\xe0\xd1\xca\x46\xe2\x14\x40\xf2\x52\x13\x3e\x27\xdd\x92\x37\xef\x85\xe4\xd5\x6f\xb4\xe2\x73\x50\x6c\x90\x89\x54\xa4\x92\xac\x9d\x72\xf2\x0a\x3f\x28\xe2\xbb\x33\x36\xa0\x2c\xe1\xdc\x82\x7d\xa7\xed\x0a\xe8\xa4\x0f\xe7\xc0\x17\x2c\xe5\x5c\xba\x80\x48\x52\xf3\x8e\xae\x5a\x46\x97\xda\x1e\x3b\x38\x20\xbf\x5e\x33\x98\x1c\xef\x08\xad\x5a\x46\xe7\x7a\x9e\x6c\x3e\x25\x6f\xea\x0d\x23\x35\xf0\x83\x08\x76\x0b\xc4\x5c\x4d\xd5\x90\x30\xf8\xfe\x7e\xe8\xc2\x35\xea\x31\x9c\xd7\x1b\x16\xf0\x94\xbe\x4d\x6b\xc1\x3d\x4d\x3a\x65\x04\xa5\xa4\x3c\x91\x86\x52\xe4\x49\x9a\x2a\x2b\xc8\x17\x15\x4a\xef\x3e\xe6\x31\xc6\x4b\x76\x97\x71\xf9\x04\x3c\x81\xa3\x60\x32\x18\xae\x66\x5c\xa9\x9a\x0d\x6d\xc9\x72\x13\x2e\x18\xcd\x13\x90\x8e\x17\x2e\x67\x03\xfb\x9e\x7d\x33\x76\x71\x28\x4d\xd3\x84\x94\x78\x1c\x86\xf4\xcf\x80\x90\x84\xc6\xf1\xe3\x6e\xb1\x71\xa8\xf4\x04\x67\x8c\xa2\xf1\x0b\x9b\xd5\xed\x1c\xb8\xbf\x64\x77\x5f\xe0\xf2\x6b\x28\x6f\xe1\x58\x60\x45\x15\x39\x70\x97\x65\x9d\x95\x0a\x98\xb1\xda\xdb\xff\xd0\x06\x67\x4c\x88\x65\x6f\x77\x83\x41\x8c\x65\x30\xb4\xc3\xa9\x46\x80\xee\xbf\x18\x1b\x32\xf6\x1f\xc2\xa4\xbe\x09\xa2\x99\xb4\xc3\x0e\x51\xad\x94\x5a\x49\x31\x69\x0b\x57\xfc\x19\x00\x51\xac\x36\xf2\x60\x57\x4c\x24\x0c\x68\x2e\xa2\x53\xaa\x4f\xd7\x1f\x96\x29\xae\xe2\x64\x23\xbf\xe7\x2d\x18\x3b\x44\xbb\xd7\x89\x70\xa3\x92\xa1\xae\x9d\xa1\x2d\xb2\xf1\x7c\x52\x5e\xda\xe7\x2e\xe1\x35\x75\x81\x3f\xc1\xab\x49\xee\x1b\x8d\x5b\x22\x96\xae\x43\x41\x36\x53\xa8\x0a\xc1\x88\x84\x1a\x5d\x59\x75\xbe\x08\x9b\x0c\x97\x09\x56\xb8\x70\xbd\x0d\x52\x9a\xf4\x56\x67\x1c\x75\x7f\x30\x65\x24\x21\xe6\xda\xcc\xa7\xe8\x36\xe7\xa6\x03\x5a\x49\xff\x86\xd5\xca\x93\x82\x04\x8d\xf5\xd3\x5e\xeb\x0a\xc8\x1b\xb7\xd6\x4f\x7b\xad\x67\xca\xbe\xe7\xf2\x2e\x6e\x6f\x9f\x43\x8f\x0d\x10\x7d\xb7\x20\x03\xe4\xd8\x8a\x56\xce\x9f\x09\x70\xe9\xaa\x7f\x1d\x34\x42\xb1\x4e\x5b\xae\x61\x1b\xf5\x12\x78\x6a\xbe\x63\x90\x00\xf1\x42\xc4\xe1\x81\xd9\x93\xcd\xa9\xd6\x8a\xf4\x49\x0e\xb1\x03\xcf\xe8\xdd\x28\x53\x17\x61\x14\xde\x90\x79\xbc\xc7\xa7\xa1\x05\x54\x03\x03\x3d\xa2\xa4\x61\x52\x14\xb5\xee\x43\x8b\xa3\xd4\xe3\xad\x58\x06\xa1\xeb\x82\x7c\x57\xd7\x55\x01\x39\xfc\x42\xe7\x57\x6d\x8e\xc8\xa4\x5a\x41\x77\xf9\x43\xf7\x5c\x8d\x69\x23\xdb\x30\x94\x8d\x31\xbc\x3d\x58\x2d\x3f\xb4\x6d\xdd\xde\xdb\x4c\xcc\xeb\x5a\x6c\x58\xab\xc4\x72\xf9\x98\x0e\x48\xda\x28\x57\xbf\xd6\x89\x56\x7e\xf4\x05\x57\xda\xb4\xad\xb3\x9c\x3c\xe8\x6f\x7b\x4f\x8b\x61\xbe\xae\x9b\x3b\x57\xa7\xa6\xe3\x95\x5a\x3b\xcd\x61\x65\xce\x3b\x39\x5d\x42\x37\x50\x15\xf3\xa5\xda\x6d\xb0\x7e\x6b\x6f\x4f\x7f\x8d\x8b\x91\x06\x26\xdc\xa8\x65\x32\x37\xd3\x45\x60\xb6\x18\xec\x5e\x57\xa4\xad\xd6\x9d\xfc\x8e\xfd\x19\x5c\x43\x7a\x55\xb1\x0c\x5b\xbb\x57\xae\xfa\x75\x3c\x1e\x75\x80\x63\xd7\xce\x2c\x8e\xa0\xe7\x80\x57\x6a\x40\xac\x0d\x06\x1d\x17\x22\xde\x45\x88\x7b\x5d\x4e\xd5\x4b\x5c\x4d\x5c\x2c\x60\x96\x9d\x9c\x26\x17\x1c\x44\xc2\x71\x41\xbe\xf0\x20\xdc\x8f\x47\x4f\x21\x45\xb7\x74\xa7\x13\x46\x6a\x0e\x89\x09\x26\x20\x2b\x83\xb6\x7b\xb3\xee\xe4\x1b\x2a\x67\xd7\x59\x8f\xc0\x01\xb2\x58\xd8\x17\x2c\x4b\xa5\x8f\xe7\x9d\xd4\x8e\xad\x6a\x1e\x6c\x06\x09\xa6\xfc\xe6\x2f\x36\x93\x7b\x0f\xc7\xc9\x71\xd5\x61\x63\x3d\x88\xde\x56\x34\x83\xc2\x1d\x27\x1a\xc4\xee\x4c\xd1\x20\x11\xf2\xbe\xce\xd0\x83\x28\x60\x21\x7d\x86\x76\x55\xad\x0d\xb8\x58\x20\x95\x7e\x73\x2a\x41\x1f\x77\xf5\x97\x61\xba\xbb\xae\x2d\x4b\xf7\xb6\xdb\x3e\x9c\x39\xf8\x85\xcd\x18\xdf\xb0\x36\xab\x1b\x5b\x67\x6d\x37\x68\xae\x63\x6b\x1f\xac\x83\xe2\x95\xd6\x43\x1e\x21\x61\x88\x28\xd1\x86\x1a\x4f\x53\x01\xcf\x4b\xad\xd5\x9d\x44\xfa\xa9\xed\xd1\x48\x4a\x34\x5c\x82\xe3\x72\xbd\xf8\x22\xee\xf6\xc6\x0e\x84\xe2\xbe\x87\x07\xc2\xc9\xb7\xba\x26\x58\x4e\xf5\x29\x8a\xdc\x97\x6c\x97\x99\x30\x35\xb6\x58\xc5\xe6\xca\x4e\xf4\x79\x0c\xae\xec\xc2\x89\x09\xbd\x43\xee\x7e\xcf\xc1\xbc\xe0\x97\x7a\x01\x49\x39\x35\x35\xd2\x2b\xf8\x94\x4f\x83\x5a\xf7\xe4\xd8\x13\xb2\x4f\xea\x06\x2a\x19\xea\x92\xac\xe3\xb3\xf9\x76\x58\x65\xa4\x6d\xcb\x7d\x80\x2c\xeb\xb1\xcd\x96\x8b\x05\xb5\xa7\x24\x81\x18\x9e\x19\x08\xcc\x6b\x3c\x17\x8c\xfc\xe8\x9d\x4e\xc1\x29\xae\xb9\x90\x19\xcf\x15\x61\xe1\x23\x18\x87\x5d\xfe\xd9\xc8\xba\xf2\xa8\x89\x88\xfc\xb7\x11\x14\x87\x77\x34\x5d\xc5\x44\xdd\x7a\xdd\x47\x60\x86\xe6\xbb\x2a\x99\xd5\xa2\x9d\x6d\x5a\x24\x7f\xa0\x66\x5c\x65\x37\x82\x42\xfd\xa0\xda\xc6\xa6\xae\xd2\x2b\xea\x05\x82\x2b\x05\x39\x8d\x37\x5d\xf5\xd6\x55\x48\xfb\xe9\x63\xd4\x18\x76\xf9\x83\xc3\x67\xd7\xa1\x33\xca\x55\x7b\x5d\x8a\x17\x25\xc9\x60\x1d\xc3\xf5\x22\x50\x83\xb7\x63\x23\x85\x67\x53\x3b\xc0\xa4\x20\x87\x6e\x4b\x85\x41\xf6\xf6\x7c\x2b\xe0\x97\x73\xbc\x21\x25\x51\xb8\x17\x81\x3a\x21\x33\x2a\x44\x6d\xe3\x5f\xe8\x69\xd7\x57\x92\x42\xd8\xa6\x6c\xeb\x95\x2f\x11\x58\x38\x52\xb7\x9e\x68\x3c\x7a\x93\x81\xc1\x71\x05\x38\x04\x36\x3a\x4f\x87\xcf\xd1\x8d\x98\xf8\x73\xd9\x38\xbd\x9e\xe6\x1e\xa2\xe6\x51\x30\x16\xbd\x3e\x63\x9d\x54\x04\xc7\xf9\x3c\x6d\xbf\x05\x9c\xeb\x9c\x3a\x0a\xc8\x55\xa7\x1f\x8e\xcf\xbc\x50\x93\xb2\xa4\x3c\x78\xb0\x5b\x7c\xde\x6c\x57\xbc\xd5\xbc\xc5\x13\x4c\xee\x3c\x85\x39\x3d\xf4\x1f\x3f\x9e\xfd\xef\x37\x3f\xfc\xc7\x24\xc8\xf3\xf8\xa4\xef\xef\x4d\x61\x6e\xba\xcf\xc9\xd3\xb4\x28\x0d\xab\xab\x75\x07\xa5\xe8\x6a\xe4\x9f\x69\x2b\x39\xad\x94\x81\x6d\x52\xd5\x1f\x0a\xf2\x01\xf6\x3b\x7b\x64\xdd\xdb\x37\xa1\xda\x5e\x29\x4a\xed\x4b\x7e\xfb\xad\x43\xe4\xdd\x35\x2f\xe1\xf4\xc9\x67\x5e\xf9\x9f\x39\xfd\x3d\x98\x4e\x2c\x85\x61\x35\x6d\x9a\x4a\x19\x6e\x0a\x09\x0f\x70\x0e\x89\xd8\xd0\x2b\xd8\x40\x6d\x53\x96\x0f\xbb\x06\x61\x5e\x36\xf4\x0c\x52\x59\x5a\xbf\x50\x13\x41\x74\x99\x3b\xfd\x62\xaa\x56\xe2\x9a\x95\x9f\x65\xab\xdd\x22\xdf\x65\x42\x57\xab\xe8\x95\x03\xe1\x7d\x5f\xfd\x0a\x1f\xbc\x5e\x6d\x94\x44\xe6\x75\xbd\x6a\x68\x8b\x0e\xc0\x4e\x74\xf4\xf0\xe8\x3d\xeb\x73\xf9\xe1\x18\xc9\x32\x25\x13\x18\x9a\xfa\x83\xf5\x3c\xcd\xf8\xf8\x8d\x9c\xbe\x5d\xaf\xa0\xd0\xcb\x3f\x7b\x83\x16\xcc\x14\x9f\xf3\x1c\xeb\xf7\x82\x49\x98\xbc\xbc\x8f\x16\x6e\xa5\x41\xcd\x3c\x10\x2b\x41\x10\x94\xfa\x8c\xdb\xa4\x2c\x3e\xc8\x4d\x11\xc9\x1f\xb4\x01\xa1\x26\xda\xe2\x20\xa7\x66\x38\x5c\x15\xfe\x0d\x16\x29\xe3\x26\x69\x37\x06\x46\x63\xac\x2d\xde\x78\x46\x0c\x54\x5e\xd7\x25\x16\x33\xe8\x5d\xa4\xf1\xee\xb0\x00\xa3\xa6\x31\xd5\xa8\xce\x18\x43\xf3\x26\x1f\x8f\x56\x50\xa0\x4a\x4e\x09\x34\xb2\xc6\x59\x09\xbe\x87\x93\xfa\x31\xdc\x55\x84\x30\x8c\x65\xd2\xa0\x65\x32\x1e\x95\x72\x47\x79\xcc\x4a\x1b\xc9\xc1\x25\x2f\x68\xae\x1f\x16\xe4\x68\x1f\x6a\x7d\xe5\x94\x0b\xdc\x5b\xb8\x70\x47\xe6\xb8\xc0\x93\x72\x4a\x94\x3e\xc0\x12\xf7\xaa\x7b\xb1\x0b\x46\x68\xa3\x3e\xb4\xc5\xf8\x59\x74\x93\x8b\x1d\x54\x0f\x09\x07\x71\x73\x07\xbf\xc5\x0c\xa7\x85\x5f\xdb\x1a\x16\x05\xc7\x8e\x50\xaf\x21\x61\x25\x35\x8b\xa1\x4f\x78\x92\xa0\x50\xbd\xcf\xba\xdf\x74\xed\x3a\x18\x3b\x2b\x5d\x7c\x4c\x56\x72\x6c\x4f\x9c\xed\x30\xe6\x7a\x97\xf0\x45\x57\xf0\xed\xb4\xf0\x70\x7f\xf8\x8c\x5a\x59\x6f\x1a\xae\x3c\xe8\xf0\xd2\x89\x7f\x64\xe9\x6d\xd5\xd2\x17\x47\x27\x97\x5a\x53\xaf\xe0\x1c\x04\x39\xd5\xba\x7a\x25\xed\x2d\x86\x7d\x2d\x2d\xc2\xea\x19\xb5\x13\xae\x90\x08\xe4\x94\x70\x57\x1c\xea\x34\x81\xdd\x9e\xcd\x36\x17\xdd\x78\x98\xf0\x05\xed\x29\xbb\xf8\x85\x17\x27\x1c\xdc\x9f\x4c\x34\xab\x67\xd1\x61\x50\xc9\x19\x74\x83\x99\x77\x00\x10\xe5\xde\xf1\xf0\x49\xa5\x33\x82\x41\xe1\x0a\x58\x52\x6f\x21\xd8\xac\xec\x57\xf3\x3c\x38\x13\x84\xfd\xbc\xdd\x1b\xb5\xaa\xde\x17\x82\x69\xc2\x0b\xaf\x2a\xb0\x70\x25\x8e\x51\xf4\xd0\x37\x14\x2d\x36\xd7\x7c\x71\x0d\x51\x6c\x17\x02\xae\x3f\x62\x34\x57\x5f\x85\x55\xaf\x9a\x8a\xdd\x2a\xc0\xfa\xe3\xd1\xf1\xd7\x4f\x85\xde\x32\x3c\xbe\xe4\x9e\xf0\x15\xdc\xda\x61\xc1\xbb\x8b\x58\x0c\xc9\x4e\x4f\x07\x88\x12\x87\xe9\x07\x30\x70\xad\xb0\x8d\x8d\xf5\xea\xfb\x49\x7a\xb5\x0e\x49\xcc\xbd\x18\xbb\xe9\x12\x87\xd9\x37\xc9\x18\x7b\xd4\xda\x86\xd9\x37\xc9\x18\x7b\xd4\xda\x0b\xb3\x6f\x06\x62\xec\x66\xd2\xa6\xcc\xc2\x6e\xad\x5b\x44\xdc\x0f\xa3\x46\xb1\x9f\xf4\x6a\xe8\xaf\x46\xac\x61\xf9\xb5\xce\x66\xb5\x90\xec\x56\x5a\x73\x5a\x19\xfd\x36\xb6\x43\xdb\x05\xeb\xfb\x00\xdb\x0d\xed\xad\x2e\x93\x1e\xcd\xb9\x4b\x7a\x09\x18\x8b\x68\x0e\x09\xa1\xea\xce\x8b\xa3\x42\x94\x17\x79\x7a\x82\x79\xd5\xf3\x0d\x6b\x3f\xb6\x5c\xe2\xe9\x50\xd2\xd5\x58\xfc\x20\xaf\xd9\x1d\x59\x51\x39\xbb\x9e\x62\xbb\x77\x6a\x73\x5d\xb1\x55\xdd\xde\x91\x8a\xde\xc1\xc6\xd0\xd5\x44\xd4\xe4\x9a\xb6\x2b\x32\xaf\x05\x53\x2d\x71\xbb\xd5\x13\xc9\xd4\xff\x7f\x9e\xcf\xdb\x07\xab\x33\x5c\x70\x1a\x0c\x52\xec\xf1\xa0\x37\xe8\x79\x67\x0f\xcb\xc6\x47\x0a\x35\xe2\x58\x9d\x0b\xaa\x12\xa6\xc8\xd5\xa2\x03\x1d\x1c\x4f\x4d\x99\x43\x48\x71\xef\x94\xe2\xc8\x3c\xf2\x6b\xb3\xe7\x70\xcc\xdd\x94\x20\xfc\x05\x2e\x04\xfe\xeb\xbb\x13\xf2\x6e\xc9\x1b\xc8\x37\x6f\x92\x66\x15\xf8\xd7\x67\xdd\x5b\x5e\x65\x39\x81\x00\x24\x95\x80\x0a\xc2\x71\xff\xd0\x63\x6e\x3a\xd9\x32\xba\x9a\x5a\x67\x91\x5c\xb1\xaa\xfe\x48\xe6\x35\xeb\x88\x72\xb7\xc1\x38\x2a\xe0\xf4\x0b\x97\x44\x30\x36\xef\x62\x48\xb2\x26\xed\x5a\x14\x64\xc1\x37\x4c\x10\x2e\x3b\x32\x5b\x77\xb2\x5e\x39\x32\xc0\xed\xc3\x8a\x0f\xb7\xc0\x86\x28\x08\x61\x2e\xcc\x41\xf2\x28\x6a\xbf\x5d\xaf\xb4\x91\x97\x3b\xa7\x4e\x97\x7f\xdb\x53\x9f\x19\x52\x2d\x27\xa7\xe4\x76\x3c\xf2\xc3\x5d\x23\xeb\xf9\x02\xf5\x6f\x8d\x94\xe7\xe1\xaa\xf3\x58\x88\xef\x8b\x7e\x75\xb5\x45\x33\xd7\x17\xf5\x1c\x1c\x90\x1f\x29\xaf\xd8\x7c\x3a\xd6\x86\xa3\x59\x5d\xfb\x64\x72\x62\xc2\x12\xa5\x3b\x82\x83\x9a\xdf\xd8\x0b\x10\xbc\xe2\x48\x5a\x6a\x17\x80\x22\xa1\xed\x00\x67\xdf\x6d\x36\x5a\xdf\xc7\x30\xa3\x55\xf5\x3f\x59\xd5\xb0\x96\xf4\xb7\x27\xf5\x12\xaf\x45\xd4\x24\xcd\xa7\x68\x84\x4c\xa7\xd3\xe0\x9c\xac\x67\x77\xf4\xb4\x85\x02\xe2\xfb\xdc\x5c\xb8\x2a\x71\xfd\xc1\x04\x7a\x33\x88\xb1\x11\xe2\xc2\xc2\x6a\xc1\x08\x42\x22\x35\x62\xac\x19\x3f\xb3\x9a\xef\x52\x29\x1f\x0a\x22\xc1\xeb\xfe\x44\xa7\xdb\x78\xd2\xbe\xd3\x3d\xe8\x75\xef\x74\xbb\xc1\x01\x72\x92\xf5\x94\xc8\x22\x16\x8d\x27\xa2\x74\xa9\x68\x8d\xef\xf9\xbb\x2a\x24\x1b\x66\x52\x60\x9c\x9e\x48\x46\xc8\x94\x11\xe3\x2a\xf0\x55\x53\x53\x30\x66\xe2\x18\xdc\x15\x93\xd7\x0d\x9c\x8e\x53\x7d\x30\x5f\x30\x1e\x09\x74\x3a\x74\xc1\xbb\x0e\x50\xb8\xe4\x13\xfa\x8e\xbe\xa1\x9d\x8e\xcd\x5a\x90\xe6\x6c\x7f\x70\xc8\xd6\xa0\x03\xcb\x0f\x0f\xdb\xc3\xb9\xde\x57\x44\xec\x02\x07\x47\x09\x64\x5d\x93\x92\x7d\x24\x5c\x34\x6b\xe9\x2c\xdc\x14\xc8\x6f\x9f\x01\x72\x45\xc5\xdd\x10\x4c\xbf\x3a\x45\xf9\xb0\x7d\x12\x88\x2f\xbe\x78\xe6\x8c\x9e\x3c\x99\x98\xe4\x7b\x7b\x4f\x9b\xdf\x13\xa7\x66\xdd\xb1\xdb\xde\xd1\x65\x5e\x92\xdb\x60\x63\xc1\x48\xd9\xae\x78\xfc\xba\xe3\x62\x41\x7e\x67\x6d\xad\x4d\x07\x33\x68\x34\xa6\x1f\xad\x10\x2e\x44\xa1\x46\xd5\x6a\x18\x2f\x20\xbe\xe0\x97\x3a\x9e\x54\x28\xda\x8b\x8c\xe7\xdf\x90\x17\xb7\x72\xea\xac\x86\x5f\x6b\xd8\x01\x76\xe7\x0a\x10\x37\xf5\xe0\x56\x86\x8a\x98\x76\x4e\xed\x2a\x58\x41\x15\x90\xbd\xd4\xe0\x85\x59\x0f\x7b\x7b\x29\x39\x38\x38\x20\x4d\xcb\x1a\xda\xea\x23\xe4\xfa\x42\xf8\x15\xe5\x42\x8d\x0b\x3b\x42\x67\xd2\x20\x86\x8b\x5f\x10\xe1\xd7\x8e\x78\xd7\x6d\xa8\xc9\x8a\x1c\x0a\x8e\x57\x0a\x0d\x73\xa6\x54\xbf\xb0\xa5\xc1\xfd\x9b\xa1\xbd\x88\xcf\xad\xa6\xa2\xd8\x87\xa4\x0b\xd2\x57\x3d\xbb\xd5\x54\x4d\x10\x13\xca\xf0\xb5\x95\xde\x3f\xe0\x03\xb1\xf7\x75\xc7\x76\xd2\x31\x38\x4a\x8a\xdb\x9d\xd0\xdc\x70\x47\x3b\xb0\x4e\xc5\x7a\xd6\xca\x92\xbe\x35\xe2\x5f\xb7\x7c\x81\x87\xef\xb9\x30\x81\x87\xf0\x84\x8e\xd8\x3f\x32\x25\x14\x19\x17\x17\x27\xe2\xb2\x20\xd8\x0b\x74\xbd\xb8\x10\x70\x24\x54\x8d\x81\x1a\x50\x60\x60\x44\x13\x1f\x98\xaa\x1e\xbd\xf0\x14\xdf\x2e\x05\xfb\xb1\xad\xc5\xc2\x4a\x35\xde\xb6\xa0\xe3\x41\x42\x87\x40\xa4\x3d\x0b\x33\x1e\xc3\xd1\x1f\x74\x72\xb7\x9f\xa1\x91\xde\x51\x23\x7d\x7a\x26\x88\xc1\xe8\x65\x69\xc1\x05\xa7\x66\xd6\xe2\x63\x4b\x9b\xbf\x76\x26\x76\x81\x0b\x05\x20\x4c\xad\xf5\x9f\x98\xce\xc4\x2e\x2a\x2f\x5a\x2b\x78\x95\xbb\x64\x84\x71\x3a\xec\x39\x20\x67\x81\x24\x6e\xc9\x28\x95\xc4\xda\xf0\x03\x62\x9a\x3b\xd3\x5f\xe8\xfb\x0b\xdc\x39\x25\xbf\x60\xcf\x9d\x52\xd2\x4f\x35\xa3\xef\xbd\x6a\xae\xa9\xa2\xeb\x61\x5e\x90\x68\xc2\xe6\xb1\x46\x14\xce\xa2\x3e\xc6\x01\xdd\xfe\x19\x2f\x85\x50\xe2\x6c\x97\x6a\x6b\xea\x09\xe3\x73\x5b\x38\x16\x4f\xa3\xc0\x1d\x0a\xee\xe6\xe1\xe0\x50\x97\x3e\xca\x24\x83\x98\xb2\x35\xbe\x5e\xd3\x26\xb3\xc5\x2d\x4b\xf4\x55\x4c\xd5\x88\xad\x45\xbb\x1f\x88\x15\xa3\x85\xf9\x13\x13\x36\x42\x8c\x91\x6f\xeb\xa7\xdb\x76\xd6\xfe\x88\xbd\x54\xaf\xc6\x60\x67\x76\xef\x35\x6d\x74\x65\x90\xb6\x4d\x6f\x34\x2d\x7e\x96\x6d\x74\x19\x73\x6c\xa8\x7a\x2d\x95\x67\x8c\x54\x08\xc9\x69\xcf\x2b\x86\x25\x79\x89\x90\x92\x6a\x0a\x65\x81\x6e\xf4\x20\x6a\xa4\x31\xb0\x6f\x6d\xb8\x20\xf0\xa7\x37\xde\x0f\x77\xc4\xcb\xe9\x73\xe1\x62\xe3\x02\xb5\x3e\x44\x31\x84\x80\x13\x08\x5d\x0b\x67\xcd\x6e\xbf\x20\xd1\x88\x86\x5f\x8e\x18\xdc\xea\xac\xe3\x5e\xb1\x05\xbc\x31\x75\x94\x83\xc1\x2d\xbf\x96\xd6\xde\x99\x83\x29\x75\x0c\x4e\xfb\xcc\x4d\x47\x7c\xf2\xc1\x62\x4c\x17\x1d\xd1\x17\xe4\x78\x0e\x77\x3e\xee\x55\x11\x3a\x2f\x76\x18\xab\xd4\x44\x4d\x4e\x41\xdf\xd2\xb1\xcd\x46\xf7\x83\x02\x3a\x1b\xdd\x3f\x60\xfb\xe7\xf9\xbc\x0d\xe3\x01\x52\x4e\xbd\x3b\x1c\x7a\x31\x01\xfd\xba\x17\x58\x0d\x65\xcb\x34\x82\x43\x40\xbd\x80\xeb\xd3\xea\xf4\x70\x3d\x2a\x51\x71\xa5\x7a\x7d\x51\xd2\x79\x9f\xfe\xad\x5d\x46\x8e\xa0\xda\xcc\x85\x5d\x77\x0e\x08\x00\x27\x85\xed\xaf\x33\xfc\x86\xf0\xee\xee\x81\x61\xda\x0f\x14\x9c\x48\x39\x35\x57\x92\x24\x33\x33\x30\xf2\x60\x62\xc6\x8f\xf9\xf7\xa2\x8b\xe6\xce\xba\x9d\xe1\x7c\x18\x42\xd7\x01\x95\xe6\x12\x06\x7b\x9e\x1e\x9e\x28\xb0\xe3\x71\x22\xa8\xf4\x4e\xf2\xd9\xf2\xee\x97\x73\x17\x58\x7a\x30\x22\x94\x27\x6a\x1d\xd1\xba\x44\x90\x90\x1d\xea\x95\xc0\x28\x17\x10\x5e\x9b\x5b\x9d\xcd\x72\x70\xe2\x08\x77\x94\xfc\x72\x1e\x45\x40\xdc\x7b\x83\x93\xbb\x6b\x18\x62\x50\x60\x62\xf8\x53\x44\x0c\xe0\xbe\xd0\x6f\xe0\xfd\x0b\xb8\x46\x65\x6f\x8f\x70\xe7\x9c\xf3\x52\xd1\x16\x3b\x2f\x98\xfc\xab\xfa\x9c\x49\xba\xc8\xbf\xd1\xcf\x5f\xe8\xbb\x57\xf4\x59\x60\x5d\xcb\x0b\xee\x38\xca\xe1\x61\x6e\x03\xc7\xd3\x01\xad\x39\x1a\x8d\xea\x70\x59\xc7\xda\x73\x14\x2b\x04\x50\x30\xe9\x5a\x0b\xaf\x54\x19\x36\x00\xec\x9d\xa8\x70\xd8\x7a\xd7\x5a\x94\x43\x72\x57\x37\xb2\x49\x41\x6a\xc0\x0f\x08\x10\xdc\xe8\x90\xe7\xe4\xd1\x5c\x52\x3d\x34\xe0\x6d\xb0\xb1\xdc\x93\x1a\x8c\x61\x80\x95\x38\xbd\xc3\x6e\xfd\x71\x6f\xc3\xc1\xbc\xd1\x7a\x2a\xc5\xc5\xd2\x13\xc9\x18\x8f\xf0\xc8\x2a\xeb\x63\x78\xd7\x67\x6b\xe1\xe9\xb6\x65\x54\x30\x66\x51\xc5\xb9\x18\xe5\x37\x05\x17\x09\xd8\x52\xd7\xe8\xe2\xc0\x5e\xee\xe7\x93\xb8\xfb\x2c\xd6\xc6\x3b\x7e\x41\x3a\xef\xae\x49\x43\xd1\x27\x32\xaf\xf3\x2e\xad\xec\x1b\x13\x05\xb9\xb5\x10\xfb\x0c\x4a\x5d\x4d\x07\x9d\xb6\x63\xa8\x7a\xbb\xe0\xbf\xbf\x26\xed\x79\x64\x57\x7b\xa3\x96\xa4\x0c\x56\xe9\xc1\x01\x9c\xba\x23\x15\xa3\x73\xd5\xa8\x6b\xa8\x72\x9a\xf0\xd6\xd5\x43\x6b\x21\xbf\xc2\x6a\x4b\xba\x80\x50\x84\xa4\x0b\xb0\x8e\x4f\xc9\xbf\x93\x7f\xd7\x11\xd7\xfd\x7d\x63\x29\xd0\x05\x39\xc5\x26\x27\x97\x26\xe2\xbd\xb0\x97\x32\x05\x95\xf7\x1a\x81\x19\x15\x44\xd6\x64\x56\x57\x18\x25\x3e\x38\x20\x14\x31\x21\x75\x4b\x28\xf9\xfb\xba\x96\x0c\x4e\xdf\x91\xee\x4e\x48\x7a\x8b\x75\x3c\x80\xe6\x4e\x2c\x5f\x20\x96\xe1\x83\x93\xf8\xc1\xa4\x37\x0f\x5e\x12\xbe\x7f\x64\x0b\x4d\x15\xd0\x87\x87\x08\x86\x79\xb0\x7f\x14\x42\xf1\xcf\x16\x98\xda\x00\xe4\x82\x02\x74\x71\xc2\x2f\xf3\x90\x52\xfb\x47\x27\x97\x3e\x35\x60\xc6\x73\xc3\x39\x59\x93\x92\x8b\x39\x86\x12\xf4\xac\x8f\x76\xcf\xda\xce\xa9\xf4\x39\xf6\x9f\xff\xa9\x1f\xeb\xb9\xea\x1f\xba\x09\xe6\x1d\xcc\xba\x37\xa3\xbf\x63\x90\x3b\x9e\xd3\xfe\xd1\xd0\xac\xfc\x8b\xb9\x6e\x3a\x2d\x05\x1b\xf4\xc4\x3e\x68\x38\x70\xf9\xd7\x7b\x01\x13\xcf\x70\x84\xdc\xb3\xfb\xcc\xd4\x83\x85\x32\x99\x24\xcc\x1d\xbd\xbf\x47\xe6\xce\x2e\xfb\xd9\xfa\x54\xc6\x8a\xb1\x17\x2d\x3e\xbd\x24\x19\x22\xd3\x52\x4e\x2b\x26\x06\x82\x52\x00\x74\xc0\x7e\xf1\xcd\x6c\x6d\x1d\x26\x13\x57\x7d\xb3\x22\x51\x49\xe5\x1b\x19\xe3\xd1\x88\x6e\x57\xda\x9f\x4d\x6b\xff\xb1\x4d\xf9\x0f\xea\x6d\xea\x3c\x6f\xbb\x11\x3e\x51\x6f\xd3\xad\x51\x95\x50\x73\xa7\xf6\xd6\xc7\x41\xa7\x67\x2b\x9a\xa8\xbb\x7b\x07\xca\x52\xbe\x5b\x58\xc2\xd4\x45\x69\x69\x74\xdf\xd3\x32\x87\x31\xc6\x6d\x32\x67\xec\x76\x73\xf9\xe0\x16\x89\x1f\x90\x4f\x23\x8d\x91\xfb\xb4\x5b\x30\x39\xd9\x77\xb3\x31\x29\x79\x13\x8c\x40\xb1\xed\xc2\xec\xfe\xbf\xa4\xf5\x9f\x43\x5a\xed\x91\xb3\x0e\x6f\x15\x7b\x09\x8e\x9f\xb2\x37\x02\xb5\xd2\x2f\xbd\xeb\x64\x3b\x24\xa9\xb8\xdb\x6d\x11\x55\x5f\x1b\x06\x62\x05\x87\x9d\x82\xab\xac\xc7\xa3\xd1\x4c\x6f\x2d\x78\xf0\x20\x60\xb6\xbd\xca\xb8\xc7\xf2\xbd\xd9\x27\x39\xe1\x40\xa5\x6d\x5e\xb8\x0d\xd0\x7c\x4f\x25\xcd\x72\x72\x71\x7c\xe9\xdd\xec\x83\xf0\xf1\x97\x82\x41\xc4\x26\x41\x7b\x93\x31\xee\xd6\x8d\xf9\x31\x84\x3b\x5b\x12\xe0\x5f\x2a\xe4\x8d\xa7\x83\x27\x51\x7d\xea\xe0\x06\x08\x65\xb3\xc3\x11\xc3\x6d\x07\x70\xc7\xe1\x0f\xf0\x0d\xf4\x8d\x52\xd6\xd7\x54\xbc\xf5\x3a\x9b\x9f\xb1\x7b\x52\x67\x79\xdd\xd6\x1f\xdf\xf2\x4a\xf3\x0c\x18\x62\x21\x85\x35\xb6\x3d\x40\xf1\x02\xd3\x95\x07\xfd\x20\xda\x93\x30\x71\xb1\x33\x73\xcd\x1b\x48\x93\x46\x2c\x1d\x7b\x35\xeb\x11\x2a\x1b\x9e\x29\x65\x8a\xa9\xdb\xa4\x0c\x82\xc0\x26\x8e\xfc\x24\x9b\xc7\x3f\x3d\xda\xc7\xd5\x1e\xe8\x8e\xf6\xa8\xa1\x88\x72\xb8\x21\xed\x12\x0c\xdd\xe9\x6a\x5d\x96\xcc\x16\x8b\x25\x41\x84\x4c\x1d\x3a\x94\xee\x9f\xa5\x70\x98\x3f\x87\xc0\x3f\x31\xb1\x8d\xbc\x46\x49\x04\xb7\x72\xed\x22\x33\x06\xe3\xa1\x22\x1d\x16\x59\x4f\x44\x06\x83\x9d\x87\xa1\xb2\x4e\xc8\x50\xb4\x7a\x9e\x0a\xe9\x28\xe6\xe7\x27\xa0\x10\xec\xca\x1e\x42\xcf\x21\xb7\x77\x4f\xc2\x10\xc9\x21\x35\x68\xbe\xdc\x8f\x47\x9b\xe4\x29\xdc\xdb\xfe\xf9\xd4\xd1\x2d\x39\x25\xb7\x89\x34\x18\x56\xfe\x82\x16\xc3\xa4\xd7\x8e\x2a\xd2\xa1\x0a\xce\xe8\x97\x4f\x43\xed\x88\x82\x39\xc3\x63\xaf\x43\x96\x77\xea\xcd\x2d\xbc\x19\xf8\xb5\xc6\x5d\x95\xac\x43\x07\x73\xa2\x8a\xab\x5b\xfb\x33\xb4\xa9\x5f\xc0\xf3\x4e\xa6\x3f\x1f\x71\x53\xeb\x16\xdd\x27\xf8\x34\xc4\x6f\x83\x4b\x00\x9d\xd8\x81\xcf\x07\x1d\x80\xa5\x8d\xf7\xfb\x28\x81\xa0\x7c\x77\x27\x59\x97\xdd\x92\x8b\x4b\xf8\x51\xa0\x61\x71\x31\x4f\xf1\x2c\x6f\xee\x55\x28\x87\xc7\xa8\x5f\xe8\x63\xd4\xc3\xc9\x61\x33\xaa\xa9\x7a\x51\x03\xfb\x37\xc9\xfb\xd7\x43\xf4\x28\xe6\x0f\xac\xcf\x49\x61\x64\xc6\xd6\x45\x6b\x74\x82\x97\xe6\x9c\xf5\xfc\x5d\x74\xf3\x84\x57\xbd\x84\xf9\xf5\x5e\x59\xac\xeb\xd6\xbb\x7f\xc2\xeb\xe0\x97\xc6\xf6\x7a\xb8\x3b\x28\xbc\x1e\x7e\x79\x6c\xaf\x87\x7f\x0f\x85\xd7\x27\x2c\x91\x45\x32\x9d\x12\xd7\x5b\x5f\x98\xff\x14\xb9\xe9\x90\x8b\x49\x99\x78\x4d\x9b\x4c\x60\x30\xe0\xe9\xe2\xb0\x35\xc8\x19\x95\x8d\xf3\x92\x08\xf2\x6a\xc8\x25\x7b\x78\x20\x82\x7c\x6b\xdf\xc6\x19\xd7\x64\x96\x03\x69\x61\x9a\x06\x96\x30\xe1\x42\x4f\xca\xd4\x1e\xb0\x8f\xdb\xc4\xa0\x27\x02\xa6\x7d\x8f\xff\x7d\xde\x47\x4d\x1d\xe3\xfb\x4c\x8f\x9a\x7a\x1c\x17\xc9\xfb\xd7\x52\x4c\x34\x30\x06\xf8\xa8\x2c\x9b\xff\x17\x7c\x3c\xfc\x03\x2c\x43\x8a\xa4\x18\xf6\x93\xfd\x95\x9a\xff\x06\x86\x89\xad\x1c\xea\xcf\xf3\xf3\xb0\x0c\xaa\x99\x78\x41\x6e\xa2\x48\x9c\x29\x20\xd5\x97\x78\xea\xa0\x82\x2e\x22\xed\xa2\x5b\xf6\xbc\xf2\x07\x2e\xe6\x91\x85\xa5\x9e\xf4\xe2\x77\xe1\x56\x0e\x41\x09\x57\x41\x9c\x56\xe1\xf8\xbb\x3e\x9d\x29\x5e\x5c\x0b\x3a\x9f\xb7\xac\xeb\xa0\x32\xd7\x85\x1d\x1e\x9f\x19\x1d\x9c\xc1\x4f\xfd\x79\x31\x41\x3d\xd5\x53\xf7\x13\x11\x18\x46\x01\xfd\x97\xb8\x7f\xc6\x33\x67\x7b\x41\x22\x04\x04\x83\xe9\xde\x41\xc4\x08\xc7\x1e\x12\xe1\x4f\x76\xe2\x6f\xc8\x2b\xc2\xf1\xc3\xb7\x5b\x9d\xf9\x88\xb4\xe8\xd8\x27\x22\x51\x57\xf5\x5a\xcc\x5d\xe5\xa3\xef\xa3\x9f\x97\x19\xf8\xee\x27\x37\x97\xf9\x33\x9d\x71\x73\x15\x86\x92\x90\x47\xef\xcc\x76\x72\x1a\x03\xbf\xf8\x94\x90\x8d\x01\xcc\x9f\xf1\x1b\x50\xdd\xfa\xaa\xd3\xb8\x75\x05\x51\x8b\x23\x2e\x83\x18\x58\x48\x5f\xc2\x4a\x2a\xc8\xf2\x5f\x8b\xe9\x9f\x70\x31\x3d\x5b\x36\xbf\x7c\x8a\x70\x2e\xc9\x2b\x72\x83\x1f\x9e\x22\xa5\x5f\xfe\x23\xc5\xb4\x20\xcb\xdd\x92\xfa\xba\xaa\x3b\x7d\x9a\xd8\xee\xc4\xca\xf9\xf5\x76\x66\xdf\x3f\xeb\xdf\x62\xa3\xfa\x87\x6e\xbc\x29\x31\xeb\x98\x9a\xee\xe0\x01\x08\x7c\xfd\x89\x47\x20\x66\xd7\x54\xb4\x6c\xb6\xe9\x5f\x82\x5d\x10\x71\x05\x01\xb4\xf4\xb5\xbf\x19\x0e\xcb\xe6\x05\x69\xf1\x8c\x82\xf9\x91\x52\xb5\x90\xea\x15\xde\xba\x72\x71\xe9\x9f\xf7\xbc\xbf\x4f\xfc\x66\xe4\x75\xfe\x88\x95\xc6\xe2\x0a\x3d\x4b\xe8\x6b\x0f\xc3\xc2\xd7\x22\x38\x36\x7a\xaf\x6b\x6e\x10\x83\x5f\x18\x8c\xe4\x13\x09\x3b\xe5\x06\xea\xde\x1e\xb1\x4d\x75\x44\xf7\xd0\xd8\x33\xa7\xa7\xe4\xc8\xcf\xb9\x83\x6b\x58\xb8\x13\xf0\x23\x45\x9c\x60\x08\x07\xe4\x28\x6d\x2b\x78\x17\x1b\xa3\xa5\xa0\x41\xd8\xa1\xf3\xe0\x4c\x79\xfc\xfe\xa8\xff\xcb\x95\xd7\x54\x74\x40\x8b\x3e\x8f\xfa\xac\xb1\x7c\x73\xe1\xcf\xe7\xb1\x63\xc0\x87\x0e\x4d\xc6\x7f\x3a\x9e\x0d\x1e\xd5\x6f\x11\x4e\xa6\xff\x76\xe4\xe2\xb2\x5d\x0b\xc9\x57\xec\x1d\x3c\x80\x0b\xe0\xeb\x8e\x09\xfc\x69\x3a\xc5\x8c\xf3\xbf\x25\x44\x59\xd7\xd0\xf6\x7f\x47\xca\x00\xf6\x8a\x98\x3b\xaf\xaa\xd6\x0c\xeb\x45\x53\x70\xe0\xef\x79\x9b\x75\x53\x38\x7f\x67\x23\x2a\xfa\x8d\x17\x3c\x80\xf1\xb1\x1c\x37\xa4\x67\xd8\xe5\x17\x36\xdb\x60\xfb\xeb\x44\xcd\xb5\x1f\x71\xd6\x75\x4c\xbd\xeb\x4b\xa6\xb3\x6b\x73\x21\x70\xf4\xea\xd0\x14\xc6\xcf\xae\x93\xf7\xeb\x41\x57\x9b\x4c\x1f\x42\x78\x76\x1d\xa1\xfc\x8e\x89\xf9\x53\x51\x4e\x5d\x53\xf9\x0f\x9c\xc8\xe0\x55\x82\xdd\x34\x71\x6f\xf9\xce\x89\xc3\x32\x75\x17\x4a\xec\x5e\x03\xb3\x94\xba\x39\xb4\x51\x61\x5e\x7a\x22\x64\x04\xec\x62\x76\x89\xc2\x04\xbf\x4c\x68\x64\x42\xaf\x93\xad\x3a\x2c\xf5\x33\xf8\x1e\xd0\x27\x29\x34\xfb\x03\xbe\xc3\xea\xcc\x5b\xa0\x33\xa3\x61\xcd\x22\xfd\x9e\xb1\xe6\x87\xbf\xaf\x69\x95\xd1\xa3\x82\xd0\xe3\xf0\x17\x2e\x8d\x1e\xe3\x47\x69\x97\x96\xaa\x59\xf0\xe3\x81\x97\xc7\xfa\x5c\xd7\x11\xdc\xa1\x7b\xec\x6b\x0e\xbc\x00\xe5\xd1\x7b\x2f\x78\x05\x09\xbb\x63\xff\xcb\xd1\xc0\x89\x77\x7e\x9c\x7a\xb1\x4d\x33\xcd\x19\x6b\xd0\x3c\x52\x93\xfd\x6b\x97\x19\x6b\x9f\x1e\xe5\x85\x35\xfd\xe9\xb1\x3e\x91\x60\xe9\xd3\xeb\xb7\x39\x2a\xc8\xe6\xd8\xdc\x60\xb5\xe1\x1d\x97\x6c\xae\xf4\xfb\xf1\x65\xbc\x53\x5b\xea\x95\xe4\xc5\xe6\x08\x8e\xf0\x54\x7c\x8e\xe1\x99\x17\x9b\x63\xef\x81\x87\x79\xd8\x72\x6f\x2f\x6c\x69\x6f\x1f\x38\xd2\x27\x6a\x14\x35\x36\xc7\xe6\x4b\x92\x02\x41\xf3\xe1\x72\xf1\x28\xa3\xeb\xb5\x2a\x54\x7f\x6b\x1c\x29\x10\x5b\xdb\x1e\xfb\xf1\x54\xef\x24\xf6\xe6\x28\xbe\xa5\x46\xa7\x82\xdc\x0f\x37\x16\xd1\x2d\x33\x1f\xf4\x55\xfd\x4e\xab\x1b\x82\x9b\x12\xa3\xcd\x11\x06\x68\x4f\xb1\xe1\xc5\xe1\x25\x9c\x45\x3e\x0e\x9f\x1e\x5d\x92\xe0\xb2\x19\x14\x3f\x77\x20\xde\x40\xb5\x1b\xa9\x7e\x50\x90\x1e\x5b\xef\x71\xc4\x42\x8f\xf1\xf8\xc4\x39\x06\x39\x8f\x23\xff\xe6\x09\xf7\x13\x35\xf8\xca\xe4\x43\x90\xb1\x41\x76\x24\x79\x57\x8e\xee\xe6\xe7\x0b\x3d\x16\xec\x98\x37\x6d\x89\x50\x8e\xc7\x91\x39\xc8\x81\x01\x29\x1c\x1b\xd3\x7a\x7e\x5e\xc6\x0c\xfc\x98\x38\x08\x26\xa2\xab\x7f\x12\x2b\xc7\x66\xf5\x81\x7a\xde\x17\xa4\xf6\x8e\x1b\x81\xc2\x49\xf4\xf3\x14\x21\xf9\x1e\x1e\x7a\xe4\x33\xd9\x24\xd7\x08\x45\x45\x7f\x0b\x47\x49\xa1\x6f\x2e\x10\xdd\x1c\xbb\x8f\x1a\xf5\xf0\x20\xc1\x1f\x82\xe1\x5f\xe9\x6b\xd9\xe3\x6e\x58\xfa\x44\xd2\x9b\x7b\x98\x60\x64\xef\xcb\xa7\x92\x5e\xe7\x46\x77\xca\x6c\x42\x72\x9e\x20\xb0\xa1\xbc\x1a\x51\x85\x5f\xbf\x00\x72\xbc\xa1\xcd\xdf\xd8\x9d\xbd\x46\x52\x59\x83\xea\x65\xfe\x64\xc9\x35\xbf\xda\x81\x5a\x05\x00\x9b\xfa\x40\xd8\xeb\x70\x0c\x14\xd1\xa5\xb6\x84\x2a\xd8\xe8\x36\xc7\xf1\x1b\xd0\xef\xb4\xea\x69\x78\x5a\x1d\x47\x8f\xfa\x8c\xa1\xd5\x11\x18\x29\xc7\x7f\x80\x15\x71\x15\xc3\xa0\x7c\x6f\xaf\x15\x18\x64\x49\xe0\xc5\xa7\x8b\xd2\xd5\x1a\x3c\xeb\x60\x56\x4f\x49\x05\xaa\x4d\x54\xe7\x02\x9f\xd2\xfa\xd8\x65\x0e\x9d\x8b\xf6\x7f\x03\x00\x00\xff\xff\x2b\xd6\x5e\x18\x0b\x9b\x00\x00"), - }, - "/src/reflect/reflect_test.go": &vfsgen۰CompressedFileInfo{ - name: "reflect_test.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 434851224, time.UTC), - uncompressedSize: 4512, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x57\x6d\x6f\xdb\x38\x12\xfe\x6c\xfd\x8a\x39\xdd\x5d\x2b\xdd\x09\xb2\x65\xb7\x29\xa0\x22\x1f\xd2\xbc\x14\x59\xb4\xf1\xa2\x0e\x76\x3f\x18\xde\x05\x23\x8d\x2c\x36\x14\xa9\x25\x29\xa7\x5e\x43\xff\x7d\x41\xda\x96\xe5\xb7\xc6\x7d\x03\xea\x90\x33\xcf\x3c\xe4\xf0\x19\x8e\xe9\x6e\x17\xfe\xff\x50\x51\x96\xc2\x67\xe5\x38\x25\x49\x1e\xc9\x14\x41\x62\xc6\x30\xd1\x7f\x6a\x54\xda\x71\x68\x51\x0a\xa9\xc1\x73\x3a\x6e\x41\x74\xee\x3a\x1d\x77\x05\x30\x43\x83\xa1\x7c\xea\x3a\xbe\xe3\x64\x15\x4f\xe0\x1e\x95\xbe\x60\x74\xca\x0b\xe4\xda\xd3\xf0\xbf\x15\x22\xbc\xf7\x61\xe1\x74\x74\x38\x7a\xa4\xa5\xe7\x3b\x75\x0b\x3f\x62\x34\xc1\xe1\x0c\x65\xc6\xc4\xd3\x89\x31\x37\x15\x4f\x3e\x90\xb9\xa8\x4e\x5d\xe4\x42\x4a\x32\x1f\x66\x57\x54\x62\xa2\x6f\x33\x92\xe0\x89\x81\xf7\xf3\x12\x19\xe5\x8f\x6a\x24\xa4\xc6\xf4\xc4\xa8\xf7\x97\xef\xa8\x56\x27\x82\x2f\x73\xc2\x2f\x18\x13\xc9\x89\xf8\x3b\x52\xe0\xbb\xb9\x46\x75\x21\xd1\x1e\xf6\xc9\xdb\x1a\x66\x99\x42\xfd\x41\x24\x8f\xa7\x6a\x83\x46\xea\x21\xbf\xe5\x33\xc2\xe8\x81\x65\x56\xc5\x10\x2e\x81\xde\x78\xb2\x6d\xb8\x24\x0a\x17\x4e\xa7\x63\xfe\x77\xae\xa8\x8c\x01\xb6\x01\x9f\x30\x99\x05\xc6\x69\x0e\x21\x6e\x9c\xbf\x11\x56\xe1\xa2\x36\x9e\x3a\x80\xa3\xd1\x23\xe4\xe9\xd7\xa3\x3b\x06\xb2\xe3\x19\x66\x5e\xe4\xef\x51\x6f\x33\x5f\x61\x46\x2a\xa6\x97\x28\xa7\x53\xef\x1c\x8b\x96\x55\xa2\x87\xd9\x0d\x45\x96\x1a\x39\x8e\x1e\xa7\xbb\x86\xba\x87\x19\xbe\x3b\xf0\xfa\x8b\xb9\x98\x9f\x2a\x86\xc7\xcb\xec\x39\x8e\xf7\x97\xdf\x1d\x7a\xc1\xa6\xdf\xbf\x2c\x72\x94\x34\xf9\x11\x8a\x53\xee\xf1\x73\x1c\xbf\x53\x9d\xdf\x72\x8d\xf2\x87\x58\xee\x85\xf8\x48\xf8\xdc\x56\xc2\xc9\x4a\xcc\x88\x84\x14\xb1\xbc\xfe\xab\x22\xcc\xb0\x29\x38\x87\xf1\xe4\xaa\x6d\x5a\x38\x9d\x6e\x17\xec\x94\x6a\x8a\xca\xe9\x2c\x38\x65\x01\xd8\x0f\x2d\x2b\x34\x75\xb9\x88\x02\x88\x5a\x53\xca\xf5\xa0\x6f\xaa\x1b\x36\xa3\xc6\xd9\x0b\x5f\x07\x60\x3f\x1a\x53\xc6\x04\x31\xb8\x5e\xf8\xda\x0f\x60\x7b\xd6\x80\xdc\x1c\x19\x13\x6e\x00\xcd\xa0\x71\x15\xe4\x11\xbd\xf1\x84\x72\x1d\x40\xd4\xf3\x03\xd8\x33\x34\xd0\x17\xe3\x81\x31\x9b\x1d\xf7\x03\x18\xd4\x01\xec\x5b\x1a\xf0\x3b\xa2\x68\x62\x1c\xbd\xf0\x75\x1d\xc0\xce\xb4\x81\xa1\x94\x42\x7a\x9c\x32\x3f\x80\xf6\xb8\xb5\xbf\x72\x4c\xb9\x9e\x28\x2d\x29\x9f\x2e\xa2\x18\x5c\xc1\xd1\x0d\xa0\x1f\x83\xab\x9f\x84\x5b\x9b\x2d\x6f\x61\xd6\x9e\x00\xd6\xe8\xf6\x8a\x19\x8f\x02\xc8\x78\xbf\x31\x59\x95\x6e\x39\xb6\x75\x5a\x26\x94\x11\xa6\x0e\xab\xd2\xf7\xdb\xde\x95\x2c\x67\x6d\xdb\x31\x5d\xce\xb6\x22\xdb\xc2\xcc\xdd\xb6\xe7\xeb\xba\x44\x5b\x2c\xcf\x09\xf3\xaa\x6e\xa3\x8f\x2b\x73\x76\x04\xd7\xa0\xfa\xcb\x49\x7b\x97\x47\xd4\x19\x7c\x9b\x3a\x27\x30\xda\xb8\x2f\x3f\x8f\xf1\x47\x79\x0e\xa2\x8f\xaf\xb5\xe1\xb1\xd7\x3f\x6a\x5b\xa2\x55\x4f\x68\x55\xcf\xb2\x48\x07\xdb\xb6\xc1\x9e\x6d\x3c\xb1\x15\xb1\x58\x44\x75\x1d\x40\x33\xeb\xd7\x3b\x3b\xd7\x79\x78\x47\xee\x3c\x5b\x46\x9b\x71\xbb\x82\xa2\x89\xad\xd1\xb3\x57\x2d\xb4\x2d\xa4\x23\x8e\x13\x62\x15\xb2\x6c\xd1\xbe\x7a\xe3\xc3\xb8\x23\xe6\x76\x96\xa7\xf1\x9b\xd3\x5f\x21\x0f\x44\xc4\x10\xad\x14\xda\xc5\x44\x31\xf4\xf7\xa4\x7e\x8e\x68\x67\x75\xdb\x45\xee\x28\x83\x99\x02\x2c\x4a\x3d\x8f\x81\x0b\x0d\x3a\x47\x50\xa4\xc0\xd0\xa6\x61\xc4\xb1\x09\x53\xae\x57\x8d\xae\x9d\x65\xdb\xbd\x73\x70\x9b\x80\xf6\x78\xaf\x4b\xae\x02\x5b\xd3\xbd\x65\x8e\x43\xf7\xce\x72\x9b\x62\xdf\xd2\x4e\xfd\x23\x55\x05\xd1\x49\x8e\x29\xe8\x79\xb9\x6e\xa2\x51\xd8\x3b\xda\x46\xcf\x5e\x79\xd1\x7e\x1b\x6d\x3a\xe2\xee\xc1\x6c\x9a\xdb\x5e\xb7\xdb\xeb\x84\xcb\x17\xc1\xa2\x6e\xf5\xbf\xc3\x1e\x57\xb9\x5f\xeb\x8d\x77\x42\xef\x58\xb6\xcf\xb1\xfa\x19\xdf\x4c\x6b\x4a\x7b\x8c\xbf\x0a\xa5\xe8\x03\x43\x60\x42\x94\xca\x54\xcd\x0b\x33\x8a\x02\x58\xff\x5d\x2b\xd4\xed\x6e\xbb\x9a\x2f\x34\xe8\x76\xe1\x7e\x78\x35\x8c\xe1\x86\x7e\x69\x18\xe6\x6b\xdc\xfc\x00\xc7\xc6\x79\x8c\xa5\x76\x9c\xb6\x01\x74\x4e\x55\x08\x23\x44\xc8\xb5\x2e\x55\xdc\xed\x4e\xa9\xce\xab\x87\x30\x11\x45\x77\x2a\xca\x1c\xe5\x67\xb5\x19\x50\xa5\x2a\x54\xdd\x37\x67\x83\x70\xf3\x00\xbb\x35\xc6\x7e\xbf\xf7\x66\xb0\xff\xea\x2a\x20\x3e\xdf\x7b\xf3\xdf\x09\xbe\x7c\x34\x63\x7a\x43\xa5\xd2\x5e\xcf\xf7\xc3\x8f\xa8\x73\x91\x7a\x3d\xdf\x71\x3a\x34\x83\xa9\xd0\x26\xb4\x08\xcd\xcf\x3e\xcf\x0f\xef\xaa\x62\x58\x69\xcf\x7f\x6b\x3d\xff\x3a\x87\x9e\xfd\xc5\xa0\xc3\x6b\xf3\xda\xc8\x3c\x77\x09\x88\xad\xfb\xbf\xb3\x00\x9e\x08\xd7\xd0\x73\x03\x63\xf0\x9d\x4e\xbd\xd4\x65\x37\xf3\xfb\x1c\x21\x21\x8c\xc1\x03\x32\xf1\x04\x19\xa1\x4c\xc1\x13\xd5\x79\x6c\xe0\x36\xa4\x63\xde\x88\xff\xb1\xa0\x73\x30\x49\x6b\x2a\xb8\x97\xf1\x00\x64\x32\x93\x01\x10\x39\x55\x3e\x2c\x40\xa2\xae\x24\x87\x8c\x87\xa4\x2c\xd9\xdc\x6b\x79\xdf\x42\xfd\x76\xc9\x05\xdf\xfa\xef\x8f\x65\x9c\x39\x05\x9b\x69\x0c\x97\x84\x9b\x8e\x24\x91\xa4\x50\x4a\x51\xa2\xd4\x73\x78\x69\xd7\x7c\x09\x22\x83\x8a\xa7\x98\x51\x8e\xe9\x32\xe3\x51\x2e\x2a\x96\xf2\x97\x1a\x4a\xc2\x69\x12\x1a\x63\x11\x5e\x12\xc6\xec\xed\xdf\xfe\xfd\x4b\x18\xfb\x64\xd3\x50\xd7\xa6\xf7\x1d\x7f\x45\x1b\x2b\x54\x0a\x15\xc8\x8a\x6b\x5a\x60\x38\x42\x7d\x43\x39\x61\xf4\x6f\x94\x01\x3c\xe5\x34\xc9\x81\x2a\xdb\x3c\x55\x55\x2e\xd5\x86\x87\x39\xbc\xb7\xb5\xf4\xcb\xa8\xf5\x8a\xa7\x9c\x6a\xcf\xd2\x37\x0a\xdd\xe7\x54\x99\x70\x62\x25\xa9\x24\x02\xe5\x10\x85\x91\x2d\xfa\x39\x68\x01\x29\x6a\x94\x05\xe5\x68\x7b\x73\x42\x2a\x85\x40\x78\x0a\x99\xbd\x2c\xa6\x77\xad\x9f\xf3\xa4\x2c\x91\xa7\x5e\x63\x1a\xc7\x83\x68\x12\xc0\x66\x3e\xe8\xc7\x93\x30\x0c\x7d\x73\x57\xd4\x23\x2d\xc1\x66\x97\x10\x85\xf0\xef\x41\xe4\xd4\xce\x3f\x01\x00\x00\xff\xff\xab\x6e\xee\x69\xa0\x11\x00\x00"), - }, - "/src/reflect/swapper.go": &vfsgen۰CompressedFileInfo{ - name: "swapper.go", - modTime: time.Date(2018, 4, 6, 18, 15, 56, 0, time.UTC), - uncompressedSize: 834, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x52\x4d\x8f\xd3\x30\x10\x3d\x7b\x7e\xc5\x23\x42\x28\xd6\x56\x69\xf7\x5a\xa9\xdc\x00\xad\x58\xd8\x43\x25\xee\x6e\x3a\x6e\x6c\x5a\xdb\xb2\x9d\x16\x68\xf3\xdf\x91\x93\xb2\x95\x40\x5a\x2d\x87\x48\x93\x79\x6f\x3e\xde\x1b\xcf\xe7\xb8\xdb\xf4\x66\xbf\x85\x4d\x44\x41\xb5\xdf\xd5\x8e\x11\x59\xef\xb9\xcd\x44\xe6\x10\x7c\xcc\xa8\x76\x26\x77\xfd\xa6\x69\xfd\x61\xbe\xf3\xa1\xe3\x68\xd3\x2d\xb0\xa9\x22\xd2\xbd\x6b\xb1\x3e\xa9\x10\x38\xd6\x69\x6f\x5a\x86\x71\x99\xa3\x56\x2d\x9f\x07\x89\x82\xd7\x66\x06\x5b\xd2\x12\x67\x12\x47\x2c\x57\xf8\xa6\xf6\x3d\x3f\xe9\xa9\x42\x92\x30\x1a\xc7\xe6\xb3\x71\xdb\x5a\xe2\xcd\x0a\xeb\xb1\xd1\x99\x84\x08\xca\x99\xb6\x7e\x37\xf2\x3f\xc4\xe8\xe3\xf9\x0b\xe7\xce\x6f\x97\xa8\xae\x53\xab\x19\x4a\xe1\xf2\xb9\xc1\x20\x49\x0c\x24\xe6\x73\x7c\x54\x29\x23\xa8\xdc\x41\xfb\x88\x71\x56\x82\xd7\x48\xe6\x17\x63\x01\xe5\xb6\xb8\x6f\xf0\xd5\xe7\xce\xb8\x1d\xb2\x47\x3a\xa9\xd0\x90\x38\x3e\xb2\x2b\x5b\xf6\xc6\xe5\xfa\xd8\x3c\xb2\xab\xa5\x24\x91\x4e\x26\xb7\x1d\x46\xf4\x4c\xa2\x55\x89\xb1\x58\x92\x10\x91\x73\x1f\xdd\x3f\x5a\x31\x2d\x5f\x5d\x6d\x5d\xe2\x8f\x3f\x5b\xfe\x01\xdf\xe7\xb2\x4a\x54\x6e\xc7\x95\xc4\x70\xed\x77\xff\x42\x3f\x12\xa2\x18\x65\x8a\x43\x0b\x5c\x2e\xb0\x53\x34\x02\xe2\xf5\xc3\x0a\x7d\xa0\xf1\x1b\x48\xa8\xa2\xd4\xa6\xe6\xa1\x9c\xcd\xa9\xfd\xd3\xc6\x72\x9b\xaf\x97\x69\x3e\x71\xae\xab\xb7\x2a\x46\xf5\xb3\x14\x7a\xad\x5f\x41\xf7\x5a\x27\xce\x95\x2c\xa4\x5a\xd2\x0b\x7a\x8c\x9e\x4c\x36\x12\xef\x57\x93\xb3\x97\xcb\x94\xb2\xb7\xd4\x28\xf0\xbf\xf4\x15\x79\x06\x77\x2b\x78\xad\x49\x08\x7b\x0b\xf3\x21\x14\x05\xaa\x79\x28\x95\xb5\x29\x6c\xd5\xac\x39\x5f\xff\x67\xcf\x90\x95\x7f\x61\x76\x86\x7c\x08\xe3\xeb\x1a\xe8\x77\x00\x00\x00\xff\xff\xf3\x76\x65\x45\x42\x03\x00\x00"), - }, - "/src/regexp": &vfsgen۰DirInfo{ - name: "regexp", - modTime: time.Date(2018, 4, 20, 9, 40, 48, 439830618, time.UTC), - }, - "/src/regexp/regexp_test.go": &vfsgen۰FileInfo{ - name: "regexp_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x72\x65\x67\x65\x78\x70\x0a\x0a\x69\x6d\x70\x6f\x72\x74\x20\x28\x0a\x09\x22\x74\x65\x73\x74\x69\x6e\x67\x22\x0a\x29\x0a\x0a\x66\x75\x6e\x63\x20\x54\x65\x73\x74\x4f\x6e\x65\x50\x61\x73\x73\x43\x75\x74\x6f\x66\x66\x28\x74\x20\x2a\x74\x65\x73\x74\x69\x6e\x67\x2e\x54\x29\x20\x7b\x0a\x09\x74\x2e\x53\x6b\x69\x70\x28\x29\x20\x2f\x2f\x20\x22\x4d\x61\x78\x69\x6d\x75\x6d\x20\x63\x61\x6c\x6c\x20\x73\x74\x61\x63\x6b\x20\x73\x69\x7a\x65\x20\x65\x78\x63\x65\x65\x64\x65\x64\x22\x20\x6f\x6e\x20\x56\x38\x0a\x7d\x0a"), - }, - "/src/runtime": &vfsgen۰DirInfo{ - name: "runtime", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 555853579, time.UTC), - }, - "/src/runtime/debug": &vfsgen۰DirInfo{ - name: "debug", - modTime: time.Date(2018, 4, 20, 9, 43, 49, 192511745, time.UTC), - }, - "/src/runtime/debug/debug.go": &vfsgen۰CompressedFileInfo{ - name: "debug.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 298, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\xce\xb1\x4e\x03\x31\x0c\xc6\xf1\xb9\x7e\x8a\x6f\x2c\x02\x9a\x34\xa5\x3c\x00\x0c\x9d\x8a\x10\xf0\x02\x49\xce\x1c\xa6\x77\x6e\x75\x71\x24\x2a\xd4\x77\x47\xbd\x0e\x87\xd8\xf0\xe2\xe1\x2f\xff\x64\xe7\x70\x9d\xaa\x74\x0d\x3e\x0b\xd1\x21\xe6\x5d\x6c\x19\x0d\xa7\xda\x12\xbd\x57\xcd\x28\x6c\x9b\xc7\x67\x1e\x32\xab\xcd\x45\x6d\x15\xae\x30\x2e\x7c\xd3\xcc\x39\x3c\xed\x0d\xd2\x1f\x3a\xee\x59\x8d\x9b\x05\x5e\xd8\xea\xa0\x10\x15\x93\xd8\x9d\xef\x4d\xb4\x5d\xd0\x6c\xb8\x84\xa5\xf7\x74\x9a\xf0\x6d\xfc\x7a\xb5\x98\x77\xf3\x74\x34\x2e\x67\x7a\xf4\xff\xad\x3b\x87\xb7\x0f\xfe\x1b\x20\x05\x4b\x6c\x1e\xb0\x57\xdc\xdf\xdd\x26\x31\x94\x63\x31\xee\xcb\x0d\xc2\xda\x63\x3b\x96\x55\xf8\x5d\xa6\x57\xc3\xda\x5f\x86\x4e\xf4\x13\x00\x00\xff\xff\xad\x79\xbd\xd2\x2a\x01\x00\x00"), - }, - "/src/runtime/pprof": &vfsgen۰DirInfo{ - name: "pprof", - modTime: time.Date(2018, 4, 20, 9, 43, 49, 197640393, time.UTC), - }, - "/src/runtime/pprof/pprof.go": &vfsgen۰CompressedFileInfo{ - name: "pprof.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 660, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x92\x4f\x6b\xc2\x40\x10\xc5\xcf\x99\x4f\x31\xe4\xb4\x69\x45\xfb\x15\x8a\x97\x1e\xda\x22\xb5\xa5\x07\xf1\xb0\x26\x13\xd9\x9a\xfd\xc3\x64\x56\x2b\xe2\x77\x2f\x6b\xa4\x2c\x18\x0a\x3d\xee\xcc\xfb\x0d\xef\x3d\x76\x36\xc3\xfb\x4d\x34\x5d\x83\x5f\x3d\x40\xd0\xf5\x4e\x6f\x09\x43\x60\xdf\x02\x18\x1b\x3c\x0b\x2a\x28\x4a\xe3\x4b\x28\xca\xfe\xe8\xea\x12\x2a\x00\x39\x06\xc2\x05\xfb\xd6\x74\x84\xbd\x70\xac\x05\x4f\x50\x38\x6d\x09\xd3\xdb\xb8\x2d\x14\x36\x22\x22\x26\x66\xfa\x12\x85\xbe\xa1\xb0\x69\x80\x56\x87\x95\x71\x42\xdc\xea\x9a\x4e\xe7\xf5\x6a\x1d\x8d\x93\x20\x0c\x45\xed\xa3\x13\x6c\xa3\xab\x55\x85\xc6\x09\x14\x07\x36\x42\xc3\xc4\xf8\xe9\x67\x7a\xf1\x24\xad\x2a\x24\x66\xcf\x70\x06\x48\x5b\x54\x01\xef\xae\x8e\x2a\xbc\xe8\xde\xbd\x3a\x60\x06\x35\xb4\x89\xdb\x0c\x4d\x8e\x99\x24\xb2\x43\x67\xba\xf1\x43\xf3\x64\x68\xf0\x92\xc9\x1f\xc6\xc5\xaf\xda\x92\xaa\xae\xf9\x33\x79\x59\x8e\xeb\x1f\x9b\x46\xed\x75\x17\x09\xb3\x3a\x26\xd8\xef\x4c\x18\x6c\x9e\xc6\xb9\x37\xb2\x7e\x4f\xb7\x68\x0e\x2c\x45\xb3\xcc\x17\x1f\x57\x28\x6f\xe2\xef\xf8\x4b\xf1\x21\xe3\xf2\x9b\x17\xfc\x89\x74\xf8\xf7\xd1\x67\xef\x77\x31\xa8\xcb\xff\x18\xea\xa9\x7e\xf3\xdc\x20\x3f\x01\x00\x00\xff\xff\x14\x4a\xfc\x56\x94\x02\x00\x00"), - }, - "/src/runtime/runtime.go": &vfsgen۰CompressedFileInfo{ - name: "runtime.go", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 556076244, time.UTC), - uncompressedSize: 5788, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x58\xef\x72\xdb\xb8\x11\xff\x4c\x3e\xc5\x96\xd3\xde\x91\x8e\x22\xd9\xe9\x25\x9d\x66\xea\x0f\x89\xee\xec\xcb\x34\xb6\x3c\x96\xd3\xde\x4c\x9a\xb9\x81\xc0\xa5\x04\x0b\x04\x58\x00\xb4\xac\xf3\xe8\x01\xfa\x20\x7d\xb1\x3e\x49\x67\x01\xfe\x93\x2d\x5f\xda\x4e\xf9\x45\xe2\xe2\xb7\x8b\xc5\xfe\xc3\x2e\x27\x13\x78\xb1\xa8\x85\xcc\xe1\xd6\xc6\x71\xc5\xf8\x9a\x2d\x11\x4c\xad\x9c\x28\x31\x8e\x45\x59\x69\xe3\x20\x8d\xa3\xa4\xa1\x4d\x84\x72\x68\x14\x93\x13\xbb\xb5\x49\x1c\x47\xc9\x52\xb8\x55\xbd\x18\x73\x5d\x4e\x96\xba\x5a\xa1\xb9\xb5\xfd\x9f\x5b\x9b\xc4\x59\x1c\x73\xad\xac\x83\xf3\xd9\x6c\x0e\xa7\x60\xb7\x76\x4c\x7f\x3b\xea\xbb\xeb\xe9\x8f\x70\x0a\x09\x81\x03\x6d\xaa\xcb\x4a\x48\x34\x44\x6d\x65\x25\x71\x3c\x99\x40\xc1\xd6\x08\x85\x36\x80\xc6\x68\x33\x5e\xea\xd8\x6d\x2b\x04\x2c\x18\x47\xb0\xce\xd4\xdc\xc1\x43\x1c\xfd\xec\xa9\x47\xfe\x27\xde\x05\x4c\xa0\xf5\x18\xeb\x0c\xbd\x09\xb5\x8c\x77\x71\x5c\xd4\x8a\x43\xea\x1a\x9e\xac\x59\x49\xdb\x3f\xc4\x60\xd0\xd5\x46\x81\x1b\x5b\x67\xe2\xdd\x13\x8e\x6a\xbd\xac\x98\x5b\x1d\x62\x49\x92\x6e\x0b\xa1\x84\x4b\x33\x5a\xbb\xb5\x57\xeb\x25\xbc\x3d\x85\x5b\x3b\x3e\x97\x7a\xc1\xe4\xf8\x1c\x5d\x9a\xfc\xb6\x71\x83\x4d\xb2\x40\xf8\x9a\x85\x33\x92\xd5\x8a\x98\x7b\x11\xb7\x76\xb6\xb8\x45\xee\xae\x9c\x49\x46\xe0\x77\x0a\xb2\x02\xb9\x95\x5c\x39\x93\x64\x07\xd9\x7f\x20\xf3\x3e\xe1\xf6\xd4\xaf\x31\xbb\x95\xd1\x9b\xeb\x10\x2e\x81\x81\x64\x8c\x3f\x34\x81\x13\x34\x48\x3d\x8a\xd8\x27\x13\x60\x77\x5a\xe4\x90\x23\xcb\x81\xeb\x1c\x01\xa5\x28\x85\x62\x4e\x68\x15\x47\x77\xcc\x00\x06\x77\xc7\x11\xc2\x29\x7c\x73\xb3\xad\xf0\x9d\xb5\x68\x08\xe0\x77\x78\xd8\xc5\xd1\xcf\x70\x0a\xd8\x99\xf9\x7c\x76\x3d\x9b\xdd\xec\xf9\xa2\x32\x9a\xa3\xb5\x07\x2c\xde\xac\x90\x21\x45\x01\x2d\xee\xd4\xe3\x3e\xa9\x1c\x0b\xa1\x30\x27\x11\x9d\x3f\x27\x49\x1c\xed\xe2\x68\xa9\x8d\xd6\x8e\x24\x36\x4c\x41\x1e\xaa\xbb\xd6\x48\x41\x8f\x46\x72\x03\xff\xcd\xf3\x82\x03\x62\x3c\x6f\x82\xcf\x6f\x32\x99\xf8\x94\xf9\x1e\x0b\x56\x4b\x77\x1e\x64\x08\x0b\x4a\x6f\x60\xa9\x15\x8e\x80\x33\xf5\xad\x83\xda\x22\x08\x07\xcc\x42\xc1\xa4\x5c\x30\xbe\x06\xa6\xb6\xa5\x36\x38\xf6\x42\x6e\x66\xdf\xcf\xde\xc2\x1c\x11\x44\x01\x0c\x16\xe8\x1c\x1a\xb0\x5a\xd6\x64\x47\x2f\x11\x31\xc7\x7c\xdc\x87\xed\xa4\xb6\x66\x22\x35\x67\x72\xb2\xd4\x7d\x0c\xbf\x37\xc8\xd6\x95\x16\xaa\x8b\xe4\xf1\xf7\xb8\xa8\x97\x4b\x34\x69\xd6\xa1\xa6\x4c\x4a\x34\xa9\x5d\x8b\x0a\x84\x72\x19\xa4\x15\x87\x5a\x28\x57\x39\x33\x82\x42\x48\x6c\x9c\x33\x02\x29\x14\x12\x66\x04\x7a\x0d\x0b\xad\xa5\x17\x2b\x54\xa1\x0f\x78\xab\x0d\xc2\x4b\xdc\xa4\x8d\x95\xad\x63\x7c\x9d\x64\x63\xda\x32\x4d\x6c\x25\x85\x4b\x46\x90\xfc\x4d\x25\xd9\xf8\x83\xca\xf1\x3e\x68\xf1\x02\x5e\x05\x47\x78\xc9\xbf\xe2\xdf\xe3\x11\x24\xc9\x88\x7e\x0a\x26\x2d\x7a\x37\x54\xcc\x38\x1f\x3c\xc4\xdc\xee\x54\x2f\xc2\x11\x92\xd1\x90\x2c\x68\xcb\x59\x41\x2a\xa4\x5e\x03\x97\x66\x2f\x4e\x9e\x83\x64\x2d\xe4\x89\xfe\x6f\x29\x6e\x7a\x95\xbc\x06\xcd\x79\x8e\xb3\x2e\x48\xf6\x17\x4e\x1a\x61\x23\x70\xa6\xc6\x47\xce\xb0\x9d\x37\x46\x50\x71\xf8\xfc\xa5\x71\x47\x46\xa4\x41\xbd\x3a\x26\xbe\xc9\xa4\xe5\x3a\x33\xac\x44\x1b\x62\xce\x81\x28\x2b\x89\x25\x2a\x87\xb9\xaf\xc4\xa1\x80\x9f\xde\xda\x71\xdc\x45\xd9\x87\x16\x43\xb1\x56\x69\x6b\xc5\x42\xe2\x78\x4f\x95\x20\x34\xe5\xe1\x6d\xa8\xcb\x51\xb3\xdf\x03\x34\xea\x7c\x13\x08\x0f\x3b\xd8\xc5\xa1\x96\x37\x88\x50\xcc\x1f\xba\xf2\xcd\x45\xcb\x9c\xc1\x25\xde\x53\x78\xa6\x05\xbd\x07\x86\x11\x50\x36\xb4\x01\xd6\x4a\xdf\x93\x39\xb8\x1f\xae\xa6\x10\x9e\x46\xb1\x38\x3a\xa3\x4d\xe8\x39\xa2\x7f\xe1\xdd\xe7\x4e\x73\x8d\x44\x67\x14\xd4\xf4\xb4\x84\x8f\x14\xd8\xf4\x08\xe5\xe2\xe8\x07\xe5\xcc\x76\x28\xb1\xab\x56\x53\x9f\x48\xdd\xab\xc6\xfb\xfe\x96\xd8\xbf\x1c\x78\x6d\xa8\x04\xd4\x4e\x28\x4c\xb2\x50\x72\x09\x9d\x04\x87\xef\xd5\xe3\x10\x4e\xa1\x20\x27\x23\x50\x42\x66\x83\x02\x79\xf1\xee\xa7\xab\xeb\xd9\x74\x9e\xaa\x90\x9e\xfb\x21\x70\x32\xd0\xc6\xf2\x15\xe6\x41\x1d\x4e\x19\x50\xb2\x35\xa6\x7c\xc5\x54\xe7\x80\x43\xdb\x5a\x74\x37\xa2\x44\x5d\xbb\x83\x17\x00\xc9\x26\x99\xc0\xa5\xb6\x98\xf2\x0c\x76\xd9\x08\x8e\xb3\x38\xfa\xd3\x4b\xde\x6d\x7e\x59\x97\xd3\xab\x4f\xe9\xf3\xda\x5d\xd6\x65\x67\x8f\x27\xb0\xc7\xc6\x73\xda\x31\xd9\xc1\x6d\x9b\x78\x71\x1b\x02\x17\x58\xce\x1d\x73\x76\x10\x05\x93\x09\x9c\xa3\x42\xc3\x24\x58\xc7\x9c\xb0\x4e\x70\x3b\x8e\xa3\x77\x52\x6a\xde\xc7\xc7\x9b\xef\x60\x32\x81\xc5\xd6\xa1\x05\x46\x4b\x8c\xd2\x83\xa9\x1c\xac\x13\x52\x82\x50\x54\x9f\xe3\xe8\x86\x34\x08\xbc\xcf\xb3\xa5\x78\x87\x8a\x32\xa7\x30\x88\x79\x16\x47\xf3\xad\x05\x38\xbc\x99\x5e\x38\xe6\xcb\x57\x61\x74\x49\x17\x85\xc3\x12\x52\x5b\x97\xa0\x0b\xf8\xe9\xfe\x9e\x58\x17\x28\xf5\x26\x8b\xa3\x8f\x5a\xaf\xeb\xca\xee\x8b\x51\x75\xb9\x40\x43\x68\x5f\xd1\xd1\x80\x0c\xb0\x38\xba\xf0\x2a\x3d\x8b\x2f\xc3\x72\x1c\x9d\x19\x44\xfb\x58\xbd\x1e\x47\xa7\xb0\xb1\x37\xe5\x05\x13\xaa\x3d\x28\x25\xce\x0a\x59\xb5\x6f\xd7\x1f\x91\x55\x9d\x6d\xff\x1b\xcb\x12\x63\x67\xa7\xff\xc4\x4a\x81\xe5\x43\xde\xa4\xec\x63\x16\xa1\x40\xd0\x9a\xad\x98\xb2\x0d\x56\xd1\x1d\x7b\x18\xab\xb4\x7a\xd9\xe1\x03\xfc\x1a\x25\x32\x8b\xf9\x13\xb8\x69\x17\x9c\x06\xb7\x42\x98\xcd\x03\x43\xc8\x0c\x3b\x94\xef\x23\x76\x60\xcb\xde\x02\x3a\x80\x83\x5d\x3f\xea\xcd\x4b\x89\x77\x28\xa1\x10\xf7\x98\xbf\xb4\xe2\x97\xb6\x94\xd5\x06\x5b\x2e\x6d\xf6\x6d\x3d\x99\x44\xe1\x48\xc2\x36\x9a\xd5\xa4\x95\xd2\x9b\xb0\x48\xe6\xec\x96\x0e\x99\x70\x1c\x47\x73\xba\x7a\x1b\xc3\x3c\x3e\xa7\x97\xb6\xd8\x82\xbf\x9e\x7b\x25\x1a\xa6\xc6\x59\x81\x29\x8e\x2e\xe6\x15\x53\x4f\x04\x95\x64\xce\xfe\x24\xb6\xc1\x3d\xe6\x9d\x32\xbe\xc2\xc0\x3c\xe0\xe5\x44\xdd\x67\xf6\xc0\xc0\xdd\x32\xbf\xaf\xf9\xfa\x47\x66\x57\x44\xed\x99\x2b\xa3\x0b\x21\xa9\x75\x5c\xd4\x7c\x8d\x0e\x56\xcc\xae\xc0\xb1\x85\xc4\x38\x3a\x9f\xf6\x19\xd9\xb3\x9c\x4f\xa1\x44\xc7\x72\xe6\x58\x1c\xcd\xdc\x0a\xcd\x9e\x9a\x04\xd1\x44\x6d\xb3\xb4\xcf\x83\xc6\x8b\xe7\xcc\x2c\x68\xfe\xe2\x5a\x4a\xe4\x4f\xdc\x45\x37\xda\xf9\xf4\x69\x21\x50\x78\xef\x5a\x1e\x4a\xaa\x0d\xa5\xc5\x8a\x55\x15\x2a\xd8\xac\x50\x41\x9f\x53\xff\xfa\xc7\x3f\xc1\xad\x84\x05\x56\xea\x9a\xae\xa4\x8f\xcc\x1e\x94\x89\x2a\x07\x6a\xe0\x29\xe6\x24\xb3\x7b\xf2\x53\xc5\x94\xb6\xc8\xb5\xca\x2d\x58\xa1\x38\xc2\xc9\x1f\xff\x40\x95\xfb\x8a\xd5\x16\x7d\x89\xbb\xb4\xbd\x81\x3d\xf5\xb2\xb5\xd7\xe7\x57\xaf\xdf\x7c\xe9\x37\xe2\xc2\xf0\x5a\x32\x03\x8b\xba\x28\x42\x8c\x1b\xe4\xd4\x39\x9c\x4f\xa1\x22\x4e\xc8\x6b\x13\xac\x44\xf7\xb7\x75\xed\x3a\x73\xf0\x39\xa5\xf2\x3f\x7d\xf1\xea\xf5\xeb\xec\x77\x24\xb7\xd9\xec\x07\x95\xff\xaf\x9b\xb5\x07\xb7\x71\xe4\x65\xc3\xd0\x36\xbf\x7f\x45\xbe\x9f\x5e\x7d\x3a\x33\x2c\xd8\xa2\x90\x9a\x35\xc2\x8b\x96\xa6\x0b\x98\x5e\x7d\x0a\xe6\x6b\x53\xe0\x7c\x4a\xd7\x3f\x45\x4f\x2b\x92\xba\x90\x38\xf2\x7d\x73\xb7\x8b\xa7\xf9\x50\xb8\x42\x13\x92\x78\x50\x2c\x1f\xe5\x2e\xbc\x39\xa1\xec\xbc\xac\xcb\xb9\xf8\x05\xa7\x92\x59\x1b\x4a\x11\x95\x94\xa9\x9f\xa4\xc6\x71\xf4\x7e\x4b\xab\xf0\xf9\xcd\xc9\x97\xfe\x52\x8b\x3c\x6d\x70\xa8\xae\xd4\xb7\x3e\xeb\x6a\x7a\x4b\xd8\x75\x37\xee\x35\xb2\xbc\xbd\x28\xd3\x12\x8e\xda\xff\xc3\x0e\x66\x8e\xee\x4c\x28\x26\xc5\x2f\x68\xd2\xfb\x11\x50\xcb\xed\xd0\xd0\x94\xfe\xb0\x6b\x80\xa1\xe9\x22\x74\xaf\x98\xae\xd8\xdf\x6b\xec\xda\x0a\x32\x6b\xad\xf0\xbe\xd2\xc6\x77\x9b\x02\xa5\x2f\x9a\xb9\xb0\xa4\xef\x06\xb8\x56\x77\x68\xac\x4f\xa1\xae\x0b\xfc\x39\xf4\x67\x19\xf8\x7e\x2b\xcd\xda\x76\x0b\x7e\xf5\xe9\xfa\xc1\x63\xd8\x3d\x16\x44\x7d\x1d\xb5\x72\x83\x09\x86\x3a\xcb\x43\x23\xcc\xa0\xb1\xf4\x23\xc4\x53\x61\x97\xac\xc4\x7e\x30\xfd\xca\x33\x10\x06\xed\x01\x49\xcc\x99\x36\x57\xd3\x3d\x75\xbc\xf4\x41\xef\xa3\x84\x24\x93\xd0\xf8\x7c\x81\xe5\x95\x2f\x67\x78\xcd\x9c\xd7\x12\x4e\xe1\xf5\xc9\x2b\x38\x82\x93\xe3\x57\xdf\xf5\x3e\x7b\x2f\x35\x5f\x0f\xa0\xa9\x69\xf0\x8f\x7c\x7b\x51\x3b\xbc\x6f\x70\x6d\x2a\x0c\xb0\x4d\x13\xd6\x4f\x03\xea\x0e\xad\x13\x4b\x02\x50\xf5\x19\xc3\x87\x02\x84\xfb\xd6\x76\xa3\x01\x39\xb5\x9b\x2b\x46\xe4\x56\x2b\x72\x34\x90\x6b\xb2\x91\xd5\xa3\x50\x39\x37\xc2\x22\x18\x2c\xf5\x5d\x10\x04\x5c\x97\xc4\x31\xde\x9f\x5c\x82\x9a\x74\xc7\xa4\x8b\xba\x80\xcf\x5f\xe8\x3a\x1a\x51\x2a\x35\xbd\x7f\xa3\xe0\xa1\x6f\x01\xcf\x4f\x97\x7e\x72\xfc\xd5\xcf\x02\xc7\x7e\x50\x6c\x5e\xb8\xae\xb6\xb4\xfd\x08\xec\xde\xb4\x98\xf4\x84\xc1\x10\xd8\x8c\xaa\x7e\x50\xec\x47\xbb\xbe\x5d\xff\xa8\xf9\x7a\x36\xbf\x59\x19\x64\xbe\x13\x6f\xe9\x9f\x94\x7c\x66\xe5\x2f\x21\x2f\x0e\x7d\x8e\xb2\x5b\x3b\xbe\x59\x61\x83\x18\x5a\xcc\xb8\x1b\xc3\x38\x85\xa7\xff\xe0\xd2\x87\x9f\x12\xb2\x8d\xe4\xb9\xd3\x55\x8b\x6a\xa3\x74\xd7\x97\x86\x76\x29\x58\xdd\x8f\x91\x7f\xc5\xf0\xdd\x8e\x01\x5f\x6a\x40\x75\x27\x8c\x56\x7e\x3a\x74\x1a\x38\x73\x7c\x15\xb6\xb3\x63\xb8\x59\xa1\x41\x9a\x2a\x37\x08\x2b\x76\xb7\x1f\x18\xcd\xd5\xa5\x72\x60\x72\xc3\xb6\xb6\xcb\xd8\x7e\x56\x58\x6a\x6f\x5a\xef\xe2\x37\xdf\x3d\x1e\x69\x3d\xcc\x7f\x2b\x9c\x15\x29\x56\x70\xb4\x57\x95\x8e\xc2\x57\xc4\x07\x9a\xf5\x95\xe0\x69\xd2\x20\xdf\xfa\xb1\xd7\xd6\x55\x28\x43\x49\xef\x95\x3f\x23\x56\xef\xa4\xb8\xc3\x74\xbf\xbc\xb5\xeb\x7e\xf2\x4a\x6d\xe3\x81\xac\x17\xed\x8f\xdb\x78\xd9\x06\x37\x53\xb6\xac\xd0\x22\x30\xd3\x5f\x1b\x1e\xbd\x31\xac\x1a\xc3\xe5\xff\x61\xf4\x5e\xa2\x0b\xf3\x76\xc5\x0f\x94\xc5\xa7\x15\xb0\x10\x2a\xf7\x73\xda\xb0\xd0\x10\xe1\x83\x2a\x74\x8f\x6f\x29\x7e\x40\x0f\x8c\xb5\xe2\x8a\xea\x5c\xd1\x2d\x0e\x2a\xde\xa3\xa2\xe6\x2f\x82\x4e\x6a\x37\xd3\xff\x3b\x00\x00\xff\xff\xa9\x51\x33\x3f\x9c\x16\x00\x00"), - }, - "/src/strings": &vfsgen۰DirInfo{ - name: "strings", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 435049523, time.UTC), - }, - "/src/strings/strings.go": &vfsgen۰CompressedFileInfo{ - name: "strings.go", - modTime: time.Date(2018, 2, 27, 18, 42, 13, 0, time.UTC), - uncompressedSize: 1759, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\x94\xd1\x6f\xe3\x44\x10\xc6\x9f\xbd\x7f\xc5\x60\x1e\xce\xa6\xa9\x9d\xb4\x4d\x49\x83\x82\x74\x0d\x52\x29\x42\xea\xe9\x0a\xe2\xe1\x74\x0f\xeb\xf5\x38\x9e\x64\xbd\x6b\xed\xac\xdb\x0b\xa8\xff\x3b\x5a\x3b\x6e\x73\xe5\x10\x02\xfa\xe4\xc6\xb3\xbf\xf9\xe6\xf3\x37\x9b\xe7\x70\x52\x74\xa4\x4b\xd8\xb2\x10\xad\x54\x3b\xb9\x41\x60\xef\xc8\x6c\x58\x08\x6a\x5a\xeb\x3c\x24\x22\x8a\x3b\x43\xca\x96\x98\x77\xbe\x5a\xc4\x42\x44\xf1\x86\x7c\xdd\x15\x99\xb2\x4d\xbe\xb1\x6d\x8d\x6e\xcb\x2f\x0f\x5b\x8e\x45\x2a\x44\xd5\x19\x05\xb7\xa6\xc4\x4f\xd7\x7b\x8f\x09\x1f\xc8\x13\x50\x50\xec\x3d\xa6\x40\xc6\xc3\x1f\x22\x72\xe8\x3b\x67\x60\xcb\xd9\xad\xf1\xe8\x8c\xd4\x77\xc5\x16\x95\x4f\x38\xcd\xd6\x52\xeb\x24\xa6\x00\xb9\xab\xe2\x49\x28\xba\xd1\xb6\x90\x3a\xbb\x41\x9f\xc4\xf7\x3d\x31\x1e\xeb\x2a\x67\x9b\x75\x2d\xdd\xda\x96\x18\x4f\x40\xa5\x69\x40\x26\xa9\x78\x3a\x56\x93\xf0\x04\x18\xdb\x83\x9c\xff\x2a\xe3\x75\x11\xb6\x7f\xe9\xf6\xb3\x64\xff\xff\x3a\xea\x91\xf0\x2f\xba\xae\x6d\x67\xfc\xdf\x74\x34\xb0\x5c\xc1\x54\x44\x79\x0e\xdc\xa2\x22\xa9\x41\x49\x46\x16\x11\x3f\x92\x57\x75\xa8\x09\x3f\x80\x46\xd3\xc3\x61\xb5\x82\xe9\x52\x44\xa3\xd6\x10\x80\xec\x7d\x67\xb0\xef\x72\x6b\x86\x0f\x90\x70\x0a\x27\x30\x7b\x7d\xf6\xfb\xe1\x31\x3d\x3a\x3f\xfd\x02\xff\xa5\x88\xaa\x5e\xf4\x6a\x05\x1c\x94\x3c\x9f\x9a\x89\x28\x7a\xfa\x0c\xf2\x24\x44\x54\x59\xd7\x57\xb5\x96\xc3\x58\xc7\x4e\xa7\x03\x2c\xbc\x59\xad\xe0\x74\x36\xd0\x0a\x87\x72\x77\x40\x99\x93\x13\x11\x45\x0c\x2b\xe0\x0f\xad\xe5\x93\x51\xd0\xf2\x63\x80\x8f\x9d\xcc\xb3\xab\x49\x01\xdf\x5c\x87\x5d\x41\x97\xc2\x61\xea\xf4\x60\x6f\xa0\xe7\x39\xfc\xda\xb2\x77\x28\x1b\x38\xd4\x65\x43\x19\x38\xd4\x84\x0c\xd6\xc0\xb8\x62\x9d\x61\x59\x61\x06\xbf\x21\x28\x69\xde\x78\x28\x2d\xf8\x5a\xfa\xac\xe7\xfc\x72\xf7\xc3\xdd\x12\x6e\xfd\x1b\x0e\x03\x30\x15\x1a\xfb\xb7\xe0\x6b\x04\x34\x9e\xdc\xf3\x92\x66\x87\x56\xf0\xf6\xdd\x6d\x40\x41\x81\x40\x4d\xab\xb1\x41\xe3\xb1\xec\x71\xc3\x5f\x63\x1d\x02\x56\x15\x29\x42\xe3\xf5\x1e\x82\x7b\x37\x77\x6f\xdf\xaf\x7f\x5c\x6d\x79\x48\x43\x45\x4a\x6a\xbd\x87\x44\x3e\x58\x2a\xa1\xe3\xa0\xfe\xc3\xc7\xb0\xac\x13\x20\xc3\x1e\xe5\x31\xb2\x63\x04\x79\xf0\x02\x4a\x72\xa8\xbc\xde\x7f\x07\xd6\x01\xdb\x06\xe1\x27\xf9\x20\xef\x95\xa3\xd6\x8f\x36\x15\x47\x62\xa9\x02\x6b\x10\xf0\x13\xb1\xe7\x34\x3b\xc2\x5e\x77\x61\x52\x62\x20\x1e\x54\x3f\x5a\xb7\x9b\x40\x89\x15\x3a\x28\x6d\x00\x91\x87\xce\x78\xd2\xc1\x11\x87\x6f\x18\x24\x18\xc4\x12\xb8\xb6\x8f\x06\x1e\x48\x42\xeb\x6c\x45\x3a\xdc\x36\x47\x64\x69\xca\xe1\x04\x48\x87\x50\xa0\x51\x75\x23\xdd\x8e\x41\x3e\x48\xd2\x32\xf8\x9c\x30\x22\xd4\xde\xb7\xbc\xcc\xf3\xcf\x2e\x39\x2d\xcd\x26\xdf\xd8\x9c\x98\x3b\xe4\x7c\xb6\xb8\xba\x9a\x7e\xdd\xff\xa3\x6c\x13\xec\x3e\x3d\x9f\x9f\x4d\x2f\x17\xf3\xf3\xf3\x30\xce\x21\x40\xc3\xe4\x49\x91\x15\x5d\x95\x7e\x39\x4c\xca\xb6\xfb\x75\x8d\x6a\x97\xa4\x21\x48\x54\x41\x91\xc9\xb2\x74\x21\xb9\x86\x74\x1f\xdd\xe3\x74\x3d\xd7\x87\x0f\xc0\x60\x2c\xb2\x92\x2d\x4e\xe0\xb1\x26\x55\x43\x8b\xae\xb2\xae\xe1\x31\x64\xef\x2c\x85\x3b\x03\x1a\x69\xa8\xed\xb4\xf4\x64\x4d\x36\x20\x5f\xc7\x6f\x02\x6c\x81\x77\xd4\x02\xf9\x0c\xee\xff\xc9\x89\x30\x37\xf9\xfc\x62\x71\x31\x5f\x5c\xaa\xc5\x4c\x4e\x67\x57\x97\x78\x71\x26\xd5\xfc\xac\xba\x9c\xcf\x0a\x35\xbf\x9c\xce\xbe\x55\xf2\x62\x7e\x71\xb6\x98\x86\xa6\xe3\x64\x50\x88\xe8\x09\x50\x33\xc2\xcb\xbc\x5f\xad\xa0\x18\x16\x5a\x1a\x52\x49\x7c\xc8\xf8\x12\x48\x6b\xdc\x48\xdd\x07\xce\x56\x60\xac\x39\xfd\x1d\x9d\x1d\xf7\x2c\x38\x42\x58\x42\xb1\x87\x07\xa9\x3b\x8c\xd3\xb0\xc2\x4f\xe2\xcf\x00\x00\x00\xff\xff\x3c\x43\xb4\x54\xdf\x06\x00\x00"), - }, - "/src/strings/strings_test.go": &vfsgen۰CompressedFileInfo{ - name: "strings_test.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 435119105, time.UTC), - uncompressedSize: 388, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x90\xc1\x4a\xc4\x40\x0c\x86\xcf\xe6\x29\xc2\x9c\x76\x55\xba\xcf\xa0\x1e\x16\x04\x41\x3a\xbd\xcb\xd8\xa6\x75\x6c\x27\x33\x24\x19\x3c\x88\xef\x2e\xa5\xf5\x24\xe2\x6d\x8f\x81\xef\xcb\x07\xff\xe9\x84\x37\xaf\x35\x2e\x03\xbe\x2b\x40\x09\xfd\x1c\x26\x42\x35\x89\x3c\xe9\x8b\x91\x1a\x40\x4c\x25\x8b\xa1\x5b\xaf\xc8\x93\x03\x18\x2b\xf7\xd8\x91\xda\xfd\xaa\x92\xdc\x2d\x4b\xee\xf5\x60\x78\xbd\x33\x4d\x77\xc4\x4f\xb8\xb2\xc6\xcf\xb1\x1c\x9c\x54\xb6\x98\xa8\x69\x29\x0c\x4f\x94\xbc\x05\xd3\x5b\xfc\x61\x37\xfb\x99\xa4\xad\x8c\x9c\x0d\xb5\x96\xb5\x48\x03\x46\xc6\x73\x2e\x6f\x24\x8f\xde\x1d\xe1\xeb\x77\xf9\x2c\xf9\xe3\xa2\xdd\x87\x9c\x4a\x10\xf2\xdb\x42\x7f\xa7\x2b\x6b\x18\x77\xec\x9f\xe7\xdf\x01\x00\x00\xff\xff\xe9\xc8\x01\xe4\x84\x01\x00\x00"), - }, - "/src/sync": &vfsgen۰DirInfo{ - name: "sync", - modTime: time.Date(2019, 8, 11, 22, 43, 46, 32745917, time.UTC), - }, - "/src/sync/atomic": &vfsgen۰DirInfo{ - name: "atomic", - modTime: time.Date(2018, 4, 20, 10, 43, 19, 305171943, time.UTC), - }, - "/src/sync/atomic/atomic.go": &vfsgen۰CompressedFileInfo{ - name: "atomic.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 3060, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x56\xcf\x6f\x9b\x3e\x14\x3f\xe3\xbf\xe2\x7d\x39\x54\xd0\x7e\x45\xa4\xad\xea\xa1\x52\x0e\xd5\x0e\x53\xa5\x49\x9b\x54\x75\x77\x07\x4c\xea\xcc\xb1\x91\xb1\x69\xa2\x28\xff\xfb\x64\x03\xc1\x80\x61\x5d\xb2\xf6\x84\x8b\xfc\xf9\xc1\x7b\x9f\xf7\x9a\xc5\x02\x6e\x56\x9a\xb2\x0c\x36\x25\x42\x05\x4e\x7f\xe1\x35\x01\xac\xc4\x96\xa6\x08\xd1\x6d\x21\xa4\x82\x08\x05\xa1\xe6\x25\xce\x49\x88\x50\x10\xae\xa9\x7a\xd1\xab\x24\x15\xdb\xc5\x5a\x14\x2f\x44\x6e\xca\xee\xb0\x29\x43\x14\x23\x94\x6b\x9e\xc2\xd3\x2b\x2e\x1e\xb9\xfa\xfc\x29\xc2\x59\x26\xe1\x9a\x9a\xf3\xff\xc0\xc9\x2b\xd8\x63\x5c\x3f\xe0\x80\x02\xc1\x32\xb8\x5f\xc2\xb5\xb9\x88\x02\xfb\x80\xa5\xb9\x89\x02\x49\x94\x96\x1c\x04\xcb\xd0\xb1\x4f\x7c\x77\xdb\x11\xdf\xdd\x9e\x88\xef\x6e\xe3\xfa\x71\x1e\xf1\x33\x75\x2c\x6b\xc7\xb3\x6e\x4c\xeb\x0b\x5c\x3f\x53\xc7\xb6\x76\x7c\xeb\xc6\xb8\xbe\xd0\x79\xa1\xa4\xc3\x5e\x28\xd9\xd1\x17\x4a\xc6\xed\xe1\x3c\x81\x1f\x82\x72\x45\x4e\x02\x36\x12\x49\xf3\xb2\xd1\xe9\xbd\x8b\x07\x7f\xff\xbd\xea\x17\xb1\x2d\xb0\x24\x0f\x3c\x9b\x08\x93\x60\x59\x2f\x51\x2b\x21\x98\x91\xa1\x39\x34\xdc\x4b\x73\xc7\xbc\xea\x8b\xb5\x6a\x4a\x6a\x82\x82\xe3\x49\x3d\xc7\xac\x24\xd3\xfa\xc3\xcc\xb9\xfa\xa6\x7f\xef\xaa\xef\x8d\xe6\xc9\x81\xfe\x88\x12\x78\x03\xdc\xb3\xf0\x21\x55\xf0\xc4\xbc\x67\xc2\x66\xfd\x5d\x5d\xcc\xcf\x42\x67\x66\x30\x10\xff\xd8\xd3\x43\x96\x79\x86\x22\x23\x4c\xe1\xd1\x8e\x35\x76\xda\xc9\x83\x9b\xfa\x92\x7f\x02\xcd\xd9\x51\xf0\xc6\xae\xd6\x18\xef\xc4\xb3\x55\x3c\xc3\x75\xfa\x8e\xde\x4a\xbf\xe8\x3b\x46\xd9\xed\xbe\xa3\xbf\x7e\x2f\x52\xf1\xc4\xb3\xd3\x19\xee\xe1\xf3\x94\xbe\x09\x3c\x6e\xbd\xd3\xed\x06\x52\xef\xd9\x01\xa8\x5f\x67\xa7\xb4\x93\x20\x4f\x04\xdc\xa6\xcf\xe2\x06\x25\x77\x8b\x3c\x8b\x1b\x15\xb1\x57\xb5\x49\xe8\xdc\x60\xfa\xfe\x21\x79\x89\x9e\x94\x90\xc4\x33\x59\x15\x66\xed\x5c\x1d\xba\x1e\x55\x98\x8d\x90\xc3\x2c\x37\x48\xf3\xfd\x73\x48\xef\xac\x19\xac\x7e\x83\xac\x37\xe0\x2d\xf8\x2d\xca\x9e\xdc\xb6\x70\x5b\xff\x39\xfc\xfc\x42\xb4\x34\x83\x5e\x4c\xb0\x45\x15\x5c\xff\xc4\x4c\x93\xd8\xf6\x33\x8a\x21\xda\x81\x85\xe4\x38\x25\x87\x63\xec\x74\xad\x4a\x2a\x1f\xce\x1a\xf2\xa0\x68\x0e\x3b\xb3\x71\x39\xb5\x4b\x38\x28\x30\xa7\x69\x14\x96\x7b\x9e\x2e\xea\x1f\xbd\xf7\x50\x1a\x2c\x88\xdc\x5e\xaa\x0c\x9f\xa1\x11\x60\xa9\xc3\xd8\xee\x62\x9a\x1b\x65\xf8\xaf\x66\xba\xba\x82\x4d\x99\x3c\x1a\x2d\x8e\xd9\xf7\xd5\x86\xa4\x2a\xda\xc5\xc9\x57\xa2\xa2\x30\x15\xbc\x54\x52\xa7\x4a\xc8\x30\x36\x88\xf1\xd5\x2a\xa9\xbc\x97\xff\xe8\x90\x72\x03\xa0\xa5\x22\x5c\xb1\x3d\xa8\x7d\x41\xb2\x29\xcb\xc6\xef\x12\x76\xe8\x88\x7e\x07\x00\x00\xff\xff\x2a\xf7\xf1\xfd\xf4\x0b\x00\x00"), - }, - "/src/sync/atomic/atomic_test.go": &vfsgen۰FileInfo{ - name: "atomic_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x61\x74\x6f\x6d\x69\x63\x5f\x74\x65\x73\x74\x0a\x0a\x69\x6d\x70\x6f\x72\x74\x20\x22\x74\x65\x73\x74\x69\x6e\x67\x22\x0a\x0a\x66\x75\x6e\x63\x20\x54\x65\x73\x74\x48\x61\x6d\x6d\x65\x72\x53\x74\x6f\x72\x65\x4c\x6f\x61\x64\x28\x74\x20\x2a\x74\x65\x73\x74\x69\x6e\x67\x2e\x54\x29\x20\x7b\x0a\x09\x74\x2e\x53\x6b\x69\x70\x28\x22\x75\x73\x65\x20\x6f\x66\x20\x75\x6e\x73\x61\x66\x65\x22\x29\x0a\x7d\x0a"), - }, - "/src/sync/cond.go": &vfsgen۰CompressedFileInfo{ - name: "cond.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 511, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x8f\x31\x73\xab\x30\x10\x84\x6b\xdd\xaf\xd8\x12\x1e\x83\x71\xfd\x6c\x9a\xe7\x96\xee\x4d\x26\xb5\x2c\x84\x7d\x41\x3e\x31\x20\x92\x61\x32\xfc\xf7\x8c\x90\x93\x14\xb6\x9a\x93\x76\x75\xfb\xcd\x56\x15\x8a\xf3\xcc\xae\xc5\xdb\x44\x34\x68\xd3\xeb\x8b\xc5\xb4\x88\x21\x0a\xcb\x60\x71\xf2\xd2\x62\x0a\xe3\x6c\x02\x3e\x49\x55\x15\x3a\xb6\xae\x9d\x30\x4f\xb6\xc5\x79\xc1\xbb\x16\x76\x4e\x83\x6f\x83\xb3\x37\x2b\x41\x07\xf6\x42\x4a\xfc\xc9\x0f\x0b\x90\x26\xa9\x06\xe9\x34\xde\xf4\x76\x8c\x7e\xe0\x6e\xf3\xe3\x6c\x78\x0a\xa4\xcc\xd5\x46\x13\xc6\x0f\xcb\x29\xdd\xe9\x19\x53\xec\xc7\x23\x0f\x60\xd9\x32\x60\xae\x5a\x70\xf6\xde\xd1\x4a\xd4\xcd\x62\x90\x19\xfc\x89\x4d\x72\xbc\x6a\x0e\x59\x1e\xab\x98\x9d\x14\x05\x29\xee\x60\x76\xe6\x8a\xba\x86\xb0\x8b\x86\x4a\x6f\xdc\x74\x6f\xb3\x9f\xac\x9c\xd4\x1a\x97\x9a\xdd\x8b\x38\x6f\xfa\x2c\x27\x75\x2c\xe3\xd7\xa4\x36\x49\x7b\x24\xfe\xe7\x8b\x68\x97\x98\x1b\x4c\x22\x6b\xbf\x91\x46\x1b\xe6\x51\xee\xc9\x52\x96\x94\xd8\xc7\x12\x61\x9c\xed\x93\xb0\x7f\xa3\xd7\xad\xd1\xd3\xbd\x83\xe0\x6f\x1d\x13\xb7\x75\xd4\xd8\x93\xea\xfc\x08\x8e\xf2\xfe\x00\xc6\x11\x72\x00\x17\xc5\x6f\xaf\xef\x6c\xb5\xd2\x4a\x5f\x01\x00\x00\xff\xff\x2c\xcb\x53\xaf\xff\x01\x00\x00"), - }, - "/src/sync/export_test.go": &vfsgen۰CompressedFileInfo{ - name: "export_test.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 168, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\xca\x4d\x0a\xc2\x30\x10\x05\xe0\x7d\x4e\xf1\x96\x8a\x3f\xf1\x02\xde\x41\x0a\xae\x25\x4d\x5f\x35\xda\x4c\x42\x32\x29\x94\xd2\xbb\xbb\x15\xdc\x7f\xd6\xe2\xd0\xb7\x30\x0d\x78\x57\x63\xb2\xf3\x1f\xf7\x24\xea\x22\xde\x18\x6b\xd1\x71\x64\xa1\x78\x0e\xe8\x17\x28\xab\xd6\x23\x84\x1c\xa0\x09\x2f\x37\x13\x92\x4e\x29\x23\xc4\x3c\x31\x52\xd4\x69\x48\x52\xcf\x66\x76\x05\x5d\x13\x0d\x91\x8f\x5c\x92\xbf\x05\xc1\x15\x63\x13\xbf\xdb\x23\x88\x62\x45\xa1\xb6\x22\xb8\x60\xfb\xd3\x77\xc9\xbf\x7e\xdd\xcc\x37\x00\x00\xff\xff\x78\xcd\x49\xae\xa8\x00\x00\x00"), - }, - "/src/sync/pool.go": &vfsgen۰CompressedFileInfo{ - name: "pool.go", - modTime: time.Date(2019, 8, 11, 22, 43, 46, 32527631, time.UTC), - uncompressedSize: 505, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x90\xcf\x4e\xf3\x30\x10\xc4\xcf\xde\xa7\x98\xaf\xa7\xe4\x03\x5a\xb8\x56\xca\x89\x03\x37\x54\x89\x63\x55\x21\xe3\x6e\x2a\x83\xeb\x58\xce\x5a\xa4\x54\x79\x77\xe4\x24\xfd\x83\x20\x97\x68\x77\x46\xbf\x99\xf5\x62\x81\x9b\xb7\x64\xdd\x16\xef\x2d\x51\xd0\xe6\x43\xef\x18\xed\xc1\x1b\x22\xbb\x0f\x4d\x14\xcc\x92\x6f\x75\xcd\x33\x22\x39\x04\xc6\xaa\x69\x1c\x5a\x89\xc9\x08\x8e\xa4\x5c\x63\xb4\x43\xfe\x46\xdb\x7c\xd5\x58\x2f\x1c\x27\xe5\xc5\x7e\x31\x92\xf5\x12\x24\x12\xa9\x56\x9a\xc8\x58\x6f\x06\x4b\xad\x0d\x1f\x7b\x52\xcf\xfc\x09\xa0\x4e\xde\x14\x25\xae\x95\x9e\x28\x6f\x51\x04\xfc\xcf\xb1\x25\x9e\x58\x7e\x7a\x72\x05\x5b\xc3\xb1\x2f\xc2\x7c\xa0\x97\xa8\x2a\xdc\xe7\x7d\x16\xc2\x3c\xd3\xff\x55\xf0\xd6\x0d\x3b\x15\x59\x52\xf4\xa3\x50\x94\xa4\x54\x4f\xe7\xa5\xb7\x8e\xf2\xdc\x61\x59\x61\xe2\xad\xaf\xd9\x77\x0f\x1b\x52\xd3\x80\x8b\x65\xf9\xcb\x33\x01\xbb\x3f\x6e\x58\x25\x29\xba\xeb\x1b\xca\xe9\x88\x2e\x37\x3f\xf5\x1c\x01\x43\x9b\x4b\x9e\x0e\x81\xfd\xf6\x94\x74\x8b\xae\x3c\xf3\x63\xf2\x62\xf7\xfc\x1a\x79\x67\x5b\xe1\x98\xb3\x1e\x1d\x6b\x9f\x42\x61\xc6\xff\xf4\xc4\x39\xae\xa7\xef\x00\x00\x00\xff\xff\xd6\xf1\x0f\x08\xf9\x01\x00\x00"), - }, - "/src/sync/sync.go": &vfsgen۰CompressedFileInfo{ - name: "sync.go", - modTime: time.Date(2019, 8, 11, 22, 43, 46, 32808177, time.UTC), - uncompressedSize: 2015, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x55\xdb\x6e\xe3\x36\x10\x7d\x36\xbf\x62\x60\x14\xa8\x94\xd8\x52\xd2\x2d\xb6\x40\xb0\x7e\x28\xb2\xc5\x22\x40\xbb\x0b\x34\x29\xfa\x10\x18\x0d\x25\x8d\x4c\xc6\x14\xa9\x72\x28\xab\x6e\x90\x7f\x5f\x0c\xa5\xf8\x92\x9b\x5f\x4c\x90\x33\x67\xce\x9c\xb9\x28\xcf\xe1\xb4\xe8\xb4\xa9\xe0\x9e\x84\x68\x65\xb9\x96\x2b\x04\xda\xda\x52\x08\xdd\xb4\xce\x07\x98\xae\x74\x50\x5d\x91\x95\xae\xc9\x57\xae\x55\xe8\xef\x69\x7f\xb8\xa7\xa9\x10\x1b\xe9\x81\xb0\xf9\x5b\xea\x80\x9e\x60\x01\x8d\x5c\x63\xd2\xc8\xf6\xf6\xa4\xd3\x36\x7c\xf8\x69\x79\xbb\x2c\x95\xb4\x50\x38\x67\x52\x21\xf2\x9c\xcd\x7f\xed\xdd\x1a\x2d\x04\x2f\xcb\x35\x41\x50\x08\xb6\x6b\x0a\xf4\xe0\x6a\xe8\x47\x28\x39\xd8\x14\x5b\xf0\x9d\x0d\xba\xc1\x7f\xae\xb1\xf1\x68\x50\x12\x42\x72\x57\x2a\xf8\x34\x87\xe0\x3b\xbc\x4b\x19\x35\x28\x19\x40\xc9\x0d\x82\x75\x01\xb6\x18\x40\x96\xff\x76\xda\x63\x15\xf1\x09\x1b\xd9\x2a\xe7\xd9\xf5\xd3\xbc\x54\x77\xa0\xed\x21\xf0\x68\xfc\x47\x17\xf0\xbf\x34\x13\x79\xce\x98\x37\x4a\x13\xb4\x1e\x37\x68\x03\x81\x04\x8b\x3d\x94\xd2\x18\x08\xee\x2d\x5f\x7e\xea\xbd\xb3\x2b\xb3\x7d\x22\x70\x1c\x9f\x71\xb5\x85\x02\x43\x8f\x68\x21\x29\xb0\x94\x1d\xe1\x6b\x49\x2a\x49\x20\x8d\x47\x59\x6d\x41\xdb\xd2\x63\x83\x36\xbc\xc8\xa7\x57\xda\x44\xd4\x48\x4c\x21\xb4\x68\x2b\x6d\x57\x91\x29\xbd\x47\xf5\x48\x2d\x8f\x25\xea\x0d\x56\x50\x7b\xd7\x44\x1c\x2e\x9b\x45\x13\xa1\x2d\x47\xed\x08\x2a\x7c\x83\xc6\x4e\xb3\x6b\x44\x50\x21\xb4\x74\x91\xe7\xef\xb6\x8f\x26\xea\x90\xf2\x5f\x3e\x7c\xcc\x9e\xba\x68\x6c\x8b\x57\x9a\x68\xf8\x4b\x85\xa8\x3b\x5b\xbe\x92\x50\x42\x30\x9a\xa6\xf0\x20\x26\x6f\x64\x9c\xd0\x0c\x6a\x69\x08\x53\xf1\x28\x06\xb2\xc7\x8a\x68\x02\xa3\xd7\x78\x70\x3f\x83\xa2\x0b\x50\x3b\x0f\xad\x77\xb5\x36\x51\x58\x67\x03\xda\x0a\x2b\x88\x5e\x48\x9c\xfb\x70\x3e\xb0\xd2\x14\xb5\xa5\xae\xe5\x59\xc2\x6a\x06\xe4\xe0\xbe\xa3\x00\x5c\xee\x28\x9e\x6c\x10\x74\xd3\x9a\xa8\xa8\x0c\xda\x59\x90\xf4\x4a\x76\x11\xff\xe6\xdb\xe7\x6f\x17\x70\x65\x37\x48\x41\xaf\x64\x60\x0c\x4d\x19\x5c\xd5\xa0\xc3\x8f\x04\xad\x23\xd2\x85\x41\xae\xf8\x0e\x74\xc6\x64\x49\x57\xe8\xa1\x72\xcc\x8a\xdc\x0c\x5c\x50\xe8\x7b\xcd\x4d\x87\x8d\xdb\x0c\x40\x50\xba\x86\x3d\xb2\xb7\x24\x1e\x15\x7c\xd2\x79\x06\x46\xd7\x6e\x18\x6b\x96\x5c\xd7\x90\x9c\x10\xcc\xf7\x75\xbc\xa5\x65\x0a\x8b\x05\x9c\xf1\xf3\xa4\x54\x70\x31\x16\xf6\x60\x1f\x4c\xd8\x2f\x02\xb1\xcd\x64\xbf\x49\x6e\x69\x09\x0b\x90\x2d\x37\x73\x72\xb0\x42\x1e\x4a\xf5\x38\x83\x23\xbb\x2c\xcb\x18\xe8\x11\xd0\x10\xbe\x8b\x73\x74\x3d\x83\x52\x45\x3f\x31\x99\xf0\x46\x10\xd1\x6d\x47\x1d\xe6\x0b\x38\x1f\xf8\x1d\x5d\xef\x12\x9a\x54\x68\x30\x60\xb2\x7b\x9d\x01\x8d\x78\x8f\x62\x72\x42\xf3\x39\x37\xd9\x73\x31\xc7\xd9\x3e\xd4\x51\x49\x5b\xb9\xba\xde\x4b\xb9\x2b\xf6\x5f\x71\x09\x0c\xaf\xba\x06\x8b\x58\x61\x95\x3f\x15\x3a\xe3\x28\xa7\xa7\x42\x4c\x7a\x96\xf6\x28\xb9\x58\x0f\x83\x36\xe9\x0f\x4a\xe0\x31\x74\xde\x32\x3d\x31\x96\xa3\xbf\x3d\x5b\xb2\x3b\x9f\xce\x2f\x96\xe2\x85\x70\xfd\xab\x40\xfb\xcc\x47\xe3\x21\x75\xc6\x3d\xd2\xea\x94\x25\x8c\xb1\xc6\x55\xfd\x42\x11\xeb\x82\xae\xb7\xbf\x6b\x0a\x97\x0a\xcb\x75\x42\xfa\x7f\x04\x16\xa6\x0d\x3e\x85\x87\xe7\xe6\xa5\xb4\xd7\xad\xb6\x89\x06\x6d\x43\x1a\x15\x8b\xe3\x1e\x13\x1b\x46\x7b\x9c\xec\x4b\xd7\x6e\xf9\x6b\xc2\x6e\xd9\xe8\xfe\x55\x5a\xf7\xac\xbd\xad\x64\x06\x0d\x26\x29\x23\x7e\xfc\x99\xd1\x78\x62\x02\x34\xda\x18\x4d\x58\x3a\x5b\xc1\x02\xce\xcf\xe2\x6f\x17\xea\x9e\xb2\x2f\xc6\x15\xd2\x64\x5f\x30\x24\xd3\xcf\x32\xe0\x34\xcd\xbe\x62\x9f\xa4\xd9\xa5\x34\x26\x99\xae\x30\xdc\xe8\x86\x6f\xaf\x18\x38\x49\xe1\xe4\x10\x73\xa4\x79\xf5\x34\xa8\x58\x1d\x7c\x90\x46\x92\x41\x79\xd7\x27\x04\x14\xbc\xb6\xab\xd8\x1a\xfb\xb8\x43\x94\x1f\xa2\xcd\x9f\x83\xdb\x6f\xde\x3b\x3f\x8d\xb5\x78\x14\xdf\x03\x00\x00\xff\xff\xaa\x5d\x20\xc4\xdf\x07\x00\x00"), - }, - "/src/sync/sync_test.go": &vfsgen۰CompressedFileInfo{ - name: "sync_test.go", - modTime: time.Date(2018, 1, 25, 23, 45, 7, 0, time.UTC), - uncompressedSize: 240, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd2\xd7\x57\xd0\x4e\x2a\xcd\xcc\x49\x51\xc8\x2a\xe6\xe2\x2a\x48\x4c\xce\x4e\x4c\x4f\x55\x28\xae\xcc\x4b\x8e\x2f\x49\x2d\x2e\xe1\xe2\xca\xcc\x2d\xc8\x2f\x2a\x51\xd0\xe0\xe2\x54\x02\x09\x64\xe6\xa5\x2b\x71\x69\x72\x71\xa5\x95\xe6\x25\x2b\x84\xa4\x16\x97\x04\xe4\xe7\xe7\x68\x94\x28\x68\x41\x25\xf5\x42\x34\x15\xaa\xb9\x38\x4b\xf4\x82\xb3\x33\x0b\x34\x34\xb9\x6a\xd1\x94\xba\x3b\x93\xa0\x38\x28\x35\x27\x35\xb1\x38\x95\x48\x1d\xce\xf9\x79\x29\xce\xf9\x05\x95\x78\x95\x03\x02\x00\x00\xff\xff\x93\xcf\x90\x60\xf0\x00\x00\x00"), - }, - "/src/sync/waitgroup.go": &vfsgen۰CompressedFileInfo{ - name: "waitgroup.go", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 557321982, time.UTC), - uncompressedSize: 446, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\xd0\x4d\x4e\xc3\x30\x10\x05\xe0\xb5\xe7\x14\x8f\x2e\x2a\x87\x0a\x5a\xe8\x0e\x35\x48\xac\x38\x02\x0b\xc4\xc2\x38\x6e\x62\x1a\x26\x51\x32\xa6\xaa\xaa\xdc\x1d\xd9\x04\x08\x3f\xcd\x2a\x7a\x1e\x7d\x7e\x9e\xe5\x12\x8b\xe7\xe0\xeb\x02\x2f\x3d\x51\x6b\xec\xce\x94\x0e\xfd\x81\x2d\x91\x1c\x5a\x87\x07\xe3\xe5\xbe\x6b\x42\x8b\x5e\xba\x60\x05\x47\x52\xb6\x09\x2c\xae\x83\x67\x21\x65\x2b\xa4\xcf\x56\x86\xc7\x99\xe3\x40\xa4\x7a\x31\xe2\xae\xf0\xb8\x7e\x0a\x9e\x65\x7d\x4d\x03\xd1\x36\xb0\x85\xde\x97\x38\xff\x62\x33\xdc\x15\x85\x2e\x5c\x2d\x26\x7a\x59\xf4\xf7\xe5\xe5\xe7\x15\x8b\x1c\xe9\x8c\x94\xdf\x62\x92\x6f\xb0\x8a\x93\xaa\x35\xec\xad\x9e\xc5\xc2\x37\x60\x57\x1a\xf1\x6f\xd3\xd2\xe3\xfc\x2c\x23\x35\xfc\x36\x6e\xb1\xc2\x7c\x9e\x92\x0a\x79\x0e\xf6\x75\x32\xc7\x00\xaf\x66\xe7\xf4\x8f\x67\xfd\xa7\xe4\xf9\x94\x39\xfb\x66\x6c\xdd\xf4\x4e\xa7\x38\x9b\xa8\xec\xeb\xa8\x9c\xda\x46\xfc\xd5\x69\x0b\x7f\xcb\x46\x75\x73\x91\xa0\x0f\xe2\x3d\x00\x00\xff\xff\x08\x4a\xda\xa3\xbe\x01\x00\x00"), - }, - "/src/syscall": &vfsgen۰DirInfo{ - name: "syscall", - modTime: time.Date(2019, 5, 1, 6, 1, 9, 584098940, time.UTC), - }, - "/src/syscall/js": &vfsgen۰DirInfo{ - name: "js", - modTime: time.Date(2019, 5, 1, 6, 1, 9, 584179814, time.UTC), - }, - "/src/syscall/js/js.go": &vfsgen۰CompressedFileInfo{ - name: "js.go", - modTime: time.Date(2019, 5, 1, 6, 1, 9, 584705329, time.UTC), - uncompressedSize: 5729, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x58\xed\x6f\xdb\xbc\x11\xff\x6c\xfd\x15\x57\x7d\xe8\x23\x35\x9a\xfc\xbc\x74\x59\xe1\xc2\x1f\xba\x61\x2d\x5a\xac\xcd\xb0\x74\xdb\x87\x20\x58\x68\x99\xb2\xe9\xc8\x94\x21\x51\x8a\xbd\xc0\xff\xfb\x70\x47\x8a\x22\x15\x39\x71\xb1\xa7\x40\x1d\xfa\xf8\xbb\x17\x1e\xef\xcd\x9c\x4e\xe1\x62\xd1\x88\x62\x09\x9b\x3a\x08\x76\x2c\xbb\x67\x2b\x4e\x6b\xb1\xdd\x95\x95\x82\x28\x98\x84\x15\xcf\x0b\x9e\xa9\x30\x98\x84\x8d\xac\x59\xce\xc3\x20\x98\x84\x2b\xa1\xd6\xcd\x22\xcd\xca\xed\x74\x55\xee\xd6\xbc\xda\xd4\xfd\x62\x53\x87\x41\x1c\x04\xea\xb0\xe3\xf0\x1d\x3f\x84\x54\x41\x90\x95\xb2\x26\x91\x48\xfa\xa7\x5c\xf2\x5c\x48\xbe\xd4\x80\x39\x88\x52\x31\xbd\xf5\xad\x29\x0a\xbd\xfa\x73\x59\x16\x9c\xc9\x8e\xbc\x5d\xf0\x4a\xaf\xaf\x55\x25\xe4\xca\xac\x0f\xdb\x45\x69\x18\xae\x16\x1b\x9e\x29\xbd\xfe\xd8\xc8\x4c\x89\x52\xa2\x25\x79\x23\x33\x88\x14\xe9\x8a\x41\x73\x47\x31\xd4\xb4\x80\xc7\x60\x52\x3f\x08\x95\xad\x41\xe1\x3a\x63\xb5\x36\xdb\xda\x38\x0b\x26\x93\x8a\xab\xa6\x92\x10\x36\x1d\x31\x74\x90\x68\xb2\x0b\x92\x4d\x51\xb8\xfb\xe6\x20\x2e\x64\xa1\x49\xbe\x14\x3c\xa1\x2f\x07\x29\x2e\x46\xdb\xee\x62\xf4\x21\x3c\x0c\x79\xc4\xc3\x10\xc5\xc5\x68\x4f\xb9\x98\x92\x28\x2e\xa6\xf3\xa0\x8b\xca\x0d\x2d\x0c\x26\x4b\x9e\xb3\xa6\x20\x19\x3b\x26\x45\x16\x85\x0b\xb6\x04\xbc\xf4\x30\x0e\x26\xc7\xe0\x68\xfc\xfe\xa9\x28\x17\xac\x88\x62\xf8\x17\x2b\x1a\x8e\x1e\x36\xc2\xb4\xc6\xef\x25\xd1\xa3\x4d\x9d\x6a\x64\x6c\x39\xd1\xad\x2f\xf2\x49\xe1\x70\xd8\x2b\x3b\x47\x9d\x05\x13\x3f\x45\x2b\x1e\x19\xc3\xa2\xc9\x28\x14\x08\x6a\x85\x47\x39\xed\xc7\xf0\x0f\x5e\x70\x56\xf3\x28\x46\x4c\x9e\x6a\x45\x73\x63\xae\x85\x23\xf6\x2a\x8f\x72\x09\xf8\x35\x52\x6b\x51\x6b\x9b\x12\x60\xd5\xaa\x86\x9b\x5b\xfa\x16\x63\x76\xf0\x2a\x67\x19\x7f\x3c\xc6\xda\x82\xde\x68\xfc\xfa\x18\x4c\xb4\x25\xb3\xa7\x67\xf8\xca\xee\xe9\x9e\xa2\x5e\xc7\x9b\x4d\x9d\xea\xeb\xb5\x8a\x7a\x92\xa7\x0d\xf5\x4c\x26\x2d\x81\x66\x73\xd8\xb2\x7b\x1e\x19\xab\x12\x28\xb8\x8c\x70\x27\x8e\x11\x94\x97\x15\x88\x04\x18\xe2\x2a\x26\x57\x5c\x8b\x26\x01\x5a\xc2\x8d\xb8\x85\xf9\xc0\x40\x46\xbc\x47\xfc\x30\xe7\xc9\x65\xe4\x43\xd0\xe4\x38\x01\x12\x81\xe8\x63\x1c\x27\x26\x7a\xe8\x46\xfe\x5a\x55\x65\x75\xfa\x4a\x0c\x20\xd6\x7f\xbc\x9c\xee\x42\xf6\x0b\x6b\xd9\x75\x56\x89\x9d\x02\x8e\xa0\x19\x84\x70\x01\x3c\xfd\xc4\x55\x14\x6e\x79\x5d\xb3\x15\x0f\xe3\xb4\xab\x0a\x56\xb3\xbe\xd6\x5e\x73\xeb\x78\x36\x08\x26\xd3\x29\x08\x29\x14\x5f\x42\xc5\x77\x15\xaf\xb9\x54\x35\x3c\xac\xb9\x5a\xf3\xca\xf0\x8a\x1a\x64\x29\xff\xf0\x5f\x5e\x95\xd0\x22\x25\x05\x55\x35\xdc\x65\x50\x6b\xae\xb7\x34\x58\xc1\x4f\xb6\xc0\xfc\x94\x06\x13\xa3\x01\x8b\x85\x3d\xb3\xef\xbf\x72\xb1\x01\xf7\x7a\x6d\xd4\x8b\x1c\x91\x30\x9f\x83\x1b\xea\x74\x63\xc6\x33\x04\x7d\x3c\xa2\xb7\x7d\x52\xb9\xd8\x24\x64\x29\x5d\x43\xcb\x2a\xac\xda\x62\x09\xfd\x3f\xc7\x13\x13\x21\x6b\xc5\x64\xc6\xaf\xf2\xc1\xc6\x8a\x2b\x92\x47\x15\xde\xd9\xe8\x0a\x32\x1e\x4e\xe7\x90\xc8\xc1\xa6\x3f\xbc\x9a\x83\x14\x05\x19\x2a\x96\x30\xef\x77\xd2\xbf\xb0\xa2\x88\x42\xde\xb2\x22\x4c\x20\x8c\xba\x5a\x14\xed\x63\x78\x04\x73\x82\xfd\x7b\x38\xc6\x58\x80\x5c\xbb\xce\x12\x92\xc0\xc1\x95\x03\x1d\x7f\x99\xc3\xc1\x0a\xf5\xce\x74\x52\xec\x9d\x6f\x5b\x00\x20\x72\x88\x30\xaa\xca\x1c\x29\xf3\xf9\xdc\xed\x24\x1a\x02\x9d\xea\x9f\xdf\xc3\x74\xea\x77\xa0\x00\xe0\x68\xa4\xec\x89\x1b\x3b\xcc\x80\xed\x17\xcb\x46\x1d\xb4\xe7\x18\xe8\xed\x3a\xcf\x80\xfd\x57\xcb\xde\xb5\xdd\x93\x12\x4c\x5b\x1a\x08\xf8\xcd\xd1\x4f\xad\xfa\x24\xbf\x69\x59\x03\xfe\xb7\x96\xdf\xb4\xf7\xd3\xfc\xba\x9d\x0d\xf8\xff\xd8\xf3\xeb\x91\xe0\x24\xbf\x6d\x62\x03\x09\x7f\xb2\x12\xec\xf0\xa0\x65\x98\xfd\x4b\xbb\x6f\x22\xf9\x18\xdf\x79\xad\x8e\x42\xe3\x2a\x8f\xf6\x7e\x4d\xb7\x39\x69\xc6\x8c\x3d\x56\xd1\x7d\x4a\x66\xc5\x76\xe4\xd0\x25\xbe\x4f\xcf\xbd\xa1\xa3\x2d\x2e\x59\xf7\x1b\xa7\x4f\x2f\x3f\x54\x15\x3b\x9c\x84\x48\xe1\xce\x02\xa6\x49\xe9\x2d\x0c\x85\x04\x6d\xa5\x8f\x77\xf4\xf9\xcb\x25\xfd\xf9\xed\x57\xfa\x73\xf9\x36\x81\x86\x00\x8d\x46\x34\x06\xd2\x18\x4c\x63\x40\x79\x51\x32\x22\xd0\x82\xd8\x68\x5a\x4c\xff\x5e\x92\x2f\x12\x53\x99\x13\xd8\xb2\xdd\x8d\x5e\xdf\x3a\x5e\x4a\xe0\xc6\xfd\xea\x58\xec\xd7\x3b\xb1\x4c\x3f\xcb\xb6\xbc\xe7\xd1\x1e\x3b\xd3\x93\x21\xe4\x4e\xc8\x96\x15\x62\x89\xfd\x69\x06\x77\x70\x01\x66\x80\x4d\xe9\xde\x30\x08\x6c\xa9\xf7\xee\x2e\x6a\xc1\xed\xc7\x92\x46\x96\xbe\x6a\x99\x32\xf5\xaa\x4d\x4d\x4d\x76\x0a\xa9\x5b\x60\xdd\x6a\xda\xa6\xed\x88\x78\x4c\xaf\x28\x26\xdf\x1b\xa1\x2d\x55\x93\xd9\x1c\x5a\x32\x32\x8a\xdf\x1b\xd2\xab\xb9\x9b\x90\xa4\x52\x9f\xf2\x35\xc9\xa2\x9e\xf7\x18\xd2\x3a\x45\x50\x98\x68\xc6\x63\xec\x9b\xd1\x9f\x28\xd5\xda\xd1\xac\xe9\x14\xb2\x52\xb6\xbc\x52\x1f\xb0\x95\x9b\x75\x8d\x8e\x6b\xb6\xd4\x9c\x84\x54\xa6\x71\xd5\x80\x03\xc0\x27\x1a\xf0\xbf\x5c\xf7\x90\x54\x1f\xce\x91\x43\x33\x03\xa4\x69\xea\x65\x80\x77\xb7\x78\x0e\xc9\x1f\x3e\x98\xb1\xc3\xdb\xc3\x76\x84\xaa\xfe\x43\xb3\xcb\xc8\xb4\xd1\x22\xad\xcb\x33\x56\xad\xb0\x28\x77\xc2\xe6\xc0\x76\x3b\x2e\x97\x91\x21\x24\xde\xd1\x3d\x9f\x18\xc4\xc8\xf5\x50\x21\xdf\xda\x68\x1d\x3d\x8e\xdb\x64\x5f\xba\x3c\x13\x3e\xaf\x5f\xfb\xe4\xae\xc2\x3c\x7f\xa9\x68\xcc\xe0\x52\x45\x0e\xbb\xaa\xdc\xf5\x5a\x71\x8e\xd9\xc6\x56\xb9\xdd\x3c\xad\x28\xdc\xd4\x33\xe8\x15\xcc\x88\x87\x57\xea\x40\x93\xd1\x16\x2e\x20\xec\xc6\x11\x06\x5d\xb1\x4c\x60\x55\x2a\x02\x74\x1a\xfc\x3c\x1a\x4f\x57\x2f\xf6\xb4\x6b\x93\x27\xe1\x92\xa6\x69\x8c\xff\xe3\x91\xeb\xf8\x88\xe5\x24\x8a\xbb\xb2\x72\xa6\xd3\x75\x07\x7a\xde\xb7\x24\xf9\x8c\x8c\x31\x16\x8c\xd8\x86\x9e\xdf\x99\x48\x79\xe9\xf7\x86\x27\x92\x18\x47\x8f\xfb\x59\x2e\xf9\x3e\x12\x98\x7a\x3f\x24\xd1\xf0\x9d\x90\x89\x0e\x14\x52\xfd\x8e\xce\xfb\x2c\xcf\x71\x1d\x69\x1e\xb5\xa8\x1b\xcd\x22\xd5\xd1\xba\x7a\x68\xe4\xf4\xd3\x5b\x57\xef\x5d\xc9\x09\x28\x37\xb3\x9d\xaa\xf6\x44\x13\xf1\xfe\xdf\x59\x7c\x5e\xba\x6a\x6d\xe3\x8e\x79\xf6\xf2\xc8\xc8\x1f\x49\x8b\x2f\xd7\x5a\xce\xd3\x20\x19\x6b\x39\x7f\xe3\x72\xa5\xd6\x7d\x10\x8c\xdd\x55\x87\x19\x61\xff\xc6\x1f\x5e\xf0\xe0\xcb\x67\x44\x19\x3f\x72\xc0\x6b\x27\xb7\x12\x18\x0c\x54\xf8\x6b\xcc\x15\x4e\x60\xbf\xae\xec\xe3\x9b\x9f\x6f\x4f\x08\x76\x92\xec\x1c\xd1\x06\x7e\xae\xfc\xa7\xaf\x4b\x63\xee\x76\x7f\x6e\x0e\x24\x7c\xaf\x1a\xb5\x3e\x44\x4f\x52\xe2\x44\x1f\x1f\x72\x53\xf8\xea\x67\xb5\x9e\x97\xa8\xee\x8f\x97\xb1\xac\x32\x09\xdb\xff\x04\xee\xa7\xcb\x93\xbf\xc0\x7b\xc8\x55\x1e\xd5\x85\xc8\xb8\xef\x4f\x47\x44\x3f\x00\x6b\xdc\x6c\xae\x17\xc3\x41\x98\x06\x82\x77\x66\x20\xc4\x59\x93\x16\x38\x5b\xde\xdc\x36\xdd\x56\x63\xf7\x1a\xbb\x69\x67\x50\xb3\xbc\x7c\xeb\x8c\x91\xbd\x21\x8f\xa7\x26\x4a\xb2\x26\x8e\x8f\x63\x6f\x5b\xee\x39\x67\xa6\x35\xd6\xcd\x6e\x57\x56\x38\x0c\x12\xa7\xff\xec\x15\x29\x78\xd3\x33\x0d\x1e\x8d\x94\x7d\x34\xea\x7e\x84\x7b\xaf\x0e\xc3\x47\x8f\xaf\x5c\xad\xcb\xa5\x89\x28\xfd\xbc\x09\x40\x27\x72\x5f\x42\xde\xf4\xbc\xcf\xbd\x87\xd4\x87\x3a\x63\x45\x31\xc5\x21\x00\x17\x50\xe6\xe6\x45\xc4\xa8\xc1\xf6\x5f\x4a\x43\xf3\x1a\xbd\xb5\xf2\xdf\x15\x4e\x5a\x55\x7f\xd5\xa8\x60\x50\x93\x82\x63\xf0\xbf\x00\x00\x00\xff\xff\xf1\xb1\x58\x20\x61\x16\x00\x00"), - }, - "/src/syscall/syscall.go": &vfsgen۰CompressedFileInfo{ - name: "syscall.go", - modTime: time.Date(2018, 2, 27, 18, 42, 13, 0, time.UTC), - uncompressedSize: 1346, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x94\x41\x6f\xf3\x36\x0c\x86\xcf\xd6\xaf\x60\x8d\x01\xb1\xf1\xb9\x76\x7b\x0d\x90\x4b\x8b\xa1\xe8\x69\x05\xda\x61\x87\xae\x07\xd9\xa6\x1d\xa6\x0a\x65\x48\x74\x96\x6e\xc8\x7f\x1f\x64\x39\x6d\x92\x02\x1b\xf0\xdd\x0c\x8b\xa4\xf8\xf2\x79\xc5\xaa\x82\x1f\xf5\x48\xa6\x85\x8d\x57\x6a\xd0\xcd\xbb\xee\x11\xfc\x87\x6f\xb4\x31\x4a\xd1\x76\xb0\x4e\x20\x53\x49\x3a\xb2\xd7\x1d\xa6\x4a\x25\x69\x4f\xb2\x1e\xeb\xb2\xb1\xdb\xaa\xb7\xc3\x1a\xdd\xc6\x7f\x7d\x6c\x7c\xaa\x72\xa5\x76\xda\xc1\x5f\xda\x31\x71\xff\xe4\x88\x05\x5b\x58\x41\xa7\x8d\xc7\xe9\xc8\x10\xe3\xdd\xd8\x75\xe8\xe0\xf5\xad\xfe\x10\x54\xaa\x1b\xb9\x01\x62\x92\x2c\x87\x7f\x54\xb2\xf1\xe5\x83\xb1\xb5\x36\xe5\x33\x4a\x96\xfe\xd2\x99\xd1\xaf\xef\x2d\x7b\x6b\x30\x2d\x60\xe3\xcb\x47\x16\x74\xac\xcd\x6f\xf5\x06\x1b\xc9\x42\x7e\x4c\x4d\xa8\x03\x83\x9c\x7d\x5d\x92\xc3\xd5\x0a\x6e\xa6\xb3\x93\xc2\x0f\xa1\x70\x33\x97\xcc\xcb\x7b\x6d\x4c\x96\x1a\xdb\xa7\x05\x78\x71\xc4\xfd\x69\x85\x3c\xe4\x9e\xb4\xbd\x02\x26\xa3\x92\xe4\xa0\x92\x43\x9e\xab\xc3\x2c\x60\x08\x62\xff\x88\xc2\x63\x37\xd4\xc1\xd5\xc5\x24\x42\x1f\xff\xd3\x06\x3a\x67\x5d\x5a\x40\x3a\xa7\x2e\x03\x14\xc1\x2d\x04\x30\x1e\xd8\x0a\xe8\x9d\x26\xa3\x6b\x83\x05\x78\x44\x58\x8b\x0c\x7e\x59\x55\xff\x49\xa7\x36\xb6\xae\xb6\xda\x0b\xba\xaa\xb5\x4d\x35\x93\xf6\xe5\xb6\x4d\x73\x15\xc4\x7c\x83\x26\x6e\xc4\x73\x79\x2f\x76\xe6\x90\xd5\x33\xbd\x49\x68\x6f\x9f\xce\x4e\x61\xb9\x82\x0b\x95\x97\x21\xe1\x4e\xea\xe0\x5b\xe6\xd5\x94\xf9\x3b\xb7\xd8\x11\xcf\x03\xbb\x0c\x2a\x1f\x79\x67\xdf\x31\xfb\xee\x84\x7a\x82\xe5\x50\x46\xc7\x41\x93\x3a\xe7\xa6\x87\x01\xb9\x3d\x61\x5b\x40\x5d\x96\x65\xae\x92\xce\xba\xe8\x9f\xd0\x3a\x71\x8b\xfb\xbb\x0f\xc1\xb3\xc8\xc5\x9f\xbc\xc8\xa3\xc5\x08\x56\x2b\xb8\xbe\x8d\xae\xaa\x1d\xea\xf7\x68\x87\x9f\x74\xd8\xeb\x92\xde\xf2\x1c\xaa\x0a\x5a\xcb\x0b\x81\xd1\x63\x1c\xb7\xe1\x02\x3c\x71\x83\x40\x02\xad\xc5\x48\x1f\xf7\x51\x33\xfd\x8d\xb0\x1d\x8d\x50\xe0\x00\xcd\x5a\x3b\xdd\x08\x3a\xaf\x2e\xdc\x7a\x72\x11\xfd\xb8\x5d\xbe\x85\xc1\x1c\xa9\x8e\x1e\xb3\x01\xe2\x0b\x2f\x9f\x6c\x20\xef\x26\xa4\x55\x05\x6c\xaf\xed\xf0\x19\xf9\xeb\x9e\x24\x6b\x6c\x8b\x40\x2c\x53\xc8\x73\x74\x50\x86\x7b\x92\x17\xa7\x87\x02\x46\x62\x19\xc4\x4d\x61\x79\x01\x37\x05\xdc\x4c\xef\xa3\xaa\xbe\x66\x0a\xe4\xa1\xb1\x03\x61\x0b\x9d\xb3\x5b\x08\xcd\x7b\x38\xee\x1f\xb1\xa0\x77\x96\x5a\x88\xfb\x87\xb8\x0f\xd2\xb3\x38\x04\x59\x23\x38\xd4\xe6\xb8\xa5\x3e\xb3\xc2\x68\x78\x21\x79\x79\x5c\x25\x47\x7e\x7e\x76\x69\x01\x0d\x44\xb7\x12\x4b\xe8\x3d\xf0\xa6\x02\xea\x80\xdb\x69\x0e\x9b\xef\xb8\x3f\xea\x00\xb7\x89\x6c\xa3\x93\x80\xe6\xd7\xae\x8e\x3f\xae\x6f\xd5\x41\xfd\x1b\x00\x00\xff\xff\xa9\xfc\xcd\x86\x42\x05\x00\x00"), - }, - "/src/syscall/syscall_darwin.go": &vfsgen۰CompressedFileInfo{ - name: "syscall_darwin.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 435329179, time.UTC), - uncompressedSize: 844, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x90\x41\x6b\x32\x31\x10\x86\xcf\x9b\x5f\x31\xec\x29\xe1\x0b\x8a\x7e\xad\x37\x4f\xc5\x83\x27\x8b\x16\xda\x9e\x64\x36\x66\x35\xdb\x6c\x36\x4c\x66\x91\x52\xfc\xef\x45\x71\xeb\x41\x2d\x6d\xf1\xb2\x0c\x2f\x9b\xe7\x79\x67\xfa\x7d\xf8\x57\xb4\xce\xaf\xa0\x4a\x42\x44\x34\x6f\xb8\xb6\x90\xde\x93\x41\xef\x85\x70\x75\x6c\x88\x41\x8a\x2c\x5f\x3b\xde\xb4\x45\xcf\x34\x75\x7f\xdd\xc4\x8d\xa5\x2a\x9d\x86\x2a\xe5\x42\x09\x51\xb6\xc1\xc0\xfe\xf3\xf8\x20\xcb\xc3\x20\x95\x82\xd6\x05\x8e\x4c\xf0\x21\x32\x57\x42\x95\x7a\xd3\xc0\x96\x02\xfa\x59\x51\x59\xc3\xb2\x54\x30\x1e\x5f\xc8\xbd\x2b\xcc\x72\x4b\x8e\xed\x92\x09\xeb\xd8\x78\x17\xac\xda\x63\x32\xb2\xdc\x52\x80\xc5\xeb\x62\xf9\x3c\x9f\x3e\x4d\x44\xb6\x13\x5d\x78\xd4\xc9\xda\x85\x36\xcd\x82\x55\x62\x77\x6c\x76\x5c\x4b\x32\x61\xd4\x80\x03\x0d\x38\xd4\x80\xff\xbb\x27\x0a\x24\x0d\x34\xd0\xb0\x0b\x34\x58\x22\x98\x10\x85\xe6\xe0\xed\xb4\x57\x38\x67\xa6\xd1\xd9\x2f\x1a\xf0\x4e\x03\xde\x6b\xc0\xd1\x1f\xb5\xdf\x43\xcf\x3b\xbc\xdc\xa6\x44\xc4\xe0\x8c\xcc\xbf\xa8\xe0\x12\x84\x86\xc1\xd5\xd1\xdb\xda\x06\xb6\xab\xfc\x24\x27\xdc\x5e\xbb\xd2\x6f\xd7\x9e\x5f\x47\x5d\xf2\xdd\xf6\xe6\xf3\x1f\x72\xf7\x4d\x3e\x03\x00\x00\xff\xff\xb8\x46\xa3\x7f\x4c\x03\x00\x00"), - }, - "/src/syscall/syscall_linux.go": &vfsgen۰FileInfo{ - name: "syscall_linux.go", - modTime: time.Date(2018, 2, 27, 18, 42, 13, 0, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x73\x79\x73\x63\x61\x6c\x6c\x0a\x0a\x63\x6f\x6e\x73\x74\x20\x65\x78\x69\x74\x54\x72\x61\x70\x20\x3d\x20\x53\x59\x53\x5f\x45\x58\x49\x54\x5f\x47\x52\x4f\x55\x50\x0a"), - }, - "/src/syscall/syscall_nonlinux.go": &vfsgen۰FileInfo{ - name: "syscall_nonlinux.go", - modTime: time.Date(2018, 2, 27, 18, 42, 13, 0, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x2c\x21\x6c\x69\x6e\x75\x78\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x73\x79\x73\x63\x61\x6c\x6c\x0a\x0a\x63\x6f\x6e\x73\x74\x20\x65\x78\x69\x74\x54\x72\x61\x70\x20\x3d\x20\x53\x59\x53\x5f\x45\x58\x49\x54\x0a"), - }, - "/src/syscall/syscall_unix.go": &vfsgen۰CompressedFileInfo{ - name: "syscall_unix.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 435672650, time.UTC), - uncompressedSize: 3370, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xe4\x57\x4d\x6f\x1b\x37\x10\x3d\xef\xfe\x8a\x09\x51\x04\xdc\x88\x5d\x7d\xb4\x0d\x8a\xba\x3a\x38\xae\x6a\x08\x70\xed\x20\xb2\x9b\x16\x41\x60\x50\xda\x59\x99\xd2\x8a\x54\x49\xae\x1c\x21\xd1\x7f\x2f\xf8\xb1\x96\x2c\xe9\x50\x23\x41\x50\x20\x37\x61\xe7\xcd\xcc\xe3\x9b\x21\x67\xd4\x6e\x43\x6b\x5c\x8b\xaa\x80\x99\x61\xcf\xee\x85\x2c\xd4\xbd\x49\xd3\x25\x9f\xcc\xf9\x14\xc1\xac\xcd\x84\x57\x55\x9a\x8a\xc5\x52\x69\x0b\x34\x4d\x88\xae\xa5\x15\x0b\x24\x69\x42\x6a\x69\x78\x89\x24\x4d\x13\x32\x15\xf6\xae\x1e\xe7\x13\xb5\x68\x4f\xd5\xf2\x0e\xf5\xcc\x6c\x7f\xcc\x0c\x49\xb3\x34\x2d\x6b\x39\x81\xe8\x7e\x8b\x72\x65\x68\x06\xef\xde\x1b\xab\x85\x9c\xc2\xc7\x34\x59\x6a\x35\x41\x63\xe0\x97\x3e\xcc\x4c\x7e\x5e\xa9\x31\xaf\xf2\x73\xb4\x94\x44\x0b\xc9\xd2\x44\x94\xd0\xe0\xfa\x1e\x77\x23\x0b\x2c\x85\xc4\xc2\x85\x48\x34\xda\x5a\x4b\x90\xa2\x4a\x93\x4d\x9a\xcc\xcc\x40\xae\x5c\xc0\xe8\x13\xc2\xa1\x5c\xb9\x50\x28\x57\x73\x5c\x1f\xcb\x77\x35\x9e\xe1\xc4\x92\x2c\x3f\xe3\x55\x45\x89\x43\x11\x06\x3e\x58\xf0\xf3\x4e\x0b\x3e\x47\xda\x1c\x80\x41\x0c\x97\x5f\xa0\x9c\xda\x3b\x9a\x65\x69\x52\x2a\x0d\xc2\x41\x3b\x27\x20\xe0\xd7\x03\xc8\x09\x88\x56\xcb\xf3\x9e\xe3\xda\xe1\x1a\xc0\x50\x16\xf8\x81\x8a\x2c\x1f\xf9\xe0\x34\x4b\x13\x9f\xf6\x9d\x78\x0f\x7d\x70\xe0\x16\x90\x3e\x81\x56\x20\xe5\x59\xcf\x71\xbd\x8b\xdf\xa4\x8d\x18\xce\x31\xdd\x44\xfd\x0d\x5a\x94\xab\xdb\x09\x9d\x33\x58\x41\xe0\x9e\x7d\x59\xf5\x7d\xee\x43\xc1\xf3\x91\x23\xc9\x60\x95\x3d\x90\xa9\xe5\x96\xce\xd7\xe5\xf2\x1b\x56\x68\x91\xce\x3d\x97\x15\xd7\x4d\xab\xff\xa1\x8a\xba\x42\x78\x31\x33\x79\x68\x02\x6f\xe4\x95\x46\x5e\xac\xaf\xb5\xc0\xe2\x5a\x5d\x28\x5e\x40\x1f\x4a\x5e\x19\xf4\xe6\x85\x90\xb5\xb9\x92\x08\x7d\xf8\xbe\xdb\xe8\x1c\xe2\xbd\x5a\x5f\xf2\x05\x52\xc9\x17\xf8\x70\xc0\x6d\x70\x47\xb4\xc0\x12\x35\x38\x1f\x9a\x45\xe2\x13\xb5\x42\xed\x6b\xde\x6e\xc3\xb6\xa3\x41\x94\x10\x8d\x58\xa4\xc9\x86\x06\x11\x1e\x33\xef\xf7\x3d\xd4\x05\x12\xe5\x31\xe2\xce\xf2\xe8\x9a\x38\x85\x92\xa3\x27\xb4\xba\x46\x4f\xe8\x9f\x5a\x68\x3c\x52\x8d\x68\x71\xd5\x48\x3c\xb9\x00\x3c\x56\x8e\x64\xc9\xa5\x98\x50\xe2\xb1\x2e\xe3\x1e\xed\xc6\x39\x1f\xca\x95\x9a\x23\x25\xd1\x4e\x1e\xb5\xf2\x23\x27\xcf\xc1\x29\xbb\x6d\xa8\x51\xb0\x53\xab\xf9\x92\x01\xef\x32\xe0\x3d\x06\xfc\x07\xa8\x85\xb4\x4b\xab\x33\xa0\xba\xcb\x40\xf7\x9a\x0f\x0c\x50\x6b\x18\x68\x2d\x95\x57\x5f\x94\x50\xba\x83\x3e\x2e\x1f\x19\x35\x64\x4e\xa0\x84\x67\x5b\x89\xb5\xc3\x96\x0d\xe7\xfd\xac\xd9\xf6\x41\x8a\xe9\xa8\x8e\x57\xbb\x93\xe5\x43\x69\x69\x96\xb1\x03\x53\x77\x6b\xf2\xbc\x1e\x0c\xbd\xc6\xe0\x15\x11\x25\xb8\x7c\x4e\xec\xd1\xdf\xa3\xdb\xb7\x6f\x86\xd7\x03\x78\xfe\x1c\x28\xef\xba\x6f\x5d\xf8\xf4\x09\xc2\xcf\x5e\xe8\x2b\xae\x35\x5f\xc7\x22\x0e\xa5\x45\x2d\x79\x15\xda\x90\xf2\x9e\xa3\x6a\x2a\x31\xc1\x9d\x87\x6d\xbc\xb6\xc8\xc0\xbb\xed\x3e\x6a\xc9\xa1\xbf\xf7\x0c\x17\x9c\x7c\xe7\x1d\x48\x74\x74\xf8\xa5\x16\xd2\x5e\xab\x33\x25\x8d\xaa\x30\x82\x0f\xa5\xd9\x4b\xc4\xa0\xc3\xa0\xb3\x7f\x54\xfc\x20\xec\xb5\xfb\xed\xd5\x0f\xb3\x24\x3f\x57\xee\x73\x7c\xf4\x7c\xb6\xb7\x5c\xcb\xf8\x0e\xee\x65\x69\xee\x6a\x88\x3f\x38\x3d\x3b\x1b\x8c\xf6\xdb\xe7\xe5\x41\x25\x19\xf0\x1f\x19\xf0\x9f\x18\xf0\x97\x5f\xa8\x97\x5e\x3e\xb1\x99\x76\x29\x7c\x95\xc6\x7a\xd6\x87\x5e\xa7\x07\x1f\xa1\xdd\x86\x39\x6a\x99\x2b\xa3\xb1\x42\x6e\x10\x94\x84\xab\x11\xfc\xc5\xe0\x8e\x2f\x97\x28\x0d\x08\x09\x42\x0a\x0b\xaa\x04\xa2\x0c\x81\xb8\x40\x34\xc5\xdf\x29\xc7\xe6\x69\x15\x79\xc3\xef\xbf\xa1\x3b\xfd\x39\xbd\xab\x1f\x94\xba\x54\x03\xad\x95\xfe\xef\x82\xfd\xef\x54\x7a\xaa\x18\x47\xda\xe5\xdb\xbe\xc3\x9f\xd3\x48\xaf\xd6\x16\x5f\x5b\xfd\xbb\x56\x8b\xb8\x4d\x9a\x87\xd5\x85\xbe\x08\x43\x01\x5d\x83\x79\x81\x76\xa7\xca\xee\x6a\x70\x23\xa4\xfd\xf9\xd4\x8f\x82\x2c\xbf\xc4\x7b\x5a\xa1\xa4\x26\x83\x16\x74\x9b\xc5\x98\xc1\xd8\x39\x6a\x2e\xa7\x08\x61\xdc\x38\x44\x5c\x5d\xc6\xee\xb9\xef\xec\xaf\x2b\x0c\x06\xc3\xcb\x3f\x4f\x2f\x9a\xb5\xc5\xcf\x8c\x11\xda\xb8\x30\x33\x18\x07\x01\xf6\x0c\x21\x39\x83\xce\x56\x8b\x70\x94\x8c\x86\x3f\x31\xf9\x6b\x25\xdc\x4c\x8b\x53\xe8\xc6\x7f\xa4\x99\xd3\xd9\x2d\x49\x9b\xf4\xdf\x00\x00\x00\xff\xff\x77\x4c\x60\x3e\x2a\x0d\x00\x00"), - }, - "/src/syscall/syscall_windows.go": &vfsgen۰CompressedFileInfo{ - name: "syscall_windows.go", - modTime: time.Date(2019, 3, 29, 2, 9, 7, 436090736, time.UTC), - uncompressedSize: 2566, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x96\xdf\x6f\xdb\x36\x10\xc7\x9f\xad\xbf\xe2\xe0\x87\x81\xf4\xb8\x38\x72\x97\xcc\x29\xe0\x87\x20\x76\xd3\x01\xee\x52\x24\x2e\x0a\xac\x28\x0a\x4a\x3c\xc9\x6c\x29\x52\x20\x29\xa3\x4e\x9a\xff\x7d\xa0\x7e\xd8\xca\x92\x02\x1b\x1a\x18\x7d\x91\x45\xde\x97\x77\x9f\xfb\x21\xc2\xe3\x31\xfc\x9a\x54\x52\x09\xf8\xec\xa2\xa8\xe4\xe9\x17\x9e\x23\xb8\xad\x4b\xb9\x52\x51\x24\x8b\xd2\x58\x0f\x43\x5b\x69\x2f\x0b\x1c\x46\xd1\x86\x5b\x28\xa4\xae\xdc\x95\x46\x98\xc1\x6f\x71\x14\x65\x95\x4e\xe1\xa6\x39\x42\xbc\xe5\x25\x03\xcd\x6d\xee\x18\xf0\x98\x01\x9f\x30\xe0\x2f\xa0\x92\xda\x97\xde\x52\x20\x36\x66\x60\x27\xdd\x06\x03\xb4\x16\x16\xd6\x6a\x43\xe1\x2e\x1a\x94\x56\x6a\xff\x9e\x5b\x2d\x75\x4e\x68\x34\xb0\xe8\x2b\xab\x3b\x35\xe9\x42\x53\x06\xc7\x0c\x16\xe7\x17\x17\x8b\x9b\xe8\xfe\x21\xc3\xe9\xf7\x20\x18\xf0\xdf\x19\xf0\x13\x06\xfc\xf4\x90\x40\x67\xff\x05\x88\x01\xff\x83\x01\x9f\x32\xe0\x67\x87\x84\x8b\x27\xff\x97\x2e\x68\x8e\xc3\x23\x28\xe3\xc9\x41\x61\x4f\x7e\x10\x36\x3c\x82\x36\x0e\xe2\xf8\xe4\xa0\xec\xd3\xe7\x65\x0f\x8f\x20\x8f\x83\x3e\x9e\x1e\x24\x15\x65\xb8\x50\x32\xb1\xdc\x6e\x49\x26\x15\x6a\x5e\x20\x8c\xc2\xd1\xf8\x94\x02\x59\x73\x2d\x14\xfe\x78\xe0\x7f\x45\xcd\xd1\x97\xd6\xa4\x5c\x08\x8b\xce\x3d\x8a\x12\x6c\x7b\x90\x29\x05\x12\x76\x9e\x9d\x82\x08\x18\x2d\xf9\xed\x76\xbe\x5c\x52\x58\x1a\x2e\x08\x0d\xae\x8d\x0d\x5e\x5b\x2f\xbf\xcc\x97\xcb\x45\xd8\xbb\x7b\xe3\xf2\x97\x30\x74\x5b\xe7\xb1\x80\xd0\x7e\x07\xda\x78\xe0\x1b\x2e\x15\x4f\x14\x32\x70\x88\xb0\xf6\xbe\x74\x2f\xc7\xe3\x5c\xfa\x75\x95\x1c\xa5\xa6\x18\xe7\xa6\x5c\xa3\xfd\xec\xf6\x2f\x89\x32\xc9\xb8\xe0\xce\xa3\x1d\x0b\x93\x8e\xdb\xdb\xd9\x1d\x15\x62\x78\xbf\xc7\x2b\x1b\xbc\xb7\xd6\xa4\x14\x5e\x49\xfd\x93\xf1\xe5\xe8\x6f\xbc\x78\x5d\xf7\x8e\xac\x41\x6a\x4f\x81\x64\x02\x9a\x9d\xba\x35\x32\x83\x35\xcc\x66\x70\xb3\x9a\x7f\xba\x7a\xb7\x7a\xfb\x6e\xf5\xe9\xf5\xf9\x5f\xf3\xe5\x22\x18\xbb\x14\xe2\x68\x70\xff\x50\xba\xb8\xbe\xbe\xba\x7e\x42\x39\xa9\x95\xed\xe2\x78\x07\x72\x89\xfe\xc2\x68\x67\x14\xbe\x31\x02\x49\xda\xbc\xb7\x1c\x0c\x0a\x23\xda\x49\x7a\x31\xa1\x40\xc2\xf0\xd4\x55\xa4\xbd\x32\xce\xab\xa2\xd8\x36\x75\xdc\x27\xf8\xde\x4a\x8f\xaf\x64\xc8\xae\x19\xd0\xce\x63\x52\x65\xf0\xe1\x63\xb2\xf5\xc8\x40\x18\xbd\xf3\xce\xc0\x6c\xd0\x2a\x5e\x96\x28\x60\x74\xb5\x7b\x7f\x14\x35\x24\xdb\xb8\x9c\xcd\x20\x86\x6f\xdf\x7a\xcb\x49\x9d\x71\x3d\xd4\x2b\xd3\xe6\x45\x92\x2a\xa3\xd1\x60\x30\xaa\xa3\xcd\xa0\x09\x47\x14\xea\xda\x42\xf7\x25\xd2\x52\xd5\x45\xfa\xce\x47\x11\xcc\x5d\x7a\x8b\xaf\xd2\x87\xd9\x0a\x5f\x20\x7e\x95\x3e\x0d\x75\xea\xca\x14\x4a\xd3\xfc\x45\x38\xba\x34\xc1\x4a\xe8\xc3\x7a\x17\x05\xd7\x62\x29\x35\x12\x0a\x24\x2d\xc4\xfe\xd2\xd8\x55\x75\x77\xa0\xa7\x5e\x99\x73\x9b\x6f\xfa\x07\x18\x70\x9b\xa7\x30\xea\xfa\xc3\x6d\xbe\x81\xd1\x87\x69\x7c\x36\xf9\xd8\xfe\x74\xc2\x27\x5b\xa7\xa5\x62\x4f\xf7\xef\x12\x3d\xea\x0d\xf9\x82\x5b\x70\xde\x4a\x9d\x53\x20\x1b\xae\x2a\x6c\x97\x0c\x32\x53\x69\x01\x89\x31\xaa\xef\x71\x38\x64\x90\x71\xe5\xb0\xef\x69\x25\x0b\xfc\xdb\x68\xfc\x53\x67\xc6\x16\xdc\x4b\xa3\x89\xbf\x95\x30\x0a\x86\x5b\xa3\x51\xee\x0d\xe1\xc6\x4e\xa1\x9b\x89\x27\xa9\x8f\x1f\x33\xfb\x6d\x89\xbd\xcd\x00\x59\xa5\xfe\x6e\x77\x1d\xf4\x8d\x14\xea\x1f\x42\xdb\x54\x1e\xd0\x47\xf7\xd1\x3f\x01\x00\x00\xff\xff\x47\x14\x60\x60\x06\x0a\x00\x00"), - }, - "/src/testing": &vfsgen۰DirInfo{ - name: "testing", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 558121153, time.UTC), - }, - "/src/testing/example.go": &vfsgen۰CompressedFileInfo{ - name: "example.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 1424, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x54\x5d\x6b\xf3\x36\x14\xbe\x96\x7e\xc5\xa9\x20\x45\x5a\x5d\x85\xdd\x06\x7c\x51\xb6\x06\x0a\xa5\x2b\xcd\x7a\x57\x18\xaa\x73\xec\x6a\xb5\x25\x23\xc9\x49\xc7\x9a\xff\x3e\x74\xec\x7c\x8e\xf7\xe6\xbd\x09\x91\x2c\x3d\xe7\xf9\x38\x47\xf3\x39\xdc\xbc\x0f\xb6\x5d\xc3\xdf\x91\xf3\xde\x54\x9f\xa6\x41\x48\x18\x93\x75\x0d\xe7\xb6\xeb\x7d\x48\x20\x39\x13\x75\x97\x04\x67\xc2\xc7\xfc\x1b\x53\xb0\xae\xa1\xbf\xc9\x76\x28\xb8\xe2\xbc\x1e\x5c\x05\x61\x70\xf7\x5f\xa6\xeb\x5b\x94\xd8\xc0\x83\x4b\x18\x9c\x69\xa7\x2d\x05\xd2\x7f\xc2\xbb\xf7\xad\x82\x7f\x39\xb3\x35\xfc\x52\x7d\x98\x94\xfe\xc9\x2b\x56\x77\x49\x3f\x07\xeb\x52\x2d\x45\x59\x96\xf0\xf2\xfa\x04\x00\xb3\xf8\xe6\x44\x01\xd8\xe8\x27\xd3\xa1\xe2\x6c\xc7\x39\x9b\xcf\xe1\x37\xd3\xa7\x21\x20\xc4\xb4\xf6\x43\xd2\x9c\x8d\x7f\x60\x51\x82\x8f\x7a\x45\x0b\xce\xb6\x05\x60\x08\x79\x33\x61\xd7\x2f\x6d\x8b\x52\x68\x01\x37\x7b\x3c\xb8\x01\xa1\x27\x08\xa1\x88\x52\x3e\x7f\x55\x82\xb3\xed\x81\xd5\xb2\xcf\xb4\x5a\x27\x47\x64\x0c\x81\x60\x15\x67\xcc\x47\x7d\xff\x65\x93\xfc\x95\x98\xb1\x43\x69\x28\x61\xcb\x33\x29\x13\x88\x53\x76\x49\x3f\xf9\xad\x54\x9c\xf9\x4f\x28\x21\x85\x01\x27\x25\x2d\x1a\x07\x43\x0f\xd6\x81\x81\x35\xd6\x18\x02\xae\xa1\x32\x6d\x0b\xd1\xc3\x16\xa1\x32\x0e\x02\x56\x7e\x83\x01\x6c\x0d\xe9\x03\x01\x47\x47\xa1\x37\xce\x56\x51\x73\x46\xf7\x20\x67\x20\xc9\x5c\xb6\x8e\x89\x84\xd7\x5d\xfa\x7d\x08\x26\x59\xef\xe4\x91\x85\x5e\x0d\xef\x92\xd8\x29\xc5\x39\x1b\x79\xf8\x88\x50\xdb\x16\x0b\x08\x18\x93\x3f\xb8\x5b\x40\x83\x09\xfc\x90\x7a\x72\x9a\x6d\x35\x9d\x95\x93\x01\x07\xc5\x71\x72\x9d\xd1\x9d\x80\x66\x9d\x1d\xbf\x1f\x03\xd8\x2f\xe5\x96\x9c\x97\x2a\xdf\xfe\x0b\x28\xae\x17\xec\xfc\xe6\xfc\x8b\xad\xcf\x00\x4e\x12\x39\x89\xa4\x3e\x4d\x44\x4c\x5d\xbb\xa0\x8b\xd6\x35\x13\x1f\x92\xb4\x80\xd9\x86\x1a\xe9\x04\x34\x97\x39\x0b\x90\x7a\x8b\x6d\x4c\x80\xda\xd8\x16\xc6\x26\xe7\x8c\xe1\x5e\x01\x45\x40\xb2\x1b\x4f\xb1\x4e\x73\xa0\xff\x0c\xb6\x5b\xf5\xa6\x42\xe9\x87\x94\xbf\x6f\x8d\xfb\xc1\x01\x6c\xf4\x1f\xe4\xe4\xa4\x12\x1b\xfd\xea\x7c\x58\x63\x0e\x9d\xf4\xd9\x1a\xa2\x0f\xe9\xd1\x3a\x8c\xb2\xf1\x49\x65\xf5\xc7\x9d\x0c\xad\xe0\xfa\x9a\x3a\xb5\x3c\xf1\x85\x11\x6b\x4a\x5c\xaf\x26\x7f\x44\xe3\xd3\xe2\xcd\xe5\x29\x22\x4a\x72\xd8\xd7\x52\xd3\xb6\x28\x80\xe2\x3a\xe3\x95\x7b\x99\xed\x00\xdb\x88\x07\x4e\x59\xf2\x55\x09\x04\xf3\x73\xd5\x8f\x15\x1b\x9f\x0a\x42\x3a\x16\x1b\xdd\x20\x90\xab\x12\x84\x80\xef\xef\xcb\x59\x3c\x7b\x22\x6e\x6f\x6f\x61\x79\xf7\xf0\xb8\x80\x59\x04\x39\x8b\x2a\x83\x1f\x5f\x8a\x02\xf2\x00\x14\x04\x38\x06\x9d\xa7\xae\x36\x6d\xc4\xa3\xb4\x8b\x17\xe8\x7f\xf8\xcf\x77\xab\xd5\x09\xfe\x25\xba\x3a\xf2\xbe\x64\x4a\x73\x29\xa7\x47\x62\xc7\xd9\x4e\xaa\x71\xda\x5f\x06\xb7\x1f\x5e\xcd\x19\x36\x7a\x99\xfb\x29\x60\x1a\x82\xe3\x3b\xfe\x5f\x00\x00\x00\xff\xff\x6d\xa8\x39\x72\x90\x05\x00\x00"), - }, - "/src/testing/ioutil.go": &vfsgen۰CompressedFileInfo{ - name: "ioutil.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - uncompressedSize: 1163, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x53\x61\x6f\x23\x35\x10\xfd\x6c\xff\x8a\x21\x12\xc8\xbe\x8d\x36\xbb\x69\x2f\x52\x7b\x04\xe9\xc8\x05\x74\x52\x29\x28\x6d\x05\x12\x42\x95\xb3\x3b\x2e\x43\x37\xf6\xca\xf6\x96\x44\xd0\xff\x8e\x6c\x6f\xb7\x94\x4f\x7c\x48\x76\x3c\x9e\x7d\xf3\xe6\xcd\xdb\xc5\x02\x8a\xfd\x40\x5d\x0b\x7f\x78\xce\x7b\xd5\x3c\xaa\x07\x84\x80\x3e\x90\x79\xe0\x9c\x0e\xbd\x75\x01\x04\x67\xb3\xfd\x29\xa0\x9f\x71\x36\x23\x1b\xff\x6d\x8a\x7d\x70\x8d\x35\x4f\x29\x3c\x99\x26\x3e\x03\x1d\x70\xc6\x25\xe7\x4f\xca\x81\x53\xa6\x85\x81\x4c\x38\x5b\x4e\xe7\xc3\x00\xb1\xb6\xfc\x61\x08\x78\xe4\x5c\x0f\xa6\x01\x87\x1e\xb1\x15\x72\xac\x85\xbf\x38\x73\x18\x06\x67\xc6\x84\x88\xa8\xe5\xb5\xfd\x53\xc8\xf2\xce\xd0\xf1\x5a\x19\x2b\x24\x14\x40\x26\xac\xce\x85\xf5\xe5\xf7\x18\x7a\x6a\x85\x94\x92\x3f\x8f\xa0\x06\x8f\xe1\x66\xd0\x9a\x8e\x42\x82\x0f\x8e\xcc\x43\x02\x4e\x1c\xca\x2b\xdb\x3c\x0a\xc9\x99\x83\xcb\x75\xe2\xc5\x19\x69\x70\xb0\x5e\x43\x15\xcb\x98\x83\xf5\xc4\x8b\xb3\x67\x9e\x13\xef\xea\xd5\xea\xfc\xfd\xf2\x3d\x14\x50\x57\xf5\xd9\x45\x75\xbe\x5c\x9e\xc1\x62\x01\x8d\x35\x3e\x28\x13\x3c\x68\x67\x0f\x70\x3d\x1c\xd0\x51\xa3\x3a\xd8\x61\x43\x3d\xfa\xdc\x38\x42\x4c\x14\xee\x4c\xf7\x42\x22\x0f\x3b\xca\x59\x7e\x0e\x56\x09\x32\x41\xd4\x78\x01\x05\xb8\x2f\x6b\xbc\x90\xf2\xd7\xfa\xf2\xb7\x38\xdc\x62\x01\x1f\x21\x4e\x18\xc8\x1a\xd5\x41\x63\xfb\x13\x58\x0d\x64\x87\x40\x5d\x79\x8b\x87\xfe\x3b\xea\x70\x0e\xc1\x82\x7a\xb2\xd4\x02\x1e\x83\x53\x90\x97\xe9\xcb\xac\x4e\x18\xcb\x44\xef\x50\xd3\x71\x14\x48\x82\xd0\xf0\xce\xfa\x32\x23\xa0\x73\xf1\x67\x9d\x8c\x92\xb4\x94\xc4\xb2\x3e\xf5\xf8\x44\x4e\x48\xce\x99\x69\xac\xd1\x1d\x35\x21\xde\x55\x9c\x69\xeb\x80\x52\xfc\x01\x08\xbe\x86\xba\xaa\xaa\x18\x16\x45\x92\xd5\xa8\x03\xc6\xdb\x08\x56\x8c\x5d\xe3\x02\x7f\x52\xe1\xf7\x1b\xec\x95\x53\x21\xb6\x2b\x60\xe4\x55\xbc\xd9\x23\x67\x4c\x67\x5a\x89\xc7\x8f\x3d\x9a\x34\x44\x44\x9d\xa7\xcc\xfd\xee\xd3\xcf\xbb\xbf\x53\xb4\xd9\x6d\x3f\xde\x6e\x73\xbc\xfd\x65\x73\x35\x87\x6a\x55\x55\x11\x83\x74\xac\xfd\xec\xb7\x47\xf2\x41\xa0\xcb\xf3\xa5\xfc\x34\x4e\x51\x7c\x78\x3d\xc0\x37\x50\x67\x5b\xb0\xff\x1a\x68\xcc\xbc\x71\xcb\x6b\xd5\xeb\x8e\x59\xf4\x10\x63\x8d\x35\x81\xcc\x80\x3c\x9f\xf7\x0e\xd5\x63\xb6\x57\xf2\xc0\xe4\x5e\x87\xaa\x4d\xa3\x69\xea\x30\x89\x36\x6d\x28\x07\xf3\x7f\x6d\x66\xd4\xe4\x72\x12\x65\x7a\x4b\x26\x5b\xc7\xcb\x2f\xd6\x60\xa8\xcb\xd6\xce\x76\x9b\xcd\xd2\x6b\xa9\x7b\x8b\x1a\x1d\xe8\x72\xd3\x59\x8f\x91\x6e\xfc\x5c\xf7\x83\x86\xf4\xdd\x97\xdf\x0e\x5a\xa3\xe3\xec\xfe\x45\x7c\xb2\xe5\xc6\xf6\x27\xf1\xd5\x7e\xd0\x73\xd0\xff\xb7\xcd\x98\xda\x0f\xba\xbc\xc9\xab\x97\xf3\x58\xcf\x9f\xf9\x3f\x01\x00\x00\xff\xff\x2f\x92\x73\x9b\x8b\x04\x00\x00"), - }, - "/src/testing/testing.go": &vfsgen۰CompressedFileInfo{ - name: "testing.go", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 558360921, time.UTC), - uncompressedSize: 642, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x91\x4f\x6f\xd3\x40\x10\xc5\xcf\xde\x4f\xf1\xc8\x85\x16\x2c\xfb\x1e\x01\x17\x50\xf9\x23\x44\x0f\x6d\xcf\x68\x63\x8f\xe3\xc1\xeb\x59\xb3\x33\x4b\x04\x55\xbe\x3b\xda\xa4\x11\x21\xea\x79\xe7\xf7\x7b\x6f\x66\xdb\x16\xaf\x37\x99\x43\x8f\x1f\xea\xdc\xe2\xbb\xc9\x6f\x09\x46\x6a\x2c\x5b\xe7\x78\x5e\x62\x32\xac\x52\x16\xe3\x99\x56\xce\xb5\x2d\xee\x47\x42\x5e\xd4\x12\xf9\x19\x9d\x0f\x81\xd2\x37\x3f\x13\xbc\xf4\x18\x92\x9f\xe9\x6e\xe2\x05\x89\xc2\x6f\x44\xc1\x13\xda\xbc\x3f\x0c\x6a\x5d\x0c\x65\x72\xf1\xc2\x1d\x78\x80\x8d\x94\x08\x3e\x11\xfe\x50\x8a\x4f\x42\xc5\x10\xb3\xf4\x0d\x3e\xc5\x1d\xfd\xa2\x54\x5f\x7a\x8a\x86\x15\x12\x0d\x3c\x2f\x81\x66\x12\xa3\x1e\x43\x4c\xf8\x18\x97\x91\xd2\x97\x3b\x78\x83\x8d\xac\x28\x5c\x0d\x8d\xd8\x11\x3a\x2f\x2f\x0d\x59\xa9\x08\x6c\xf4\x67\xb8\x37\x8e\xd2\xe0\x41\xa9\x74\x52\x82\x5a\xde\x28\x58\xd4\xc8\xf7\x8d\x1b\xb2\x74\x67\xfb\x5e\x69\x59\x93\xc5\xae\xa1\x96\x58\xb6\x78\x74\x55\xdb\xe2\xe1\x99\xd3\x24\xfa\x99\x39\x91\xc2\xa3\x58\x4a\x90\x0f\x97\x2b\x35\x07\xfc\xfe\xf6\xc3\xed\x1a\x9f\x4f\xa5\xca\x85\x96\xa8\xca\x9b\x40\x8d\xab\x12\x59\x4e\x82\xd5\x9b\x2c\x93\xc4\x9d\xbc\x5b\xb9\xbd\x3b\x36\xbb\x7a\xd5\xc5\x79\x8e\x72\xfd\xef\x13\xce\x2a\x9e\xb2\x6e\xca\x5b\x69\xfa\xbd\xc6\xc0\x81\x6a\x04\x16\xaa\x11\x27\xac\xdf\x5e\x34\x3a\xe0\xd7\xae\xe2\x01\x2f\xe2\x54\xa0\x53\xfe\x7f\xb6\xc7\xbd\xab\xf6\xee\xf9\x27\x57\x55\x37\x1c\x68\x7d\xcc\x72\x55\xf5\x95\x85\xd6\xc7\xcc\x42\xed\xdd\xdf\x00\x00\x00\xff\xff\x1b\x9f\xb2\xfc\x82\x02\x00\x00"), - }, - "/src/text": &vfsgen۰DirInfo{ - name: "text", - modTime: time.Date(2018, 4, 20, 9, 17, 51, 715639756, time.UTC), - }, - "/src/text/template": &vfsgen۰DirInfo{ - name: "template", - modTime: time.Date(2018, 4, 20, 10, 35, 24, 780257322, time.UTC), - }, - "/src/text/template/template.go": &vfsgen۰FileInfo{ - name: "template.go", - modTime: time.Date(2017, 10, 12, 19, 45, 13, 0, time.UTC), - content: []byte("\x2f\x2f\x20\x2b\x62\x75\x69\x6c\x64\x20\x6a\x73\x0a\x0a\x70\x61\x63\x6b\x61\x67\x65\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x0a\x0a\x63\x6f\x6e\x73\x74\x20\x6d\x61\x78\x45\x78\x65\x63\x44\x65\x70\x74\x68\x20\x3d\x20\x33\x30\x30\x30\x0a"), - }, - "/src/time": &vfsgen۰DirInfo{ - name: "time", - modTime: time.Date(2019, 4, 14, 20, 40, 36, 659127630, time.UTC), - }, - "/src/time/time.go": &vfsgen۰CompressedFileInfo{ - name: "time.go", - modTime: time.Date(2018, 2, 27, 18, 42, 13, 0, time.UTC), - uncompressedSize: 2155, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x55\xdf\x6f\xdb\x36\x10\x7e\x26\xff\x8a\x9b\xb0\x21\x64\xa3\x48\xf9\x51\x64\x58\x10\x0f\xd8\x92\x35\x08\xd0\xd4\xc0\x92\xbe\xac\x28\x06\x9a\x3a\xd9\x74\x64\x52\x20\xa9\x38\x8e\xeb\xff\x7d\x20\x29\x2b\x76\xbb\x15\x98\x9e\xc4\xe3\xf1\xee\xfb\x3e\x1e\xef\xca\x12\x0e\x27\x9d\x6a\x2a\x98\x3b\x4a\x5b\x21\x1f\xc5\x14\xc1\xab\x05\x52\xaa\x16\xad\xb1\x1e\x18\x25\x99\xed\x74\xb0\x65\x94\x92\x6c\xaa\xfc\xac\x9b\x14\xd2\x2c\xca\xa9\x69\x67\x68\xe7\xee\xf5\x67\xee\x32\xca\x29\x2d\x4b\xb8\x13\x8f\x08\xae\xb3\x29\x5a\xf1\x51\xab\x67\xa8\x3b\x2d\x41\xe8\x2a\x99\x1e\xd4\x02\xc1\x79\xdb\x49\x0f\xca\x83\x45\xdf\x59\xed\x40\x58\x04\xd1\x2c\xc5\xca\x81\xd2\xb2\xe9\x2a\xac\x60\xa9\xfc\x0c\xfc\x4c\x39\xd8\x42\x64\x15\xba\x56\x79\x84\xeb\xab\x3f\x78\x1e\x12\x4e\x50\x8a\xce\x21\xf8\x19\xae\x0e\x2c\x82\x46\x0c\x47\x6b\x63\x41\x69\x8f\x56\x8b\x46\xbd\x08\xaf\x8c\x2e\xf1\x79\x6f\x0d\xa6\x7e\x45\x54\x5e\x0b\x8f\x05\xdc\x23\x82\x72\xae\x43\x98\x79\xdf\xba\x8b\xb2\xfc\x2e\xef\xe8\xea\xca\xd3\x9f\x7f\x29\x68\x64\xa9\xb4\xf2\x8c\xc3\x9a\x92\xb2\x04\xf1\x64\x54\x05\x15\x8a\x0a\xa4\xa9\x10\xb0\x51\x0b\xa5\x63\x6e\x4a\x9e\x84\x85\xbf\x21\x8a\x31\x82\x20\x13\x3b\xce\xe1\x98\xd3\x0d\xa5\x7e\xd5\x22\xf4\xda\x07\x07\xbb\x95\x6b\x4d\x89\x82\xf4\x29\xed\xcf\x4e\x29\x59\xce\x50\xf7\xcb\xf3\xb7\x94\xb4\x68\x95\xa9\x86\x65\xdd\x3b\x07\x68\x2c\xaa\x51\x0b\x89\xeb\x4d\x0e\x9d\xd2\xbe\xf5\x96\x53\x22\xec\x74\x1b\x70\xbb\x4d\x49\xc8\x6c\x3a\x0f\x6f\xe6\xae\x18\x4f\xe6\x28\x3d\x25\x42\x7a\xf5\x84\x00\x13\x63\x9a\x80\x72\xe0\xfb\xde\x48\xd1\x24\xd2\x15\x5c\x8c\x60\xee\x8a\x9b\xc6\x4c\x44\x53\xdc\xa0\x67\x59\x10\x36\xe3\xc5\x07\x5c\x32\x4e\x89\x0b\x1e\x55\x71\xef\xad\xd2\xd3\x60\x50\xc1\xa0\x74\x85\xcf\xbf\xaf\x3c\x32\x97\xc3\x01\x3b\xe0\x94\xcc\xbf\xb5\xf3\x60\x57\x35\x28\x18\x8d\xe0\xe8\x04\xbe\x7c\x81\x79\xff\xbb\xa6\x84\x34\x01\xc7\x7b\x23\x0b\x2d\xa2\xa8\xd9\xc7\x87\xab\x8c\x12\x92\x2a\x8c\x92\x0d\xfd\xc6\xc5\x7d\x52\x87\x27\x70\x01\xf3\xcf\x3b\x7b\x2f\x46\x87\xbd\x4f\x9f\xc3\xcf\x7a\xbd\x77\x26\x87\xaa\xb8\x12\x4d\xc3\xb2\x29\xfa\x70\x37\xc1\x67\x5c\xd7\x0e\x7d\xc6\x8b\x5b\x1d\x2e\xff\x0d\x1c\x9d\x1f\xe7\x50\x8b\xc6\xe1\x66\x33\x48\xd5\x5f\xe8\x07\xa1\x0d\xe3\xe9\x86\x02\xec\x84\xee\x7b\xa2\xed\x27\x4c\x69\xce\xdf\xc6\x44\x31\x0a\xbb\x53\x4d\xa3\x1c\x4a\xa3\x2b\x3e\xa4\xd3\x66\xc9\x38\x30\x87\x32\x79\xe5\xa0\xfb\xff\xb3\xd3\x1c\x16\x46\x9b\x64\x8f\xf7\xa6\x83\xd8\x7b\x00\x07\x60\x1a\xca\x3e\xcd\x7d\xca\x90\xa7\x18\x4c\xc3\x4f\xfb\x1b\x3c\x07\x3d\xa4\xbf\x6f\x10\x5b\x56\xc1\x75\x67\x63\xc1\xc7\x34\x32\xa4\x59\x88\x47\x64\x72\x26\x74\x5f\xd5\xeb\x4d\xb8\xed\x81\x7e\x22\xfb\xa3\x4b\x6c\x4d\xe7\xb3\x3c\x88\x73\xdb\xbf\xe5\x54\x8d\x2c\x56\x34\x87\x35\xc8\xc6\x38\x64\x92\xc3\x26\x01\x63\x55\xb9\x2b\x07\xa7\xe4\xf2\x48\x0e\xa8\x9c\x17\x36\xc6\xb5\xcc\xc3\x9b\xdd\x27\x16\xf1\xf9\xa2\x2f\xf2\x11\x78\xdb\x21\x25\x95\xaa\xeb\x80\x99\xf9\x22\xbe\xb4\xa3\x7d\x91\xf8\xa0\xcd\xde\x15\x84\x1a\x8d\x27\x7f\x85\x93\xcb\xcb\xb3\x93\x50\x9f\x50\x96\xb0\x10\x7e\x56\xdc\x89\xe7\xdb\xf4\x76\x77\x0b\x73\x7b\xe2\x12\x8e\x63\x2d\xc7\xc5\x08\x8e\xe3\xa6\x2f\xb6\xef\x71\xf7\x71\xfd\x3f\xa1\x28\xd9\x65\x17\x6b\x93\x92\x90\xd6\x17\x7d\xd3\xf8\x61\xd4\xe7\x26\x3d\xd9\xc3\xd1\xb0\x19\xac\xbb\xda\x71\x4a\x02\x30\x32\x35\xe0\x8b\x9a\xf9\x42\xd8\x69\xec\x5e\x24\x5c\x43\x00\x7f\x78\xc2\x77\x54\x37\xed\x7f\x88\x1e\x9a\x49\x48\xfa\x35\x2d\xd9\xa0\xb0\xaf\xbc\x06\x05\x38\x25\x4b\xe1\x7e\x4b\x3c\x2e\x02\xc0\xc4\x89\xfe\x0b\xbb\xbe\x80\x07\xff\x01\x4f\x6d\xac\xc4\xbf\x54\xfb\x4e\x35\xf8\xce\xd8\x07\x74\x3e\x34\xa3\x17\xd5\x8e\x75\xb3\x8a\x98\x82\x62\x1b\x4a\x43\x93\x0e\x2f\xfc\xde\x74\x56\xa2\x8b\x5d\xc1\xc5\xd6\x15\x5e\x6e\x62\x52\xdc\x8c\xff\x1c\x8f\x1f\x18\x87\x43\xc8\xca\x46\x4d\xca\x60\x2d\xc3\x31\xa5\x6b\x53\xbc\xa8\x36\xcb\x43\xb0\xb2\x7c\xed\x67\xa0\x1c\x48\xd3\xaa\x30\xa9\xac\x59\x40\x0a\xfa\x3a\xe7\xbc\xe9\xa7\x47\x9a\xc6\x4a\x4f\xc3\xac\x64\x4e\x69\x19\x47\x1d\x58\x14\x4d\x9c\x5e\xc3\x91\xca\xa0\xd3\x07\x9e\x0f\x93\x68\x68\x9d\x7d\xf4\x1c\x24\x4c\x56\x1e\x63\xf3\xd9\x6f\x3d\x5f\x15\x8d\xdb\xf6\x9c\x18\x64\x5c\xa7\xca\xda\xed\x4f\xa9\x7f\x67\x5b\xbf\xc0\xe1\x6a\x26\xec\x95\xa9\x30\xcb\x41\xf2\xbe\x17\xd2\x0d\xfd\x27\x00\x00\xff\xff\xbc\xb4\x65\x1c\x6b\x08\x00\x00"), - }, - "/src/time/time_test.go": &vfsgen۰CompressedFileInfo{ - name: "time_test.go", - modTime: time.Date(2019, 4, 14, 20, 40, 36, 659553958, time.UTC), - uncompressedSize: 147, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\x8c\xc1\x0e\x82\x30\x10\x05\xcf\xec\x57\xbc\xf4\xd4\x6a\x02\x7f\xe2\x05\xee\xa6\xd6\x05\x56\xa0\x6d\xe8\x36\x1e\x8c\xff\x6e\x9a\x78\x9d\xc9\xcc\x30\xe0\xfa\xa8\xb2\x3f\xf1\x2a\x44\xd9\x87\xcd\x2f\x0c\x95\x83\xef\xca\x45\x89\xe4\xc8\xe9\x54\x58\xea\x4c\x03\x12\x17\x43\x8e\x68\xae\x31\x60\xe2\xa2\xe3\xce\x9c\xad\xe2\xf2\xb7\xfd\xe4\xf0\xa1\x4e\xfb\x71\x93\x6c\x4d\x3b\xf5\xb7\xf4\xb6\x0e\x52\x10\x93\xc2\x87\x50\x4f\xaf\x0c\x8e\xa9\x2e\x2b\xe6\x74\x42\x57\x46\xeb\x8d\xa3\x2f\xfd\x02\x00\x00\xff\xff\x49\x24\xa9\x3b\x93\x00\x00\x00"), - }, - "/src/unicode": &vfsgen۰DirInfo{ - name: "unicode", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 558751983, time.UTC), - }, - "/src/unicode/unicode.go": &vfsgen۰CompressedFileInfo{ - name: "unicode.go", - modTime: time.Date(2018, 8, 25, 22, 2, 53, 559085211, time.UTC), - uncompressedSize: 658, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x91\x41\x8f\xd3\x30\x10\x85\xcf\xf6\xaf\x78\xa7\x28\x51\xba\x64\xcb\x71\xd5\x72\x29\x12\x08\xb1\x97\x72\xac\x0a\xf2\x3a\x93\xc6\xe0\xd8\xd6\xc4\x91\x40\xdb\xfe\x77\x64\x27\x0d\xcb\xcd\x9e\x79\xf3\x66\xe6\x9b\xa6\x41\xfd\x32\x19\xdb\xe2\xe7\x28\x65\x50\xfa\x97\xba\x10\x26\x67\xb4\x6f\x49\xca\x6e\x72\x1a\xd1\x97\x3f\xb4\x1a\x09\xc6\xc5\x0d\x18\x3c\x39\xda\x20\x45\x8e\xca\x5d\x08\xa7\xf3\xe1\xfe\xae\x50\x0e\x2a\x04\x6a\x8f\x93\xa3\x45\xd8\xf9\xc9\xb5\xcf\x2a\x04\xe3\x2e\x78\xf1\xde\x56\x78\x95\xc2\x74\x98\x4d\x77\x78\xc4\xf5\x8a\x67\xf5\xfb\x90\xbf\xfb\x25\xfe\x2a\x85\x60\x8a\x13\x3b\x1c\x29\x58\xa5\x69\x20\x17\x0f\xbd\xe2\x0d\x3a\x65\x47\x92\xe2\x26\x85\xf5\x78\xda\xe3\x51\x8a\xde\xa4\x87\x25\x57\xae\x83\x55\x52\x74\x9e\x61\x3d\x76\xe8\x4d\x36\x1c\xb2\xc8\xa3\x46\xd9\x9b\x07\xeb\xab\xe6\xbd\x14\x42\x73\x0a\x17\x6b\xe1\x69\x38\xa3\x69\x10\x88\x3b\xcf\x83\x72\x9a\xa0\xd9\x44\xa3\x95\x45\x72\xfc\xe4\x43\x4f\xfc\xe5\xdb\x13\x2e\x14\xa1\xda\x96\x69\x1c\xd1\x13\x27\x44\x63\x24\xd5\xc2\x77\xd0\x3e\xfc\x49\x2b\xc7\x9e\xb0\x02\x92\x22\x6d\x9e\xc0\x94\x9a\xdf\x7d\xf5\x55\x5a\x98\x51\x14\xe0\xfc\x5a\x12\x9f\x4d\x86\x24\x44\x4b\x36\xaa\x34\xdd\x3d\xf3\x31\x05\x4e\x19\xd1\xb9\x4a\x0a\xd3\x61\x16\x7d\x48\x0c\x33\xf7\x5c\x79\x87\xf7\xb6\x57\x8d\xb2\xe4\x87\x37\x91\xaa\xf8\xbe\xc5\x75\xd6\x64\xcf\x62\x5b\x55\x1b\x44\x9e\xd2\xa4\x09\xf0\x3f\x1f\xd4\x73\xa3\x35\x7d\x5b\x96\xc1\xee\xbf\x26\xb9\x7b\x6f\xb0\xc7\x90\x44\x20\xbb\x5c\x33\x1d\x6b\x8f\x01\x35\xb6\x73\xf5\x4d\xae\xe6\xf7\x9b\xde\xe4\xdf\x00\x00\x00\xff\xff\x20\xe3\x22\xd1\x92\x02\x00\x00"), - }, - } - fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src"].(os.FileInfo), - } - fs["/src"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/bytes"].(os.FileInfo), - fs["/src/crypto"].(os.FileInfo), - fs["/src/database"].(os.FileInfo), - fs["/src/debug"].(os.FileInfo), - fs["/src/encoding"].(os.FileInfo), - fs["/src/fmt"].(os.FileInfo), - fs["/src/go"].(os.FileInfo), - fs["/src/internal"].(os.FileInfo), - fs["/src/io"].(os.FileInfo), - fs["/src/math"].(os.FileInfo), - fs["/src/net"].(os.FileInfo), - fs["/src/os"].(os.FileInfo), - fs["/src/reflect"].(os.FileInfo), - fs["/src/regexp"].(os.FileInfo), - fs["/src/runtime"].(os.FileInfo), - fs["/src/strings"].(os.FileInfo), - fs["/src/sync"].(os.FileInfo), - fs["/src/syscall"].(os.FileInfo), - fs["/src/testing"].(os.FileInfo), - fs["/src/text"].(os.FileInfo), - fs["/src/time"].(os.FileInfo), - fs["/src/unicode"].(os.FileInfo), - } - fs["/src/bytes"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/bytes/bytes.go"].(os.FileInfo), - fs["/src/bytes/bytes_test.go"].(os.FileInfo), - } - fs["/src/crypto"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/crypto/internal"].(os.FileInfo), - fs["/src/crypto/rand"].(os.FileInfo), - fs["/src/crypto/x509"].(os.FileInfo), - } - fs["/src/crypto/internal"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/crypto/internal/subtle"].(os.FileInfo), - } - fs["/src/crypto/internal/subtle"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/crypto/internal/subtle/aliasing.go"].(os.FileInfo), - } - fs["/src/crypto/rand"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/crypto/rand/rand.go"].(os.FileInfo), - } - fs["/src/crypto/x509"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/crypto/x509/x509.go"].(os.FileInfo), - fs["/src/crypto/x509/x509_test.go"].(os.FileInfo), - } - fs["/src/database"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/database/sql"].(os.FileInfo), - } - fs["/src/database/sql"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/database/sql/driver"].(os.FileInfo), - } - fs["/src/database/sql/driver"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/database/sql/driver/driver_test.go"].(os.FileInfo), - } - fs["/src/debug"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/debug/elf"].(os.FileInfo), - } - fs["/src/debug/elf"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/debug/elf/elf_test.go"].(os.FileInfo), - } - fs["/src/encoding"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/encoding/gob"].(os.FileInfo), - fs["/src/encoding/json"].(os.FileInfo), - } - fs["/src/encoding/gob"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/encoding/gob/gob_test.go"].(os.FileInfo), - } - fs["/src/encoding/json"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/encoding/json/stream_test.go"].(os.FileInfo), - } - fs["/src/fmt"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/fmt/fmt_test.go"].(os.FileInfo), - } - fs["/src/go"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/go/token"].(os.FileInfo), - } - fs["/src/go/token"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/go/token/token_test.go"].(os.FileInfo), - } - fs["/src/internal"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/internal/bytealg"].(os.FileInfo), - fs["/src/internal/cpu"].(os.FileInfo), - fs["/src/internal/fmtsort"].(os.FileInfo), - fs["/src/internal/poll"].(os.FileInfo), - fs["/src/internal/syscall"].(os.FileInfo), - fs["/src/internal/testenv"].(os.FileInfo), - } - fs["/src/internal/bytealg"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/internal/bytealg/bytealg.go"].(os.FileInfo), - } - fs["/src/internal/cpu"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/internal/cpu/cpu.go"].(os.FileInfo), - } - fs["/src/internal/fmtsort"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/internal/fmtsort/fmtsort_test.go"].(os.FileInfo), - } - fs["/src/internal/poll"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/internal/poll/fd_poll.go"].(os.FileInfo), - } - fs["/src/internal/syscall"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/internal/syscall/unix"].(os.FileInfo), - } - fs["/src/internal/syscall/unix"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/internal/syscall/unix/unix.go"].(os.FileInfo), - } - fs["/src/internal/testenv"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/internal/testenv/testenv.go"].(os.FileInfo), - } - fs["/src/io"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/io/io_test.go"].(os.FileInfo), - } - fs["/src/math"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/math/big"].(os.FileInfo), - fs["/src/math/bits"].(os.FileInfo), - fs["/src/math/math.go"].(os.FileInfo), - fs["/src/math/math_test.go"].(os.FileInfo), - fs["/src/math/rand"].(os.FileInfo), - } - fs["/src/math/big"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/math/big/big.go"].(os.FileInfo), - fs["/src/math/big/big_test.go"].(os.FileInfo), - } - fs["/src/math/bits"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/math/bits/bits.go"].(os.FileInfo), - } - fs["/src/math/rand"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/math/rand/rand_test.go"].(os.FileInfo), - } - fs["/src/net"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/net/http"].(os.FileInfo), - fs["/src/net/net.go"].(os.FileInfo), - } - fs["/src/net/http"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/net/http/cookiejar"].(os.FileInfo), - fs["/src/net/http/fetch.go"].(os.FileInfo), - fs["/src/net/http/http.go"].(os.FileInfo), - } - fs["/src/net/http/cookiejar"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/net/http/cookiejar/example_test.go"].(os.FileInfo), - } - fs["/src/os"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/os/os.go"].(os.FileInfo), - fs["/src/os/signal"].(os.FileInfo), - } - fs["/src/os/signal"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/os/signal/signal.go"].(os.FileInfo), - } - fs["/src/reflect"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/reflect/example_test.go"].(os.FileInfo), - fs["/src/reflect/reflect.go"].(os.FileInfo), - fs["/src/reflect/reflect_test.go"].(os.FileInfo), - fs["/src/reflect/swapper.go"].(os.FileInfo), - } - fs["/src/regexp"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/regexp/regexp_test.go"].(os.FileInfo), - } - fs["/src/runtime"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/runtime/debug"].(os.FileInfo), - fs["/src/runtime/pprof"].(os.FileInfo), - fs["/src/runtime/runtime.go"].(os.FileInfo), - } - fs["/src/runtime/debug"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/runtime/debug/debug.go"].(os.FileInfo), - } - fs["/src/runtime/pprof"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/runtime/pprof/pprof.go"].(os.FileInfo), - } - fs["/src/strings"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/strings/strings.go"].(os.FileInfo), - fs["/src/strings/strings_test.go"].(os.FileInfo), - } - fs["/src/sync"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/sync/atomic"].(os.FileInfo), - fs["/src/sync/cond.go"].(os.FileInfo), - fs["/src/sync/export_test.go"].(os.FileInfo), - fs["/src/sync/pool.go"].(os.FileInfo), - fs["/src/sync/sync.go"].(os.FileInfo), - fs["/src/sync/sync_test.go"].(os.FileInfo), - fs["/src/sync/waitgroup.go"].(os.FileInfo), - } - fs["/src/sync/atomic"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/sync/atomic/atomic.go"].(os.FileInfo), - fs["/src/sync/atomic/atomic_test.go"].(os.FileInfo), - } - fs["/src/syscall"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/syscall/js"].(os.FileInfo), - fs["/src/syscall/syscall.go"].(os.FileInfo), - fs["/src/syscall/syscall_darwin.go"].(os.FileInfo), - fs["/src/syscall/syscall_linux.go"].(os.FileInfo), - fs["/src/syscall/syscall_nonlinux.go"].(os.FileInfo), - fs["/src/syscall/syscall_unix.go"].(os.FileInfo), - fs["/src/syscall/syscall_windows.go"].(os.FileInfo), - } - fs["/src/syscall/js"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/syscall/js/js.go"].(os.FileInfo), - } - fs["/src/testing"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/testing/example.go"].(os.FileInfo), - fs["/src/testing/ioutil.go"].(os.FileInfo), - fs["/src/testing/testing.go"].(os.FileInfo), - } - fs["/src/text"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/text/template"].(os.FileInfo), - } - fs["/src/text/template"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/text/template/template.go"].(os.FileInfo), - } - fs["/src/time"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/time/time.go"].(os.FileInfo), - fs["/src/time/time_test.go"].(os.FileInfo), - } - fs["/src/unicode"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ - fs["/src/unicode/unicode.go"].(os.FileInfo), - } - - return fs -}() - -type vfsgen۰FS map[string]interface{} - -func (fs vfsgen۰FS) Open(path string) (http.File, error) { - path = pathpkg.Clean("/" + path) - f, ok := fs[path] - if !ok { - return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist} - } - - switch f := f.(type) { - case *vfsgen۰CompressedFileInfo: - gr, err := gzip.NewReader(bytes.NewReader(f.compressedContent)) - if err != nil { - // This should never happen because we generate the gzip bytes such that they are always valid. - panic("unexpected error reading own gzip compressed bytes: " + err.Error()) - } - return &vfsgen۰CompressedFile{ - vfsgen۰CompressedFileInfo: f, - gr: gr, - }, nil - case *vfsgen۰FileInfo: - return &vfsgen۰File{ - vfsgen۰FileInfo: f, - Reader: bytes.NewReader(f.content), - }, nil - case *vfsgen۰DirInfo: - return &vfsgen۰Dir{ - vfsgen۰DirInfo: f, - }, nil - default: - // This should never happen because we generate only the above types. - panic(fmt.Sprintf("unexpected type %T", f)) - } -} - -// vfsgen۰CompressedFileInfo is a static definition of a gzip compressed file. -type vfsgen۰CompressedFileInfo struct { - name string - modTime time.Time - compressedContent []byte - uncompressedSize int64 -} - -func (f *vfsgen۰CompressedFileInfo) Readdir(count int) ([]os.FileInfo, error) { - return nil, fmt.Errorf("cannot Readdir from file %s", f.name) -} -func (f *vfsgen۰CompressedFileInfo) Stat() (os.FileInfo, error) { return f, nil } - -func (f *vfsgen۰CompressedFileInfo) GzipBytes() []byte { - return f.compressedContent -} - -func (f *vfsgen۰CompressedFileInfo) Name() string { return f.name } -func (f *vfsgen۰CompressedFileInfo) Size() int64 { return f.uncompressedSize } -func (f *vfsgen۰CompressedFileInfo) Mode() os.FileMode { return 0444 } -func (f *vfsgen۰CompressedFileInfo) ModTime() time.Time { return f.modTime } -func (f *vfsgen۰CompressedFileInfo) IsDir() bool { return false } -func (f *vfsgen۰CompressedFileInfo) Sys() interface{} { return nil } - -// vfsgen۰CompressedFile is an opened compressedFile instance. -type vfsgen۰CompressedFile struct { - *vfsgen۰CompressedFileInfo - gr *gzip.Reader - grPos int64 // Actual gr uncompressed position. - seekPos int64 // Seek uncompressed position. -} - -func (f *vfsgen۰CompressedFile) Read(p []byte) (n int, err error) { - if f.grPos > f.seekPos { - // Rewind to beginning. - err = f.gr.Reset(bytes.NewReader(f.compressedContent)) - if err != nil { - return 0, err - } - f.grPos = 0 - } - if f.grPos < f.seekPos { - // Fast-forward. - _, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos) - if err != nil { - return 0, err - } - f.grPos = f.seekPos - } - n, err = f.gr.Read(p) - f.grPos += int64(n) - f.seekPos = f.grPos - return n, err -} -func (f *vfsgen۰CompressedFile) Seek(offset int64, whence int) (int64, error) { - switch whence { - case io.SeekStart: - f.seekPos = 0 + offset - case io.SeekCurrent: - f.seekPos += offset - case io.SeekEnd: - f.seekPos = f.uncompressedSize + offset - default: - panic(fmt.Errorf("invalid whence value: %v", whence)) - } - return f.seekPos, nil -} -func (f *vfsgen۰CompressedFile) Close() error { - return f.gr.Close() -} - -// vfsgen۰FileInfo is a static definition of an uncompressed file (because it's not worth gzip compressing). -type vfsgen۰FileInfo struct { - name string - modTime time.Time - content []byte -} - -func (f *vfsgen۰FileInfo) Readdir(count int) ([]os.FileInfo, error) { - return nil, fmt.Errorf("cannot Readdir from file %s", f.name) -} -func (f *vfsgen۰FileInfo) Stat() (os.FileInfo, error) { return f, nil } - -func (f *vfsgen۰FileInfo) NotWorthGzipCompressing() {} - -func (f *vfsgen۰FileInfo) Name() string { return f.name } -func (f *vfsgen۰FileInfo) Size() int64 { return int64(len(f.content)) } -func (f *vfsgen۰FileInfo) Mode() os.FileMode { return 0444 } -func (f *vfsgen۰FileInfo) ModTime() time.Time { return f.modTime } -func (f *vfsgen۰FileInfo) IsDir() bool { return false } -func (f *vfsgen۰FileInfo) Sys() interface{} { return nil } - -// vfsgen۰File is an opened file instance. -type vfsgen۰File struct { - *vfsgen۰FileInfo - *bytes.Reader -} - -func (f *vfsgen۰File) Close() error { - return nil -} - -// vfsgen۰DirInfo is a static definition of a directory. -type vfsgen۰DirInfo struct { - name string - modTime time.Time - entries []os.FileInfo -} - -func (d *vfsgen۰DirInfo) Read([]byte) (int, error) { - return 0, fmt.Errorf("cannot Read from directory %s", d.name) -} -func (d *vfsgen۰DirInfo) Close() error { return nil } -func (d *vfsgen۰DirInfo) Stat() (os.FileInfo, error) { return d, nil } - -func (d *vfsgen۰DirInfo) Name() string { return d.name } -func (d *vfsgen۰DirInfo) Size() int64 { return 0 } -func (d *vfsgen۰DirInfo) Mode() os.FileMode { return 0755 | os.ModeDir } -func (d *vfsgen۰DirInfo) ModTime() time.Time { return d.modTime } -func (d *vfsgen۰DirInfo) IsDir() bool { return true } -func (d *vfsgen۰DirInfo) Sys() interface{} { return nil } - -// vfsgen۰Dir is an opened dir instance. -type vfsgen۰Dir struct { - *vfsgen۰DirInfo - pos int // Position within entries for Seek and Readdir. -} - -func (d *vfsgen۰Dir) Seek(offset int64, whence int) (int64, error) { - if offset == 0 && whence == io.SeekStart { - d.pos = 0 - return 0, nil - } - return 0, fmt.Errorf("unsupported Seek in directory %s", d.name) -} - -func (d *vfsgen۰Dir) Readdir(count int) ([]os.FileInfo, error) { - if d.pos >= len(d.entries) && count > 0 { - return nil, io.EOF - } - if count <= 0 || count > len(d.entries)-d.pos { - count = len(d.entries) - d.pos - } - e := d.entries[d.pos : d.pos+count] - d.pos += count - return e, nil -} diff --git a/compiler/natives/doc.go b/compiler/natives/natives.go similarity index 71% rename from compiler/natives/doc.go rename to compiler/natives/natives.go index c176d5b3..37332976 100644 --- a/compiler/natives/doc.go +++ b/compiler/natives/natives.go @@ -5,4 +5,9 @@ // in src subfolder. package natives -//go:generate vfsgendev -source="github.com/gopherjs/gopherjs/compiler/natives".FS -tag=gopherjsdev +import "embed" + +// FS is a virtual filesystem that contains native packages. +// +//go:embed src +var FS embed.FS diff --git a/compiler/natives/src/bufio/bufio_test.go b/compiler/natives/src/bufio/bufio_test.go new file mode 100644 index 00000000..b97fe22f --- /dev/null +++ b/compiler/natives/src/bufio/bufio_test.go @@ -0,0 +1,10 @@ +//go:build js +// +build js + +package bufio_test + +import "testing" + +func TestReadStringAllocs(t *testing.T) { + t.Skip("Memory allocation counters are not available in GopherJS.") +} diff --git a/compiler/natives/src/bytes/bytes.go b/compiler/natives/src/bytes/bytes.go index 2bf919ef..1f74edc4 100644 --- a/compiler/natives/src/bytes/bytes.go +++ b/compiler/natives/src/bytes/bytes.go @@ -1,3 +1,4 @@ +//go:build js // +build js package bytes diff --git a/compiler/natives/src/bytes/bytes_test.go b/compiler/natives/src/bytes/bytes_test.go index 1984e16d..e9d0e169 100644 --- a/compiler/natives/src/bytes/bytes_test.go +++ b/compiler/natives/src/bytes/bytes_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package bytes_test diff --git a/compiler/natives/src/crypto/ed25519/ed25519vectors_test.go b/compiler/natives/src/crypto/ed25519/ed25519vectors_test.go new file mode 100644 index 00000000..90f455b9 --- /dev/null +++ b/compiler/natives/src/crypto/ed25519/ed25519vectors_test.go @@ -0,0 +1,10 @@ +//go:build js +// +build js + +package ed25519_test + +import "testing" + +func TestEd25519Vectors(t *testing.T) { + t.Skip("exec.Command() is not supported by GopherJS") +} diff --git a/compiler/natives/src/crypto/ed25519/internal/edwards25519/field/fe_test.go b/compiler/natives/src/crypto/ed25519/internal/edwards25519/field/fe_test.go new file mode 100644 index 00000000..c448d519 --- /dev/null +++ b/compiler/natives/src/crypto/ed25519/internal/edwards25519/field/fe_test.go @@ -0,0 +1,10 @@ +//go:build js +// +build js + +package field + +import ( + "testing/quick" +) + +var quickCheckConfig1024 = &quick.Config{MaxCount: 100} diff --git a/compiler/natives/src/crypto/ed25519/internal/edwards25519/scalar_test.go b/compiler/natives/src/crypto/ed25519/internal/edwards25519/scalar_test.go new file mode 100644 index 00000000..612663d1 --- /dev/null +++ b/compiler/natives/src/crypto/ed25519/internal/edwards25519/scalar_test.go @@ -0,0 +1,10 @@ +//go:build js +// +build js + +package edwards25519 + +import ( + "testing/quick" +) + +var quickCheckConfig32 = &quick.Config{MaxCount: 100} diff --git a/compiler/natives/src/crypto/internal/subtle/aliasing.go b/compiler/natives/src/crypto/internal/subtle/aliasing.go index 50cc9712..104ac82b 100644 --- a/compiler/natives/src/crypto/internal/subtle/aliasing.go +++ b/compiler/natives/src/crypto/internal/subtle/aliasing.go @@ -1,7 +1,12 @@ +//go:build js // +build js package subtle +// This file duplicated is these two locations: +// - src/crypto/internal/subtle/ +// - src/golang.org/x/crypto/internal/subtle/ + import "github.com/gopherjs/gopherjs/js" // AnyOverlap reports whether x and y share memory at any (not necessarily diff --git a/compiler/natives/src/crypto/rand/rand.go b/compiler/natives/src/crypto/rand/rand.go index afdab8b8..1c3631a0 100644 --- a/compiler/natives/src/crypto/rand/rand.go +++ b/compiler/natives/src/crypto/rand/rand.go @@ -1,3 +1,4 @@ +//go:build js // +build js package rand @@ -8,13 +9,9 @@ import ( "github.com/gopherjs/gopherjs/js" ) -func init() { - Reader = &rngReader{} -} - -type rngReader struct{} +type reader struct{} -func (r *rngReader) Read(b []byte) (n int, err error) { +func (r *reader) Read(b []byte) (n int, err error) { array := js.InternalObject(b).Get("$array") offset := js.InternalObject(b).Get("$offset").Int() @@ -47,15 +44,3 @@ func (r *rngReader) Read(b []byte) (n int, err error) { return 0, errors.New("crypto/rand not available in this environment") } - -func batched(f func([]byte) bool, readMax int) func([]byte) bool { - return func(buf []byte) bool { - for len(buf) > readMax { - if !f(buf[:readMax]) { - return false - } - buf = buf[readMax:] - } - return len(buf) == 0 || f(buf) - } -} diff --git a/compiler/natives/src/crypto/tls/handshake_test.go b/compiler/natives/src/crypto/tls/handshake_test.go new file mode 100644 index 00000000..c9e19d5e --- /dev/null +++ b/compiler/natives/src/crypto/tls/handshake_test.go @@ -0,0 +1,73 @@ +//go:build js + +package tls + +import ( + "context" + "runtime" + "testing" +) + +// Same as upstream, except we check for GOARCH=ecmascript instead of wasm. +// This override can be removed after https://github.com/golang/go/pull/51827 +// is available in the upstream (likely in Go 1.19). +func TestServerHandshakeContextCancellation(t *testing.T) { + c, s := localPipe(t) + ctx, cancel := context.WithCancel(context.Background()) + unblockClient := make(chan struct{}) + defer close(unblockClient) + go func() { + cancel() + <-unblockClient + _ = c.Close() + }() + conn := Server(s, testConfig) + // Initiates server side handshake, which will block until a client hello is read + // unless the cancellation works. + err := conn.HandshakeContext(ctx) + if err == nil { + t.Fatal("Server handshake did not error when the context was canceled") + } + if err != context.Canceled { + t.Errorf("Unexpected server handshake error: %v", err) + } + if runtime.GOARCH == "ecmascript" { + t.Skip("conn.Close does not error as expected when called multiple times on WASM") + } + err = conn.Close() + if err == nil { + t.Error("Server connection was not closed when the context was canceled") + } +} + +// Same as upstream, except we check for GOARCH=ecmascript instead of wasm. +// This override can be removed after https://github.com/golang/go/pull/51827 +// is available in the upstream (likely in Go 1.19). +func TestClientHandshakeContextCancellation(t *testing.T) { + c, s := localPipe(t) + ctx, cancel := context.WithCancel(context.Background()) + unblockServer := make(chan struct{}) + defer close(unblockServer) + go func() { + cancel() + <-unblockServer + _ = s.Close() + }() + cli := Client(c, testConfig) + // Initiates client side handshake, which will block until the client hello is read + // by the server, unless the cancellation works. + err := cli.HandshakeContext(ctx) + if err == nil { + t.Fatal("Client handshake did not error when the context was canceled") + } + if err != context.Canceled { + t.Errorf("Unexpected client handshake error: %v", err) + } + if runtime.GOARCH == "ecmascript" { + t.Skip("conn.Close does not error as expected when called multiple times on WASM") + } + err = cli.Close() + if err == nil { + t.Error("Client connection was not closed when the context was canceled") + } +} diff --git a/compiler/natives/src/crypto/x509/x509.go b/compiler/natives/src/crypto/x509/x509.go deleted file mode 100644 index a0391f75..00000000 --- a/compiler/natives/src/crypto/x509/x509.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build js - -package x509 - -import "errors" - -func loadSystemRoots() (*CertPool, error) { - return nil, errors.New("crypto/x509: system root pool is not available in GopherJS") -} diff --git a/compiler/natives/src/crypto/x509/x509_test.go b/compiler/natives/src/crypto/x509/x509_test.go deleted file mode 100644 index 7a99c388..00000000 --- a/compiler/natives/src/crypto/x509/x509_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// +build js - -package x509 - -import "testing" - -func TestSystemCertPool(t *testing.T) { - t.Skip("no system roots") -} - -func TestSystemRoots(t *testing.T) { - t.Skip("no system roots") -} - -func TestEnvVars(t *testing.T) { - t.Skip("no system roots") -} - -func TestSystemVerify(t *testing.T) { - t.Skip("no system") -} - -func TestImports(t *testing.T) { - t.Skip("no system") -} diff --git a/compiler/natives/src/database/sql/driver/driver_test.go b/compiler/natives/src/database/sql/driver/driver_test.go index c52960c4..446da47c 100644 --- a/compiler/natives/src/database/sql/driver/driver_test.go +++ b/compiler/natives/src/database/sql/driver/driver_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package driver diff --git a/compiler/natives/src/debug/elf/elf_test.go b/compiler/natives/src/debug/elf/elf_test.go deleted file mode 100644 index 4c636e40..00000000 --- a/compiler/natives/src/debug/elf/elf_test.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build js - -package elf - -import "testing" - -func TestNoSectionOverlaps(t *testing.T) { - t.Skip("not 6l") -} diff --git a/compiler/natives/src/embed/embed.go b/compiler/natives/src/embed/embed.go new file mode 100644 index 00000000..18cb7001 --- /dev/null +++ b/compiler/natives/src/embed/embed.go @@ -0,0 +1,20 @@ +//go:build js +// +build js + +package embed + +func buildFS(list []struct { + name string + data string + hash [16]byte +}) (f FS) { + n := len(list) + files := make([]file, n) + for i := 0; i < n; i++ { + files[i].name = list[i].name + files[i].data = list[i].data + files[i].hash = list[i].hash + } + f.files = &files + return +} diff --git a/compiler/natives/src/encoding/gob/gob_test.go b/compiler/natives/src/encoding/gob/gob_test.go index b105e4f3..94a999cc 100644 --- a/compiler/natives/src/encoding/gob/gob_test.go +++ b/compiler/natives/src/encoding/gob/gob_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package gob @@ -60,7 +61,7 @@ func TestEndToEnd(t *testing.T) { B: 18, C: -5, M: map[string]*float64{"pi": &pi, "e": &e}, - M2: map[int]T3{4: T3{X: pi, Z: &meaning}, 10: T3{X: e, Z: &fingers}}, + M2: map[int]T3{4: {X: pi, Z: &meaning}, 10: {X: e, Z: &fingers}}, Mstring: map[string]string{"pi": "3.14", "e": "2.71"}, Mintptr: map[int]*int{meaning: &fingers, fingers: &meaning}, Mcomp: map[complex128]complex128{comp1: comp2, comp2: comp1}, diff --git a/compiler/natives/src/encoding/json/stream_test.go b/compiler/natives/src/encoding/json/stream_test.go index 7e6f37a0..adad8e15 100644 --- a/compiler/natives/src/encoding/json/stream_test.go +++ b/compiler/natives/src/encoding/json/stream_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package json diff --git a/compiler/natives/src/fmt/fmt_test.go b/compiler/natives/src/fmt/fmt_test.go index 65b00026..70797693 100644 --- a/compiler/natives/src/fmt/fmt_test.go +++ b/compiler/natives/src/fmt/fmt_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package fmt_test diff --git a/compiler/natives/src/go/doc/doc_test.go b/compiler/natives/src/go/doc/doc_test.go new file mode 100644 index 00000000..4d35e880 --- /dev/null +++ b/compiler/natives/src/go/doc/doc_test.go @@ -0,0 +1,37 @@ +//go:build js + +package doc + +import ( + "fmt" + "testing" +) + +func compareSlices(t *testing.T, name string, got, want interface{}, compareElem interface{}) { + // TODO(nevkontakte): Remove this override after generics are supported. + // https://github.com/gopherjs/gopherjs/issues/1013. + switch got.(type) { + case []*Func: + got := got.([]*Func) + want := want.([]*Func) + compareElem := compareElem.(func(t *testing.T, msg string, got, want *Func)) + if len(got) != len(want) { + t.Errorf("%s: got %d, want %d", name, len(got), len(want)) + } + for i := 0; i < len(got) && i < len(want); i++ { + compareElem(t, fmt.Sprintf("%s[%d]", name, i), got[i], want[i]) + } + case []*Type: + got := got.([]*Type) + want := want.([]*Type) + compareElem := compareElem.(func(t *testing.T, msg string, got, want *Type)) + if len(got) != len(want) { + t.Errorf("%s: got %d, want %d", name, len(got), len(want)) + } + for i := 0; i < len(got) && i < len(want); i++ { + compareElem(t, fmt.Sprintf("%s[%d]", name, i), got[i], want[i]) + } + default: + t.Errorf("unexpected argument type %T", got) + } +} diff --git a/compiler/natives/src/go/parser/parser_test.go b/compiler/natives/src/go/parser/parser_test.go new file mode 100644 index 00000000..7fded29f --- /dev/null +++ b/compiler/natives/src/go/parser/parser_test.go @@ -0,0 +1,11 @@ +//go:build js + +package parser + +import ( + "testing" +) + +func TestParseDepthLimit(t *testing.T) { + t.Skip("causes call stack exhaustion on js/ecmascript") +} diff --git a/compiler/natives/src/go/token/token_test.go b/compiler/natives/src/go/token/token_test.go index f975308d..335ee077 100644 --- a/compiler/natives/src/go/token/token_test.go +++ b/compiler/natives/src/go/token/token_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package token diff --git a/compiler/natives/src/golang.org/x/crypto/internal/subtle/aliasing.go b/compiler/natives/src/golang.org/x/crypto/internal/subtle/aliasing.go new file mode 100644 index 00000000..104ac82b --- /dev/null +++ b/compiler/natives/src/golang.org/x/crypto/internal/subtle/aliasing.go @@ -0,0 +1,20 @@ +//go:build js +// +build js + +package subtle + +// This file duplicated is these two locations: +// - src/crypto/internal/subtle/ +// - src/golang.org/x/crypto/internal/subtle/ + +import "github.com/gopherjs/gopherjs/js" + +// AnyOverlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +func AnyOverlap(x, y []byte) bool { + // GopherJS: We can't rely on pointer arithmetic, so use GopherJS slice internals. + return len(x) > 0 && len(y) > 0 && + js.InternalObject(x).Get("$array") == js.InternalObject(y).Get("$array") && + js.InternalObject(x).Get("$offset").Int() <= js.InternalObject(y).Get("$offset").Int()+len(y)-1 && + js.InternalObject(y).Get("$offset").Int() <= js.InternalObject(x).Get("$offset").Int()+len(x)-1 +} diff --git a/compiler/natives/src/hash/maphash/maphash.go b/compiler/natives/src/hash/maphash/maphash.go new file mode 100644 index 00000000..877366f0 --- /dev/null +++ b/compiler/natives/src/hash/maphash/maphash.go @@ -0,0 +1,130 @@ +//go:build js +// +build js + +package maphash + +// used in hash{32,64}.go to seed the hash function +var hashkey [4]uint32 + +func init() { + for i := range hashkey { + hashkey[i] = runtime_fastrand() + } + hashkey[0] |= 1 // make sure these numbers are odd + hashkey[1] |= 1 + hashkey[2] |= 1 + hashkey[3] |= 1 +} + +func _rthash(b []byte, seed uint64) uint64 { + if len(b) == 0 { + return seed + } + // The runtime hasher only works on uintptr. Since GopherJS implements a + // 32-bit environment, we use two parallel hashers on the lower and upper 32 + // bits. + lo := memhash(b, uint32(seed), uint32(len(b))) + hi := memhash(b, uint32(seed>>32), uint32(len(b))) + return uint64(hi)<<32 | uint64(lo) +} + +// The implementation below is adapted from the upstream runtime/hash32.go +// and avoids use of unsafe, which GopherJS doesn't support well and leads to +// worse performance. +// +// Note that this hashing function is not actually used by GopherJS maps, since +// we use JS maps instead, but it may be still applicable for use with custom +// map types. +// +// Hashing algorithm inspired by wyhash: +// https://github.com/wangyi-fudan/wyhash/blob/ceb019b530e2c1c14d70b79bfa2bc49de7d95bc1/Modern%20Non-Cryptographic%20Hash%20Function%20and%20Pseudorandom%20Number%20Generator.pdf +func memhash(p []byte, seed uint32, s uint32) uintptr { + a, b := mix32(uint32(seed), uint32(s^hashkey[0])) + if s == 0 { + return uintptr(a ^ b) + } + for ; s > 8; s -= 8 { + a ^= readUnaligned32(p) + b ^= readUnaligned32(add(p, 4)) + a, b = mix32(a, b) + p = add(p, 8) + } + if s >= 4 { + a ^= readUnaligned32(p) + b ^= readUnaligned32(add(p, s-4)) + } else { + t := uint32(p[0]) + t |= uint32(add(p, s>>1)[0]) << 8 + t |= uint32(add(p, s-1)[0]) << 16 + b ^= t + } + a, b = mix32(a, b) + a, b = mix32(a, b) + return uintptr(a ^ b) +} + +func add(p []byte, x uint32) []byte { + return p[x:] +} + +// Note: These routines perform the read in little endian. +func readUnaligned32(p []byte) uint32 { + return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 +} + +func mix32(a, b uint32) (uint32, uint32) { + c := uint64(a^uint32(hashkey[1])) * uint64(b^uint32(hashkey[2])) + return uint32(c), uint32(c >> 32) +} + +/* + The following functions were modified in Go 1.17 to improve performance, + but at the expense of being unsafe, and thus incompatible with GopherJS. + To compensate, we have reverted these to the unoptimized Go 1.16 versions + for now. + + See upstream issue https://github.com/golang/go/issues/47342 to implement + a purego version of this package, which should render this hack (and + likely this entire file) obsolete. +*/ + +// Write is borrowed from Go 1.16. +func (h *Hash) Write(b []byte) (int, error) { + size := len(b) + for h.n+len(b) > len(h.buf) { + k := copy(h.buf[h.n:], b) + h.n = len(h.buf) + b = b[k:] + h.flush() + } + h.n += copy(h.buf[h.n:], b) + return size, nil +} + +// WriteString is borrowed from Go 1.16. +func (h *Hash) WriteString(s string) (int, error) { + size := len(s) + for h.n+len(s) > len(h.buf) { + k := copy(h.buf[h.n:], s) + h.n = len(h.buf) + s = s[k:] + h.flush() + } + h.n += copy(h.buf[h.n:], s) + return size, nil +} + +func (h *Hash) flush() { + if h.n != len(h.buf) { + panic("maphash: flush of partially full buffer") + } + h.initSeed() + h.state.s = _rthash(h.buf[:], h.state.s) + h.n = 0 +} + +// Sum64 is borrowed from Go 1.16. +func (h *Hash) Sum64() uint64 { + h.initSeed() + return _rthash(h.buf[:h.n], h.state.s) +} diff --git a/compiler/natives/src/internal/bytealg/bytealg.go b/compiler/natives/src/internal/bytealg/bytealg.go index 13d4ddd0..dbcc6dec 100644 --- a/compiler/natives/src/internal/bytealg/bytealg.go +++ b/compiler/natives/src/internal/bytealg/bytealg.go @@ -1,3 +1,4 @@ +//go:build js // +build js package bytealg @@ -13,3 +14,21 @@ func Equal(a, b []byte) bool { } return true } + +func IndexByte(b []byte, c byte) int { + for i, x := range b { + if x == c { + return i + } + } + return -1 +} + +func IndexByteString(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 +} diff --git a/compiler/natives/src/internal/cpu/cpu.go b/compiler/natives/src/internal/cpu/cpu.go index 7777d3ae..794a58d6 100644 --- a/compiler/natives/src/internal/cpu/cpu.go +++ b/compiler/natives/src/internal/cpu/cpu.go @@ -1,3 +1,4 @@ +//go:build js // +build js package cpu @@ -6,3 +7,5 @@ const ( CacheLineSize = 0 CacheLinePadSize = 0 ) + +func doinit() {} diff --git a/compiler/natives/src/internal/fmtsort/fmtsort_test.go b/compiler/natives/src/internal/fmtsort/fmtsort_test.go index a3ceb760..f45987d7 100644 --- a/compiler/natives/src/internal/fmtsort/fmtsort_test.go +++ b/compiler/natives/src/internal/fmtsort/fmtsort_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package fmtsort_test @@ -13,7 +14,7 @@ import ( // needsSkip reports whether the kind doesn't work for sorting on GopherJS. func needsSkip(k reflect.Kind) bool { switch k { - case reflect.Ptr, reflect.Chan: + case reflect.Ptr, reflect.Chan, reflect.UnsafePointer: return true } return false diff --git a/compiler/natives/src/internal/goarch/goarch_js.go b/compiler/natives/src/internal/goarch/goarch_js.go new file mode 100644 index 00000000..98618d69 --- /dev/null +++ b/compiler/natives/src/internal/goarch/goarch_js.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = WASM + _DefaultPhysPageSize = 65536 + _PCQuantum = 1 + _MinFrameSize = 0 + _StackAlign = PtrSize +) diff --git a/compiler/natives/src/internal/goarch/zgoarch_js.go b/compiler/natives/src/internal/goarch/zgoarch_js.go new file mode 100644 index 00000000..0d2f8755 --- /dev/null +++ b/compiler/natives/src/internal/goarch/zgoarch_js.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build js + +package goarch + +const GOARCH = `wasm` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 1 diff --git a/compiler/natives/src/internal/intern/intern.go b/compiler/natives/src/internal/intern/intern.go new file mode 100644 index 00000000..077b99aa --- /dev/null +++ b/compiler/natives/src/internal/intern/intern.go @@ -0,0 +1,29 @@ +//go:build js + +package intern + +var ( + eth0 = &Value{cmpVal: "eth0"} + eth1 = &Value{cmpVal: "eth1"} +) + +func get(k key) *Value { + // Interning implementation in this package unavoidably relies upon + // runtime.SetFinalizer(), which GopherJS doesn't support (at least until it + // is considered safe to use the WeakMap API). Without working finalizers + // using this package would create memory leaks. + // + // Considering that this package is supposed to serve as an optimization tool, + // it is better to make it explicitly unusable and work around it at the call + // sites. + + // net/netip tests use intern API with a few fixed values. It is easier to + // special-case them here than to override the entire test set. + if k.isString && k.s == "eth0" { + return eth0 + } else if k.isString && k.s == "eth1" { + return eth1 + } + + panic("internal/intern is not supported by GopherJS") +} diff --git a/compiler/natives/src/internal/poll/fd_poll.go b/compiler/natives/src/internal/poll/fd_poll.go deleted file mode 100644 index 5690f9fe..00000000 --- a/compiler/natives/src/internal/poll/fd_poll.go +++ /dev/null @@ -1,88 +0,0 @@ -// +build js - -package poll - -import "time" - -// pollDesc is a no-op implementation of an I/O poller for GOARCH=js. -// -// Its implementation is based on NaCL in gc compiler (see GOROOT/src/internal/poll/fd_poll_nacl.go), -// but it does even less. -type pollDesc struct { - closing bool -} - -func (pd *pollDesc) init(fd *FD) error { return nil } - -func (pd *pollDesc) close() {} - -func (pd *pollDesc) evict() { pd.closing = true } - -func (pd *pollDesc) prepare(mode int, isFile bool) error { - if pd.closing { - return errClosing(isFile) - } - return nil -} - -func (pd *pollDesc) prepareRead(isFile bool) error { return pd.prepare('r', isFile) } - -func (pd *pollDesc) prepareWrite(isFile bool) error { return pd.prepare('w', isFile) } - -func (pd *pollDesc) wait(mode int, isFile bool) error { - if pd.closing { - return errClosing(isFile) - } - return ErrTimeout -} - -func (pd *pollDesc) waitRead(isFile bool) error { return pd.wait('r', isFile) } - -func (pd *pollDesc) waitWrite(isFile bool) error { return pd.wait('w', isFile) } - -func (*pollDesc) waitCanceled(mode int) {} - -func (*pollDesc) pollable() bool { return true } - -func (*FD) SetDeadline(t time.Time) error { return nil } - -func (*FD) SetReadDeadline(t time.Time) error { return nil } - -func (*FD) SetWriteDeadline(t time.Time) error { return nil } - -// PollDescriptor returns the descriptor being used by the poller, -// or ^uintptr(0) if there isn't one. This is only used for testing. -func PollDescriptor() uintptr { - return ^uintptr(0) -} - -// Copy of sync.runtime_Semacquire. -func runtime_Semacquire(s *uint32) { - if *s == 0 { - ch := make(chan bool) - semWaiters[s] = append(semWaiters[s], ch) - <-ch - } - *s-- -} - -// Copy of sync.runtime_Semrelease. -func runtime_Semrelease(s *uint32) { - *s++ - - w := semWaiters[s] - if len(w) == 0 { - return - } - - ch := w[0] - w = w[1:] - semWaiters[s] = w - if len(w) == 0 { - delete(semWaiters, s) - } - - ch <- true -} - -var semWaiters = make(map[*uint32][]chan bool) diff --git a/compiler/natives/src/internal/poll/semaphore.go b/compiler/natives/src/internal/poll/semaphore.go new file mode 100644 index 00000000..5e4f5ea8 --- /dev/null +++ b/compiler/natives/src/internal/poll/semaphore.go @@ -0,0 +1,14 @@ +//go:build js +// +build js + +package poll + +import ( + _ "unsafe" // For go:linkname +) + +//go:linkname runtime_Semacquire sync.runtime_Semacquire +func runtime_Semacquire(s *uint32) + +//go:linkname runtime_Semrelease sync.runtime_Semrelease +func runtime_Semrelease(s *uint32) diff --git a/compiler/natives/src/internal/reflectlite/all_test.go b/compiler/natives/src/internal/reflectlite/all_test.go new file mode 100644 index 00000000..55639d27 --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/all_test.go @@ -0,0 +1,22 @@ +//go:build js +// +build js + +package reflectlite_test + +import ( + . "internal/reflectlite" + "testing" +) + +func TestTypes(t *testing.T) { + for i, tt := range typeTests { + if i == 30 { + continue + } + testReflectType(t, i, Field(ValueOf(tt.i), 0).Type(), tt.s) + } +} + +func TestNameBytesAreAligned(t *testing.T) { + t.Skip("TestNameBytesAreAligned") +} diff --git a/compiler/natives/src/internal/reflectlite/export_test.go b/compiler/natives/src/internal/reflectlite/export_test.go new file mode 100644 index 00000000..d663e65b --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/export_test.go @@ -0,0 +1,35 @@ +//go:build js +// +build js + +package reflectlite + +import ( + "unsafe" +) + +// Field returns the i'th field of the struct v. +// It panics if v's Kind is not Struct or i is out of range. +func Field(v Value, i int) Value { + if v.kind() != Struct { + panic(&ValueError{"reflect.Value.Field", v.kind()}) + } + return v.Field(i) +} + +func TField(typ Type, i int) Type { + t := typ.(*rtype) + if t.Kind() != Struct { + panic("reflect: Field of non-struct type") + } + tt := (*structType)(unsafe.Pointer(t)) + return StructFieldType(tt, i) +} + +// Field returns the i'th struct field. +func StructFieldType(t *structType, i int) Type { + if i < 0 || i >= len(t.fields) { + panic("reflect: Field index out of bounds") + } + p := &t.fields[i] + return toType(p.typ) +} diff --git a/compiler/natives/src/internal/reflectlite/reflect_mirror_test.go b/compiler/natives/src/internal/reflectlite/reflect_mirror_test.go new file mode 100644 index 00000000..01504f58 --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/reflect_mirror_test.go @@ -0,0 +1,12 @@ +//go:build js +// +build js + +package reflectlite_test + +import ( + "testing" +) + +func TestMirrorWithReflect(t *testing.T) { + t.Skip("TestMirrorWithReflect") +} diff --git a/compiler/natives/src/internal/reflectlite/reflectlite.go b/compiler/natives/src/internal/reflectlite/reflectlite.go new file mode 100644 index 00000000..e4c03bed --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/reflectlite.go @@ -0,0 +1,968 @@ +//go:build js +// +build js + +package reflectlite + +import ( + "unsafe" + + "github.com/gopherjs/gopherjs/js" +) + +var initialized = false + +func init() { + // avoid dead code elimination + used := func(i interface{}) {} + used(rtype{}) + used(uncommonType{}) + used(method{}) + used(arrayType{}) + used(chanType{}) + used(funcType{}) + used(interfaceType{}) + used(mapType{}) + used(ptrType{}) + used(sliceType{}) + used(structType{}) + used(imethod{}) + used(structField{}) + + initialized = true + uint8Type = TypeOf(uint8(0)).(*rtype) // set for real +} + +var ( + uint8Type *rtype +) + +var ( + idJsType = "_jsType" + idReflectType = "_reflectType" + idKindType = "kindType" + idRtype = "_rtype" +) + +func jsType(typ Type) *js.Object { + return js.InternalObject(typ).Get(idJsType) +} + +func reflectType(typ *js.Object) *rtype { + if typ.Get(idReflectType) == js.Undefined { + rt := &rtype{ + size: uintptr(typ.Get("size").Int()), + kind: uint8(typ.Get("kind").Int()), + str: newNameOff(newName(internalStr(typ.Get("string")), "", typ.Get("exported").Bool())), + } + js.InternalObject(rt).Set(idJsType, typ) + typ.Set(idReflectType, js.InternalObject(rt)) + + methodSet := js.Global.Call("$methodSet", typ) + if methodSet.Length() != 0 || typ.Get("named").Bool() { + rt.tflag |= tflagUncommon + if typ.Get("named").Bool() { + rt.tflag |= tflagNamed + } + var reflectMethods []method + for i := 0; i < methodSet.Length(); i++ { // Exported methods first. + m := methodSet.Index(i) + exported := internalStr(m.Get("pkg")) == "" + if !exported { + continue + } + reflectMethods = append(reflectMethods, method{ + name: newNameOff(newName(internalStr(m.Get("name")), "", exported)), + mtyp: newTypeOff(reflectType(m.Get("typ"))), + }) + } + xcount := uint16(len(reflectMethods)) + for i := 0; i < methodSet.Length(); i++ { // Unexported methods second. + m := methodSet.Index(i) + exported := internalStr(m.Get("pkg")) == "" + if exported { + continue + } + reflectMethods = append(reflectMethods, method{ + name: newNameOff(newName(internalStr(m.Get("name")), "", exported)), + mtyp: newTypeOff(reflectType(m.Get("typ"))), + }) + } + ut := &uncommonType{ + pkgPath: newNameOff(newName(internalStr(typ.Get("pkg")), "", false)), + mcount: uint16(methodSet.Length()), + xcount: xcount, + _methods: reflectMethods, + } + uncommonTypeMap[rt] = ut + js.InternalObject(ut).Set(idJsType, typ) + } + + switch rt.Kind() { + case Array: + setKindType(rt, &arrayType{ + elem: reflectType(typ.Get("elem")), + len: uintptr(typ.Get("len").Int()), + }) + case Chan: + dir := BothDir + if typ.Get("sendOnly").Bool() { + dir = SendDir + } + if typ.Get("recvOnly").Bool() { + dir = RecvDir + } + setKindType(rt, &chanType{ + elem: reflectType(typ.Get("elem")), + dir: uintptr(dir), + }) + case Func: + params := typ.Get("params") + in := make([]*rtype, params.Length()) + for i := range in { + in[i] = reflectType(params.Index(i)) + } + results := typ.Get("results") + out := make([]*rtype, results.Length()) + for i := range out { + out[i] = reflectType(results.Index(i)) + } + outCount := uint16(results.Length()) + if typ.Get("variadic").Bool() { + outCount |= 1 << 15 + } + setKindType(rt, &funcType{ + rtype: *rt, + inCount: uint16(params.Length()), + outCount: outCount, + _in: in, + _out: out, + }) + case Interface: + methods := typ.Get("methods") + imethods := make([]imethod, methods.Length()) + for i := range imethods { + m := methods.Index(i) + imethods[i] = imethod{ + name: newNameOff(newName(internalStr(m.Get("name")), "", internalStr(m.Get("pkg")) == "")), + typ: newTypeOff(reflectType(m.Get("typ"))), + } + } + setKindType(rt, &interfaceType{ + rtype: *rt, + pkgPath: newName(internalStr(typ.Get("pkg")), "", false), + methods: imethods, + }) + case Map: + setKindType(rt, &mapType{ + key: reflectType(typ.Get("key")), + elem: reflectType(typ.Get("elem")), + }) + case Ptr: + setKindType(rt, &ptrType{ + elem: reflectType(typ.Get("elem")), + }) + case Slice: + setKindType(rt, &sliceType{ + elem: reflectType(typ.Get("elem")), + }) + case Struct: + fields := typ.Get("fields") + reflectFields := make([]structField, fields.Length()) + for i := range reflectFields { + f := fields.Index(i) + offsetEmbed := uintptr(i) << 1 + if f.Get("embedded").Bool() { + offsetEmbed |= 1 + } + reflectFields[i] = structField{ + name: newName(internalStr(f.Get("name")), internalStr(f.Get("tag")), f.Get("exported").Bool()), + typ: reflectType(f.Get("typ")), + offsetEmbed: offsetEmbed, + } + } + setKindType(rt, &structType{ + rtype: *rt, + pkgPath: newName(internalStr(typ.Get("pkgPath")), "", false), + fields: reflectFields, + }) + } + } + + return (*rtype)(unsafe.Pointer(typ.Get(idReflectType).Unsafe())) +} + +func setKindType(rt *rtype, kindType interface{}) { + js.InternalObject(rt).Set(idKindType, js.InternalObject(kindType)) + js.InternalObject(kindType).Set(idRtype, js.InternalObject(rt)) +} + +type uncommonType struct { + pkgPath nameOff + mcount uint16 + xcount uint16 + moff uint32 + + _methods []method +} + +func (t *uncommonType) methods() []method { + return t._methods +} + +func (t *uncommonType) exportedMethods() []method { + return t._methods[:t.xcount:t.xcount] +} + +var uncommonTypeMap = make(map[*rtype]*uncommonType) + +func (t *rtype) uncommon() *uncommonType { + return uncommonTypeMap[t] +} + +type funcType struct { + rtype `reflect:"func"` + inCount uint16 + outCount uint16 + + _in []*rtype + _out []*rtype +} + +func (t *funcType) in() []*rtype { + return t._in +} + +func (t *funcType) out() []*rtype { + return t._out +} + +type name struct { + bytes *byte +} + +type nameData struct { + name string + tag string + exported bool +} + +var nameMap = make(map[*byte]*nameData) + +func (n name) name() (s string) { return nameMap[n.bytes].name } +func (n name) tag() (s string) { return nameMap[n.bytes].tag } +func (n name) pkgPath() string { return "" } +func (n name) isExported() bool { return nameMap[n.bytes].exported } + +func newName(n, tag string, exported bool) name { + b := new(byte) + nameMap[b] = &nameData{ + name: n, + tag: tag, + exported: exported, + } + return name{ + bytes: b, + } +} + +var nameOffList []name + +func (t *rtype) nameOff(off nameOff) name { + return nameOffList[int(off)] +} + +func newNameOff(n name) nameOff { + i := len(nameOffList) + nameOffList = append(nameOffList, n) + return nameOff(i) +} + +var typeOffList []*rtype + +func (t *rtype) typeOff(off typeOff) *rtype { + return typeOffList[int(off)] +} + +func newTypeOff(t *rtype) typeOff { + i := len(typeOffList) + typeOffList = append(typeOffList, t) + return typeOff(i) +} + +func internalStr(strObj *js.Object) string { + var c struct{ str string } + js.InternalObject(c).Set("str", strObj) // get string without internalizing + return c.str +} + +func isWrapped(typ Type) bool { + return jsType(typ).Get("wrapped").Bool() +} + +func copyStruct(dst, src *js.Object, typ Type) { + fields := jsType(typ).Get("fields") + for i := 0; i < fields.Length(); i++ { + prop := fields.Index(i).Get("prop").String() + dst.Set(prop, src.Get(prop)) + } +} + +func makeValue(t Type, v *js.Object, fl flag) Value { + rt := t.common() + if t.Kind() == Array || t.Kind() == Struct || t.Kind() == Ptr { + return Value{rt, unsafe.Pointer(v.Unsafe()), fl | flag(t.Kind())} + } + return Value{rt, unsafe.Pointer(js.Global.Call("$newDataPointer", v, jsType(rt.ptrTo())).Unsafe()), fl | flag(t.Kind()) | flagIndir} +} + +func MakeSlice(typ Type, len, cap int) Value { + if typ.Kind() != Slice { + panic("reflect.MakeSlice of non-slice type") + } + if len < 0 { + panic("reflect.MakeSlice: negative len") + } + if cap < 0 { + panic("reflect.MakeSlice: negative cap") + } + if len > cap { + panic("reflect.MakeSlice: len > cap") + } + + return makeValue(typ, js.Global.Call("$makeSlice", jsType(typ), len, cap, js.InternalObject(func() *js.Object { return jsType(typ.Elem()).Call("zero") })), 0) +} + +func TypeOf(i interface{}) Type { + if !initialized { // avoid error of uint8Type + return &rtype{} + } + if i == nil { + return nil + } + return reflectType(js.InternalObject(i).Get("constructor")) +} + +func ValueOf(i interface{}) Value { + if i == nil { + return Value{} + } + return makeValue(reflectType(js.InternalObject(i).Get("constructor")), js.InternalObject(i).Get("$val"), 0) +} + +func ArrayOf(count int, elem Type) Type { + return reflectType(js.Global.Call("$arrayType", jsType(elem), count)) +} + +func ChanOf(dir ChanDir, t Type) Type { + return reflectType(js.Global.Call("$chanType", jsType(t), dir == SendDir, dir == RecvDir)) +} + +func FuncOf(in, out []Type, variadic bool) Type { + if variadic && (len(in) == 0 || in[len(in)-1].Kind() != Slice) { + panic("reflect.FuncOf: last arg of variadic func must be slice") + } + + jsIn := make([]*js.Object, len(in)) + for i, v := range in { + jsIn[i] = jsType(v) + } + jsOut := make([]*js.Object, len(out)) + for i, v := range out { + jsOut[i] = jsType(v) + } + return reflectType(js.Global.Call("$funcType", jsIn, jsOut, variadic)) +} + +func MapOf(key, elem Type) Type { + switch key.Kind() { + case Func, Map, Slice: + panic("reflect.MapOf: invalid key type " + key.String()) + } + + return reflectType(js.Global.Call("$mapType", jsType(key), jsType(elem))) +} + +func (t *rtype) ptrTo() *rtype { + return reflectType(js.Global.Call("$ptrType", jsType(t))) +} + +func SliceOf(t Type) Type { + return reflectType(js.Global.Call("$sliceType", jsType(t))) +} + +func Zero(typ Type) Value { + return makeValue(typ, jsType(typ).Call("zero"), 0) +} + +func unsafe_New(typ *rtype) unsafe.Pointer { + switch typ.Kind() { + case Struct: + return unsafe.Pointer(jsType(typ).Get("ptr").New().Unsafe()) + case Array: + return unsafe.Pointer(jsType(typ).Call("zero").Unsafe()) + default: + return unsafe.Pointer(js.Global.Call("$newDataPointer", jsType(typ).Call("zero"), jsType(typ.ptrTo())).Unsafe()) + } +} + +func makeInt(f flag, bits uint64, t Type) Value { + typ := t.common() + ptr := unsafe_New(typ) + switch typ.Kind() { + case Int8: + *(*int8)(ptr) = int8(bits) + case Int16: + *(*int16)(ptr) = int16(bits) + case Int, Int32: + *(*int32)(ptr) = int32(bits) + case Int64: + *(*int64)(ptr) = int64(bits) + case Uint8: + *(*uint8)(ptr) = uint8(bits) + case Uint16: + *(*uint16)(ptr) = uint16(bits) + case Uint, Uint32, Uintptr: + *(*uint32)(ptr) = uint32(bits) + case Uint64: + *(*uint64)(ptr) = uint64(bits) + } + return Value{typ, ptr, f | flagIndir | flag(typ.Kind())} +} + +func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { + if typ.Kind() != Func { + panic("reflect: call of MakeFunc with non-Func type") + } + + t := typ.common() + ftyp := (*funcType)(unsafe.Pointer(t)) + + fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + args := make([]Value, ftyp.NumIn()) + for i := range args { + argType := ftyp.In(i).common() + args[i] = makeValue(argType, arguments[i], 0) + } + resultsSlice := fn(args) + switch ftyp.NumOut() { + case 0: + return nil + case 1: + return resultsSlice[0].object() + default: + results := js.Global.Get("Array").New(ftyp.NumOut()) + for i, r := range resultsSlice { + results.SetIndex(i, r.object()) + } + return results + } + }) + + return Value{t, unsafe.Pointer(fv.Unsafe()), flag(Func)} +} + +func typedmemmove(t *rtype, dst, src unsafe.Pointer) { + js.InternalObject(dst).Call("$set", js.InternalObject(src).Call("$get")) +} + +func loadScalar(p unsafe.Pointer, n uintptr) uintptr { + return js.InternalObject(p).Call("$get").Unsafe() +} + +func makechan(typ *rtype, size int) (ch unsafe.Pointer) { + ctyp := (*chanType)(unsafe.Pointer(typ)) + return unsafe.Pointer(js.Global.Get("$Chan").New(jsType(ctyp.elem), size).Unsafe()) +} + +func makemap(t *rtype, cap int) (m unsafe.Pointer) { + return unsafe.Pointer(js.Global.Get("Map").New().Unsafe()) +} + +func keyFor(t *rtype, key unsafe.Pointer) (*js.Object, string) { + kv := js.InternalObject(key) + if kv.Get("$get") != js.Undefined { + kv = kv.Call("$get") + } + k := jsType(t.Key()).Call("keyFor", kv).String() + return kv, k +} + +func mapaccess(t *rtype, m, key unsafe.Pointer) unsafe.Pointer { + _, k := keyFor(t, key) + entry := js.InternalObject(m).Call("get", k) + if entry == js.Undefined { + return nil + } + return unsafe.Pointer(js.Global.Call("$newDataPointer", entry.Get("v"), jsType(PtrTo(t.Elem()))).Unsafe()) +} + +func mapassign(t *rtype, m, key, val unsafe.Pointer) { + kv, k := keyFor(t, key) + jsVal := js.InternalObject(val).Call("$get") + et := t.Elem() + if et.Kind() == Struct { + newVal := jsType(et).Call("zero") + copyStruct(newVal, jsVal, et) + jsVal = newVal + } + entry := js.Global.Get("Object").New() + entry.Set("k", kv) + entry.Set("v", jsVal) + js.InternalObject(m).Call("set", k, entry) +} + +func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) { + _, k := keyFor(t, key) + js.InternalObject(m).Call("delete", k) +} + +type mapIter struct { + t Type + m *js.Object + keys *js.Object + i int + + // last is the last object the iterator indicates. If this object exists, the functions that return the + // current key or value returns this object, regardless of the current iterator. It is because the current + // iterator might be stale due to key deletion in a loop. + last *js.Object +} + +func (iter *mapIter) skipUntilValidKey() { + for iter.i < iter.keys.Length() { + k := iter.keys.Index(iter.i) + if iter.m.Call("get", k) != js.Undefined { + break + } + // The key is already deleted. Move on the next item. + iter.i++ + } +} + +func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer { + return unsafe.Pointer(&mapIter{t, js.InternalObject(m), js.Global.Get("Array").Call("from", js.InternalObject(m).Call("keys")), 0, nil}) +} + +type TypeEx interface { + Type + Key() Type +} + +func mapiterkey(it unsafe.Pointer) unsafe.Pointer { + iter := (*mapIter)(it) + var kv *js.Object + if iter.last != nil { + kv = iter.last + } else { + iter.skipUntilValidKey() + if iter.i == iter.keys.Length() { + return nil + } + k := iter.keys.Index(iter.i) + kv = iter.m.Call("get", k) + + // Record the key-value pair for later accesses. + iter.last = kv + } + return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(iter.t.(TypeEx).Key()))).Unsafe()) +} + +func mapiternext(it unsafe.Pointer) { + iter := (*mapIter)(it) + iter.last = nil + iter.i++ +} + +func maplen(m unsafe.Pointer) int { + return js.InternalObject(m).Get("size").Int() +} + +func cvtDirect(v Value, typ Type) Value { + var srcVal = v.object() + if srcVal == jsType(v.typ).Get("nil") { + return makeValue(typ, jsType(typ).Get("nil"), v.flag) + } + + var val *js.Object + switch k := typ.Kind(); k { + case Slice: + slice := jsType(typ).New(srcVal.Get("$array")) + slice.Set("$offset", srcVal.Get("$offset")) + slice.Set("$length", srcVal.Get("$length")) + slice.Set("$capacity", srcVal.Get("$capacity")) + val = js.Global.Call("$newDataPointer", slice, jsType(PtrTo(typ))) + case Ptr: + if typ.Elem().Kind() == Struct { + if typ.Elem() == v.typ.Elem() { + val = srcVal + break + } + val = jsType(typ).New() + copyStruct(val, srcVal, typ.Elem()) + break + } + val = jsType(typ).New(srcVal.Get("$get"), srcVal.Get("$set")) + case Struct: + val = jsType(typ).Get("ptr").New() + copyStruct(val, srcVal, typ) + case Array, Bool, Chan, Func, Interface, Map, String: + val = js.InternalObject(v.ptr) + default: + panic(&ValueError{"reflect.Convert", k}) + } + return Value{typ.common(), unsafe.Pointer(val.Unsafe()), v.flag.ro() | v.flag&flagIndir | flag(typ.Kind())} +} + +func Copy(dst, src Value) int { + dk := dst.kind() + if dk != Array && dk != Slice { + panic(&ValueError{"reflect.Copy", dk}) + } + if dk == Array { + dst.mustBeAssignable() + } + dst.mustBeExported() + + sk := src.kind() + var stringCopy bool + if sk != Array && sk != Slice { + stringCopy = sk == String && dst.typ.Elem().Kind() == Uint8 + if !stringCopy { + panic(&ValueError{"reflect.Copy", sk}) + } + } + src.mustBeExported() + + if !stringCopy { + typesMustMatch("reflect.Copy", dst.typ.Elem(), src.typ.Elem()) + } + + dstVal := dst.object() + if dk == Array { + dstVal = jsType(SliceOf(dst.typ.Elem())).New(dstVal) + } + + srcVal := src.object() + if sk == Array { + srcVal = jsType(SliceOf(src.typ.Elem())).New(srcVal) + } + + if stringCopy { + return js.Global.Call("$copyString", dstVal, srcVal).Int() + } + return js.Global.Call("$copySlice", dstVal, srcVal).Int() +} + +func methodReceiver(op string, v Value, i int) (_ *rtype, t *funcType, fn unsafe.Pointer) { + var prop string + if v.typ.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.methods) { + panic("reflect: internal error: invalid method index") + } + m := &tt.methods[i] + if !tt.nameOff(m.name).isExported() { + panic("reflect: " + op + " of unexported method") + } + t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ))) + prop = tt.nameOff(m.name).name() + } else { + ms := v.typ.exportedMethods() + if uint(i) >= uint(len(ms)) { + panic("reflect: internal error: invalid method index") + } + m := ms[i] + if !v.typ.nameOff(m.name).isExported() { + panic("reflect: " + op + " of unexported method") + } + t = (*funcType)(unsafe.Pointer(v.typ.typeOff(m.mtyp))) + prop = js.Global.Call("$methodSet", jsType(v.typ)).Index(i).Get("prop").String() + } + rcvr := v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + fn = unsafe.Pointer(rcvr.Get(prop).Unsafe()) + return +} + +func valueInterface(v Value) interface{} { + if v.flag == 0 { + panic(&ValueError{"reflect.Value.Interface", 0}) + } + + if v.flag&flagMethod != 0 { + v = makeMethodValue("Interface", v) + } + + if isWrapped(v.typ) { + if v.flag&flagIndir != 0 && v.Kind() == Struct { + cv := jsType(v.typ).Call("zero") + copyStruct(cv, v.object(), v.typ) + return interface{}(unsafe.Pointer(jsType(v.typ).New(cv).Unsafe())) + } + return interface{}(unsafe.Pointer(jsType(v.typ).New(v.object()).Unsafe())) + } + return interface{}(unsafe.Pointer(v.object().Unsafe())) +} + +func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) { + js.InternalObject(dst).Call("$set", js.InternalObject(src)) +} + +func methodName() string { + return "?FIXME?" +} + +func makeMethodValue(op string, v Value) Value { + if v.flag&flagMethod == 0 { + panic("reflect: internal error: invalid use of makePartialFunc") + } + + _, _, fn := methodReceiver(op, v, int(v.flag)>>flagMethodShift) + rcvr := v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + return js.InternalObject(fn).Call("apply", rcvr, arguments) + }) + return Value{v.Type().common(), unsafe.Pointer(fv.Unsafe()), v.flag.ro() | flag(Func)} +} + +var jsObjectPtr = reflectType(js.Global.Get("$jsObjectPtr")) + +func wrapJsObject(typ Type, val *js.Object) *js.Object { + if typ == jsObjectPtr { + return jsType(jsObjectPtr).New(val) + } + return val +} + +func unwrapJsObject(typ Type, val *js.Object) *js.Object { + if typ == jsObjectPtr { + return val.Get("object") + } + return val +} + +func getJsTag(tag string) string { + for tag != "" { + // skip leading space + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + + // scan to colon. + // a space or a quote is a syntax error + i = 0 + for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' { + i++ + } + if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + name := string(tag[:i]) + tag = tag[i+1:] + + // scan quoted string to find value + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if name == "js" { + value, _ := unquote(qvalue) + return value + } + } + return "" +} + +// PtrTo returns the pointer type with element t. +// For example, if t represents type Foo, PtrTo(t) represents *Foo. +func PtrTo(t Type) Type { + return t.(*rtype).ptrTo() +} + +// copyVal returns a Value containing the map key or value at ptr, +// allocating a new variable as needed. +func copyVal(typ *rtype, fl flag, ptr unsafe.Pointer) Value { + if ifaceIndir(typ) { + // Copy result so future changes to the map + // won't change the underlying value. + c := unsafe_New(typ) + typedmemmove(typ, c, ptr) + return Value{typ, c, fl | flagIndir} + } + return Value{typ, *(*unsafe.Pointer)(ptr), fl} +} + +var selectHelper = js.Global.Get("$select").Interface().(func(...interface{}) *js.Object) + +func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool) { + comms := [][]*js.Object{{js.InternalObject(ch)}} + if nb { + comms = append(comms, []*js.Object{}) + } + selectRes := selectHelper(comms) + if nb && selectRes.Index(0).Int() == 1 { + return false, false + } + recvRes := selectRes.Index(1) + js.InternalObject(val).Call("$set", recvRes.Index(0)) + return true, recvRes.Index(1).Bool() +} + +func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool { + comms := [][]*js.Object{{js.InternalObject(ch), js.InternalObject(val).Call("$get")}} + if nb { + comms = append(comms, []*js.Object{}) + } + selectRes := selectHelper(comms) + if nb && selectRes.Index(0).Int() == 1 { + return false + } + return true +} + +func rselect(rselects []runtimeSelect) (chosen int, recvOK bool) { + comms := make([][]*js.Object, len(rselects)) + for i, s := range rselects { + switch SelectDir(s.dir) { + case SelectDefault: + comms[i] = []*js.Object{} + case SelectRecv: + ch := js.Global.Get("$chanNil") + if js.InternalObject(s.ch) != js.InternalObject(0) { + ch = js.InternalObject(s.ch) + } + comms[i] = []*js.Object{ch} + case SelectSend: + ch := js.Global.Get("$chanNil") + var val *js.Object + if js.InternalObject(s.ch) != js.InternalObject(0) { + ch = js.InternalObject(s.ch) + val = js.InternalObject(s.val).Call("$get") + } + comms[i] = []*js.Object{ch, val} + } + } + selectRes := selectHelper(comms) + c := selectRes.Index(0).Int() + if SelectDir(rselects[c].dir) == SelectRecv { + recvRes := selectRes.Index(1) + js.InternalObject(rselects[c].val).Call("$set", recvRes.Index(0)) + return c, recvRes.Index(1).Bool() + } + return c, false +} + +func DeepEqual(a1, a2 interface{}) bool { + i1 := js.InternalObject(a1) + i2 := js.InternalObject(a2) + if i1 == i2 { + return true + } + if i1 == nil || i2 == nil || i1.Get("constructor") != i2.Get("constructor") { + return false + } + return deepValueEqualJs(ValueOf(a1), ValueOf(a2), nil) +} + +func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { + if !v1.IsValid() || !v2.IsValid() { + return !v1.IsValid() && !v2.IsValid() + } + if v1.Type() != v2.Type() { + return false + } + if v1.Type() == jsObjectPtr { + return unwrapJsObject(jsObjectPtr, v1.object()) == unwrapJsObject(jsObjectPtr, v2.object()) + } + + switch v1.Kind() { + case Array, Map, Slice, Struct: + for _, entry := range visited { + if v1.ptr == entry[0] && v2.ptr == entry[1] { + return true + } + } + visited = append(visited, [2]unsafe.Pointer{v1.ptr, v2.ptr}) + } + + switch v1.Kind() { + case Array, Slice: + if v1.Kind() == Slice { + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.object() == v2.object() { + return true + } + } + var n = v1.Len() + if n != v2.Len() { + return false + } + for i := 0; i < n; i++ { + if !deepValueEqualJs(v1.Index(i), v2.Index(i), visited) { + return false + } + } + return true + case Interface: + if v1.IsNil() || v2.IsNil() { + return v1.IsNil() && v2.IsNil() + } + return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) + case Ptr: + return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) + case Struct: + var n = v1.NumField() + for i := 0; i < n; i++ { + if !deepValueEqualJs(v1.Field(i), v2.Field(i), visited) { + return false + } + } + return true + case Map: + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.object() == v2.object() { + return true + } + var keys = v1.MapKeys() + if len(keys) != v2.Len() { + return false + } + for _, k := range keys { + val1 := v1.MapIndex(k) + val2 := v2.MapIndex(k) + if !val1.IsValid() || !val2.IsValid() || !deepValueEqualJs(val1, val2, visited) { + return false + } + } + return true + case Func: + return v1.IsNil() && v2.IsNil() + case UnsafePointer: + return v1.object() == v2.object() + } + + return js.Global.Call("$interfaceIsEqual", js.InternalObject(valueInterface(v1)), js.InternalObject(valueInterface(v2))).Bool() +} diff --git a/compiler/natives/src/internal/reflectlite/swapper.go b/compiler/natives/src/internal/reflectlite/swapper.go new file mode 100644 index 00000000..b8827c22 --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/swapper.go @@ -0,0 +1,37 @@ +//go:build js +// +build js + +package reflectlite + +import "github.com/gopherjs/gopherjs/js" + +func Swapper(slice interface{}) func(i, j int) { + v := ValueOf(slice) + if v.Kind() != Slice { + panic(&ValueError{Method: "Swapper", Kind: v.Kind()}) + } + // Fast path for slices of size 0 and 1. Nothing to swap. + vLen := uint(v.Len()) + switch vLen { + case 0: + return func(i, j int) { panic("reflect: slice index out of range") } + case 1: + return func(i, j int) { + if i != 0 || j != 0 { + panic("reflect: slice index out of range") + } + } + } + a := js.InternalObject(slice).Get("$array") + off := js.InternalObject(slice).Get("$offset").Int() + return func(i, j int) { + if uint(i) >= vLen || uint(j) >= vLen { + panic("reflect: slice index out of range") + } + i += off + j += off + tmp := a.Index(i) + a.SetIndex(i, a.Index(j)) + a.SetIndex(j, tmp) + } +} diff --git a/compiler/natives/src/internal/reflectlite/type.go b/compiler/natives/src/internal/reflectlite/type.go new file mode 100644 index 00000000..0a41e862 --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/type.go @@ -0,0 +1,103 @@ +//go:build js +// +build js + +package reflectlite + +import ( + "unsafe" + + "github.com/gopherjs/gopherjs/js" +) + +func (t *rtype) Comparable() bool { + switch t.Kind() { + case Func, Slice, Map: + return false + case Array: + return t.Elem().Comparable() + case Struct: + for i := 0; i < t.NumField(); i++ { + ft := t.Field(i) + if !ft.typ.Comparable() { + return false + } + } + } + return true +} + +func (t *rtype) IsVariadic() bool { + if t.Kind() != Func { + panic("reflect: IsVariadic of non-func type") + } + tt := (*funcType)(unsafe.Pointer(t)) + return tt.outCount&(1<<15) != 0 +} + +func (t *rtype) kindType() *rtype { + return (*rtype)(unsafe.Pointer(js.InternalObject(t).Get(idKindType))) +} + +func (t *rtype) Field(i int) structField { + if t.Kind() != Struct { + panic("reflect: Field of non-struct type") + } + tt := (*structType)(unsafe.Pointer(t)) + if i < 0 || i >= len(tt.fields) { + panic("reflect: Field index out of bounds") + } + return tt.fields[i] +} + +func (t *rtype) Key() Type { + if t.Kind() != Map { + panic("reflect: Key of non-map type") + } + tt := (*mapType)(unsafe.Pointer(t)) + return toType(tt.key) +} + +func (t *rtype) NumField() int { + if t.Kind() != Struct { + panic("reflect: NumField of non-struct type") + } + tt := (*structType)(unsafe.Pointer(t)) + return len(tt.fields) +} + +func (t *rtype) Method(i int) (m Method) { + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.Method(i) + } + methods := t.exportedMethods() + if i < 0 || i >= len(methods) { + panic("reflect: Method index out of range") + } + p := methods[i] + pname := t.nameOff(p.name) + m.Name = pname.name() + fl := flag(Func) + mtyp := t.typeOff(p.mtyp) + ft := (*funcType)(unsafe.Pointer(mtyp)) + in := make([]Type, 0, 1+len(ft.in())) + in = append(in, t) + for _, arg := range ft.in() { + in = append(in, arg) + } + out := make([]Type, 0, len(ft.out())) + for _, ret := range ft.out() { + out = append(out, ret) + } + mt := FuncOf(in, out, ft.IsVariadic()) + m.Type = mt + prop := js.Global.Call("$methodSet", js.InternalObject(t).Get(idJsType)).Index(i).Get("prop").String() + fn := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + rcvr := arguments[0] + return rcvr.Get(prop).Call("apply", rcvr, arguments[1:]) + }) + m.Func = Value{mt.(*rtype), unsafe.Pointer(fn.Unsafe()), fl} + + m.Index = i + return m +} diff --git a/compiler/natives/src/internal/reflectlite/utils.go b/compiler/natives/src/internal/reflectlite/utils.go new file mode 100644 index 00000000..f7418247 --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/utils.go @@ -0,0 +1,101 @@ +//go:build js +// +build js + +package reflectlite + +import ( + "unsafe" +) + +type ChanDir int + +const ( + RecvDir ChanDir = 1 << iota // <-chan + SendDir // chan<- + BothDir = RecvDir | SendDir // chan +) + +type errorString struct { + s string +} + +func (e *errorString) Error() string { + return e.s +} + +var ( + ErrSyntax = &errorString{"invalid syntax"} +) + +func unquote(s string) (string, error) { + if len(s) < 2 { + return s, nil + } + if s[0] == '\'' || s[0] == '"' { + if s[len(s)-1] == s[0] { + return s[1 : len(s)-1], nil + } + return "", ErrSyntax + } + return s, nil +} + +// Method represents a single method. +type Method struct { + // Name is the method name. + // PkgPath is the package path that qualifies a lower case (unexported) + // method name. It is empty for upper case (exported) method names. + // The combination of PkgPath and Name uniquely identifies a method + // in a method set. + // See https://golang.org/ref/spec#Uniqueness_of_identifiers + Name string + PkgPath string + + Type Type // method type + Func Value // func with receiver as first argument + Index int // index for Type.Method +} + +// A SelectDir describes the communication direction of a select case. +type SelectDir int + +// NOTE: These values must match ../runtime/select.go:/selectDir. + +const ( + _ SelectDir = iota + SelectSend // case Chan <- Send + SelectRecv // case <-Chan: + SelectDefault // default +) + +// A runtimeSelect is a single case passed to rselect. +// This must match ../runtime/select.go:/runtimeSelect +type runtimeSelect struct { + dir SelectDir // SelectSend, SelectRecv or SelectDefault + typ *rtype // channel type + ch unsafe.Pointer // channel + val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir) +} + +func (f flag) mustBe(expected Kind) { + // TODO(mvdan): use f.kind() again once mid-stack inlining gets better + if Kind(f&flagKindMask) != expected { + panic(&ValueError{methodName(), f.kind()}) + } +} + +// A StructTag is the tag string in a struct field. +// +// By convention, tag strings are a concatenation of +// optionally space-separated key:"value" pairs. +// Each key is a non-empty string consisting of non-control +// characters other than space (U+0020 ' '), quote (U+0022 '"'), +// and colon (U+003A ':'). Each value is quoted using U+0022 '"' +// characters and Go string literal syntax. +type StructTag string + +func typesMustMatch(what string, t1, t2 Type) { + if t1 != t2 { + panic(what + ": " + t1.String() + " != " + t2.String()) + } +} diff --git a/compiler/natives/src/internal/reflectlite/value.go b/compiler/natives/src/internal/reflectlite/value.go new file mode 100644 index 00000000..32d31072 --- /dev/null +++ b/compiler/natives/src/internal/reflectlite/value.go @@ -0,0 +1,586 @@ +//go:build js +// +build js + +package reflectlite + +import ( + "unsafe" + + "github.com/gopherjs/gopherjs/js" +) + +func (v Value) object() *js.Object { + if v.typ.Kind() == Array || v.typ.Kind() == Struct { + return js.InternalObject(v.ptr) + } + if v.flag&flagIndir != 0 { + val := js.InternalObject(v.ptr).Call("$get") + if val != js.Global.Get("$ifaceNil") && val.Get("constructor") != jsType(v.typ) { + switch v.typ.Kind() { + case Uint64, Int64: + val = jsType(v.typ).New(val.Get("$high"), val.Get("$low")) + case Complex64, Complex128: + val = jsType(v.typ).New(val.Get("$real"), val.Get("$imag")) + case Slice: + if val == val.Get("constructor").Get("nil") { + val = jsType(v.typ).Get("nil") + break + } + newVal := jsType(v.typ).New(val.Get("$array")) + newVal.Set("$offset", val.Get("$offset")) + newVal.Set("$length", val.Get("$length")) + newVal.Set("$capacity", val.Get("$capacity")) + val = newVal + } + } + return js.InternalObject(val.Unsafe()) + } + return js.InternalObject(v.ptr) +} + +func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value { + if v.flag&flagMethod != 0 { + v = makeMethodValue(context, v) + } + switch { + case directlyAssignable(dst, v.typ): + // Overwrite type so that they match. + // Same memory layout, so no harm done. + fl := v.flag&(flagAddr|flagIndir) | v.flag.ro() + fl |= flag(dst.Kind()) + return Value{dst, v.ptr, fl} + + case implements(dst, v.typ): + if target == nil { + target = unsafe_New(dst) + } + // GopherJS: Skip the v.Kind() == Interface && v.IsNil() if statement + // from upstream. ifaceE2I below does not panic, and it needs + // to run, given its custom implementation. + x := valueInterface(v) + if dst.NumMethod() == 0 { + *(*interface{})(target) = x + } else { + ifaceE2I(dst, x, target) + } + return Value{dst, target, flagIndir | flag(Interface)} + } + + // Failed. + panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String()) +} + +var callHelper = js.Global.Get("$call").Interface().(func(...interface{}) *js.Object) + +func (v Value) call(op string, in []Value) []Value { + var ( + t *funcType + fn unsafe.Pointer + rcvr *js.Object + ) + if v.flag&flagMethod != 0 { + _, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) + rcvr = v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + } else { + t = (*funcType)(unsafe.Pointer(v.typ)) + fn = unsafe.Pointer(v.object().Unsafe()) + rcvr = js.Undefined + } + + if fn == nil { + panic("reflect.Value.Call: call of nil function") + } + + isSlice := op == "CallSlice" + n := t.NumIn() + if isSlice { + if !t.IsVariadic() { + panic("reflect: CallSlice of non-variadic function") + } + if len(in) < n { + panic("reflect: CallSlice with too few input arguments") + } + if len(in) > n { + panic("reflect: CallSlice with too many input arguments") + } + } else { + if t.IsVariadic() { + n-- + } + if len(in) < n { + panic("reflect: Call with too few input arguments") + } + if !t.IsVariadic() && len(in) > n { + panic("reflect: Call with too many input arguments") + } + } + for _, x := range in { + if x.Kind() == Invalid { + panic("reflect: " + op + " using zero Value argument") + } + } + for i := 0; i < n; i++ { + if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) { + panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String()) + } + } + if !isSlice && t.IsVariadic() { + // prepare slice for remaining values + m := len(in) - n + slice := MakeSlice(t.In(n), m, m) + elem := t.In(n).Elem() + for i := 0; i < m; i++ { + x := in[n+i] + if xt := x.Type(); !xt.AssignableTo(elem) { + panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op) + } + slice.Index(i).Set(x) + } + origIn := in + in = make([]Value, n+1) + copy(in[:n], origIn) + in[n] = slice + } + + nin := len(in) + if nin != t.NumIn() { + panic("reflect.Value.Call: wrong argument count") + } + nout := t.NumOut() + + argsArray := js.Global.Get("Array").New(t.NumIn()) + for i, arg := range in { + argsArray.SetIndex(i, unwrapJsObject(t.In(i), arg.assignTo("reflect.Value.Call", t.In(i).common(), nil).object())) + } + results := callHelper(js.InternalObject(fn), rcvr, argsArray) + + switch nout { + case 0: + return nil + case 1: + return []Value{makeValue(t.Out(0), wrapJsObject(t.Out(0), results), 0)} + default: + ret := make([]Value, nout) + for i := range ret { + ret[i] = makeValue(t.Out(i), wrapJsObject(t.Out(i), results.Index(i)), 0) + } + return ret + } +} + +func (v Value) Cap() int { + k := v.kind() + switch k { + case Array: + return v.typ.Len() + case Chan, Slice: + return v.object().Get("$capacity").Int() + } + panic(&ValueError{"reflect.Value.Cap", k}) +} + +func (v Value) Index(i int) Value { + switch k := v.kind(); k { + case Array: + tt := (*arrayType)(unsafe.Pointer(v.typ)) + if i < 0 || i > int(tt.len) { + panic("reflect: array index out of range") + } + typ := tt.elem + fl := v.flag&(flagIndir|flagAddr) | v.flag.ro() | flag(typ.Kind()) + + a := js.InternalObject(v.ptr) + if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), + js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), + ).Unsafe()), fl} + } + return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) + + case Slice: + s := v.object() + if i < 0 || i >= s.Get("$length").Int() { + panic("reflect: slice index out of range") + } + tt := (*sliceType)(unsafe.Pointer(v.typ)) + typ := tt.elem + fl := flagAddr | flagIndir | v.flag.ro() | flag(typ.Kind()) + + i += s.Get("$offset").Int() + a := s.Get("$array") + if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), + js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), + ).Unsafe()), fl} + } + return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) + + case String: + str := *(*string)(v.ptr) + if i < 0 || i >= len(str) { + panic("reflect: string index out of range") + } + fl := v.flag.ro() | flag(Uint8) | flagIndir + c := str[i] + return Value{uint8Type, unsafe.Pointer(&c), fl} + + default: + panic(&ValueError{"reflect.Value.Index", k}) + } +} + +func (v Value) InterfaceData() [2]uintptr { + panic("InterfaceData is not supported by GopherJS") +} + +func (v Value) IsNil() bool { + switch k := v.kind(); k { + case Ptr, Slice: + return v.object() == jsType(v.typ).Get("nil") + case Chan: + return v.object() == js.Global.Get("$chanNil") + case Func: + return v.object() == js.Global.Get("$throwNilPointerError") + case Map: + return v.object() == js.InternalObject(false) + case Interface: + return v.object() == js.Global.Get("$ifaceNil") + case UnsafePointer: + return v.object().Unsafe() == 0 + default: + panic(&ValueError{"reflect.Value.IsNil", k}) + } +} + +func (v Value) Len() int { + switch k := v.kind(); k { + case Array, String: + return v.object().Length() + case Slice: + return v.object().Get("$length").Int() + case Chan: + return v.object().Get("$buffer").Get("length").Int() + case Map: + return v.object().Get("size").Int() + default: + panic(&ValueError{"reflect.Value.Len", k}) + } +} + +func (v Value) Pointer() uintptr { + switch k := v.kind(); k { + case Chan, Map, Ptr, UnsafePointer: + if v.IsNil() { + return 0 + } + return v.object().Unsafe() + case Func: + if v.IsNil() { + return 0 + } + return 1 + case Slice: + if v.IsNil() { + return 0 + } + return v.object().Get("$array").Unsafe() + default: + panic(&ValueError{"reflect.Value.Pointer", k}) + } +} + +func (v Value) Set(x Value) { + v.mustBeAssignable() + x.mustBeExported() + x = x.assignTo("reflect.Set", v.typ, nil) + if v.flag&flagIndir != 0 { + switch v.typ.Kind() { + case Array: + jsType(v.typ).Call("copy", js.InternalObject(v.ptr), js.InternalObject(x.ptr)) + case Interface: + js.InternalObject(v.ptr).Call("$set", js.InternalObject(valueInterface(x))) + case Struct: + copyStruct(js.InternalObject(v.ptr), js.InternalObject(x.ptr), v.typ) + default: + js.InternalObject(v.ptr).Call("$set", x.object()) + } + return + } + v.ptr = x.ptr +} + +func (v Value) SetBytes(x []byte) { + v.mustBeAssignable() + v.mustBe(Slice) + if v.typ.Elem().Kind() != Uint8 { + panic("reflect.Value.SetBytes of non-byte slice") + } + slice := js.InternalObject(x) + if v.typ.Name() != "" || v.typ.Elem().Name() != "" { + typedSlice := jsType(v.typ).New(slice.Get("$array")) + typedSlice.Set("$offset", slice.Get("$offset")) + typedSlice.Set("$length", slice.Get("$length")) + typedSlice.Set("$capacity", slice.Get("$capacity")) + slice = typedSlice + } + js.InternalObject(v.ptr).Call("$set", slice) +} + +func (v Value) SetCap(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := js.InternalObject(v.ptr).Call("$get") + if n < s.Get("$length").Int() || n > s.Get("$capacity").Int() { + panic("reflect: slice capacity out of range in SetCap") + } + newSlice := jsType(v.typ).New(s.Get("$array")) + newSlice.Set("$offset", s.Get("$offset")) + newSlice.Set("$length", s.Get("$length")) + newSlice.Set("$capacity", n) + js.InternalObject(v.ptr).Call("$set", newSlice) +} + +func (v Value) SetLen(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := js.InternalObject(v.ptr).Call("$get") + if n < 0 || n > s.Get("$capacity").Int() { + panic("reflect: slice length out of range in SetLen") + } + newSlice := jsType(v.typ).New(s.Get("$array")) + newSlice.Set("$offset", s.Get("$offset")) + newSlice.Set("$length", n) + newSlice.Set("$capacity", s.Get("$capacity")) + js.InternalObject(v.ptr).Call("$set", newSlice) +} + +func (v Value) Slice(i, j int) Value { + var ( + cap int + typ Type + s *js.Object + ) + switch kind := v.kind(); kind { + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = SliceOf(tt.elem) + s = jsType(typ).New(v.object()) + + case Slice: + typ = v.typ + s = v.object() + cap = s.Get("$capacity").Int() + + case String: + str := *(*string)(v.ptr) + if i < 0 || j < i || j > len(str) { + panic("reflect.Value.Slice: string slice index out of bounds") + } + return ValueOf(str[i:j]) + + default: + panic(&ValueError{"reflect.Value.Slice", kind}) + } + + if i < 0 || j < i || j > cap { + panic("reflect.Value.Slice: slice index out of bounds") + } + + return makeValue(typ, js.Global.Call("$subslice", s, i, j), v.flag.ro()) +} + +func (v Value) Slice3(i, j, k int) Value { + var ( + cap int + typ Type + s *js.Object + ) + switch kind := v.kind(); kind { + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = SliceOf(tt.elem) + s = jsType(typ).New(v.object()) + + case Slice: + typ = v.typ + s = v.object() + cap = s.Get("$capacity").Int() + + default: + panic(&ValueError{"reflect.Value.Slice3", kind}) + } + + if i < 0 || j < i || k < j || k > cap { + panic("reflect.Value.Slice3: slice index out of bounds") + } + + return makeValue(typ, js.Global.Call("$subslice", s, i, j, k), v.flag.ro()) +} + +func (v Value) Close() { + v.mustBe(Chan) + v.mustBeExported() + js.Global.Call("$close", v.object()) +} + +func (v Value) Elem() Value { + switch k := v.kind(); k { + case Interface: + val := v.object() + if val == js.Global.Get("$ifaceNil") { + return Value{} + } + typ := reflectType(val.Get("constructor")) + return makeValue(typ, val.Get("$val"), v.flag.ro()) + + case Ptr: + if v.IsNil() { + return Value{} + } + val := v.object() + tt := (*ptrType)(unsafe.Pointer(v.typ)) + fl := v.flag&flagRO | flagIndir | flagAddr + fl |= flag(tt.elem.Kind()) + return Value{tt.elem, unsafe.Pointer(wrapJsObject(tt.elem, val).Unsafe()), fl} + + default: + panic(&ValueError{"reflect.Value.Elem", k}) + } +} + +// NumField returns the number of fields in the struct v. +// It panics if v's Kind is not Struct. +func (v Value) NumField() int { + v.mustBe(Struct) + tt := (*structType)(unsafe.Pointer(v.typ)) + return len(tt.fields) +} + +// MapKeys returns a slice containing all the keys present in the map, +// in unspecified order. +// It panics if v's Kind is not Map. +// It returns an empty slice if v represents a nil map. +func (v Value) MapKeys() []Value { + v.mustBe(Map) + tt := (*mapType)(unsafe.Pointer(v.typ)) + keyType := tt.key + + fl := v.flag.ro() | flag(keyType.Kind()) + + m := v.pointer() + mlen := int(0) + if m != nil { + mlen = maplen(m) + } + it := mapiterinit(v.typ, m) + a := make([]Value, mlen) + var i int + for i = 0; i < len(a); i++ { + key := mapiterkey(it) + if key == nil { + // Someone deleted an entry from the map since we + // called maplen above. It's a data race, but nothing + // we can do about it. + break + } + a[i] = copyVal(keyType, fl, key) + mapiternext(it) + } + return a[:i] +} + +// MapIndex returns the value associated with key in the map v. +// It panics if v's Kind is not Map. +// It returns the zero Value if key is not found in the map or if v represents a nil map. +// As in Go, the key's value must be assignable to the map's key type. +func (v Value) MapIndex(key Value) Value { + v.mustBe(Map) + tt := (*mapType)(unsafe.Pointer(v.typ)) + + // Do not require key to be exported, so that DeepEqual + // and other programs can use all the keys returned by + // MapKeys as arguments to MapIndex. If either the map + // or the key is unexported, though, the result will be + // considered unexported. This is consistent with the + // behavior for structs, which allow read but not write + // of unexported fields. + key = key.assignTo("reflect.Value.MapIndex", tt.key, nil) + + var k unsafe.Pointer + if key.flag&flagIndir != 0 { + k = key.ptr + } else { + k = unsafe.Pointer(&key.ptr) + } + e := mapaccess(v.typ, v.pointer(), k) + if e == nil { + return Value{} + } + typ := tt.elem + fl := (v.flag | key.flag).ro() + fl |= flag(typ.Kind()) + return copyVal(typ, fl, e) +} + +func (v Value) Field(i int) Value { + if v.kind() != Struct { + panic(&ValueError{"reflect.Value.Field", v.kind()}) + } + tt := (*structType)(unsafe.Pointer(v.typ)) + if uint(i) >= uint(len(tt.fields)) { + panic("reflect: Field index out of range") + } + + prop := jsType(v.typ).Get("fields").Index(i).Get("prop").String() + field := &tt.fields[i] + typ := field.typ + + fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind()) + if !field.name.isExported() { + if field.embedded() { + fl |= flagEmbedRO + } else { + fl |= flagStickyRO + } + } + + if tag := tt.fields[i].name.tag(); tag != "" && i != 0 { + if jsTag := getJsTag(tag); jsTag != "" { + for { + v = v.Field(0) + if v.typ == jsObjectPtr { + o := v.object().Get("object") + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return js.Global.Call("$internalize", o.Get(jsTag), jsType(typ)) }), + js.InternalObject(func(x *js.Object) { o.Set(jsTag, js.Global.Call("$externalize", x, jsType(typ))) }), + ).Unsafe()), fl} + } + if v.typ.Kind() == Ptr { + v = v.Elem() + } + } + } + } + + s := js.InternalObject(v.ptr) + if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return wrapJsObject(typ, s.Get(prop)) }), + js.InternalObject(func(x *js.Object) { s.Set(prop, unwrapJsObject(typ, x)) }), + ).Unsafe()), fl} + } + return makeValue(typ, wrapJsObject(typ, s.Get(prop)), fl) +} diff --git a/compiler/natives/src/internal/syscall/unix/unix.go b/compiler/natives/src/internal/syscall/unix/unix.go deleted file mode 100644 index f8a620d3..00000000 --- a/compiler/natives/src/internal/syscall/unix/unix.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build js - -package unix - -const randomTrap = 0 -const fstatatTrap = 0 - -func IsNonblock(fd int) (nonblocking bool, err error) { - return false, nil -} diff --git a/compiler/natives/src/internal/testenv/testenv.go b/compiler/natives/src/internal/testenv/testenv.go deleted file mode 100644 index 481414e0..00000000 --- a/compiler/natives/src/internal/testenv/testenv.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build js - -package testenv - -import ( - "runtime" - "strings" -) - -// HasExec reports whether the current system can start new processes -// using os.StartProcess or (more commonly) exec.Command. -func HasExec() bool { - switch runtime.GOOS { - case "nacl": - return false - case "darwin": - if strings.HasPrefix(runtime.GOARCH, "arm") { - return false - } - } - switch runtime.GOARCH { - case "js": - return false - } - return true -} diff --git a/compiler/natives/src/internal/unsafeheader/unsafeheader_test.go b/compiler/natives/src/internal/unsafeheader/unsafeheader_test.go new file mode 100644 index 00000000..f20cf31f --- /dev/null +++ b/compiler/natives/src/internal/unsafeheader/unsafeheader_test.go @@ -0,0 +1,10 @@ +//go:build js +// +build js + +package unsafeheader_test + +import "testing" + +func TestWriteThroughHeader(t *testing.T) { + t.Skip("GopherJS uses different slice and string implementation than internal/unsafeheader.") +} diff --git a/compiler/natives/src/io/io_test.go b/compiler/natives/src/io/io_test.go index a436fe02..d746b370 100644 --- a/compiler/natives/src/io/io_test.go +++ b/compiler/natives/src/io/io_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package io_test @@ -10,14 +11,13 @@ func TestMultiWriter_WriteStringSingleAlloc(t *testing.T) { t.Skip() } -func TestMultiReaderFlatten(t *testing.T) { - t.Skip("test relies on runtime.Callers and runtime.CallersFrames, which GopherJS doesn't support") -} - -func TestMultiWriterSingleChainFlatten(t *testing.T) { - t.Skip("test relies on runtime.Callers and runtime.CallersFrames, which GopherJS doesn't support") -} - func TestMultiReaderFreesExhaustedReaders(t *testing.T) { t.Skip("test relies on runtime.SetFinalizer, which GopherJS does not implement") } + +func TestCopyLargeWriter(t *testing.T) { + // This test actually behaves more or less correctly, but it triggers a + // different code path that panics instead of returning an error due to a bug + // referenced below. + t.Skip("https://github.com/gopherjs/gopherjs/issues/1003") +} diff --git a/compiler/natives/src/math/big/big.go b/compiler/natives/src/math/big/big.go index 83374757..9e068d5c 100644 --- a/compiler/natives/src/math/big/big.go +++ b/compiler/natives/src/math/big/big.go @@ -1,3 +1,4 @@ +//go:build js // +build js package big diff --git a/compiler/natives/src/math/big/big_test.go b/compiler/natives/src/math/big/big_test.go index 78209562..acad9a04 100644 --- a/compiler/natives/src/math/big/big_test.go +++ b/compiler/natives/src/math/big/big_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package big @@ -11,3 +12,7 @@ func TestBytes(t *testing.T) { func TestModSqrt(t *testing.T) { t.Skip("slow") } + +func TestLinkerGC(t *testing.T) { + t.Skip("The test is specific to GC's linker.") +} diff --git a/compiler/natives/src/math/bits/bits.go b/compiler/natives/src/math/bits/bits.go index d29fcf07..b434603a 100644 --- a/compiler/natives/src/math/bits/bits.go +++ b/compiler/natives/src/math/bits/bits.go @@ -1,3 +1,4 @@ +//go:build js // +build js package bits @@ -16,3 +17,86 @@ var ( overflowError error = _err("runtime error: integer overflow") divideError error = _err("runtime error: integer divide by zero") ) + +func Mul32(x, y uint32) (hi, lo uint32) { + // Avoid slow 64-bit integers for better performance. Adapted from Mul64(). + const mask16 = 1<<16 - 1 + x0 := x & mask16 + x1 := x >> 16 + y0 := y & mask16 + y1 := y >> 16 + w0 := x0 * y0 + t := x1*y0 + w0>>16 + w1 := t & mask16 + w2 := t >> 16 + w1 += x0 * y1 + hi = x1*y1 + w2 + w1>>16 + lo = x * y + return +} + +func Add32(x, y, carry uint32) (sum, carryOut uint32) { + // Avoid slow 64-bit integers for better performance. Adapted from Add64(). + sum = x + y + carry + carryOut = ((x & y) | ((x | y) &^ sum)) >> 31 + return +} + +func Div32(hi, lo, y uint32) (quo, rem uint32) { + // Avoid slow 64-bit integers for better performance. Adapted from Div64(). + const ( + two16 = 1 << 16 + mask16 = two16 - 1 + ) + if y == 0 { + panic(divideError) + } + if y <= hi { + panic(overflowError) + } + + s := uint(LeadingZeros32(y)) + y <<= s + + yn1 := y >> 16 + yn0 := y & mask16 + un16 := hi<>(32-s) + un10 := lo << s + un1 := un10 >> 16 + un0 := un10 & mask16 + q1 := un16 / yn1 + rhat := un16 - q1*yn1 + + for q1 >= two16 || q1*yn0 > two16*rhat+un1 { + q1-- + rhat += yn1 + if rhat >= two16 { + break + } + } + + un21 := un16*two16 + un1 - q1*y + q0 := un21 / yn1 + rhat = un21 - q0*yn1 + + for q0 >= two16 || q0*yn0 > two16*rhat+un0 { + q0-- + rhat += yn1 + if rhat >= two16 { + break + } + } + + return q1*two16 + q0, (un21*two16 + un0 - q0*y) >> s +} + +func Rem32(hi, lo, y uint32) uint32 { + // We scale down hi so that hi < y, then use Div32 to compute the + // rem with the guarantee that it won't panic on quotient overflow. + // Given that + // hi ≡ hi%y (mod y) + // we have + // hi<<64 + lo ≡ (hi%y)<<64 + lo (mod y) + _, rem := Div32(hi%y, lo, y) + return rem +} diff --git a/compiler/natives/src/math/math.go b/compiler/natives/src/math/math.go index ca09bfcd..d1ed66ed 100644 --- a/compiler/natives/src/math/math.go +++ b/compiler/natives/src/math/math.go @@ -1,3 +1,4 @@ +//go:build js // +build js package math @@ -7,10 +8,15 @@ import ( ) var math = js.Global.Get("Math") -var zero float64 = 0 -var posInf = 1 / zero -var negInf = -1 / zero -var nan = 0 / zero +var _zero float64 = 0 +var posInf = 1 / _zero +var negInf = -1 / _zero + +// Usually, NaN can be obtained in JavaScript with `0 / 0` operation. However, +// in V8, `0 / _zero` yields a bitwise-different value of NaN compared to the +// default NaN or `0 / 0`. Unfortunately, Go compiler forbids division by zero, +// so we have to get this value from prelude. +var nan = js.Global.Get("$NaN").Float() func Acos(x float64) float64 { return math.Call("acos", x).Float() diff --git a/compiler/natives/src/math/math_test.go b/compiler/natives/src/math/math_test.go index daf35dda..eb62dd1d 100644 --- a/compiler/natives/src/math/math_test.go +++ b/compiler/natives/src/math/math_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package math_test @@ -6,12 +7,13 @@ import ( "testing" ) -// Slighly higher tolerances than upstream, otherwise TestGamma fails. +// Slightly higher tolerances than upstream, otherwise TestGamma fails. // TODO: Is there a better way to fix TestGamma? It's weird that only one test -// requires increasing tolerances. Perhaps there's a better fix? Maybe we -// should override TestGamma specifically and not the package-wide tolerances, -// because this will cause many other tests to be less accurate. Or maybe this -// is fine? +// +// requires increasing tolerances. Perhaps there's a better fix? Maybe we +// should override TestGamma specifically and not the package-wide tolerances, +// because this will cause many other tests to be less accurate. Or maybe this +// is fine? func close(a, b float64) bool { return tolerance(a, b, 4e-14) } func veryclose(a, b float64) bool { return tolerance(a, b, 6e-15) } diff --git a/compiler/natives/src/math/rand/rand_test.go b/compiler/natives/src/math/rand/rand_test.go index 0475334d..2246dfec 100644 --- a/compiler/natives/src/math/rand/rand_test.go +++ b/compiler/natives/src/math/rand/rand_test.go @@ -1,6 +1,7 @@ +//go:build js // +build js -package rand +package rand_test import "testing" diff --git a/compiler/natives/src/net/fastrand.go b/compiler/natives/src/net/fastrand.go new file mode 100644 index 00000000..861217a9 --- /dev/null +++ b/compiler/natives/src/net/fastrand.go @@ -0,0 +1,11 @@ +//go:build js +// +build js + +package net + +import ( + _ "unsafe" // For go:linkname +) + +//go:linkname fastrand runtime.fastrand +func fastrand() uint32 diff --git a/compiler/natives/src/net/http/client_test.go b/compiler/natives/src/net/http/client_test.go new file mode 100644 index 00000000..302b800d --- /dev/null +++ b/compiler/natives/src/net/http/client_test.go @@ -0,0 +1,21 @@ +//go:build js + +package http_test + +import ( + "testing" +) + +func testClientTimeout(t *testing.T, h2 bool) { + // The original test expects Client.Timeout error to be returned, but under + // GopherJS an "i/o timeout" error is frequently returned. Otherwise the test + // seems to be working correctly. + t.Skip("Flaky test under GopherJS.") +} + +func testClientTimeout_Headers(t *testing.T, h2 bool) { + // The original test expects Client.Timeout error to be returned, but under + // GopherJS an "i/o timeout" error is frequently returned. Otherwise the test + // seems to be working correctly. + t.Skip("Flaky test under GopherJS.") +} diff --git a/compiler/natives/src/net/http/clientserver_test.go b/compiler/natives/src/net/http/clientserver_test.go new file mode 100644 index 00000000..35b44dd4 --- /dev/null +++ b/compiler/natives/src/net/http/clientserver_test.go @@ -0,0 +1,16 @@ +//go:build js && wasm +// +build js,wasm + +package http_test + +import ( + "testing" +) + +func testTransportGCRequest(t *testing.T, h2, body bool) { + t.Skip("The test relies on runtime.SetFinalizer(), which is not supported by GopherJS.") +} + +func testWriteHeaderAfterWrite(t *testing.T, h2, hijack bool) { + t.Skip("GopherJS source maps don't preserve original function names in stack traces, which this test relied on.") +} diff --git a/compiler/natives/src/net/http/cookiejar/example_test.go b/compiler/natives/src/net/http/cookiejar/example_test.go index ab42bef6..09de5d0c 100644 --- a/compiler/natives/src/net/http/cookiejar/example_test.go +++ b/compiler/natives/src/net/http/cookiejar/example_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package cookiejar_test diff --git a/compiler/natives/src/net/http/fetch.go b/compiler/natives/src/net/http/fetch.go deleted file mode 100644 index f9123d09..00000000 --- a/compiler/natives/src/net/http/fetch.go +++ /dev/null @@ -1,134 +0,0 @@ -// +build js - -package http - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "strconv" - - "github.com/gopherjs/gopherjs/js" -) - -// streamReader implements an io.ReadCloser wrapper for ReadableStream of https://fetch.spec.whatwg.org/. -type streamReader struct { - pending []byte - stream *js.Object -} - -func (r *streamReader) Read(p []byte) (n int, err error) { - if len(r.pending) == 0 { - var ( - bCh = make(chan []byte) - errCh = make(chan error) - ) - r.stream.Call("read").Call("then", - func(result *js.Object) { - if result.Get("done").Bool() { - errCh <- io.EOF - return - } - bCh <- result.Get("value").Interface().([]byte) - }, - func(reason *js.Object) { - // Assumes it's a DOMException. - errCh <- errors.New(reason.Get("message").String()) - }, - ) - select { - case b := <-bCh: - r.pending = b - case err := <-errCh: - return 0, err - } - } - n = copy(p, r.pending) - r.pending = r.pending[n:] - return n, nil -} - -func (r *streamReader) Close() error { - // This ignores any error returned from cancel method. So far, I did not encounter any concrete - // situation where reporting the error is meaningful. Most users ignore error from resp.Body.Close(). - // If there's a need to report error here, it can be implemented and tested when that need comes up. - r.stream.Call("cancel") - return nil -} - -// fetchTransport is a RoundTripper that is implemented using Fetch API. It supports streaming -// response bodies. -type fetchTransport struct{} - -func (t *fetchTransport) RoundTrip(req *Request) (*Response, error) { - headers := js.Global.Get("Headers").New() - for key, values := range req.Header { - for _, value := range values { - headers.Call("append", key, value) - } - } - opt := map[string]interface{}{ - "method": req.Method, - "headers": headers, - "credentials": "same-origin", - } - if req.Body != nil { - // TODO: Find out if request body can be streamed into the fetch request rather than in advance here. - // See BufferSource at https://fetch.spec.whatwg.org/#body-mixin. - body, err := ioutil.ReadAll(req.Body) - if err != nil { - req.Body.Close() // RoundTrip must always close the body, including on errors. - return nil, err - } - req.Body.Close() - opt["body"] = body - } - respPromise := js.Global.Call("fetch", req.URL.String(), opt) - - var ( - respCh = make(chan *Response) - errCh = make(chan error) - ) - respPromise.Call("then", - func(result *js.Object) { - header := Header{} - result.Get("headers").Call("forEach", func(value, key *js.Object) { - ck := CanonicalHeaderKey(key.String()) - header[ck] = append(header[ck], value.String()) - }) - - contentLength := int64(-1) - if cl, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil { - contentLength = cl - } - - select { - case respCh <- &Response{ - Status: result.Get("status").String() + " " + StatusText(result.Get("status").Int()), - StatusCode: result.Get("status").Int(), - Header: header, - ContentLength: contentLength, - Body: &streamReader{stream: result.Get("body").Call("getReader")}, - Request: req, - }: - case <-req.Context().Done(): - } - }, - func(reason *js.Object) { - select { - case errCh <- fmt.Errorf("net/http: fetch() failed: %s", reason.String()): - case <-req.Context().Done(): - } - }, - ) - select { - case <-req.Context().Done(): - // TODO: Abort request if possible using Fetch API. - return nil, errors.New("net/http: request canceled") - case resp := <-respCh: - return resp, nil - case err := <-errCh: - return nil, err - } -} diff --git a/compiler/natives/src/net/http/http.go b/compiler/natives/src/net/http/http.go index 105f7283..7843235b 100644 --- a/compiler/natives/src/net/http/http.go +++ b/compiler/natives/src/net/http/http.go @@ -1,3 +1,4 @@ +//go:build js // +build js package http @@ -15,8 +16,9 @@ import ( var DefaultTransport = func() RoundTripper { switch { - case js.Global.Get("fetch") != js.Undefined && js.Global.Get("ReadableStream") != js.Undefined: // ReadableStream is used as a check for support of streaming response bodies, see https://fetch.spec.whatwg.org/#streams. - return &fetchTransport{} + case js.Global.Get("fetch") != js.Undefined: + // Use standard library js/wasm fetch-based implementation. + return &Transport{} case js.Global.Get("XMLHttpRequest") != js.Undefined: return &XHRTransport{} default: diff --git a/compiler/natives/src/net/http/http_wasm_test.go b/compiler/natives/src/net/http/http_wasm_test.go new file mode 100644 index 00000000..d078c0ea --- /dev/null +++ b/compiler/natives/src/net/http/http_wasm_test.go @@ -0,0 +1,16 @@ +//go:build js && wasm +// +build js,wasm + +package http + +func init() { + // Use standard transport with fake networking under tests. Although GopherJS + // supports "real" http.Client implementations using Fetch or XMLHttpRequest + // APIs, tests also need to start local web servers, which is not supported + // for those APIs. + // TODO(nevkontakte): We could test our real implementations if we mock out + // browser APIs and redirect them to the fake networking stack, but this is + // not easy. + jsFetchMissing = true + DefaultTransport = &Transport{} +} diff --git a/compiler/natives/src/net/http/main_test.go b/compiler/natives/src/net/http/main_test.go new file mode 100644 index 00000000..bb747d12 --- /dev/null +++ b/compiler/natives/src/net/http/main_test.go @@ -0,0 +1,43 @@ +//go:build js && wasm +// +build js,wasm + +package http_test + +import ( + "runtime" + "sort" + "strings" +) + +// This is an almost verbatim copy of the upstream, except one line which was +// adjusted to match GopherJS call stacks. +// This overlay can be remoed if/when https://github.com/golang/go/pull/49128 +// is merged and reached a stable Go release (likely 1.18). +func interestingGoroutines() (gs []string) { + buf := make([]byte, 2<<20) + buf = buf[:runtime.Stack(buf, true)] + for _, g := range strings.Split(string(buf), "\n\n") { + sl := strings.SplitN(g, "\n", 2) + if len(sl) != 2 { + continue + } + stack := strings.TrimSpace(sl[1]) + if stack == "" || + strings.Contains(stack, "testing.(*M).before.func1") || + strings.Contains(stack, "os/signal.signal_recv") || + strings.Contains(stack, "created by net.startServer") || + strings.Contains(stack, "created by testing.RunTests") || + strings.Contains(stack, "closeWriteAndWait") || + strings.Contains(stack, "testing.Main(") || + // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) + strings.Contains(stack, "runtime.goexit") || + strings.Contains(stack, "created by runtime.gc") || + strings.Contains(stack, "interestingGoroutines") || // ← Changed line. + strings.Contains(stack, "runtime.MHeap_Scavenger") { + continue + } + gs = append(gs, stack) + } + sort.Strings(gs) + return +} diff --git a/compiler/natives/src/net/http/server_test.go b/compiler/natives/src/net/http/server_test.go new file mode 100644 index 00000000..f55704dc --- /dev/null +++ b/compiler/natives/src/net/http/server_test.go @@ -0,0 +1,10 @@ +//go:build js +// +build js + +package http_test + +import "testing" + +func TestTimeoutHandlerSuperfluousLogs(t *testing.T) { + t.Skip("https://github.com/gopherjs/gopherjs/issues/1085") +} diff --git a/compiler/natives/src/net/http/transport_test.go b/compiler/natives/src/net/http/transport_test.go new file mode 100644 index 00000000..a173e47e --- /dev/null +++ b/compiler/natives/src/net/http/transport_test.go @@ -0,0 +1,14 @@ +//go:build js +// +build js + +package http_test + +import "testing" + +func TestTransportPersistConnLeakNeverIdle(t *testing.T) { + t.Skip("test relied on runtime.SetFinalizer(), which is not supported by GopherJS.") +} + +func TestTransportPersistConnContextLeakMaxConnsPerHost(t *testing.T) { + t.Skip("test relied on runtime.SetFinalizer(), which is not supported by GopherJS.") +} diff --git a/compiler/natives/src/net/net.go b/compiler/natives/src/net/net.go deleted file mode 100644 index 10e1161b..00000000 --- a/compiler/natives/src/net/net.go +++ /dev/null @@ -1,65 +0,0 @@ -// +build js - -package net - -import ( - "errors" - "syscall" - - "github.com/gopherjs/gopherjs/js" -) - -func Listen(net, laddr string) (Listener, error) { - panic(errors.New("network access is not supported by GopherJS")) -} - -func (d *Dialer) Dial(network, address string) (Conn, error) { - panic(errors.New("network access is not supported by GopherJS")) -} - -func sysInit() { -} - -func probeIPv4Stack() bool { - return false -} - -func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { - return false, false -} - -func probeWindowsIPStack() (supportsVistaIP bool) { - return false -} - -func maxListenerBacklog() int { - return syscall.SOMAXCONN -} - -// Copy of strings.IndexByte. -func byteIndex(s string, c byte) int { - return js.InternalObject(s).Call("indexOf", js.Global.Get("String").Call("fromCharCode", c)).Int() -} - -// Copy of bytes.Equal. -func bytesEqual(x, y []byte) bool { - if len(x) != len(y) { - return false - } - for i, b := range x { - if b != y[i] { - return false - } - } - return true -} - -// Copy of bytes.IndexByte. -func bytesIndexByte(s []byte, c byte) int { - for i, b := range s { - if b == c { - return i - } - } - return -1 -} diff --git a/compiler/natives/src/net/netip/export_test.go b/compiler/natives/src/net/netip/export_test.go new file mode 100644 index 00000000..45a905e1 --- /dev/null +++ b/compiler/natives/src/net/netip/export_test.go @@ -0,0 +1,20 @@ +//go:build js + +package netip + +import ( + "fmt" + "internal/intern" +) + +//gopherjs:prune-original +func MkAddr(u Uint128, z interface{}) Addr { + switch z := z.(type) { + case *intern.Value: + return Addr{u, z.Get().(string)} + case string: + return Addr{u, z} + default: + panic(fmt.Errorf("unexpected type %T of the z argument")) + } +} diff --git a/compiler/natives/src/net/netip/fuzz_test.go b/compiler/natives/src/net/netip/fuzz_test.go new file mode 100644 index 00000000..574201d2 --- /dev/null +++ b/compiler/natives/src/net/netip/fuzz_test.go @@ -0,0 +1,10 @@ +//go:build js + +package netip_test + +import "testing" + +func checkStringParseRoundTrip(t *testing.T, x interface{}, parse interface{}) { + // TODO(nevkontakte): This function requires generics to function. + // Re-enable after https://github.com/gopherjs/gopherjs/issues/1013 is resolved. +} diff --git a/compiler/natives/src/net/netip/netip.go b/compiler/natives/src/net/netip/netip.go new file mode 100644 index 00000000..92a61900 --- /dev/null +++ b/compiler/natives/src/net/netip/netip.go @@ -0,0 +1,39 @@ +//go:build js + +package netip + +type Addr struct { + addr uint128 + // Unlike the upstream, we store the string directly instead of trying to + // use internal/intern package for optimization. + z string +} + +var ( + // Sentinel values for different zones. \x00 character makes it unlikely for + // the sentinel value to clash with any real-life IPv6 zone index. + z0 = "" + z4 = "\x00ipv4" + z6noz = "\x00ipv6noz" +) + +//gopherjs:prune-original +func (ip Addr) Zone() string { + if ip.z == z4 || ip.z == z6noz { + return "" + } + return ip.z +} + +//gopherjs:prune-original +func (ip Addr) WithZone(zone string) Addr { + if !ip.Is6() { + return ip + } + if zone == "" { + ip.z = z6noz + return ip + } + ip.z = zone + return ip +} diff --git a/compiler/natives/src/os/file.go b/compiler/natives/src/os/file.go new file mode 100644 index 00000000..37d4275f --- /dev/null +++ b/compiler/natives/src/os/file.go @@ -0,0 +1,9 @@ +//go:build js +// +build js + +package os + +// WriteString copied from Go 1.16, before it was made more peformant, and unsafe. +func (f *File) WriteString(s string) (n int, err error) { + return f.Write([]byte(s)) +} diff --git a/compiler/natives/src/os/os.go b/compiler/natives/src/os/os.go index 96f36d28..a45e1350 100644 --- a/compiler/natives/src/os/os.go +++ b/compiler/natives/src/os/os.go @@ -1,23 +1,28 @@ +//go:build js // +build js package os import ( "errors" + _ "unsafe" // for go:linkname "github.com/gopherjs/gopherjs/js" ) +const isBigEndian = false + func runtime_args() []string { // not called on Windows return Args } func init() { if process := js.Global.Get("process"); process != js.Undefined { - argv := process.Get("argv") - Args = make([]string, argv.Length()-1) - for i := 0; i < argv.Length()-1; i++ { - Args[i] = argv.Index(i + 1).String() + if argv := process.Get("argv"); argv != js.Undefined && argv.Length() >= 1 { + Args = make([]string, argv.Length()-1) + for i := 0; i < argv.Length()-1; i++ { + Args[i] = argv.Index(i + 1).String() + } } } if len(Args) == 0 { @@ -30,3 +35,6 @@ func runtime_beforeExit() {} func executable() (string, error) { return "", errors.New("Executable not implemented for GOARCH=js") } + +//go:linkname fastrand runtime.fastrand +func fastrand() uint32 diff --git a/compiler/natives/src/os/signal/signal.go b/compiler/natives/src/os/signal/signal.go index a560f8ec..fe38d22b 100644 --- a/compiler/natives/src/os/signal/signal.go +++ b/compiler/natives/src/os/signal/signal.go @@ -1,3 +1,4 @@ +//go:build js // +build js package signal diff --git a/compiler/natives/src/reflect/example_test.go b/compiler/natives/src/reflect/example_test.go index 0953ebd1..0deab2ed 100644 --- a/compiler/natives/src/reflect/example_test.go +++ b/compiler/natives/src/reflect/example_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package reflect_test diff --git a/compiler/natives/src/reflect/reflect.go b/compiler/natives/src/reflect/reflect.go index 3d0cc4e0..17eb97ad 100644 --- a/compiler/natives/src/reflect/reflect.go +++ b/compiler/natives/src/reflect/reflect.go @@ -1,12 +1,16 @@ -// +build js +//go:build js && go1.18 +// +build js,go1.18 package reflect import ( "errors" + "runtime" "strconv" "unsafe" + "internal/itoa" + "github.com/gopherjs/gopherjs/js" ) @@ -33,6 +37,23 @@ func init() { uint8Type = TypeOf(uint8(0)).(*rtype) // set for real } +// New returns a Value representing a pointer to a new zero value +// for the specified type. That is, the returned Value's Type is PtrTo(typ). +// +// The upstream version includes an extra check to avoid creating types that +// are tagged as go:notinheap. This shouldn't matter in GopherJS, and tracking +// that state is over-complex, so we just skip that check. +func New(typ Type) Value { + if typ == nil { + panic("reflect: New(nil)") + } + t := typ.(*rtype) + pt := t.ptrTo() + ptr := unsafe_New(t) + fl := flag(Ptr) + return Value{pt, ptr, fl} +} + func jsType(typ Type) *js.Object { return js.InternalObject(typ).Get("jsType") } @@ -42,7 +63,7 @@ func reflectType(typ *js.Object) *rtype { rt := &rtype{ size: uintptr(typ.Get("size").Int()), kind: uint8(typ.Get("kind").Int()), - str: newNameOff(newName(internalStr(typ.Get("string")), "", typ.Get("exported").Bool())), + str: resolveReflectName(newName(internalStr(typ.Get("string")), "", typ.Get("exported").Bool())), } js.InternalObject(rt).Set("jsType", typ) typ.Set("reflectType", js.InternalObject(rt)) @@ -61,7 +82,7 @@ func reflectType(typ *js.Object) *rtype { continue } reflectMethods = append(reflectMethods, method{ - name: newNameOff(newName(internalStr(m.Get("name")), "", exported)), + name: resolveReflectName(newMethodName(m)), mtyp: newTypeOff(reflectType(m.Get("typ"))), }) } @@ -73,18 +94,18 @@ func reflectType(typ *js.Object) *rtype { continue } reflectMethods = append(reflectMethods, method{ - name: newNameOff(newName(internalStr(m.Get("name")), "", exported)), + name: resolveReflectName(newMethodName(m)), mtyp: newTypeOff(reflectType(m.Get("typ"))), }) } ut := &uncommonType{ - pkgPath: newNameOff(newName(internalStr(typ.Get("pkg")), "", false)), + pkgPath: resolveReflectName(newName(internalStr(typ.Get("pkg")), "", false)), mcount: uint16(methodSet.Length()), xcount: xcount, _methods: reflectMethods, } - uncommonTypeMap[rt] = ut js.InternalObject(ut).Set("jsType", typ) + js.InternalObject(rt).Set("uncommonType", js.InternalObject(ut)) } switch rt.Kind() { @@ -133,7 +154,7 @@ func reflectType(typ *js.Object) *rtype { for i := range imethods { m := methods.Index(i) imethods[i] = imethod{ - name: newNameOff(newName(internalStr(m.Get("name")), "", internalStr(m.Get("pkg")) == "")), + name: resolveReflectName(newMethodName(m)), typ: newTypeOff(reflectType(m.Get("typ"))), } } @@ -203,10 +224,12 @@ func (t *uncommonType) exportedMethods() []method { return t._methods[:t.xcount:t.xcount] } -var uncommonTypeMap = make(map[*rtype]*uncommonType) - func (t *rtype) uncommon() *uncommonType { - return uncommonTypeMap[t] + obj := js.InternalObject(t).Get("uncommonType") + if obj == js.Undefined { + return nil + } + return (*uncommonType)(unsafe.Pointer(obj.Unsafe())) } type funcType struct { @@ -234,13 +257,14 @@ type nameData struct { name string tag string exported bool + pkgPath string } var nameMap = make(map[*byte]*nameData) func (n name) name() (s string) { return nameMap[n.bytes].name } func (n name) tag() (s string) { return nameMap[n.bytes].tag } -func (n name) pkgPath() string { return "" } +func (n name) pkgPath() string { return nameMap[n.bytes].pkgPath } func (n name) isExported() bool { return nameMap[n.bytes].exported } func newName(n, tag string, exported bool) name { @@ -255,13 +279,28 @@ func newName(n, tag string, exported bool) name { } } +// newMethodName creates name instance for a method. +// +// Input object is expected to be an entry of the "methods" list of the +// corresponding JS type. +func newMethodName(m *js.Object) name { + b := new(byte) + nameMap[b] = &nameData{ + name: internalStr(m.Get("name")), + tag: "", + pkgPath: internalStr(m.Get("pkg")), + exported: internalStr(m.Get("pkg")) == "", + } + return name{bytes: b} +} + var nameOffList []name func (t *rtype) nameOff(off nameOff) name { return nameOffList[int(off)] } -func newNameOff(n name) nameOff { +func resolveReflectName(n name) nameOff { i := len(nameOffList) nameOffList = append(nameOffList, n) return nameOff(i) @@ -279,6 +318,15 @@ func newTypeOff(t *rtype) typeOff { return typeOff(i) } +// addReflectOff adds a pointer to the reflection lookup map in the runtime. +// It returns a new ID that can be used as a typeOff or textOff, and will +// be resolved correctly. Implemented in the runtime package. +func addReflectOff(ptr unsafe.Pointer) int32 { + i := len(typeOffList) + typeOffList = append(typeOffList, (*rtype)(ptr)) + return int32(i) +} + func internalStr(strObj *js.Object) string { var c struct{ str string } js.InternalObject(c).Set("str", strObj) // get string without internalizing @@ -340,6 +388,10 @@ func ValueOf(i interface{}) Value { } func ArrayOf(count int, elem Type) Type { + if count < 0 { + panic("reflect: negative length passed to ArrayOf") + } + return reflectType(js.Global.Call("$arrayType", jsType(elem), count)) } @@ -380,44 +432,95 @@ func SliceOf(t Type) Type { return reflectType(js.Global.Call("$sliceType", jsType(t))) } -// func StructOf(fields []StructField) Type { -// jsFields := make([]*js.Object, len(fields)) -// fset := map[string]struct{}{} -// for i, f := range fields { -// if f.Type == nil { -// panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type") -// } - -// name := f.Name -// if name == "" { -// // Embedded field -// if f.Type.Kind() == Ptr { -// // Embedded ** and *interface{} are illegal -// elem := f.Type.Elem() -// if k := elem.Kind(); k == Ptr || k == Interface { -// panic("reflect.StructOf: illegal anonymous field type " + f.Type.String()) -// } -// name = elem.String() -// } else { -// name = f.Type.String() -// } -// } - -// if _, dup := fset[name]; dup { -// panic("reflect.StructOf: duplicate field " + name) -// } -// fset[name] = struct{}{} - -// jsf := js.Global.Get("Object").New() -// jsf.Set("prop", name) -// jsf.Set("name", name) -// jsf.Set("exported", true) -// jsf.Set("typ", jsType(f.Type)) -// jsf.Set("tag", f.Tag) -// jsFields[i] = jsf -// } -// return reflectType(js.Global.Call("$structType", "", jsFields)) -// } +func StructOf(fields []StructField) Type { + var ( + jsFields = make([]*js.Object, len(fields)) + fset = map[string]struct{}{} + pkgpath string + hasGCProg bool + ) + for i, field := range fields { + if field.Name == "" { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name") + } + if !isValidFieldName(field.Name) { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has invalid name") + } + if field.Type == nil { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type") + } + f, fpkgpath := runtimeStructField(field) + ft := f.typ + if ft.kind&kindGCProg != 0 { + hasGCProg = true + } + if fpkgpath != "" { + if pkgpath == "" { + pkgpath = fpkgpath + } else if pkgpath != fpkgpath { + panic("reflect.Struct: fields with different PkgPath " + pkgpath + " and " + fpkgpath) + } + } + name := field.Name + if f.embedded() { + // Embedded field + if field.Type.Kind() == Ptr { + // Embedded ** and *interface{} are illegal + elem := field.Type.Elem() + if k := elem.Kind(); k == Ptr || k == Interface { + panic("reflect.StructOf: illegal anonymous field type " + field.Type.String()) + } + } + switch field.Type.Kind() { + case Interface: + case Ptr: + ptr := (*ptrType)(unsafe.Pointer(ft)) + if unt := ptr.uncommon(); unt != nil { + if i > 0 && unt.mcount > 0 { + // Issue 15924. + panic("reflect: embedded type with methods not implemented if type is not first field") + } + if len(fields) > 1 { + panic("reflect: embedded type with methods not implemented if there is more than one field") + } + } + default: + if unt := ft.uncommon(); unt != nil { + if i > 0 && unt.mcount > 0 { + // Issue 15924. + panic("reflect: embedded type with methods not implemented if type is not first field") + } + if len(fields) > 1 && ft.kind&kindDirectIface != 0 { + panic("reflect: embedded type with methods not implemented for non-pointer type") + } + } + } + } + + if _, dup := fset[name]; dup && name != "_" { + panic("reflect.StructOf: duplicate field " + name) + } + fset[name] = struct{}{} + // To be consistent with Compiler's behavior we need to avoid externalizing + // the "name" property. The line below is effectively an inverse of the + // internalStr() function. + jsf := js.InternalObject(struct{ name string }{name}) + // The rest is set through the js.Object() interface, which the compiler will + // externalize for us. + jsf.Set("prop", name) + jsf.Set("exported", f.name.isExported()) + jsf.Set("typ", jsType(field.Type)) + jsf.Set("tag", field.Tag) + jsf.Set("embedded", field.Anonymous) + jsFields[i] = jsf + } + _ = hasGCProg + typ := js.Global.Call("$structType", "", jsFields) + if pkgpath != "" { + typ.Set("pkgPath", pkgpath) + } + return reflectType(typ) +} func Zero(typ Type) Value { return makeValue(typ, jsType(typ).Call("zero"), 0) @@ -467,12 +570,27 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { ftyp := (*funcType)(unsafe.Pointer(t)) fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + // Convert raw JS arguments into []Value the user-supplied function expects. args := make([]Value, ftyp.NumIn()) for i := range args { argType := ftyp.In(i).common() args[i] = makeValue(argType, arguments[i], 0) } + + // Call the user-supplied function. resultsSlice := fn(args) + + // Verify that returned value types are compatible with the function type specified by the caller. + if want, got := ftyp.NumOut(), len(resultsSlice); want != got { + panic("reflect: expected " + strconv.Itoa(want) + " return values, got " + strconv.Itoa(got)) + } + for i, rtyp := range ftyp.out() { + if !resultsSlice[i].Type().AssignableTo(rtyp) { + panic("reflect: " + strconv.Itoa(i) + " return value type is not compatible with the function declaration") + } + } + + // Rearrange return values according to the expected function signature. switch ftyp.NumOut() { case 0: return nil @@ -504,21 +622,24 @@ func makechan(typ *rtype, size int) (ch unsafe.Pointer) { } func makemap(t *rtype, cap int) (m unsafe.Pointer) { - return unsafe.Pointer(js.Global.Get("Object").New().Unsafe()) + return unsafe.Pointer(js.Global.Get("Map").New().Unsafe()) } -func keyFor(t *rtype, key unsafe.Pointer) (*js.Object, string) { +func keyFor(t *rtype, key unsafe.Pointer) (*js.Object, *js.Object) { kv := js.InternalObject(key) if kv.Get("$get") != js.Undefined { kv = kv.Call("$get") } - k := jsType(t.Key()).Call("keyFor", kv).String() + k := jsType(t.Key()).Call("keyFor", kv) return kv, k } func mapaccess(t *rtype, m, key unsafe.Pointer) unsafe.Pointer { + if !js.InternalObject(m).Bool() { + return nil // nil map + } _, k := keyFor(t, key) - entry := js.InternalObject(m).Get(k) + entry := js.InternalObject(m).Call("get", k) if entry == js.Undefined { return nil } @@ -537,30 +658,54 @@ func mapassign(t *rtype, m, key, val unsafe.Pointer) { entry := js.Global.Get("Object").New() entry.Set("k", kv) entry.Set("v", jsVal) - js.InternalObject(m).Set(k, entry) + js.InternalObject(m).Call("set", k, entry) } func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) { _, k := keyFor(t, key) - js.InternalObject(m).Delete(k) + if !js.InternalObject(m).Bool() { + return // nil map + } + js.InternalObject(m).Call("delete", k) +} + +// TODO(nevkonatkte): The following three "faststr" implementations are meant to +// perform better for the common case of string-keyed maps (see upstream: +// https://github.com/golang/go/commit/23832ba2e2fb396cda1dacf3e8afcb38ec36dcba) +// However, the stubs below will perform the same or worse because of the extra +// string-to-pointer conversion. Not sure how to fix this without significant +// code duplication, however. + +func mapaccess_faststr(t *rtype, m unsafe.Pointer, key string) (val unsafe.Pointer) { + return mapaccess(t, m, unsafe.Pointer(&key)) } -type mapIter struct { +func mapassign_faststr(t *rtype, m unsafe.Pointer, key string, val unsafe.Pointer) { + mapassign(t, m, unsafe.Pointer(&key), val) +} + +func mapdelete_faststr(t *rtype, m unsafe.Pointer, key string) { + mapdelete(t, m, unsafe.Pointer(&key)) +} + +type hiter struct { t Type - m *js.Object + m *js.Object // Underlying map object. keys *js.Object i int - // last is the last object the iterator indicates. If this object exists, the functions that return the - // current key or value returns this object, regardless of the current iterator. It is because the current - // iterator might be stale due to key deletion in a loop. + // last is the last object the iterator indicates. If this object exists, the + // functions that return the current key or value returns this object, + // regardless of the current iterator. It is because the current iterator + // might be stale due to key deletion in a loop. last *js.Object } -func (iter *mapIter) skipUntilValidKey() { +func (iter *hiter) skipUntilValidKey() { for iter.i < iter.keys.Length() { k := iter.keys.Index(iter.i) - if iter.m.Get(k.String()) != js.Undefined { + entry := iter.m.Call("get", k) + if entry != js.Undefined { break } // The key is already deleted. Move on the next item. @@ -568,58 +713,70 @@ func (iter *mapIter) skipUntilValidKey() { } } -func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer { - return unsafe.Pointer(&mapIter{t, js.InternalObject(m), js.Global.Call("$keys", js.InternalObject(m)), 0, nil}) +func mapiterinit(t *rtype, m unsafe.Pointer, it *hiter) { + mapObj := js.InternalObject(m) + keys := js.Global.Get("Array").New() + if mapObj.Get("keys") != js.Undefined { + keysIter := mapObj.Call("keys") + if mapObj.Get("keys") != js.Undefined { + keys = js.Global.Get("Array").Call("from", keysIter) + } + } + + *it = hiter{ + t: t, + m: mapObj, + keys: keys, + i: 0, + last: nil, + } } -func mapiterkey(it unsafe.Pointer) unsafe.Pointer { - iter := (*mapIter)(it) +func mapiterkey(it *hiter) unsafe.Pointer { var kv *js.Object - if iter.last != nil { - kv = iter.last + if it.last != nil { + kv = it.last } else { - iter.skipUntilValidKey() - if iter.i == iter.keys.Length() { + it.skipUntilValidKey() + if it.i == it.keys.Length() { return nil } - k := iter.keys.Index(iter.i) - kv = iter.m.Get(k.String()) + k := it.keys.Index(it.i) + kv = it.m.Call("get", k) // Record the key-value pair for later accesses. - iter.last = kv + it.last = kv } - return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(iter.t.Key()))).Unsafe()) + return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(it.t.Key()))).Unsafe()) } -func mapitervalue(it unsafe.Pointer) unsafe.Pointer { - iter := (*mapIter)(it) +func mapiterelem(it *hiter) unsafe.Pointer { var kv *js.Object - if iter.last != nil { - kv = iter.last + if it.last != nil { + kv = it.last } else { - iter.skipUntilValidKey() - if iter.i == iter.keys.Length() { + it.skipUntilValidKey() + if it.i == it.keys.Length() { return nil } - k := iter.keys.Index(iter.i) - kv = iter.m.Get(k.String()) - iter.last = kv + k := it.keys.Index(it.i) + kv = it.m.Call("get", k) + it.last = kv } - return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("v"), jsType(PtrTo(iter.t.Elem()))).Unsafe()) + return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("v"), jsType(PtrTo(it.t.Elem()))).Unsafe()) } -func mapiternext(it unsafe.Pointer) { - iter := (*mapIter)(it) - iter.last = nil - iter.i++ +func mapiternext(it *hiter) { + it.last = nil + it.i++ } func maplen(m unsafe.Pointer) int { - return js.Global.Call("$keys", js.InternalObject(m)).Length() + return js.InternalObject(m).Get("size").Int() } func cvtDirect(v Value, typ Type) Value { - var srcVal = v.object() + srcVal := v.object() if srcVal == jsType(v.typ).Get("nil") { return makeValue(typ, jsType(typ).Get("nil"), v.flag) } @@ -633,20 +790,26 @@ func cvtDirect(v Value, typ Type) Value { slice.Set("$capacity", srcVal.Get("$capacity")) val = js.Global.Call("$newDataPointer", slice, jsType(PtrTo(typ))) case Ptr: - if typ.Elem().Kind() == Struct { + switch typ.Elem().Kind() { + case Struct: if typ.Elem() == v.typ.Elem() { val = srcVal break } val = jsType(typ).New() copyStruct(val, srcVal, typ.Elem()) - break + case Array: + // Unlike other pointers, array pointers are "wrapped" types (see + // isWrapped() in the compiler package), and are represented by a native + // javascript array object here. + val = srcVal + default: + val = jsType(typ).New(srcVal.Get("$get"), srcVal.Get("$set")) } - val = jsType(typ).New(srcVal.Get("$get"), srcVal.Get("$set")) case Struct: val = jsType(typ).Get("ptr").New() copyStruct(val, srcVal, typ) - case Array, Bool, Chan, Func, Interface, Map, String: + case Array, Bool, Chan, Func, Interface, Map, String, UnsafePointer: val = js.InternalObject(v.ptr) default: panic(&ValueError{"reflect.Convert", k}) @@ -654,6 +817,19 @@ func cvtDirect(v Value, typ Type) Value { return Value{typ.common(), unsafe.Pointer(val.Unsafe()), v.flag.ro() | v.flag&flagIndir | flag(typ.Kind())} } +// convertOp: []T -> *[N]T +func cvtSliceArrayPtr(v Value, t Type) Value { + slice := v.object() + + slen := slice.Get("$length").Int() + alen := t.Elem().Len() + if alen > slen { + panic("reflect: cannot convert slice with length " + itoa.Itoa(slen) + " to pointer to array with length " + itoa.Itoa(alen)) + } + array := js.Global.Call("$sliceToGoArray", slice, jsType(t)) + return Value{t.common(), unsafe.Pointer(array.Unsafe()), v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Ptr)} +} + func Copy(dst, src Value) int { dk := dst.kind() if dk != Array && dk != Slice { @@ -739,6 +915,11 @@ func valueInterface(v Value, safe bool) interface{} { } if isWrapped(v.typ) { + if v.flag&flagIndir != 0 && v.Kind() == Struct { + cv := jsType(v.typ).Call("zero") + copyStruct(cv, v.object(), v.typ) + return interface{}(unsafe.Pointer(jsType(v.typ).New(cv).Unsafe())) + } return interface{}(unsafe.Pointer(jsType(v.typ).New(v.object()).Unsafe())) } return interface{}(unsafe.Pointer(v.object().Unsafe())) @@ -748,10 +929,6 @@ func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) { js.InternalObject(dst).Call("$set", js.InternalObject(src)) } -func methodName() string { - return "?FIXME?" -} - func makeMethodValue(op string, v Value) Value { if v.flag&flagMethod == 0 { panic("reflect: internal error: invalid use of makePartialFunc") @@ -1225,7 +1402,7 @@ func (v Value) Len() int { case Chan: return v.object().Get("$buffer").Get("length").Int() case Map: - return js.Global.Call("$keys", v.object()).Length() + return v.object().Get("size").Int() default: panic(&ValueError{"reflect.Value.Len", k}) } @@ -1498,7 +1675,7 @@ func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { return true } } - var n = v1.Len() + n := v1.Len() if n != v2.Len() { return false } @@ -1516,7 +1693,7 @@ func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { case Ptr: return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) case Struct: - var n = v1.NumField() + n := v1.NumField() for i := 0; i < n; i++ { if !deepValueEqualJs(v1.Field(i), v2.Field(i), visited) { return false @@ -1530,7 +1707,7 @@ func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { if v1.object() == v2.object() { return true } - var keys = v1.MapKeys() + keys := v1.MapKeys() if len(keys) != v2.Len() { return false } @@ -1550,3 +1727,35 @@ func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { return js.Global.Call("$interfaceIsEqual", js.InternalObject(valueInterface(v1, false)), js.InternalObject(valueInterface(v2, false))).Bool() } + +func methodNameSkip() string { + pc, _, _, _ := runtime.Caller(3) + f := runtime.FuncForPC(pc) + if f == nil { + return "unknown method" + } + // Function name extracted from the call stack can be different from vanilla + // Go. Here we try to fix stuff like "Object.$packages.reflect.Q.ptr.SetIterKey" + // into "Value.SetIterKey". + // This workaround may become obsolete after https://github.com/gopherjs/gopherjs/issues/1085 + // is resolved. + name := f.Name() + idx := len(name) - 1 + for idx > 0 { + if name[idx] == '.' { + break + } + idx-- + } + if idx < 0 { + return name + } + return "Value" + name[idx:] +} + +func verifyNotInHeapPtr(p uintptr) bool { + // Go runtime uses this method to make sure that a uintptr won't crash GC if + // interpreted as a heap pointer. This is not relevant for GopherJS, so we can + // always return true. + return true +} diff --git a/compiler/natives/src/reflect/reflect_go116.go b/compiler/natives/src/reflect/reflect_go116.go new file mode 100644 index 00000000..4c8e0000 --- /dev/null +++ b/compiler/natives/src/reflect/reflect_go116.go @@ -0,0 +1,1749 @@ +//go:build js && !go1.17 +// +build js,!go1.17 + +package reflect + +import ( + "errors" + "runtime" + "strconv" + "unsafe" + + "github.com/gopherjs/gopherjs/js" +) + +var initialized = false + +func init() { + // avoid dead code elimination + used := func(i interface{}) {} + used(rtype{}) + used(uncommonType{}) + used(method{}) + used(arrayType{}) + used(chanType{}) + used(funcType{}) + used(interfaceType{}) + used(mapType{}) + used(ptrType{}) + used(sliceType{}) + used(structType{}) + used(imethod{}) + used(structField{}) + + initialized = true + uint8Type = TypeOf(uint8(0)).(*rtype) // set for real +} + +// New returns a Value representing a pointer to a new zero value +// for the specified type. That is, the returned Value's Type is PtrTo(typ). +// +// The upstream version includes an extra check to avoid creating types that +// are tagged as go:notinheap. This shouldn't matter in GopherJS, and tracking +// that state is over-complex, so we just skip that check. +func New(typ Type) Value { + if typ == nil { + panic("reflect: New(nil)") + } + t := typ.(*rtype) + pt := t.ptrTo() + ptr := unsafe_New(t) + fl := flag(Ptr) + return Value{pt, ptr, fl} +} + +func jsType(typ Type) *js.Object { + return js.InternalObject(typ).Get("jsType") +} + +func reflectType(typ *js.Object) *rtype { + if typ.Get("reflectType") == js.Undefined { + rt := &rtype{ + size: uintptr(typ.Get("size").Int()), + kind: uint8(typ.Get("kind").Int()), + str: resolveReflectName(newName(internalStr(typ.Get("string")), "", typ.Get("exported").Bool())), + } + js.InternalObject(rt).Set("jsType", typ) + typ.Set("reflectType", js.InternalObject(rt)) + + methodSet := js.Global.Call("$methodSet", typ) + if methodSet.Length() != 0 || typ.Get("named").Bool() { + rt.tflag |= tflagUncommon + if typ.Get("named").Bool() { + rt.tflag |= tflagNamed + } + var reflectMethods []method + for i := 0; i < methodSet.Length(); i++ { // Exported methods first. + m := methodSet.Index(i) + exported := internalStr(m.Get("pkg")) == "" + if !exported { + continue + } + reflectMethods = append(reflectMethods, method{ + name: resolveReflectName(newMethodName(m)), + mtyp: newTypeOff(reflectType(m.Get("typ"))), + }) + } + xcount := uint16(len(reflectMethods)) + for i := 0; i < methodSet.Length(); i++ { // Unexported methods second. + m := methodSet.Index(i) + exported := internalStr(m.Get("pkg")) == "" + if exported { + continue + } + reflectMethods = append(reflectMethods, method{ + name: resolveReflectName(newMethodName(m)), + mtyp: newTypeOff(reflectType(m.Get("typ"))), + }) + } + ut := &uncommonType{ + pkgPath: resolveReflectName(newName(internalStr(typ.Get("pkg")), "", false)), + mcount: uint16(methodSet.Length()), + xcount: xcount, + _methods: reflectMethods, + } + js.InternalObject(ut).Set("jsType", typ) + js.InternalObject(rt).Set("uncommonType", js.InternalObject(ut)) + } + + switch rt.Kind() { + case Array: + setKindType(rt, &arrayType{ + elem: reflectType(typ.Get("elem")), + len: uintptr(typ.Get("len").Int()), + }) + case Chan: + dir := BothDir + if typ.Get("sendOnly").Bool() { + dir = SendDir + } + if typ.Get("recvOnly").Bool() { + dir = RecvDir + } + setKindType(rt, &chanType{ + elem: reflectType(typ.Get("elem")), + dir: uintptr(dir), + }) + case Func: + params := typ.Get("params") + in := make([]*rtype, params.Length()) + for i := range in { + in[i] = reflectType(params.Index(i)) + } + results := typ.Get("results") + out := make([]*rtype, results.Length()) + for i := range out { + out[i] = reflectType(results.Index(i)) + } + outCount := uint16(results.Length()) + if typ.Get("variadic").Bool() { + outCount |= 1 << 15 + } + setKindType(rt, &funcType{ + rtype: *rt, + inCount: uint16(params.Length()), + outCount: outCount, + _in: in, + _out: out, + }) + case Interface: + methods := typ.Get("methods") + imethods := make([]imethod, methods.Length()) + for i := range imethods { + m := methods.Index(i) + imethods[i] = imethod{ + name: resolveReflectName(newMethodName(m)), + typ: newTypeOff(reflectType(m.Get("typ"))), + } + } + setKindType(rt, &interfaceType{ + rtype: *rt, + pkgPath: newName(internalStr(typ.Get("pkg")), "", false), + methods: imethods, + }) + case Map: + setKindType(rt, &mapType{ + key: reflectType(typ.Get("key")), + elem: reflectType(typ.Get("elem")), + }) + case Ptr: + setKindType(rt, &ptrType{ + elem: reflectType(typ.Get("elem")), + }) + case Slice: + setKindType(rt, &sliceType{ + elem: reflectType(typ.Get("elem")), + }) + case Struct: + fields := typ.Get("fields") + reflectFields := make([]structField, fields.Length()) + for i := range reflectFields { + f := fields.Index(i) + offsetEmbed := uintptr(i) << 1 + if f.Get("embedded").Bool() { + offsetEmbed |= 1 + } + reflectFields[i] = structField{ + name: newName(internalStr(f.Get("name")), internalStr(f.Get("tag")), f.Get("exported").Bool()), + typ: reflectType(f.Get("typ")), + offsetEmbed: offsetEmbed, + } + } + setKindType(rt, &structType{ + rtype: *rt, + pkgPath: newName(internalStr(typ.Get("pkgPath")), "", false), + fields: reflectFields, + }) + } + } + + return (*rtype)(unsafe.Pointer(typ.Get("reflectType").Unsafe())) +} + +func setKindType(rt *rtype, kindType interface{}) { + js.InternalObject(rt).Set("kindType", js.InternalObject(kindType)) + js.InternalObject(kindType).Set("rtype", js.InternalObject(rt)) +} + +type uncommonType struct { + pkgPath nameOff + mcount uint16 + xcount uint16 + moff uint32 + + _methods []method +} + +func (t *uncommonType) methods() []method { + return t._methods +} + +func (t *uncommonType) exportedMethods() []method { + return t._methods[:t.xcount:t.xcount] +} + +func (t *rtype) uncommon() *uncommonType { + obj := js.InternalObject(t).Get("uncommonType") + if obj == js.Undefined { + return nil + } + return (*uncommonType)(unsafe.Pointer(obj.Unsafe())) +} + +type funcType struct { + rtype `reflect:"func"` + inCount uint16 + outCount uint16 + + _in []*rtype + _out []*rtype +} + +func (t *funcType) in() []*rtype { + return t._in +} + +func (t *funcType) out() []*rtype { + return t._out +} + +type name struct { + bytes *byte +} + +type nameData struct { + name string + tag string + exported bool + pkgPath string +} + +var nameMap = make(map[*byte]*nameData) + +func (n name) name() (s string) { return nameMap[n.bytes].name } +func (n name) tag() (s string) { return nameMap[n.bytes].tag } +func (n name) pkgPath() string { return nameMap[n.bytes].pkgPath } +func (n name) isExported() bool { return nameMap[n.bytes].exported } + +func newName(n, tag string, exported bool) name { + b := new(byte) + nameMap[b] = &nameData{ + name: n, + tag: tag, + exported: exported, + } + return name{ + bytes: b, + } +} + +// newMethodName creates name instance for a method. +// +// Input object is expected to be an entry of the "methods" list of the +// corresponding JS type. +func newMethodName(m *js.Object) name { + b := new(byte) + nameMap[b] = &nameData{ + name: internalStr(m.Get("name")), + tag: "", + pkgPath: internalStr(m.Get("pkg")), + exported: internalStr(m.Get("pkg")) == "", + } + return name{bytes: b} +} + +var nameOffList []name + +func (t *rtype) nameOff(off nameOff) name { + return nameOffList[int(off)] +} + +func resolveReflectName(n name) nameOff { + i := len(nameOffList) + nameOffList = append(nameOffList, n) + return nameOff(i) +} + +var typeOffList []*rtype + +func (t *rtype) typeOff(off typeOff) *rtype { + return typeOffList[int(off)] +} + +func newTypeOff(t *rtype) typeOff { + i := len(typeOffList) + typeOffList = append(typeOffList, t) + return typeOff(i) +} + +// addReflectOff adds a pointer to the reflection lookup map in the runtime. +// It returns a new ID that can be used as a typeOff or textOff, and will +// be resolved correctly. Implemented in the runtime package. +func addReflectOff(ptr unsafe.Pointer) int32 { + i := len(typeOffList) + typeOffList = append(typeOffList, (*rtype)(ptr)) + return int32(i) +} + +func internalStr(strObj *js.Object) string { + var c struct{ str string } + js.InternalObject(c).Set("str", strObj) // get string without internalizing + return c.str +} + +func isWrapped(typ Type) bool { + return jsType(typ).Get("wrapped").Bool() +} + +func copyStruct(dst, src *js.Object, typ Type) { + fields := jsType(typ).Get("fields") + for i := 0; i < fields.Length(); i++ { + prop := fields.Index(i).Get("prop").String() + dst.Set(prop, src.Get(prop)) + } +} + +func makeValue(t Type, v *js.Object, fl flag) Value { + rt := t.common() + if t.Kind() == Array || t.Kind() == Struct || t.Kind() == Ptr { + return Value{rt, unsafe.Pointer(v.Unsafe()), fl | flag(t.Kind())} + } + return Value{rt, unsafe.Pointer(js.Global.Call("$newDataPointer", v, jsType(rt.ptrTo())).Unsafe()), fl | flag(t.Kind()) | flagIndir} +} + +func MakeSlice(typ Type, len, cap int) Value { + if typ.Kind() != Slice { + panic("reflect.MakeSlice of non-slice type") + } + if len < 0 { + panic("reflect.MakeSlice: negative len") + } + if cap < 0 { + panic("reflect.MakeSlice: negative cap") + } + if len > cap { + panic("reflect.MakeSlice: len > cap") + } + + return makeValue(typ, js.Global.Call("$makeSlice", jsType(typ), len, cap, js.InternalObject(func() *js.Object { return jsType(typ.Elem()).Call("zero") })), 0) +} + +func TypeOf(i interface{}) Type { + if !initialized { // avoid error of uint8Type + return &rtype{} + } + if i == nil { + return nil + } + return reflectType(js.InternalObject(i).Get("constructor")) +} + +func ValueOf(i interface{}) Value { + if i == nil { + return Value{} + } + return makeValue(reflectType(js.InternalObject(i).Get("constructor")), js.InternalObject(i).Get("$val"), 0) +} + +func ArrayOf(count int, elem Type) Type { + if count < 0 { + panic("reflect: negative length passed to ArrayOf") + } + + return reflectType(js.Global.Call("$arrayType", jsType(elem), count)) +} + +func ChanOf(dir ChanDir, t Type) Type { + return reflectType(js.Global.Call("$chanType", jsType(t), dir == SendDir, dir == RecvDir)) +} + +func FuncOf(in, out []Type, variadic bool) Type { + if variadic && (len(in) == 0 || in[len(in)-1].Kind() != Slice) { + panic("reflect.FuncOf: last arg of variadic func must be slice") + } + + jsIn := make([]*js.Object, len(in)) + for i, v := range in { + jsIn[i] = jsType(v) + } + jsOut := make([]*js.Object, len(out)) + for i, v := range out { + jsOut[i] = jsType(v) + } + return reflectType(js.Global.Call("$funcType", jsIn, jsOut, variadic)) +} + +func MapOf(key, elem Type) Type { + switch key.Kind() { + case Func, Map, Slice: + panic("reflect.MapOf: invalid key type " + key.String()) + } + + return reflectType(js.Global.Call("$mapType", jsType(key), jsType(elem))) +} + +func (t *rtype) ptrTo() *rtype { + return reflectType(js.Global.Call("$ptrType", jsType(t))) +} + +func SliceOf(t Type) Type { + return reflectType(js.Global.Call("$sliceType", jsType(t))) +} + +func StructOf(fields []StructField) Type { + var ( + jsFields = make([]*js.Object, len(fields)) + fset = map[string]struct{}{} + pkgpath string + hasGCProg bool + ) + for i, field := range fields { + if field.Name == "" { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name") + } + if !isValidFieldName(field.Name) { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has invalid name") + } + if field.Type == nil { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type") + } + f, fpkgpath := runtimeStructField(field) + ft := f.typ + if ft.kind&kindGCProg != 0 { + hasGCProg = true + } + if fpkgpath != "" { + if pkgpath == "" { + pkgpath = fpkgpath + } else if pkgpath != fpkgpath { + panic("reflect.Struct: fields with different PkgPath " + pkgpath + " and " + fpkgpath) + } + } + name := field.Name + if f.embedded() { + // Embedded field + if field.Type.Kind() == Ptr { + // Embedded ** and *interface{} are illegal + elem := field.Type.Elem() + if k := elem.Kind(); k == Ptr || k == Interface { + panic("reflect.StructOf: illegal anonymous field type " + field.Type.String()) + } + } + switch field.Type.Kind() { + case Interface: + case Ptr: + ptr := (*ptrType)(unsafe.Pointer(ft)) + if unt := ptr.uncommon(); unt != nil { + if i > 0 && unt.mcount > 0 { + // Issue 15924. + panic("reflect: embedded type with methods not implemented if type is not first field") + } + if len(fields) > 1 { + panic("reflect: embedded type with methods not implemented if there is more than one field") + } + } + default: + if unt := ft.uncommon(); unt != nil { + if i > 0 && unt.mcount > 0 { + // Issue 15924. + panic("reflect: embedded type with methods not implemented if type is not first field") + } + if len(fields) > 1 && ft.kind&kindDirectIface != 0 { + panic("reflect: embedded type with methods not implemented for non-pointer type") + } + } + } + } + + if _, dup := fset[name]; dup && name != "_" { + panic("reflect.StructOf: duplicate field " + name) + } + fset[name] = struct{}{} + // To be consistent with Compiler's behavior we need to avoid externalizing + // the "name" property. The line below is effectively an inverse of the + // internalStr() function. + jsf := js.InternalObject(struct{ name string }{name}) + // The rest is set through the js.Object() interface, which the compiler will + // externalize for us. + jsf.Set("prop", name) + jsf.Set("exported", f.name.isExported()) + jsf.Set("typ", jsType(field.Type)) + jsf.Set("tag", field.Tag) + jsf.Set("embedded", field.Anonymous) + jsFields[i] = jsf + } + _ = hasGCProg + typ := js.Global.Call("$structType", "", jsFields) + if pkgpath != "" { + typ.Set("pkgPath", pkgpath) + } + return reflectType(typ) +} + +func Zero(typ Type) Value { + return makeValue(typ, jsType(typ).Call("zero"), 0) +} + +func unsafe_New(typ *rtype) unsafe.Pointer { + switch typ.Kind() { + case Struct: + return unsafe.Pointer(jsType(typ).Get("ptr").New().Unsafe()) + case Array: + return unsafe.Pointer(jsType(typ).Call("zero").Unsafe()) + default: + return unsafe.Pointer(js.Global.Call("$newDataPointer", jsType(typ).Call("zero"), jsType(typ.ptrTo())).Unsafe()) + } +} + +func makeInt(f flag, bits uint64, t Type) Value { + typ := t.common() + ptr := unsafe_New(typ) + switch typ.Kind() { + case Int8: + *(*int8)(ptr) = int8(bits) + case Int16: + *(*int16)(ptr) = int16(bits) + case Int, Int32: + *(*int32)(ptr) = int32(bits) + case Int64: + *(*int64)(ptr) = int64(bits) + case Uint8: + *(*uint8)(ptr) = uint8(bits) + case Uint16: + *(*uint16)(ptr) = uint16(bits) + case Uint, Uint32, Uintptr: + *(*uint32)(ptr) = uint32(bits) + case Uint64: + *(*uint64)(ptr) = uint64(bits) + } + return Value{typ, ptr, f | flagIndir | flag(typ.Kind())} +} + +func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { + if typ.Kind() != Func { + panic("reflect: call of MakeFunc with non-Func type") + } + + t := typ.common() + ftyp := (*funcType)(unsafe.Pointer(t)) + + fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + // Convert raw JS arguments into []Value the user-supplied function expects. + args := make([]Value, ftyp.NumIn()) + for i := range args { + argType := ftyp.In(i).common() + args[i] = makeValue(argType, arguments[i], 0) + } + + // Call the user-supplied function. + resultsSlice := fn(args) + + // Verify that returned value types are compatible with the function type specified by the caller. + if want, got := ftyp.NumOut(), len(resultsSlice); want != got { + panic("reflect: expected " + strconv.Itoa(want) + " return values, got " + strconv.Itoa(got)) + } + for i, rtyp := range ftyp.out() { + if !resultsSlice[i].Type().AssignableTo(rtyp) { + panic("reflect: " + strconv.Itoa(i) + " return value type is not compatible with the function declaration") + } + } + + // Rearrange return values according to the expected function signature. + switch ftyp.NumOut() { + case 0: + return nil + case 1: + return resultsSlice[0].object() + default: + results := js.Global.Get("Array").New(ftyp.NumOut()) + for i, r := range resultsSlice { + results.SetIndex(i, r.object()) + } + return results + } + }) + + return Value{t, unsafe.Pointer(fv.Unsafe()), flag(Func)} +} + +func typedmemmove(t *rtype, dst, src unsafe.Pointer) { + js.InternalObject(dst).Call("$set", js.InternalObject(src).Call("$get")) +} + +func loadScalar(p unsafe.Pointer, n uintptr) uintptr { + return js.InternalObject(p).Call("$get").Unsafe() +} + +func makechan(typ *rtype, size int) (ch unsafe.Pointer) { + ctyp := (*chanType)(unsafe.Pointer(typ)) + return unsafe.Pointer(js.Global.Get("$Chan").New(jsType(ctyp.elem), size).Unsafe()) +} + +func makemap(t *rtype, cap int) (m unsafe.Pointer) { + return unsafe.Pointer(js.Global.Get("Map").New().Unsafe()) +} + +func keyFor(t *rtype, key unsafe.Pointer) (*js.Object, *js.Object) { + kv := js.InternalObject(key) + if kv.Get("$get") != js.Undefined { + kv = kv.Call("$get") + } + k := jsType(t.Key()).Call("keyFor", kv) + return kv, k +} + +func mapaccess(t *rtype, m, key unsafe.Pointer) unsafe.Pointer { + if !js.InternalObject(m).Bool() { + return nil // nil map + } + _, k := keyFor(t, key) + entry := js.InternalObject(m).Call("get", k) + if entry == js.Undefined { + return nil + } + return unsafe.Pointer(js.Global.Call("$newDataPointer", entry.Get("v"), jsType(PtrTo(t.Elem()))).Unsafe()) +} + +func mapassign(t *rtype, m, key, val unsafe.Pointer) { + kv, k := keyFor(t, key) + jsVal := js.InternalObject(val).Call("$get") + et := t.Elem() + if et.Kind() == Struct { + newVal := jsType(et).Call("zero") + copyStruct(newVal, jsVal, et) + jsVal = newVal + } + entry := js.Global.Get("Object").New() + entry.Set("k", kv) + entry.Set("v", jsVal) + js.InternalObject(m).Call("set", k, entry) +} + +func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) { + _, k := keyFor(t, key) + if !js.InternalObject(m).Bool() { + return // nil map + } + js.InternalObject(m).Call("delete", k) +} + +// TODO(nevkonatkte): The following three "faststr" implementations are meant to +// perform better for the common case of string-keyed maps (see upstream: +// https://github.com/golang/go/commit/23832ba2e2fb396cda1dacf3e8afcb38ec36dcba) +// However, the stubs below will perform the same or worse because of the extra +// string-to-pointer conversion. Not sure how to fix this without significant +// code duplication, however. + +func mapaccess_faststr(t *rtype, m unsafe.Pointer, key string) (val unsafe.Pointer) { + return mapaccess(t, m, unsafe.Pointer(&key)) +} + +func mapassign_faststr(t *rtype, m unsafe.Pointer, key string, val unsafe.Pointer) { + mapassign(t, m, unsafe.Pointer(&key), val) +} + +func mapdelete_faststr(t *rtype, m unsafe.Pointer, key string) { + mapdelete(t, m, unsafe.Pointer(&key)) +} + +type hiter struct { + t Type + m *js.Object // Underlying map object. + keys *js.Object + i int + + // last is the last object the iterator indicates. If this object exists, the + // functions that return the current key or value returns this object, + // regardless of the current iterator. It is because the current iterator + // might be stale due to key deletion in a loop. + last *js.Object +} + +func (iter *hiter) skipUntilValidKey() { + for iter.i < iter.keys.Length() { + k := iter.keys.Index(iter.i) + entry := iter.m.Call("get", k) + if entry != js.Undefined { + break + } + // The key is already deleted. Move on the next item. + iter.i++ + } +} + +func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer { + mapObj := js.InternalObject(m) + keys := js.Global.Get("Array").New() + if mapObj.Get("keys") != js.Undefined { + keysIter := mapObj.Call("keys") + if mapObj.Get("keys") != js.Undefined { + keys = js.Global.Get("Array").Call("from", keysIter) + } + } + + return unsafe.Pointer(&hiter{ + t: t, + m: mapObj, + keys: keys, + i: 0, + last: nil, + }) +} + +func mapiterkey(_it unsafe.Pointer) unsafe.Pointer { + it := (*hiter)(_it) + var kv *js.Object + if it.last != nil { + kv = it.last + } else { + it.skipUntilValidKey() + if it.i == it.keys.Length() { + return nil + } + k := it.keys.Index(it.i) + kv = it.m.Call("get", k) + + // Record the key-value pair for later accesses. + it.last = kv + } + return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(it.t.Key()))).Unsafe()) +} + +func mapiterelem(_it unsafe.Pointer) unsafe.Pointer { + it := (*hiter)(_it) + var kv *js.Object + if it.last != nil { + kv = it.last + } else { + it.skipUntilValidKey() + if it.i == it.keys.Length() { + return nil + } + k := it.keys.Index(it.i) + kv = it.m.Call("get", k) + it.last = kv + } + return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("v"), jsType(PtrTo(it.t.Elem()))).Unsafe()) +} + +func mapiternext(_it unsafe.Pointer) { + it := (*hiter)(_it) + it.last = nil + it.i++ +} + +func maplen(m unsafe.Pointer) int { + return js.InternalObject(m).Get("size").Int() +} + +func cvtDirect(v Value, typ Type) Value { + srcVal := v.object() + if srcVal == jsType(v.typ).Get("nil") { + return makeValue(typ, jsType(typ).Get("nil"), v.flag) + } + + var val *js.Object + switch k := typ.Kind(); k { + case Slice: + slice := jsType(typ).New(srcVal.Get("$array")) + slice.Set("$offset", srcVal.Get("$offset")) + slice.Set("$length", srcVal.Get("$length")) + slice.Set("$capacity", srcVal.Get("$capacity")) + val = js.Global.Call("$newDataPointer", slice, jsType(PtrTo(typ))) + case Ptr: + switch typ.Elem().Kind() { + case Struct: + if typ.Elem() == v.typ.Elem() { + val = srcVal + break + } + val = jsType(typ).New() + copyStruct(val, srcVal, typ.Elem()) + case Array: + // Unlike other pointers, array pointers are "wrapped" types (see + // isWrapped() in the compiler package), and are represented by a native + // javascript array object here. + val = srcVal + default: + val = jsType(typ).New(srcVal.Get("$get"), srcVal.Get("$set")) + } + case Struct: + val = jsType(typ).Get("ptr").New() + copyStruct(val, srcVal, typ) + case Array, Bool, Chan, Func, Interface, Map, String, UnsafePointer: + val = js.InternalObject(v.ptr) + default: + panic(&ValueError{"reflect.Convert", k}) + } + return Value{typ.common(), unsafe.Pointer(val.Unsafe()), v.flag.ro() | v.flag&flagIndir | flag(typ.Kind())} +} + +func Copy(dst, src Value) int { + dk := dst.kind() + if dk != Array && dk != Slice { + panic(&ValueError{"reflect.Copy", dk}) + } + if dk == Array { + dst.mustBeAssignable() + } + dst.mustBeExported() + + sk := src.kind() + var stringCopy bool + if sk != Array && sk != Slice { + stringCopy = sk == String && dst.typ.Elem().Kind() == Uint8 + if !stringCopy { + panic(&ValueError{"reflect.Copy", sk}) + } + } + src.mustBeExported() + + if !stringCopy { + typesMustMatch("reflect.Copy", dst.typ.Elem(), src.typ.Elem()) + } + + dstVal := dst.object() + if dk == Array { + dstVal = jsType(SliceOf(dst.typ.Elem())).New(dstVal) + } + + srcVal := src.object() + if sk == Array { + srcVal = jsType(SliceOf(src.typ.Elem())).New(srcVal) + } + + if stringCopy { + return js.Global.Call("$copyString", dstVal, srcVal).Int() + } + return js.Global.Call("$copySlice", dstVal, srcVal).Int() +} + +func methodReceiver(op string, v Value, i int) (_ *rtype, t *funcType, fn unsafe.Pointer) { + var prop string + if v.typ.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.methods) { + panic("reflect: internal error: invalid method index") + } + m := &tt.methods[i] + if !tt.nameOff(m.name).isExported() { + panic("reflect: " + op + " of unexported method") + } + t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ))) + prop = tt.nameOff(m.name).name() + } else { + ms := v.typ.exportedMethods() + if uint(i) >= uint(len(ms)) { + panic("reflect: internal error: invalid method index") + } + m := ms[i] + if !v.typ.nameOff(m.name).isExported() { + panic("reflect: " + op + " of unexported method") + } + t = (*funcType)(unsafe.Pointer(v.typ.typeOff(m.mtyp))) + prop = js.Global.Call("$methodSet", jsType(v.typ)).Index(i).Get("prop").String() + } + rcvr := v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + fn = unsafe.Pointer(rcvr.Get(prop).Unsafe()) + return +} + +func valueInterface(v Value, safe bool) interface{} { + if v.flag == 0 { + panic(&ValueError{"reflect.Value.Interface", 0}) + } + if safe && v.flag&flagRO != 0 { + panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") + } + if v.flag&flagMethod != 0 { + v = makeMethodValue("Interface", v) + } + + if isWrapped(v.typ) { + if v.flag&flagIndir != 0 && v.Kind() == Struct { + cv := jsType(v.typ).Call("zero") + copyStruct(cv, v.object(), v.typ) + return interface{}(unsafe.Pointer(jsType(v.typ).New(cv).Unsafe())) + } + return interface{}(unsafe.Pointer(jsType(v.typ).New(v.object()).Unsafe())) + } + return interface{}(unsafe.Pointer(v.object().Unsafe())) +} + +func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) { + js.InternalObject(dst).Call("$set", js.InternalObject(src)) +} + +func makeMethodValue(op string, v Value) Value { + if v.flag&flagMethod == 0 { + panic("reflect: internal error: invalid use of makePartialFunc") + } + + _, _, fn := methodReceiver(op, v, int(v.flag)>>flagMethodShift) + rcvr := v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + return js.InternalObject(fn).Call("apply", rcvr, arguments) + }) + return Value{v.Type().common(), unsafe.Pointer(fv.Unsafe()), v.flag.ro() | flag(Func)} +} + +func (t *rtype) pointers() bool { + switch t.Kind() { + case Ptr, Map, Chan, Func, Struct, Array: + return true + default: + return false + } +} + +func (t *rtype) Comparable() bool { + switch t.Kind() { + case Func, Slice, Map: + return false + case Array: + return t.Elem().Comparable() + case Struct: + for i := 0; i < t.NumField(); i++ { + if !t.Field(i).Type.Comparable() { + return false + } + } + } + return true +} + +func (t *rtype) Method(i int) (m Method) { + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.Method(i) + } + methods := t.exportedMethods() + if i < 0 || i >= len(methods) { + panic("reflect: Method index out of range") + } + p := methods[i] + pname := t.nameOff(p.name) + m.Name = pname.name() + fl := flag(Func) + mtyp := t.typeOff(p.mtyp) + ft := (*funcType)(unsafe.Pointer(mtyp)) + in := make([]Type, 0, 1+len(ft.in())) + in = append(in, t) + for _, arg := range ft.in() { + in = append(in, arg) + } + out := make([]Type, 0, len(ft.out())) + for _, ret := range ft.out() { + out = append(out, ret) + } + mt := FuncOf(in, out, ft.IsVariadic()) + m.Type = mt + prop := js.Global.Call("$methodSet", js.InternalObject(t).Get("jsType")).Index(i).Get("prop").String() + fn := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + rcvr := arguments[0] + return rcvr.Get(prop).Call("apply", rcvr, arguments[1:]) + }) + m.Func = Value{mt.(*rtype), unsafe.Pointer(fn.Unsafe()), fl} + + m.Index = i + return m +} + +func (v Value) object() *js.Object { + if v.typ.Kind() == Array || v.typ.Kind() == Struct { + return js.InternalObject(v.ptr) + } + if v.flag&flagIndir != 0 { + val := js.InternalObject(v.ptr).Call("$get") + if val != js.Global.Get("$ifaceNil") && val.Get("constructor") != jsType(v.typ) { + switch v.typ.Kind() { + case Uint64, Int64: + val = jsType(v.typ).New(val.Get("$high"), val.Get("$low")) + case Complex64, Complex128: + val = jsType(v.typ).New(val.Get("$real"), val.Get("$imag")) + case Slice: + if val == val.Get("constructor").Get("nil") { + val = jsType(v.typ).Get("nil") + break + } + newVal := jsType(v.typ).New(val.Get("$array")) + newVal.Set("$offset", val.Get("$offset")) + newVal.Set("$length", val.Get("$length")) + newVal.Set("$capacity", val.Get("$capacity")) + val = newVal + } + } + return js.InternalObject(val.Unsafe()) + } + return js.InternalObject(v.ptr) +} + +func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value { + if v.flag&flagMethod != 0 { + v = makeMethodValue(context, v) + } + + switch { + case directlyAssignable(dst, v.typ): + // Overwrite type so that they match. + // Same memory layout, so no harm done. + fl := v.flag&(flagAddr|flagIndir) | v.flag.ro() + fl |= flag(dst.Kind()) + return Value{dst, v.ptr, fl} + + case implements(dst, v.typ): + if target == nil { + target = unsafe_New(dst) + } + // GopherJS: Skip the v.Kind() == Interface && v.IsNil() if statement + // from upstream. ifaceE2I below does not panic, and it needs + // to run, given its custom implementation. + x := valueInterface(v, false) + if dst.NumMethod() == 0 { + *(*interface{})(target) = x + } else { + ifaceE2I(dst, x, target) + } + return Value{dst, target, flagIndir | flag(Interface)} + } + + // Failed. + panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String()) +} + +var callHelper = js.Global.Get("$call").Interface().(func(...interface{}) *js.Object) + +func (v Value) call(op string, in []Value) []Value { + var ( + t *funcType + fn unsafe.Pointer + rcvr *js.Object + ) + if v.flag&flagMethod != 0 { + _, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) + rcvr = v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + } else { + t = (*funcType)(unsafe.Pointer(v.typ)) + fn = unsafe.Pointer(v.object().Unsafe()) + rcvr = js.Undefined + } + + if fn == nil { + panic("reflect.Value.Call: call of nil function") + } + + isSlice := op == "CallSlice" + n := t.NumIn() + if isSlice { + if !t.IsVariadic() { + panic("reflect: CallSlice of non-variadic function") + } + if len(in) < n { + panic("reflect: CallSlice with too few input arguments") + } + if len(in) > n { + panic("reflect: CallSlice with too many input arguments") + } + } else { + if t.IsVariadic() { + n-- + } + if len(in) < n { + panic("reflect: Call with too few input arguments") + } + if !t.IsVariadic() && len(in) > n { + panic("reflect: Call with too many input arguments") + } + } + for _, x := range in { + if x.Kind() == Invalid { + panic("reflect: " + op + " using zero Value argument") + } + } + for i := 0; i < n; i++ { + if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) { + panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String()) + } + } + if !isSlice && t.IsVariadic() { + // prepare slice for remaining values + m := len(in) - n + slice := MakeSlice(t.In(n), m, m) + elem := t.In(n).Elem() + for i := 0; i < m; i++ { + x := in[n+i] + if xt := x.Type(); !xt.AssignableTo(elem) { + panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op) + } + slice.Index(i).Set(x) + } + origIn := in + in = make([]Value, n+1) + copy(in[:n], origIn) + in[n] = slice + } + + nin := len(in) + if nin != t.NumIn() { + panic("reflect.Value.Call: wrong argument count") + } + nout := t.NumOut() + + argsArray := js.Global.Get("Array").New(t.NumIn()) + for i, arg := range in { + argsArray.SetIndex(i, unwrapJsObject(t.In(i), arg.assignTo("reflect.Value.Call", t.In(i).common(), nil).object())) + } + results := callHelper(js.InternalObject(fn), rcvr, argsArray) + + switch nout { + case 0: + return nil + case 1: + return []Value{makeValue(t.Out(0), wrapJsObject(t.Out(0), results), 0)} + default: + ret := make([]Value, nout) + for i := range ret { + ret[i] = makeValue(t.Out(i), wrapJsObject(t.Out(i), results.Index(i)), 0) + } + return ret + } +} + +func (v Value) Cap() int { + k := v.kind() + switch k { + case Array: + return v.typ.Len() + case Chan, Slice: + return v.object().Get("$capacity").Int() + } + panic(&ValueError{"reflect.Value.Cap", k}) +} + +var jsObjectPtr = reflectType(js.Global.Get("$jsObjectPtr")) + +func wrapJsObject(typ Type, val *js.Object) *js.Object { + if typ == jsObjectPtr { + return jsType(jsObjectPtr).New(val) + } + return val +} + +func unwrapJsObject(typ Type, val *js.Object) *js.Object { + if typ == jsObjectPtr { + return val.Get("object") + } + return val +} + +func (v Value) Elem() Value { + switch k := v.kind(); k { + case Interface: + val := v.object() + if val == js.Global.Get("$ifaceNil") { + return Value{} + } + typ := reflectType(val.Get("constructor")) + return makeValue(typ, val.Get("$val"), v.flag.ro()) + + case Ptr: + if v.IsNil() { + return Value{} + } + val := v.object() + tt := (*ptrType)(unsafe.Pointer(v.typ)) + fl := v.flag&flagRO | flagIndir | flagAddr + fl |= flag(tt.elem.Kind()) + return Value{tt.elem, unsafe.Pointer(wrapJsObject(tt.elem, val).Unsafe()), fl} + + default: + panic(&ValueError{"reflect.Value.Elem", k}) + } +} + +func (v Value) Field(i int) Value { + if v.kind() != Struct { + panic(&ValueError{"reflect.Value.Field", v.kind()}) + } + tt := (*structType)(unsafe.Pointer(v.typ)) + if uint(i) >= uint(len(tt.fields)) { + panic("reflect: Field index out of range") + } + + prop := jsType(v.typ).Get("fields").Index(i).Get("prop").String() + field := &tt.fields[i] + typ := field.typ + + fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind()) + if !field.name.isExported() { + if field.embedded() { + fl |= flagEmbedRO + } else { + fl |= flagStickyRO + } + } + + if tag := tt.fields[i].name.tag(); tag != "" && i != 0 { + if jsTag := getJsTag(tag); jsTag != "" { + for { + v = v.Field(0) + if v.typ == jsObjectPtr { + o := v.object().Get("object") + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return js.Global.Call("$internalize", o.Get(jsTag), jsType(typ)) }), + js.InternalObject(func(x *js.Object) { o.Set(jsTag, js.Global.Call("$externalize", x, jsType(typ))) }), + ).Unsafe()), fl} + } + if v.typ.Kind() == Ptr { + v = v.Elem() + } + } + } + } + + s := js.InternalObject(v.ptr) + if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return wrapJsObject(typ, s.Get(prop)) }), + js.InternalObject(func(x *js.Object) { s.Set(prop, unwrapJsObject(typ, x)) }), + ).Unsafe()), fl} + } + return makeValue(typ, wrapJsObject(typ, s.Get(prop)), fl) +} + +func getJsTag(tag string) string { + for tag != "" { + // skip leading space + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + + // scan to colon. + // a space or a quote is a syntax error + i = 0 + for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' { + i++ + } + if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + name := string(tag[:i]) + tag = tag[i+1:] + + // scan quoted string to find value + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if name == "js" { + value, _ := strconv.Unquote(qvalue) + return value + } + } + return "" +} + +func (v Value) Index(i int) Value { + switch k := v.kind(); k { + case Array: + tt := (*arrayType)(unsafe.Pointer(v.typ)) + if i < 0 || i > int(tt.len) { + panic("reflect: array index out of range") + } + typ := tt.elem + fl := v.flag&(flagIndir|flagAddr) | v.flag.ro() | flag(typ.Kind()) + + a := js.InternalObject(v.ptr) + if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), + js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), + ).Unsafe()), fl} + } + return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) + + case Slice: + s := v.object() + if i < 0 || i >= s.Get("$length").Int() { + panic("reflect: slice index out of range") + } + tt := (*sliceType)(unsafe.Pointer(v.typ)) + typ := tt.elem + fl := flagAddr | flagIndir | v.flag.ro() | flag(typ.Kind()) + + i += s.Get("$offset").Int() + a := s.Get("$array") + if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), + js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), + ).Unsafe()), fl} + } + return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) + + case String: + str := *(*string)(v.ptr) + if i < 0 || i >= len(str) { + panic("reflect: string index out of range") + } + fl := v.flag.ro() | flag(Uint8) | flagIndir + c := str[i] + return Value{uint8Type, unsafe.Pointer(&c), fl} + + default: + panic(&ValueError{"reflect.Value.Index", k}) + } +} + +func (v Value) InterfaceData() [2]uintptr { + panic(errors.New("InterfaceData is not supported by GopherJS")) +} + +func (v Value) IsNil() bool { + switch k := v.kind(); k { + case Ptr, Slice: + return v.object() == jsType(v.typ).Get("nil") + case Chan: + return v.object() == js.Global.Get("$chanNil") + case Func: + return v.object() == js.Global.Get("$throwNilPointerError") + case Map: + return v.object() == js.InternalObject(false) + case Interface: + return v.object() == js.Global.Get("$ifaceNil") + case UnsafePointer: + return v.object().Unsafe() == 0 + default: + panic(&ValueError{"reflect.Value.IsNil", k}) + } +} + +func (v Value) Len() int { + switch k := v.kind(); k { + case Array, String: + return v.object().Length() + case Slice: + return v.object().Get("$length").Int() + case Chan: + return v.object().Get("$buffer").Get("length").Int() + case Map: + return v.object().Get("size").Int() + default: + panic(&ValueError{"reflect.Value.Len", k}) + } +} + +func (v Value) Pointer() uintptr { + switch k := v.kind(); k { + case Chan, Map, Ptr, UnsafePointer: + if v.IsNil() { + return 0 + } + return v.object().Unsafe() + case Func: + if v.IsNil() { + return 0 + } + return 1 + case Slice: + if v.IsNil() { + return 0 + } + return v.object().Get("$array").Unsafe() + default: + panic(&ValueError{"reflect.Value.Pointer", k}) + } +} + +func (v Value) Set(x Value) { + v.mustBeAssignable() + x.mustBeExported() + x = x.assignTo("reflect.Set", v.typ, nil) + if v.flag&flagIndir != 0 { + switch v.typ.Kind() { + case Array: + jsType(v.typ).Call("copy", js.InternalObject(v.ptr), js.InternalObject(x.ptr)) + case Interface: + js.InternalObject(v.ptr).Call("$set", js.InternalObject(valueInterface(x, false))) + case Struct: + copyStruct(js.InternalObject(v.ptr), js.InternalObject(x.ptr), v.typ) + default: + js.InternalObject(v.ptr).Call("$set", x.object()) + } + return + } + v.ptr = x.ptr +} + +func (v Value) SetBytes(x []byte) { + v.mustBeAssignable() + v.mustBe(Slice) + if v.typ.Elem().Kind() != Uint8 { + panic("reflect.Value.SetBytes of non-byte slice") + } + slice := js.InternalObject(x) + if v.typ.Name() != "" || v.typ.Elem().Name() != "" { + typedSlice := jsType(v.typ).New(slice.Get("$array")) + typedSlice.Set("$offset", slice.Get("$offset")) + typedSlice.Set("$length", slice.Get("$length")) + typedSlice.Set("$capacity", slice.Get("$capacity")) + slice = typedSlice + } + js.InternalObject(v.ptr).Call("$set", slice) +} + +func (v Value) SetCap(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := js.InternalObject(v.ptr).Call("$get") + if n < s.Get("$length").Int() || n > s.Get("$capacity").Int() { + panic("reflect: slice capacity out of range in SetCap") + } + newSlice := jsType(v.typ).New(s.Get("$array")) + newSlice.Set("$offset", s.Get("$offset")) + newSlice.Set("$length", s.Get("$length")) + newSlice.Set("$capacity", n) + js.InternalObject(v.ptr).Call("$set", newSlice) +} + +func (v Value) SetLen(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := js.InternalObject(v.ptr).Call("$get") + if n < 0 || n > s.Get("$capacity").Int() { + panic("reflect: slice length out of range in SetLen") + } + newSlice := jsType(v.typ).New(s.Get("$array")) + newSlice.Set("$offset", s.Get("$offset")) + newSlice.Set("$length", n) + newSlice.Set("$capacity", s.Get("$capacity")) + js.InternalObject(v.ptr).Call("$set", newSlice) +} + +func (v Value) Slice(i, j int) Value { + var ( + cap int + typ Type + s *js.Object + ) + switch kind := v.kind(); kind { + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = SliceOf(tt.elem) + s = jsType(typ).New(v.object()) + + case Slice: + typ = v.typ + s = v.object() + cap = s.Get("$capacity").Int() + + case String: + str := *(*string)(v.ptr) + if i < 0 || j < i || j > len(str) { + panic("reflect.Value.Slice: string slice index out of bounds") + } + return ValueOf(str[i:j]) + + default: + panic(&ValueError{"reflect.Value.Slice", kind}) + } + + if i < 0 || j < i || j > cap { + panic("reflect.Value.Slice: slice index out of bounds") + } + + return makeValue(typ, js.Global.Call("$subslice", s, i, j), v.flag.ro()) +} + +func (v Value) Slice3(i, j, k int) Value { + var ( + cap int + typ Type + s *js.Object + ) + switch kind := v.kind(); kind { + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = SliceOf(tt.elem) + s = jsType(typ).New(v.object()) + + case Slice: + typ = v.typ + s = v.object() + cap = s.Get("$capacity").Int() + + default: + panic(&ValueError{"reflect.Value.Slice3", kind}) + } + + if i < 0 || j < i || k < j || k > cap { + panic("reflect.Value.Slice3: slice index out of bounds") + } + + return makeValue(typ, js.Global.Call("$subslice", s, i, j, k), v.flag.ro()) +} + +func (v Value) Close() { + v.mustBe(Chan) + v.mustBeExported() + js.Global.Call("$close", v.object()) +} + +var selectHelper = js.Global.Get("$select").Interface().(func(...interface{}) *js.Object) + +func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool) { + comms := [][]*js.Object{{js.InternalObject(ch)}} + if nb { + comms = append(comms, []*js.Object{}) + } + selectRes := selectHelper(comms) + if nb && selectRes.Index(0).Int() == 1 { + return false, false + } + recvRes := selectRes.Index(1) + js.InternalObject(val).Call("$set", recvRes.Index(0)) + return true, recvRes.Index(1).Bool() +} + +func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool { + comms := [][]*js.Object{{js.InternalObject(ch), js.InternalObject(val).Call("$get")}} + if nb { + comms = append(comms, []*js.Object{}) + } + selectRes := selectHelper(comms) + if nb && selectRes.Index(0).Int() == 1 { + return false + } + return true +} + +func rselect(rselects []runtimeSelect) (chosen int, recvOK bool) { + comms := make([][]*js.Object, len(rselects)) + for i, s := range rselects { + switch SelectDir(s.dir) { + case SelectDefault: + comms[i] = []*js.Object{} + case SelectRecv: + ch := js.Global.Get("$chanNil") + if js.InternalObject(s.ch) != js.InternalObject(0) { + ch = js.InternalObject(s.ch) + } + comms[i] = []*js.Object{ch} + case SelectSend: + ch := js.Global.Get("$chanNil") + var val *js.Object + if js.InternalObject(s.ch) != js.InternalObject(0) { + ch = js.InternalObject(s.ch) + val = js.InternalObject(s.val).Call("$get") + } + comms[i] = []*js.Object{ch, val} + } + } + selectRes := selectHelper(comms) + c := selectRes.Index(0).Int() + if SelectDir(rselects[c].dir) == SelectRecv { + recvRes := selectRes.Index(1) + js.InternalObject(rselects[c].val).Call("$set", recvRes.Index(0)) + return c, recvRes.Index(1).Bool() + } + return c, false +} + +func DeepEqual(a1, a2 interface{}) bool { + i1 := js.InternalObject(a1) + i2 := js.InternalObject(a2) + if i1 == i2 { + return true + } + if i1 == nil || i2 == nil || i1.Get("constructor") != i2.Get("constructor") { + return false + } + return deepValueEqualJs(ValueOf(a1), ValueOf(a2), nil) +} + +func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { + if !v1.IsValid() || !v2.IsValid() { + return !v1.IsValid() && !v2.IsValid() + } + if v1.Type() != v2.Type() { + return false + } + if v1.Type() == jsObjectPtr { + return unwrapJsObject(jsObjectPtr, v1.object()) == unwrapJsObject(jsObjectPtr, v2.object()) + } + + switch v1.Kind() { + case Array, Map, Slice, Struct: + for _, entry := range visited { + if v1.ptr == entry[0] && v2.ptr == entry[1] { + return true + } + } + visited = append(visited, [2]unsafe.Pointer{v1.ptr, v2.ptr}) + } + + switch v1.Kind() { + case Array, Slice: + if v1.Kind() == Slice { + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.object() == v2.object() { + return true + } + } + n := v1.Len() + if n != v2.Len() { + return false + } + for i := 0; i < n; i++ { + if !deepValueEqualJs(v1.Index(i), v2.Index(i), visited) { + return false + } + } + return true + case Interface: + if v1.IsNil() || v2.IsNil() { + return v1.IsNil() && v2.IsNil() + } + return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) + case Ptr: + return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) + case Struct: + n := v1.NumField() + for i := 0; i < n; i++ { + if !deepValueEqualJs(v1.Field(i), v2.Field(i), visited) { + return false + } + } + return true + case Map: + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.object() == v2.object() { + return true + } + keys := v1.MapKeys() + if len(keys) != v2.Len() { + return false + } + for _, k := range keys { + val1 := v1.MapIndex(k) + val2 := v2.MapIndex(k) + if !val1.IsValid() || !val2.IsValid() || !deepValueEqualJs(val1, val2, visited) { + return false + } + } + return true + case Func: + return v1.IsNil() && v2.IsNil() + case UnsafePointer: + return v1.object() == v2.object() + } + + return js.Global.Call("$interfaceIsEqual", js.InternalObject(valueInterface(v1, false)), js.InternalObject(valueInterface(v2, false))).Bool() +} + +func methodNameSkip() string { + pc, _, _, _ := runtime.Caller(3) + f := runtime.FuncForPC(pc) + if f == nil { + return "unknown method" + } + // Function name extracted from the call stack can be different from vanilla + // Go. Here we try to fix stuff like "Object.$packages.reflect.Q.ptr.SetIterKey" + // into "Value.SetIterKey". + // This workaround may become obsolete after https://github.com/gopherjs/gopherjs/issues/1085 + // is resolved. + name := f.Name() + idx := len(name) - 1 + for idx > 0 { + if name[idx] == '.' { + break + } + idx-- + } + if idx < 0 { + return name + } + return "Value" + name[idx:] +} + +func verifyNotInHeapPtr(p uintptr) bool { + // Go runtime uses this method to make sure that a uintptr won't crash GC if + // interpreted as a heap pointer. This is not relevant for GopherJS, so we can + // always return true. + return true +} diff --git a/compiler/natives/src/reflect/reflect_go117.go b/compiler/natives/src/reflect/reflect_go117.go new file mode 100644 index 00000000..9c651180 --- /dev/null +++ b/compiler/natives/src/reflect/reflect_go117.go @@ -0,0 +1,1782 @@ +//go:build js && go1.17 && !go1.18 +// +build js,go1.17,!go1.18 + +package reflect + +import ( + "errors" + "runtime" + "strconv" + "unsafe" + + "internal/itoa" + + "github.com/gopherjs/gopherjs/js" +) + +var initialized = false + +func init() { + // avoid dead code elimination + used := func(i interface{}) {} + used(rtype{}) + used(uncommonType{}) + used(method{}) + used(arrayType{}) + used(chanType{}) + used(funcType{}) + used(interfaceType{}) + used(mapType{}) + used(ptrType{}) + used(sliceType{}) + used(structType{}) + used(imethod{}) + used(structField{}) + + initialized = true + uint8Type = TypeOf(uint8(0)).(*rtype) // set for real +} + +// New returns a Value representing a pointer to a new zero value +// for the specified type. That is, the returned Value's Type is PtrTo(typ). +// +// The upstream version includes an extra check to avoid creating types that +// are tagged as go:notinheap. This shouldn't matter in GopherJS, and tracking +// that state is over-complex, so we just skip that check. +func New(typ Type) Value { + if typ == nil { + panic("reflect: New(nil)") + } + t := typ.(*rtype) + pt := t.ptrTo() + ptr := unsafe_New(t) + fl := flag(Ptr) + return Value{pt, ptr, fl} +} + +func jsType(typ Type) *js.Object { + return js.InternalObject(typ).Get("jsType") +} + +func reflectType(typ *js.Object) *rtype { + if typ.Get("reflectType") == js.Undefined { + rt := &rtype{ + size: uintptr(typ.Get("size").Int()), + kind: uint8(typ.Get("kind").Int()), + str: resolveReflectName(newName(internalStr(typ.Get("string")), "", typ.Get("exported").Bool())), + } + js.InternalObject(rt).Set("jsType", typ) + typ.Set("reflectType", js.InternalObject(rt)) + + methodSet := js.Global.Call("$methodSet", typ) + if methodSet.Length() != 0 || typ.Get("named").Bool() { + rt.tflag |= tflagUncommon + if typ.Get("named").Bool() { + rt.tflag |= tflagNamed + } + var reflectMethods []method + for i := 0; i < methodSet.Length(); i++ { // Exported methods first. + m := methodSet.Index(i) + exported := internalStr(m.Get("pkg")) == "" + if !exported { + continue + } + reflectMethods = append(reflectMethods, method{ + name: resolveReflectName(newMethodName(m)), + mtyp: newTypeOff(reflectType(m.Get("typ"))), + }) + } + xcount := uint16(len(reflectMethods)) + for i := 0; i < methodSet.Length(); i++ { // Unexported methods second. + m := methodSet.Index(i) + exported := internalStr(m.Get("pkg")) == "" + if exported { + continue + } + reflectMethods = append(reflectMethods, method{ + name: resolveReflectName(newMethodName(m)), + mtyp: newTypeOff(reflectType(m.Get("typ"))), + }) + } + ut := &uncommonType{ + pkgPath: resolveReflectName(newName(internalStr(typ.Get("pkg")), "", false)), + mcount: uint16(methodSet.Length()), + xcount: xcount, + _methods: reflectMethods, + } + js.InternalObject(ut).Set("jsType", typ) + js.InternalObject(rt).Set("uncommonType", js.InternalObject(ut)) + } + + switch rt.Kind() { + case Array: + setKindType(rt, &arrayType{ + elem: reflectType(typ.Get("elem")), + len: uintptr(typ.Get("len").Int()), + }) + case Chan: + dir := BothDir + if typ.Get("sendOnly").Bool() { + dir = SendDir + } + if typ.Get("recvOnly").Bool() { + dir = RecvDir + } + setKindType(rt, &chanType{ + elem: reflectType(typ.Get("elem")), + dir: uintptr(dir), + }) + case Func: + params := typ.Get("params") + in := make([]*rtype, params.Length()) + for i := range in { + in[i] = reflectType(params.Index(i)) + } + results := typ.Get("results") + out := make([]*rtype, results.Length()) + for i := range out { + out[i] = reflectType(results.Index(i)) + } + outCount := uint16(results.Length()) + if typ.Get("variadic").Bool() { + outCount |= 1 << 15 + } + setKindType(rt, &funcType{ + rtype: *rt, + inCount: uint16(params.Length()), + outCount: outCount, + _in: in, + _out: out, + }) + case Interface: + methods := typ.Get("methods") + imethods := make([]imethod, methods.Length()) + for i := range imethods { + m := methods.Index(i) + imethods[i] = imethod{ + name: resolveReflectName(newMethodName(m)), + typ: newTypeOff(reflectType(m.Get("typ"))), + } + } + setKindType(rt, &interfaceType{ + rtype: *rt, + pkgPath: newName(internalStr(typ.Get("pkg")), "", false), + methods: imethods, + }) + case Map: + setKindType(rt, &mapType{ + key: reflectType(typ.Get("key")), + elem: reflectType(typ.Get("elem")), + }) + case Ptr: + setKindType(rt, &ptrType{ + elem: reflectType(typ.Get("elem")), + }) + case Slice: + setKindType(rt, &sliceType{ + elem: reflectType(typ.Get("elem")), + }) + case Struct: + fields := typ.Get("fields") + reflectFields := make([]structField, fields.Length()) + for i := range reflectFields { + f := fields.Index(i) + offsetEmbed := uintptr(i) << 1 + if f.Get("embedded").Bool() { + offsetEmbed |= 1 + } + reflectFields[i] = structField{ + name: newName(internalStr(f.Get("name")), internalStr(f.Get("tag")), f.Get("exported").Bool()), + typ: reflectType(f.Get("typ")), + offsetEmbed: offsetEmbed, + } + } + setKindType(rt, &structType{ + rtype: *rt, + pkgPath: newName(internalStr(typ.Get("pkgPath")), "", false), + fields: reflectFields, + }) + } + } + + return (*rtype)(unsafe.Pointer(typ.Get("reflectType").Unsafe())) +} + +func setKindType(rt *rtype, kindType interface{}) { + js.InternalObject(rt).Set("kindType", js.InternalObject(kindType)) + js.InternalObject(kindType).Set("rtype", js.InternalObject(rt)) +} + +type uncommonType struct { + pkgPath nameOff + mcount uint16 + xcount uint16 + moff uint32 + + _methods []method +} + +func (t *uncommonType) methods() []method { + return t._methods +} + +func (t *uncommonType) exportedMethods() []method { + return t._methods[:t.xcount:t.xcount] +} + +func (t *rtype) uncommon() *uncommonType { + obj := js.InternalObject(t).Get("uncommonType") + if obj == js.Undefined { + return nil + } + return (*uncommonType)(unsafe.Pointer(obj.Unsafe())) +} + +type funcType struct { + rtype `reflect:"func"` + inCount uint16 + outCount uint16 + + _in []*rtype + _out []*rtype +} + +func (t *funcType) in() []*rtype { + return t._in +} + +func (t *funcType) out() []*rtype { + return t._out +} + +type name struct { + bytes *byte +} + +type nameData struct { + name string + tag string + exported bool + pkgPath string +} + +var nameMap = make(map[*byte]*nameData) + +func (n name) name() (s string) { return nameMap[n.bytes].name } +func (n name) tag() (s string) { return nameMap[n.bytes].tag } +func (n name) pkgPath() string { return nameMap[n.bytes].pkgPath } +func (n name) isExported() bool { return nameMap[n.bytes].exported } + +func newName(n, tag string, exported bool) name { + b := new(byte) + nameMap[b] = &nameData{ + name: n, + tag: tag, + exported: exported, + } + return name{ + bytes: b, + } +} + +// newMethodName creates name instance for a method. +// +// Input object is expected to be an entry of the "methods" list of the +// corresponding JS type. +func newMethodName(m *js.Object) name { + b := new(byte) + nameMap[b] = &nameData{ + name: internalStr(m.Get("name")), + tag: "", + pkgPath: internalStr(m.Get("pkg")), + exported: internalStr(m.Get("pkg")) == "", + } + return name{bytes: b} +} + +var nameOffList []name + +func (t *rtype) nameOff(off nameOff) name { + return nameOffList[int(off)] +} + +func resolveReflectName(n name) nameOff { + i := len(nameOffList) + nameOffList = append(nameOffList, n) + return nameOff(i) +} + +var typeOffList []*rtype + +func (t *rtype) typeOff(off typeOff) *rtype { + return typeOffList[int(off)] +} + +func newTypeOff(t *rtype) typeOff { + i := len(typeOffList) + typeOffList = append(typeOffList, t) + return typeOff(i) +} + +// addReflectOff adds a pointer to the reflection lookup map in the runtime. +// It returns a new ID that can be used as a typeOff or textOff, and will +// be resolved correctly. Implemented in the runtime package. +func addReflectOff(ptr unsafe.Pointer) int32 { + i := len(typeOffList) + typeOffList = append(typeOffList, (*rtype)(ptr)) + return int32(i) +} + +func internalStr(strObj *js.Object) string { + var c struct{ str string } + js.InternalObject(c).Set("str", strObj) // get string without internalizing + return c.str +} + +func isWrapped(typ Type) bool { + return jsType(typ).Get("wrapped").Bool() +} + +func copyStruct(dst, src *js.Object, typ Type) { + fields := jsType(typ).Get("fields") + for i := 0; i < fields.Length(); i++ { + prop := fields.Index(i).Get("prop").String() + dst.Set(prop, src.Get(prop)) + } +} + +func makeValue(t Type, v *js.Object, fl flag) Value { + rt := t.common() + if t.Kind() == Array || t.Kind() == Struct || t.Kind() == Ptr { + return Value{rt, unsafe.Pointer(v.Unsafe()), fl | flag(t.Kind())} + } + return Value{rt, unsafe.Pointer(js.Global.Call("$newDataPointer", v, jsType(rt.ptrTo())).Unsafe()), fl | flag(t.Kind()) | flagIndir} +} + +func MakeSlice(typ Type, len, cap int) Value { + if typ.Kind() != Slice { + panic("reflect.MakeSlice of non-slice type") + } + if len < 0 { + panic("reflect.MakeSlice: negative len") + } + if cap < 0 { + panic("reflect.MakeSlice: negative cap") + } + if len > cap { + panic("reflect.MakeSlice: len > cap") + } + + return makeValue(typ, js.Global.Call("$makeSlice", jsType(typ), len, cap, js.InternalObject(func() *js.Object { return jsType(typ.Elem()).Call("zero") })), 0) +} + +func TypeOf(i interface{}) Type { + if !initialized { // avoid error of uint8Type + return &rtype{} + } + if i == nil { + return nil + } + return reflectType(js.InternalObject(i).Get("constructor")) +} + +func ValueOf(i interface{}) Value { + if i == nil { + return Value{} + } + return makeValue(reflectType(js.InternalObject(i).Get("constructor")), js.InternalObject(i).Get("$val"), 0) +} + +func ArrayOf(count int, elem Type) Type { + if count < 0 { + panic("reflect: negative length passed to ArrayOf") + } + + return reflectType(js.Global.Call("$arrayType", jsType(elem), count)) +} + +func ChanOf(dir ChanDir, t Type) Type { + return reflectType(js.Global.Call("$chanType", jsType(t), dir == SendDir, dir == RecvDir)) +} + +func FuncOf(in, out []Type, variadic bool) Type { + if variadic && (len(in) == 0 || in[len(in)-1].Kind() != Slice) { + panic("reflect.FuncOf: last arg of variadic func must be slice") + } + + jsIn := make([]*js.Object, len(in)) + for i, v := range in { + jsIn[i] = jsType(v) + } + jsOut := make([]*js.Object, len(out)) + for i, v := range out { + jsOut[i] = jsType(v) + } + return reflectType(js.Global.Call("$funcType", jsIn, jsOut, variadic)) +} + +func MapOf(key, elem Type) Type { + switch key.Kind() { + case Func, Map, Slice: + panic("reflect.MapOf: invalid key type " + key.String()) + } + + return reflectType(js.Global.Call("$mapType", jsType(key), jsType(elem))) +} + +func (t *rtype) ptrTo() *rtype { + return reflectType(js.Global.Call("$ptrType", jsType(t))) +} + +func SliceOf(t Type) Type { + return reflectType(js.Global.Call("$sliceType", jsType(t))) +} + +func StructOf(fields []StructField) Type { + var ( + jsFields = make([]*js.Object, len(fields)) + fset = map[string]struct{}{} + pkgpath string + hasGCProg bool + ) + for i, field := range fields { + if field.Name == "" { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name") + } + if !isValidFieldName(field.Name) { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has invalid name") + } + if field.Type == nil { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type") + } + f, fpkgpath := runtimeStructField(field) + ft := f.typ + if ft.kind&kindGCProg != 0 { + hasGCProg = true + } + if fpkgpath != "" { + if pkgpath == "" { + pkgpath = fpkgpath + } else if pkgpath != fpkgpath { + panic("reflect.Struct: fields with different PkgPath " + pkgpath + " and " + fpkgpath) + } + } + name := field.Name + if f.embedded() { + // Embedded field + if field.Type.Kind() == Ptr { + // Embedded ** and *interface{} are illegal + elem := field.Type.Elem() + if k := elem.Kind(); k == Ptr || k == Interface { + panic("reflect.StructOf: illegal anonymous field type " + field.Type.String()) + } + } + switch field.Type.Kind() { + case Interface: + case Ptr: + ptr := (*ptrType)(unsafe.Pointer(ft)) + if unt := ptr.uncommon(); unt != nil { + if i > 0 && unt.mcount > 0 { + // Issue 15924. + panic("reflect: embedded type with methods not implemented if type is not first field") + } + if len(fields) > 1 { + panic("reflect: embedded type with methods not implemented if there is more than one field") + } + } + default: + if unt := ft.uncommon(); unt != nil { + if i > 0 && unt.mcount > 0 { + // Issue 15924. + panic("reflect: embedded type with methods not implemented if type is not first field") + } + if len(fields) > 1 && ft.kind&kindDirectIface != 0 { + panic("reflect: embedded type with methods not implemented for non-pointer type") + } + } + } + } + + if _, dup := fset[name]; dup && name != "_" { + panic("reflect.StructOf: duplicate field " + name) + } + fset[name] = struct{}{} + // To be consistent with Compiler's behavior we need to avoid externalizing + // the "name" property. The line below is effectively an inverse of the + // internalStr() function. + jsf := js.InternalObject(struct{ name string }{name}) + // The rest is set through the js.Object() interface, which the compiler will + // externalize for us. + jsf.Set("prop", name) + jsf.Set("exported", f.name.isExported()) + jsf.Set("typ", jsType(field.Type)) + jsf.Set("tag", field.Tag) + jsf.Set("embedded", field.Anonymous) + jsFields[i] = jsf + } + _ = hasGCProg + typ := js.Global.Call("$structType", "", jsFields) + if pkgpath != "" { + typ.Set("pkgPath", pkgpath) + } + return reflectType(typ) +} + +func Zero(typ Type) Value { + return makeValue(typ, jsType(typ).Call("zero"), 0) +} + +func unsafe_New(typ *rtype) unsafe.Pointer { + switch typ.Kind() { + case Struct: + return unsafe.Pointer(jsType(typ).Get("ptr").New().Unsafe()) + case Array: + return unsafe.Pointer(jsType(typ).Call("zero").Unsafe()) + default: + return unsafe.Pointer(js.Global.Call("$newDataPointer", jsType(typ).Call("zero"), jsType(typ.ptrTo())).Unsafe()) + } +} + +func makeInt(f flag, bits uint64, t Type) Value { + typ := t.common() + ptr := unsafe_New(typ) + switch typ.Kind() { + case Int8: + *(*int8)(ptr) = int8(bits) + case Int16: + *(*int16)(ptr) = int16(bits) + case Int, Int32: + *(*int32)(ptr) = int32(bits) + case Int64: + *(*int64)(ptr) = int64(bits) + case Uint8: + *(*uint8)(ptr) = uint8(bits) + case Uint16: + *(*uint16)(ptr) = uint16(bits) + case Uint, Uint32, Uintptr: + *(*uint32)(ptr) = uint32(bits) + case Uint64: + *(*uint64)(ptr) = uint64(bits) + } + return Value{typ, ptr, f | flagIndir | flag(typ.Kind())} +} + +func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { + if typ.Kind() != Func { + panic("reflect: call of MakeFunc with non-Func type") + } + + t := typ.common() + ftyp := (*funcType)(unsafe.Pointer(t)) + + fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + // Convert raw JS arguments into []Value the user-supplied function expects. + args := make([]Value, ftyp.NumIn()) + for i := range args { + argType := ftyp.In(i).common() + args[i] = makeValue(argType, arguments[i], 0) + } + + // Call the user-supplied function. + resultsSlice := fn(args) + + // Verify that returned value types are compatible with the function type specified by the caller. + if want, got := ftyp.NumOut(), len(resultsSlice); want != got { + panic("reflect: expected " + strconv.Itoa(want) + " return values, got " + strconv.Itoa(got)) + } + for i, rtyp := range ftyp.out() { + if !resultsSlice[i].Type().AssignableTo(rtyp) { + panic("reflect: " + strconv.Itoa(i) + " return value type is not compatible with the function declaration") + } + } + + // Rearrange return values according to the expected function signature. + switch ftyp.NumOut() { + case 0: + return nil + case 1: + return resultsSlice[0].object() + default: + results := js.Global.Get("Array").New(ftyp.NumOut()) + for i, r := range resultsSlice { + results.SetIndex(i, r.object()) + } + return results + } + }) + + return Value{t, unsafe.Pointer(fv.Unsafe()), flag(Func)} +} + +func typedmemmove(t *rtype, dst, src unsafe.Pointer) { + js.InternalObject(dst).Call("$set", js.InternalObject(src).Call("$get")) +} + +func loadScalar(p unsafe.Pointer, n uintptr) uintptr { + return js.InternalObject(p).Call("$get").Unsafe() +} + +func makechan(typ *rtype, size int) (ch unsafe.Pointer) { + ctyp := (*chanType)(unsafe.Pointer(typ)) + return unsafe.Pointer(js.Global.Get("$Chan").New(jsType(ctyp.elem), size).Unsafe()) +} + +func makemap(t *rtype, cap int) (m unsafe.Pointer) { + return unsafe.Pointer(js.Global.Get("Map").New().Unsafe()) +} + +func keyFor(t *rtype, key unsafe.Pointer) (*js.Object, *js.Object) { + kv := js.InternalObject(key) + if kv.Get("$get") != js.Undefined { + kv = kv.Call("$get") + } + k := jsType(t.Key()).Call("keyFor", kv) + return kv, k +} + +func mapaccess(t *rtype, m, key unsafe.Pointer) unsafe.Pointer { + if !js.InternalObject(m).Bool() { + return nil // nil map + } + _, k := keyFor(t, key) + entry := js.InternalObject(m).Call("get", k) + if entry == js.Undefined { + return nil + } + return unsafe.Pointer(js.Global.Call("$newDataPointer", entry.Get("v"), jsType(PtrTo(t.Elem()))).Unsafe()) +} + +func mapassign(t *rtype, m, key, val unsafe.Pointer) { + kv, k := keyFor(t, key) + jsVal := js.InternalObject(val).Call("$get") + et := t.Elem() + if et.Kind() == Struct { + newVal := jsType(et).Call("zero") + copyStruct(newVal, jsVal, et) + jsVal = newVal + } + entry := js.Global.Get("Object").New() + entry.Set("k", kv) + entry.Set("v", jsVal) + js.InternalObject(m).Call("set", k, entry) +} + +func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) { + _, k := keyFor(t, key) + if !js.InternalObject(m).Bool() { + return // nil map + } + js.InternalObject(m).Call("delete", k) +} + +// TODO(nevkonatkte): The following three "faststr" implementations are meant to +// perform better for the common case of string-keyed maps (see upstream: +// https://github.com/golang/go/commit/23832ba2e2fb396cda1dacf3e8afcb38ec36dcba) +// However, the stubs below will perform the same or worse because of the extra +// string-to-pointer conversion. Not sure how to fix this without significant +// code duplication, however. + +func mapaccess_faststr(t *rtype, m unsafe.Pointer, key string) (val unsafe.Pointer) { + return mapaccess(t, m, unsafe.Pointer(&key)) +} + +func mapassign_faststr(t *rtype, m unsafe.Pointer, key string, val unsafe.Pointer) { + mapassign(t, m, unsafe.Pointer(&key), val) +} + +func mapdelete_faststr(t *rtype, m unsafe.Pointer, key string) { + mapdelete(t, m, unsafe.Pointer(&key)) +} + +type hiter struct { + t Type + m *js.Object // Underlying map object. + keys *js.Object + i int + + // last is the last object the iterator indicates. If this object exists, the + // functions that return the current key or value returns this object, + // regardless of the current iterator. It is because the current iterator + // might be stale due to key deletion in a loop. + last *js.Object +} + +func (iter *hiter) skipUntilValidKey() { + for iter.i < iter.keys.Length() { + k := iter.keys.Index(iter.i) + entry := iter.m.Call("get", k) + if entry != js.Undefined { + break + } + // The key is already deleted. Move on the next item. + iter.i++ + } +} + +func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer { + mapObj := js.InternalObject(m) + keys := js.Global.Get("Array").New() + if mapObj.Get("keys") != js.Undefined { + keysIter := mapObj.Call("keys") + if mapObj.Get("keys") != js.Undefined { + keys = js.Global.Get("Array").Call("from", keysIter) + } + } + + return unsafe.Pointer(&hiter{ + t: t, + m: mapObj, + keys: keys, + i: 0, + last: nil, + }) +} + +func mapiterkey(_it unsafe.Pointer) unsafe.Pointer { + it := (*hiter)(_it) + var kv *js.Object + if it.last != nil { + kv = it.last + } else { + it.skipUntilValidKey() + if it.i == it.keys.Length() { + return nil + } + k := it.keys.Index(it.i) + kv = it.m.Call("get", k) + + // Record the key-value pair for later accesses. + it.last = kv + } + return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(it.t.Key()))).Unsafe()) +} + +func mapiterelem(_it unsafe.Pointer) unsafe.Pointer { + it := (*hiter)(_it) + var kv *js.Object + if it.last != nil { + kv = it.last + } else { + it.skipUntilValidKey() + if it.i == it.keys.Length() { + return nil + } + k := it.keys.Index(it.i) + kv = it.m.Call("get", k) + it.last = kv + } + return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("v"), jsType(PtrTo(it.t.Elem()))).Unsafe()) +} + +func mapiternext(_it unsafe.Pointer) { + it := (*hiter)(_it) + it.last = nil + it.i++ +} + +func maplen(m unsafe.Pointer) int { + return js.InternalObject(m).Get("size").Int() +} + +func cvtDirect(v Value, typ Type) Value { + srcVal := v.object() + if srcVal == jsType(v.typ).Get("nil") { + return makeValue(typ, jsType(typ).Get("nil"), v.flag) + } + + var val *js.Object + switch k := typ.Kind(); k { + case Slice: + slice := jsType(typ).New(srcVal.Get("$array")) + slice.Set("$offset", srcVal.Get("$offset")) + slice.Set("$length", srcVal.Get("$length")) + slice.Set("$capacity", srcVal.Get("$capacity")) + val = js.Global.Call("$newDataPointer", slice, jsType(PtrTo(typ))) + case Ptr: + switch typ.Elem().Kind() { + case Struct: + if typ.Elem() == v.typ.Elem() { + val = srcVal + break + } + val = jsType(typ).New() + copyStruct(val, srcVal, typ.Elem()) + case Array: + // Unlike other pointers, array pointers are "wrapped" types (see + // isWrapped() in the compiler package), and are represented by a native + // javascript array object here. + val = srcVal + default: + val = jsType(typ).New(srcVal.Get("$get"), srcVal.Get("$set")) + } + case Struct: + val = jsType(typ).Get("ptr").New() + copyStruct(val, srcVal, typ) + case Array, Bool, Chan, Func, Interface, Map, String, UnsafePointer: + val = js.InternalObject(v.ptr) + default: + panic(&ValueError{"reflect.Convert", k}) + } + return Value{typ.common(), unsafe.Pointer(val.Unsafe()), v.flag.ro() | v.flag&flagIndir | flag(typ.Kind())} +} + +// convertOp: []T -> *[N]T +func cvtSliceArrayPtr(v Value, t Type) Value { + slice := v.object() + + slen := slice.Get("$length").Int() + alen := t.Elem().Len() + if alen > slen { + panic("reflect: cannot convert slice with length " + itoa.Itoa(slen) + " to pointer to array with length " + itoa.Itoa(alen)) + } + array := js.Global.Call("$sliceToGoArray", slice, jsType(t)) + return Value{t.common(), unsafe.Pointer(array.Unsafe()), v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Ptr)} +} + +func Copy(dst, src Value) int { + dk := dst.kind() + if dk != Array && dk != Slice { + panic(&ValueError{"reflect.Copy", dk}) + } + if dk == Array { + dst.mustBeAssignable() + } + dst.mustBeExported() + + sk := src.kind() + var stringCopy bool + if sk != Array && sk != Slice { + stringCopy = sk == String && dst.typ.Elem().Kind() == Uint8 + if !stringCopy { + panic(&ValueError{"reflect.Copy", sk}) + } + } + src.mustBeExported() + + if !stringCopy { + typesMustMatch("reflect.Copy", dst.typ.Elem(), src.typ.Elem()) + } + + dstVal := dst.object() + if dk == Array { + dstVal = jsType(SliceOf(dst.typ.Elem())).New(dstVal) + } + + srcVal := src.object() + if sk == Array { + srcVal = jsType(SliceOf(src.typ.Elem())).New(srcVal) + } + + if stringCopy { + return js.Global.Call("$copyString", dstVal, srcVal).Int() + } + return js.Global.Call("$copySlice", dstVal, srcVal).Int() +} + +func methodReceiver(op string, v Value, i int) (_ *rtype, t *funcType, fn unsafe.Pointer) { + var prop string + if v.typ.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.methods) { + panic("reflect: internal error: invalid method index") + } + m := &tt.methods[i] + if !tt.nameOff(m.name).isExported() { + panic("reflect: " + op + " of unexported method") + } + t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ))) + prop = tt.nameOff(m.name).name() + } else { + ms := v.typ.exportedMethods() + if uint(i) >= uint(len(ms)) { + panic("reflect: internal error: invalid method index") + } + m := ms[i] + if !v.typ.nameOff(m.name).isExported() { + panic("reflect: " + op + " of unexported method") + } + t = (*funcType)(unsafe.Pointer(v.typ.typeOff(m.mtyp))) + prop = js.Global.Call("$methodSet", jsType(v.typ)).Index(i).Get("prop").String() + } + rcvr := v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + fn = unsafe.Pointer(rcvr.Get(prop).Unsafe()) + return +} + +func valueInterface(v Value, safe bool) interface{} { + if v.flag == 0 { + panic(&ValueError{"reflect.Value.Interface", 0}) + } + if safe && v.flag&flagRO != 0 { + panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") + } + if v.flag&flagMethod != 0 { + v = makeMethodValue("Interface", v) + } + + if isWrapped(v.typ) { + if v.flag&flagIndir != 0 && v.Kind() == Struct { + cv := jsType(v.typ).Call("zero") + copyStruct(cv, v.object(), v.typ) + return interface{}(unsafe.Pointer(jsType(v.typ).New(cv).Unsafe())) + } + return interface{}(unsafe.Pointer(jsType(v.typ).New(v.object()).Unsafe())) + } + return interface{}(unsafe.Pointer(v.object().Unsafe())) +} + +func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) { + js.InternalObject(dst).Call("$set", js.InternalObject(src)) +} + +func makeMethodValue(op string, v Value) Value { + if v.flag&flagMethod == 0 { + panic("reflect: internal error: invalid use of makePartialFunc") + } + + _, _, fn := methodReceiver(op, v, int(v.flag)>>flagMethodShift) + rcvr := v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + return js.InternalObject(fn).Call("apply", rcvr, arguments) + }) + return Value{v.Type().common(), unsafe.Pointer(fv.Unsafe()), v.flag.ro() | flag(Func)} +} + +func (t *rtype) pointers() bool { + switch t.Kind() { + case Ptr, Map, Chan, Func, Struct, Array: + return true + default: + return false + } +} + +func (t *rtype) Comparable() bool { + switch t.Kind() { + case Func, Slice, Map: + return false + case Array: + return t.Elem().Comparable() + case Struct: + for i := 0; i < t.NumField(); i++ { + if !t.Field(i).Type.Comparable() { + return false + } + } + } + return true +} + +func (t *rtype) Method(i int) (m Method) { + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.Method(i) + } + methods := t.exportedMethods() + if i < 0 || i >= len(methods) { + panic("reflect: Method index out of range") + } + p := methods[i] + pname := t.nameOff(p.name) + m.Name = pname.name() + fl := flag(Func) + mtyp := t.typeOff(p.mtyp) + ft := (*funcType)(unsafe.Pointer(mtyp)) + in := make([]Type, 0, 1+len(ft.in())) + in = append(in, t) + for _, arg := range ft.in() { + in = append(in, arg) + } + out := make([]Type, 0, len(ft.out())) + for _, ret := range ft.out() { + out = append(out, ret) + } + mt := FuncOf(in, out, ft.IsVariadic()) + m.Type = mt + prop := js.Global.Call("$methodSet", js.InternalObject(t).Get("jsType")).Index(i).Get("prop").String() + fn := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { + rcvr := arguments[0] + return rcvr.Get(prop).Call("apply", rcvr, arguments[1:]) + }) + m.Func = Value{mt.(*rtype), unsafe.Pointer(fn.Unsafe()), fl} + + m.Index = i + return m +} + +func (v Value) object() *js.Object { + if v.typ.Kind() == Array || v.typ.Kind() == Struct { + return js.InternalObject(v.ptr) + } + if v.flag&flagIndir != 0 { + val := js.InternalObject(v.ptr).Call("$get") + if val != js.Global.Get("$ifaceNil") && val.Get("constructor") != jsType(v.typ) { + switch v.typ.Kind() { + case Uint64, Int64: + val = jsType(v.typ).New(val.Get("$high"), val.Get("$low")) + case Complex64, Complex128: + val = jsType(v.typ).New(val.Get("$real"), val.Get("$imag")) + case Slice: + if val == val.Get("constructor").Get("nil") { + val = jsType(v.typ).Get("nil") + break + } + newVal := jsType(v.typ).New(val.Get("$array")) + newVal.Set("$offset", val.Get("$offset")) + newVal.Set("$length", val.Get("$length")) + newVal.Set("$capacity", val.Get("$capacity")) + val = newVal + } + } + return js.InternalObject(val.Unsafe()) + } + return js.InternalObject(v.ptr) +} + +func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value { + if v.flag&flagMethod != 0 { + v = makeMethodValue(context, v) + } + + switch { + case directlyAssignable(dst, v.typ): + // Overwrite type so that they match. + // Same memory layout, so no harm done. + fl := v.flag&(flagAddr|flagIndir) | v.flag.ro() + fl |= flag(dst.Kind()) + return Value{dst, v.ptr, fl} + + case implements(dst, v.typ): + if target == nil { + target = unsafe_New(dst) + } + // GopherJS: Skip the v.Kind() == Interface && v.IsNil() if statement + // from upstream. ifaceE2I below does not panic, and it needs + // to run, given its custom implementation. + x := valueInterface(v, false) + if dst.NumMethod() == 0 { + *(*interface{})(target) = x + } else { + ifaceE2I(dst, x, target) + } + return Value{dst, target, flagIndir | flag(Interface)} + } + + // Failed. + panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String()) +} + +var callHelper = js.Global.Get("$call").Interface().(func(...interface{}) *js.Object) + +func (v Value) call(op string, in []Value) []Value { + var ( + t *funcType + fn unsafe.Pointer + rcvr *js.Object + ) + if v.flag&flagMethod != 0 { + _, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) + rcvr = v.object() + if isWrapped(v.typ) { + rcvr = jsType(v.typ).New(rcvr) + } + } else { + t = (*funcType)(unsafe.Pointer(v.typ)) + fn = unsafe.Pointer(v.object().Unsafe()) + rcvr = js.Undefined + } + + if fn == nil { + panic("reflect.Value.Call: call of nil function") + } + + isSlice := op == "CallSlice" + n := t.NumIn() + if isSlice { + if !t.IsVariadic() { + panic("reflect: CallSlice of non-variadic function") + } + if len(in) < n { + panic("reflect: CallSlice with too few input arguments") + } + if len(in) > n { + panic("reflect: CallSlice with too many input arguments") + } + } else { + if t.IsVariadic() { + n-- + } + if len(in) < n { + panic("reflect: Call with too few input arguments") + } + if !t.IsVariadic() && len(in) > n { + panic("reflect: Call with too many input arguments") + } + } + for _, x := range in { + if x.Kind() == Invalid { + panic("reflect: " + op + " using zero Value argument") + } + } + for i := 0; i < n; i++ { + if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) { + panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String()) + } + } + if !isSlice && t.IsVariadic() { + // prepare slice for remaining values + m := len(in) - n + slice := MakeSlice(t.In(n), m, m) + elem := t.In(n).Elem() + for i := 0; i < m; i++ { + x := in[n+i] + if xt := x.Type(); !xt.AssignableTo(elem) { + panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op) + } + slice.Index(i).Set(x) + } + origIn := in + in = make([]Value, n+1) + copy(in[:n], origIn) + in[n] = slice + } + + nin := len(in) + if nin != t.NumIn() { + panic("reflect.Value.Call: wrong argument count") + } + nout := t.NumOut() + + argsArray := js.Global.Get("Array").New(t.NumIn()) + for i, arg := range in { + argsArray.SetIndex(i, unwrapJsObject(t.In(i), arg.assignTo("reflect.Value.Call", t.In(i).common(), nil).object())) + } + results := callHelper(js.InternalObject(fn), rcvr, argsArray) + + switch nout { + case 0: + return nil + case 1: + return []Value{makeValue(t.Out(0), wrapJsObject(t.Out(0), results), 0)} + default: + ret := make([]Value, nout) + for i := range ret { + ret[i] = makeValue(t.Out(i), wrapJsObject(t.Out(i), results.Index(i)), 0) + } + return ret + } +} + +func (v Value) Cap() int { + k := v.kind() + switch k { + case Array: + return v.typ.Len() + case Chan, Slice: + return v.object().Get("$capacity").Int() + } + panic(&ValueError{"reflect.Value.Cap", k}) +} + +var jsObjectPtr = reflectType(js.Global.Get("$jsObjectPtr")) + +func wrapJsObject(typ Type, val *js.Object) *js.Object { + if typ == jsObjectPtr { + return jsType(jsObjectPtr).New(val) + } + return val +} + +func unwrapJsObject(typ Type, val *js.Object) *js.Object { + if typ == jsObjectPtr { + return val.Get("object") + } + return val +} + +func (v Value) Elem() Value { + switch k := v.kind(); k { + case Interface: + val := v.object() + if val == js.Global.Get("$ifaceNil") { + return Value{} + } + typ := reflectType(val.Get("constructor")) + return makeValue(typ, val.Get("$val"), v.flag.ro()) + + case Ptr: + if v.IsNil() { + return Value{} + } + val := v.object() + tt := (*ptrType)(unsafe.Pointer(v.typ)) + fl := v.flag&flagRO | flagIndir | flagAddr + fl |= flag(tt.elem.Kind()) + return Value{tt.elem, unsafe.Pointer(wrapJsObject(tt.elem, val).Unsafe()), fl} + + default: + panic(&ValueError{"reflect.Value.Elem", k}) + } +} + +func (v Value) Field(i int) Value { + if v.kind() != Struct { + panic(&ValueError{"reflect.Value.Field", v.kind()}) + } + tt := (*structType)(unsafe.Pointer(v.typ)) + if uint(i) >= uint(len(tt.fields)) { + panic("reflect: Field index out of range") + } + + prop := jsType(v.typ).Get("fields").Index(i).Get("prop").String() + field := &tt.fields[i] + typ := field.typ + + fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind()) + if !field.name.isExported() { + if field.embedded() { + fl |= flagEmbedRO + } else { + fl |= flagStickyRO + } + } + + if tag := tt.fields[i].name.tag(); tag != "" && i != 0 { + if jsTag := getJsTag(tag); jsTag != "" { + for { + v = v.Field(0) + if v.typ == jsObjectPtr { + o := v.object().Get("object") + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return js.Global.Call("$internalize", o.Get(jsTag), jsType(typ)) }), + js.InternalObject(func(x *js.Object) { o.Set(jsTag, js.Global.Call("$externalize", x, jsType(typ))) }), + ).Unsafe()), fl} + } + if v.typ.Kind() == Ptr { + v = v.Elem() + } + } + } + } + + s := js.InternalObject(v.ptr) + if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return wrapJsObject(typ, s.Get(prop)) }), + js.InternalObject(func(x *js.Object) { s.Set(prop, unwrapJsObject(typ, x)) }), + ).Unsafe()), fl} + } + return makeValue(typ, wrapJsObject(typ, s.Get(prop)), fl) +} + +func getJsTag(tag string) string { + for tag != "" { + // skip leading space + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + + // scan to colon. + // a space or a quote is a syntax error + i = 0 + for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' { + i++ + } + if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + name := string(tag[:i]) + tag = tag[i+1:] + + // scan quoted string to find value + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if name == "js" { + value, _ := strconv.Unquote(qvalue) + return value + } + } + return "" +} + +func (v Value) Index(i int) Value { + switch k := v.kind(); k { + case Array: + tt := (*arrayType)(unsafe.Pointer(v.typ)) + if i < 0 || i > int(tt.len) { + panic("reflect: array index out of range") + } + typ := tt.elem + fl := v.flag&(flagIndir|flagAddr) | v.flag.ro() | flag(typ.Kind()) + + a := js.InternalObject(v.ptr) + if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), + js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), + ).Unsafe()), fl} + } + return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) + + case Slice: + s := v.object() + if i < 0 || i >= s.Get("$length").Int() { + panic("reflect: slice index out of range") + } + tt := (*sliceType)(unsafe.Pointer(v.typ)) + typ := tt.elem + fl := flagAddr | flagIndir | v.flag.ro() | flag(typ.Kind()) + + i += s.Get("$offset").Int() + a := s.Get("$array") + if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { + return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( + js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), + js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), + ).Unsafe()), fl} + } + return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) + + case String: + str := *(*string)(v.ptr) + if i < 0 || i >= len(str) { + panic("reflect: string index out of range") + } + fl := v.flag.ro() | flag(Uint8) | flagIndir + c := str[i] + return Value{uint8Type, unsafe.Pointer(&c), fl} + + default: + panic(&ValueError{"reflect.Value.Index", k}) + } +} + +func (v Value) InterfaceData() [2]uintptr { + panic(errors.New("InterfaceData is not supported by GopherJS")) +} + +func (v Value) IsNil() bool { + switch k := v.kind(); k { + case Ptr, Slice: + return v.object() == jsType(v.typ).Get("nil") + case Chan: + return v.object() == js.Global.Get("$chanNil") + case Func: + return v.object() == js.Global.Get("$throwNilPointerError") + case Map: + return v.object() == js.InternalObject(false) + case Interface: + return v.object() == js.Global.Get("$ifaceNil") + case UnsafePointer: + return v.object().Unsafe() == 0 + default: + panic(&ValueError{"reflect.Value.IsNil", k}) + } +} + +func (v Value) Len() int { + switch k := v.kind(); k { + case Array, String: + return v.object().Length() + case Slice: + return v.object().Get("$length").Int() + case Chan: + return v.object().Get("$buffer").Get("length").Int() + case Map: + return v.object().Get("size").Int() + default: + panic(&ValueError{"reflect.Value.Len", k}) + } +} + +func (v Value) Pointer() uintptr { + switch k := v.kind(); k { + case Chan, Map, Ptr, UnsafePointer: + if v.IsNil() { + return 0 + } + return v.object().Unsafe() + case Func: + if v.IsNil() { + return 0 + } + return 1 + case Slice: + if v.IsNil() { + return 0 + } + return v.object().Get("$array").Unsafe() + default: + panic(&ValueError{"reflect.Value.Pointer", k}) + } +} + +func (v Value) Set(x Value) { + v.mustBeAssignable() + x.mustBeExported() + x = x.assignTo("reflect.Set", v.typ, nil) + if v.flag&flagIndir != 0 { + switch v.typ.Kind() { + case Array: + jsType(v.typ).Call("copy", js.InternalObject(v.ptr), js.InternalObject(x.ptr)) + case Interface: + js.InternalObject(v.ptr).Call("$set", js.InternalObject(valueInterface(x, false))) + case Struct: + copyStruct(js.InternalObject(v.ptr), js.InternalObject(x.ptr), v.typ) + default: + js.InternalObject(v.ptr).Call("$set", x.object()) + } + return + } + v.ptr = x.ptr +} + +func (v Value) SetBytes(x []byte) { + v.mustBeAssignable() + v.mustBe(Slice) + if v.typ.Elem().Kind() != Uint8 { + panic("reflect.Value.SetBytes of non-byte slice") + } + slice := js.InternalObject(x) + if v.typ.Name() != "" || v.typ.Elem().Name() != "" { + typedSlice := jsType(v.typ).New(slice.Get("$array")) + typedSlice.Set("$offset", slice.Get("$offset")) + typedSlice.Set("$length", slice.Get("$length")) + typedSlice.Set("$capacity", slice.Get("$capacity")) + slice = typedSlice + } + js.InternalObject(v.ptr).Call("$set", slice) +} + +func (v Value) SetCap(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := js.InternalObject(v.ptr).Call("$get") + if n < s.Get("$length").Int() || n > s.Get("$capacity").Int() { + panic("reflect: slice capacity out of range in SetCap") + } + newSlice := jsType(v.typ).New(s.Get("$array")) + newSlice.Set("$offset", s.Get("$offset")) + newSlice.Set("$length", s.Get("$length")) + newSlice.Set("$capacity", n) + js.InternalObject(v.ptr).Call("$set", newSlice) +} + +func (v Value) SetLen(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := js.InternalObject(v.ptr).Call("$get") + if n < 0 || n > s.Get("$capacity").Int() { + panic("reflect: slice length out of range in SetLen") + } + newSlice := jsType(v.typ).New(s.Get("$array")) + newSlice.Set("$offset", s.Get("$offset")) + newSlice.Set("$length", n) + newSlice.Set("$capacity", s.Get("$capacity")) + js.InternalObject(v.ptr).Call("$set", newSlice) +} + +func (v Value) Slice(i, j int) Value { + var ( + cap int + typ Type + s *js.Object + ) + switch kind := v.kind(); kind { + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = SliceOf(tt.elem) + s = jsType(typ).New(v.object()) + + case Slice: + typ = v.typ + s = v.object() + cap = s.Get("$capacity").Int() + + case String: + str := *(*string)(v.ptr) + if i < 0 || j < i || j > len(str) { + panic("reflect.Value.Slice: string slice index out of bounds") + } + return ValueOf(str[i:j]) + + default: + panic(&ValueError{"reflect.Value.Slice", kind}) + } + + if i < 0 || j < i || j > cap { + panic("reflect.Value.Slice: slice index out of bounds") + } + + return makeValue(typ, js.Global.Call("$subslice", s, i, j), v.flag.ro()) +} + +func (v Value) Slice3(i, j, k int) Value { + var ( + cap int + typ Type + s *js.Object + ) + switch kind := v.kind(); kind { + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = SliceOf(tt.elem) + s = jsType(typ).New(v.object()) + + case Slice: + typ = v.typ + s = v.object() + cap = s.Get("$capacity").Int() + + default: + panic(&ValueError{"reflect.Value.Slice3", kind}) + } + + if i < 0 || j < i || k < j || k > cap { + panic("reflect.Value.Slice3: slice index out of bounds") + } + + return makeValue(typ, js.Global.Call("$subslice", s, i, j, k), v.flag.ro()) +} + +func (v Value) Close() { + v.mustBe(Chan) + v.mustBeExported() + js.Global.Call("$close", v.object()) +} + +var selectHelper = js.Global.Get("$select").Interface().(func(...interface{}) *js.Object) + +func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool) { + comms := [][]*js.Object{{js.InternalObject(ch)}} + if nb { + comms = append(comms, []*js.Object{}) + } + selectRes := selectHelper(comms) + if nb && selectRes.Index(0).Int() == 1 { + return false, false + } + recvRes := selectRes.Index(1) + js.InternalObject(val).Call("$set", recvRes.Index(0)) + return true, recvRes.Index(1).Bool() +} + +func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool { + comms := [][]*js.Object{{js.InternalObject(ch), js.InternalObject(val).Call("$get")}} + if nb { + comms = append(comms, []*js.Object{}) + } + selectRes := selectHelper(comms) + if nb && selectRes.Index(0).Int() == 1 { + return false + } + return true +} + +func rselect(rselects []runtimeSelect) (chosen int, recvOK bool) { + comms := make([][]*js.Object, len(rselects)) + for i, s := range rselects { + switch SelectDir(s.dir) { + case SelectDefault: + comms[i] = []*js.Object{} + case SelectRecv: + ch := js.Global.Get("$chanNil") + if js.InternalObject(s.ch) != js.InternalObject(0) { + ch = js.InternalObject(s.ch) + } + comms[i] = []*js.Object{ch} + case SelectSend: + ch := js.Global.Get("$chanNil") + var val *js.Object + if js.InternalObject(s.ch) != js.InternalObject(0) { + ch = js.InternalObject(s.ch) + val = js.InternalObject(s.val).Call("$get") + } + comms[i] = []*js.Object{ch, val} + } + } + selectRes := selectHelper(comms) + c := selectRes.Index(0).Int() + if SelectDir(rselects[c].dir) == SelectRecv { + recvRes := selectRes.Index(1) + js.InternalObject(rselects[c].val).Call("$set", recvRes.Index(0)) + return c, recvRes.Index(1).Bool() + } + return c, false +} + +func DeepEqual(a1, a2 interface{}) bool { + i1 := js.InternalObject(a1) + i2 := js.InternalObject(a2) + if i1 == i2 { + return true + } + if i1 == nil || i2 == nil || i1.Get("constructor") != i2.Get("constructor") { + return false + } + return deepValueEqualJs(ValueOf(a1), ValueOf(a2), nil) +} + +func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { + if !v1.IsValid() || !v2.IsValid() { + return !v1.IsValid() && !v2.IsValid() + } + if v1.Type() != v2.Type() { + return false + } + if v1.Type() == jsObjectPtr { + return unwrapJsObject(jsObjectPtr, v1.object()) == unwrapJsObject(jsObjectPtr, v2.object()) + } + + switch v1.Kind() { + case Array, Map, Slice, Struct: + for _, entry := range visited { + if v1.ptr == entry[0] && v2.ptr == entry[1] { + return true + } + } + visited = append(visited, [2]unsafe.Pointer{v1.ptr, v2.ptr}) + } + + switch v1.Kind() { + case Array, Slice: + if v1.Kind() == Slice { + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.object() == v2.object() { + return true + } + } + n := v1.Len() + if n != v2.Len() { + return false + } + for i := 0; i < n; i++ { + if !deepValueEqualJs(v1.Index(i), v2.Index(i), visited) { + return false + } + } + return true + case Interface: + if v1.IsNil() || v2.IsNil() { + return v1.IsNil() && v2.IsNil() + } + return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) + case Ptr: + return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) + case Struct: + n := v1.NumField() + for i := 0; i < n; i++ { + if !deepValueEqualJs(v1.Field(i), v2.Field(i), visited) { + return false + } + } + return true + case Map: + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.object() == v2.object() { + return true + } + keys := v1.MapKeys() + if len(keys) != v2.Len() { + return false + } + for _, k := range keys { + val1 := v1.MapIndex(k) + val2 := v2.MapIndex(k) + if !val1.IsValid() || !val2.IsValid() || !deepValueEqualJs(val1, val2, visited) { + return false + } + } + return true + case Func: + return v1.IsNil() && v2.IsNil() + case UnsafePointer: + return v1.object() == v2.object() + } + + return js.Global.Call("$interfaceIsEqual", js.InternalObject(valueInterface(v1, false)), js.InternalObject(valueInterface(v2, false))).Bool() +} + +func methodNameSkip() string { + pc, _, _, _ := runtime.Caller(3) + f := runtime.FuncForPC(pc) + if f == nil { + return "unknown method" + } + // Function name extracted from the call stack can be different from vanilla + // Go. Here we try to fix stuff like "Object.$packages.reflect.Q.ptr.SetIterKey" + // into "Value.SetIterKey". + // This workaround may become obsolete after https://github.com/gopherjs/gopherjs/issues/1085 + // is resolved. + name := f.Name() + idx := len(name) - 1 + for idx > 0 { + if name[idx] == '.' { + break + } + idx-- + } + if idx < 0 { + return name + } + return "Value" + name[idx:] +} + +func verifyNotInHeapPtr(p uintptr) bool { + // Go runtime uses this method to make sure that a uintptr won't crash GC if + // interpreted as a heap pointer. This is not relevant for GopherJS, so we can + // always return true. + return true +} + +// copy from go1.18 +func (v Value) CanConvert(t Type) bool { + vt := v.Type() + if !vt.ConvertibleTo(t) { + return false + } + // Currently the only conversion that is OK in terms of type + // but that can panic depending on the value is converting + // from slice to pointer-to-array. + if vt.Kind() == Slice && t.Kind() == Ptr && t.Elem().Kind() == Array { + n := t.Elem().Len() + if n > v.Len() { + return false + } + } + return true +} diff --git a/compiler/natives/src/reflect/reflect_go117_test.go b/compiler/natives/src/reflect/reflect_go117_test.go new file mode 100644 index 00000000..102bf180 --- /dev/null +++ b/compiler/natives/src/reflect/reflect_go117_test.go @@ -0,0 +1,185 @@ +//go:build js && !go1.18 +// +build js,!go1.18 + +package reflect_test + +import ( + "math" + "reflect" + "testing" +) + +func TestAlignment(t *testing.T) { + t.Skip() +} + +func TestSliceOverflow(t *testing.T) { + t.Skip() +} + +func TestFuncLayout(t *testing.T) { + t.Skip() +} + +func TestArrayOfDirectIface(t *testing.T) { + t.Skip() +} + +func TestTypelinksSorted(t *testing.T) { + t.Skip() +} + +func TestGCBits(t *testing.T) { + t.Skip() +} + +func TestChanAlloc(t *testing.T) { + t.Skip() +} + +func TestNameBytesAreAligned(t *testing.T) { + t.Skip() +} + +func TestOffsetLock(t *testing.T) { + t.Skip() +} + +func TestSelectOnInvalid(t *testing.T) { + reflect.Select([]reflect.SelectCase{ + { + Dir: reflect.SelectRecv, + Chan: reflect.Value{}, + }, { + Dir: reflect.SelectSend, + Chan: reflect.Value{}, + Send: reflect.ValueOf(1), + }, { + Dir: reflect.SelectDefault, + }, + }) +} + +func TestStructOfDirectIface(t *testing.T) { + t.Skip("reflect.Value.InterfaceData is not supported by GopherJS.") +} + +func TestStructOfWithInterface(t *testing.T) { + // TODO(nevkontakte) Most of this test actually passes, but there is something + // about embedding fields with methods that can or can't be stored in an + // interface value directly that GopherJS does differently from upstream. As + // a result, GopherJS's implementation of StructOf() doesn't panic where + // upstream does. It seems to be a result of our implementation not propagating + // the kindDirectIface flag in struct types created by StructOf(), but at this + // point I wasn't able to figure out what that flag actually means in the + // GopherJS context or how it maps onto our own reflection implementation. + t.Skip("GopherJS doesn't support storing types directly in interfaces.") +} + +var deepEqualTests = []DeepEqualTest{ + // Equalities + {nil, nil, true}, + {1, 1, true}, + {int32(1), int32(1), true}, + {0.5, 0.5, true}, + {float32(0.5), float32(0.5), true}, + {"hello", "hello", true}, + {make([]int, 10), make([]int, 10), true}, + {&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true}, + {Basic{1, 0.5}, Basic{1, 0.5}, true}, + {error(nil), error(nil), true}, + {map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true}, + {fn1, fn2, true}, + + // Inequalities + {1, 2, false}, + {int32(1), int32(2), false}, + {0.5, 0.6, false}, + {float32(0.5), float32(0.6), false}, + {"hello", "hey", false}, + {make([]int, 10), make([]int, 11), false}, + {&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false}, + {Basic{1, 0.5}, Basic{1, 0.6}, false}, + {Basic{1, 0}, Basic{2, 0}, false}, + {map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false}, + {map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false}, + {map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false}, + {map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false}, + {nil, 1, false}, + {1, nil, false}, + {fn1, fn3, false}, + {fn3, fn3, false}, + {[][]int{{1}}, [][]int{{2}}, false}, + {math.NaN(), math.NaN(), false}, + {&[1]float64{math.NaN()}, &[1]float64{math.NaN()}, false}, + {&[1]float64{math.NaN()}, self{}, true}, + {[]float64{math.NaN()}, []float64{math.NaN()}, false}, + {[]float64{math.NaN()}, self{}, true}, + {map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false}, + {map[float64]float64{math.NaN(): 1}, self{}, true}, + + // Nil vs empty: not the same. + {[]int{}, []int(nil), false}, + {[]int{}, []int{}, true}, + {[]int(nil), []int(nil), true}, + {map[int]int{}, map[int]int(nil), false}, + {map[int]int{}, map[int]int{}, true}, + {map[int]int(nil), map[int]int(nil), true}, + + // Mismatched types + {1, 1.0, false}, + {int32(1), int64(1), false}, + {0.5, "hello", false}, + {[]int{1, 2, 3}, [3]int{1, 2, 3}, false}, + {&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false}, + {Basic{1, 0.5}, NotBasic{1, 0.5}, false}, + {map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false}, + + // Possible loops. + {&loop1, &loop1, true}, + //{&loop1, &loop2, true}, // TODO: Fix. + {&loopy1, &loopy1, true}, + //{&loopy1, &loopy2, true}, // TODO: Fix. +} + +// TODO: Fix this. See https://github.com/gopherjs/gopherjs/issues/763. +func TestIssue22073(t *testing.T) { + m := reflect.ValueOf(NonExportedFirst(0)).Method(0) + + if got := m.Type().NumOut(); got != 0 { + t.Errorf("NumOut: got %v, want 0", got) + } + + // TODO: Fix this. The call below fails with: + // + // var $call = function(fn, rcvr, args) { return fn.apply(rcvr, args); }; + // ^ + // TypeError: Cannot read property 'apply' of undefined + + // Shouldn't panic. + //m.Call(nil) +} + +func TestCallReturnsEmpty(t *testing.T) { + t.Skip("test uses runtime.SetFinalizer, which is not supported by GopherJS") +} + +func init() { + // TODO: This is a failure in 1.11, try to determine the cause and fix. + typeTests = append(typeTests[:31], typeTests[32:]...) // skip test case #31 +} + +func TestConvertNaNs(t *testing.T) { + // This test is exactly the same as the upstream, except it uses a "quiet NaN" + // value instead of "signalling NaN". JavaScript appears to coerce all NaNs + // into quiet ones, but for the purpose of this test either is fine. + + const qnan uint32 = 0x7fc00001 // Originally: 0x7f800001. + type myFloat32 float32 + x := V(myFloat32(math.Float32frombits(qnan))) + y := x.Convert(reflect.TypeOf(float32(0))) + z := y.Interface().(float32) + if got := math.Float32bits(z); got != qnan { + t.Errorf("quiet nan conversion got %x, want %x", got, qnan) + } +} diff --git a/compiler/natives/src/reflect/reflect_test.go b/compiler/natives/src/reflect/reflect_test.go index 878a685c..de9bc99d 100644 --- a/compiler/natives/src/reflect/reflect_test.go +++ b/compiler/natives/src/reflect/reflect_test.go @@ -1,10 +1,11 @@ -// +build js +//go:build js && go1.18 +// +build js,go1.18 package reflect_test import ( "math" - "reflect" + . "reflect" "testing" ) @@ -45,54 +46,34 @@ func TestOffsetLock(t *testing.T) { } func TestSelectOnInvalid(t *testing.T) { - reflect.Select([]reflect.SelectCase{ + Select([]SelectCase{ { - Dir: reflect.SelectRecv, - Chan: reflect.Value{}, + Dir: SelectRecv, + Chan: Value{}, }, { - Dir: reflect.SelectSend, - Chan: reflect.Value{}, - Send: reflect.ValueOf(1), + Dir: SelectSend, + Chan: Value{}, + Send: ValueOf(1), }, { - Dir: reflect.SelectDefault, + Dir: SelectDefault, }, }) } -func TestStructOfFieldName(t *testing.T) { - t.Skip("StructOf") -} - -func TestStructOf(t *testing.T) { - t.Skip("StructOf") -} - -func TestStructOfExportRules(t *testing.T) { - t.Skip("StructOf") -} - -func TestStructOfGC(t *testing.T) { - t.Skip("StructOf") -} - -func TestStructOfAlg(t *testing.T) { - t.Skip("StructOf") -} - -func TestStructOfGenericAlg(t *testing.T) { - t.Skip("StructOf") -} - func TestStructOfDirectIface(t *testing.T) { - t.Skip("StructOf") + t.Skip("reflect.Value.InterfaceData is not supported by GopherJS.") } func TestStructOfWithInterface(t *testing.T) { - t.Skip("StructOf") -} - -func TestStructOfTooManyFields(t *testing.T) { - t.Skip("StructOf") + // TODO(nevkontakte) Most of this test actually passes, but there is something + // about embedding fields with methods that can or can't be stored in an + // interface value directly that GopherJS does differently from upstream. As + // a result, GopherJS's implementation of StructOf() doesn't panic where + // upstream does. It seems to be a result of our implementation not propagating + // the kindDirectIface flag in struct types created by StructOf(), but at this + // point I wasn't able to figure out what that flag actually means in the + // GopherJS context or how it maps onto our own reflection implementation. + t.Skip("GopherJS doesn't support storing types directly in interfaces.") } var deepEqualTests = []DeepEqualTest{ @@ -163,7 +144,7 @@ var deepEqualTests = []DeepEqualTest{ // TODO: Fix this. See https://github.com/gopherjs/gopherjs/issues/763. func TestIssue22073(t *testing.T) { - m := reflect.ValueOf(NonExportedFirst(0)).Method(0) + m := ValueOf(NonExportedFirst(0)).Method(0) if got := m.Type().NumOut(); got != 0 { t.Errorf("NumOut: got %v, want 0", got) @@ -187,3 +168,126 @@ func init() { // TODO: This is a failure in 1.11, try to determine the cause and fix. typeTests = append(typeTests[:31], typeTests[32:]...) // skip test case #31 } + +func TestConvertNaNs(t *testing.T) { + // This test is exactly the same as the upstream, except it uses a "quiet NaN" + // value instead of "signalling NaN". JavaScript appears to coerce all NaNs + // into quiet ones, but for the purpose of this test either is fine. + + const qnan uint32 = 0x7fc00001 // Originally: 0x7f800001. + type myFloat32 float32 + x := V(myFloat32(math.Float32frombits(qnan))) + y := x.Convert(TypeOf(float32(0))) + z := y.Interface().(float32) + if got := math.Float32bits(z); got != qnan { + t.Errorf("quiet nan conversion got %x, want %x", got, qnan) + } +} + +func TestMapIterSet(t *testing.T) { + m := make(map[string]interface{}, len(valueTests)) + for _, tt := range valueTests { + m[tt.s] = tt.i + } + v := ValueOf(m) + + k := New(v.Type().Key()).Elem() + e := New(v.Type().Elem()).Elem() + + iter := v.MapRange() + for iter.Next() { + k.SetIterKey(iter) + e.SetIterValue(iter) + want := m[k.String()] + got := e.Interface() + if got != want { + t.Errorf("%q: want (%T) %v, got (%T) %v", k.String(), want, want, got, got) + } + if setkey, key := valueToString(k), valueToString(iter.Key()); setkey != key { + t.Errorf("MapIter.Key() = %q, MapIter.SetKey() = %q", key, setkey) + } + if setval, val := valueToString(e), valueToString(iter.Value()); setval != val { + t.Errorf("MapIter.Value() = %q, MapIter.SetValue() = %q", val, setval) + } + } + + // Upstream test also tests allocations made by the iterator. GopherJS doesn't + // support runtime.ReadMemStats(), so we leave that part out. +} + +type inner struct { + x int +} + +type outer struct { + y int + inner +} + +func (*inner) M() int { return 1 } +func (*outer) M() int { return 2 } + +func TestNestedMethods(t *testing.T) { + // This test is similar to the upstream, but avoids using the unsupported + // Value.UnsafePointer() method. + typ := TypeOf((*outer)(nil)) + args := []Value{ + ValueOf((*outer)(nil)), // nil receiver + } + if typ.NumMethod() != 1 { + t.Errorf("Wrong method table for outer, found methods:") + for i := 0; i < typ.NumMethod(); i++ { + m := typ.Method(i) + t.Errorf("\t%d: %s\n", i, m.Name) + } + } + if got := typ.Method(0).Func.Call(args)[0]; got.Int() != 2 { + t.Errorf("Wrong method table for outer, expected return value 2, got: %v", got) + } + if got := ValueOf((*outer).M).Call(args)[0]; got.Int() != 2 { + t.Errorf("Wrong method table for outer, expected return value 2, got: %v", got) + } +} + +func TestEmbeddedMethods(t *testing.T) { + // This test is similar to the upstream, but avoids using the unsupported + // Value.UnsafePointer() method. + typ := TypeOf((*OuterInt)(nil)) + if typ.NumMethod() != 1 { + t.Errorf("Wrong method table for OuterInt: (m=%p)", (*OuterInt).M) + for i := 0; i < typ.NumMethod(); i++ { + m := typ.Method(i) + t.Errorf("\t%d: %s %p\n", i, m.Name, m.Func.UnsafePointer()) + } + } + + i := &InnerInt{3} + if v := ValueOf(i).Method(0).Call(nil)[0].Int(); v != 3 { + t.Errorf("i.M() = %d, want 3", v) + } + + o := &OuterInt{1, InnerInt{2}} + if v := ValueOf(o).Method(0).Call(nil)[0].Int(); v != 2 { + t.Errorf("i.M() = %d, want 2", v) + } + + f := (*OuterInt).M + if v := f(o); v != 2 { + t.Errorf("f(o) = %d, want 2", v) + } +} + +func TestNotInHeapDeref(t *testing.T) { + t.Skip("GopherJS doesn't support //go:notinheap") +} + +func TestMethodCallValueCodePtr(t *testing.T) { + t.Skip("methodValueCallCodePtr() is not applicable in GopherJS") +} + +type B struct{} + +//gopherjs:prune-original +func TestIssue50208(t *testing.T) { + t.Skip("This test required generics, which are not yet supported: https://github.com/gopherjs/gopherjs/issues/1013") +} diff --git a/compiler/natives/src/reflect/swapper.go b/compiler/natives/src/reflect/swapper.go index a94f7961..068f984e 100644 --- a/compiler/natives/src/reflect/swapper.go +++ b/compiler/natives/src/reflect/swapper.go @@ -1,3 +1,4 @@ +//go:build js // +build js package reflect diff --git a/compiler/natives/src/regexp/regexp_test.go b/compiler/natives/src/regexp/regexp_test.go index 823aec6f..d5fa9211 100644 --- a/compiler/natives/src/regexp/regexp_test.go +++ b/compiler/natives/src/regexp/regexp_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package regexp diff --git a/compiler/natives/src/runtime/debug/debug.go b/compiler/natives/src/runtime/debug/debug.go index 6597dcc4..b45da62b 100644 --- a/compiler/natives/src/runtime/debug/debug.go +++ b/compiler/natives/src/runtime/debug/debug.go @@ -1,3 +1,4 @@ +//go:build js // +build js package debug diff --git a/compiler/natives/src/runtime/fastrand.go b/compiler/natives/src/runtime/fastrand.go new file mode 100644 index 00000000..8f6ab629 --- /dev/null +++ b/compiler/natives/src/runtime/fastrand.go @@ -0,0 +1,15 @@ +//go:build js +// +build js + +package runtime + +import "github.com/gopherjs/gopherjs/js" + +func fastrand() uint32 { + // In the upstream this function is implemented with a + // custom algorithm that uses bit manipulation, but it is likely to be slower + // than calling Math.random(). + // TODO(nevkontakte): We should verify that it actually is faster and has a + // similar distribution. + return uint32(js.Global.Get("Math").Call("random").Float() * (1<<32 - 1)) +} diff --git a/compiler/natives/src/runtime/pprof/pprof.go b/compiler/natives/src/runtime/pprof/pprof.go index 8af83a90..f398ca21 100644 --- a/compiler/natives/src/runtime/pprof/pprof.go +++ b/compiler/natives/src/runtime/pprof/pprof.go @@ -1,3 +1,4 @@ +//go:build js // +build js package pprof diff --git a/compiler/natives/src/runtime/runtime.go b/compiler/natives/src/runtime/runtime.go index 4e4fb62c..ebd85d84 100644 --- a/compiler/natives/src/runtime/runtime.go +++ b/compiler/natives/src/runtime/runtime.go @@ -1,30 +1,69 @@ +//go:build js // +build js package runtime import ( - "runtime/internal/sys" - "github.com/gopherjs/gopherjs/js" ) -const GOOS = sys.GOOS -const GOARCH = "js" +const GOOS = "js" +const GOARCH = "ecmascript" const Compiler = "gopherjs" -// fake for error.go -type eface struct { - _type *_type -} -type _type struct { - str string +// The Error interface identifies a run time error. +type Error interface { + error + + // RuntimeError is a no-op function but + // serves to distinguish types that are run time + // errors from ordinary errors: a type is a + // run time error if it has a RuntimeError method. + RuntimeError() } -func (t *_type) string() string { - return t.str +// TODO(nevkontakte): In the upstream, this struct is meant to be compatible +// with reflect.rtype, but here we use a minimal stub that satisfies the API +// TypeAssertionError expects, which we dynamically instantiate in $assertType(). +type _type struct{ str string } + +func (t *_type) string() string { return t.str } +func (t *_type) pkgpath() string { return "" } + +// A TypeAssertionError explains a failed type assertion. +type TypeAssertionError struct { + _interface *_type + concrete *_type + asserted *_type + missingMethod string // one method needed by Interface, missing from Concrete } -func (t *_type) pkgpath() string { - return "" + +func (*TypeAssertionError) RuntimeError() {} + +func (e *TypeAssertionError) Error() string { + inter := "interface" + if e._interface != nil { + inter = e._interface.string() + } + as := e.asserted.string() + if e.concrete == nil { + return "interface conversion: " + inter + " is nil, not " + as + } + cs := e.concrete.string() + if e.missingMethod == "" { + msg := "interface conversion: " + inter + " is " + cs + ", not " + as + if cs == as { + // provide slightly clearer error message + if e.concrete.pkgpath() != e.asserted.pkgpath() { + msg += " (types from different packages)" + } else { + msg += " (types from different scopes)" + } + } + return msg + } + return "interface conversion: " + cs + " is not " + as + + ": missing method " + e.missingMethod } func init() { @@ -32,6 +71,7 @@ func init() { js.Global.Set("$jsObjectPtr", jsPkg.Get("Object").Get("ptr")) js.Global.Set("$jsErrorPtr", jsPkg.Get("Error").Get("ptr")) js.Global.Set("$throwRuntimeError", js.InternalObject(throw)) + buildVersion = js.Global.Get("$goVersion").String() // avoid dead code elimination var e error e = &TypeAssertionError{} @@ -43,39 +83,223 @@ func GOROOT() string { if process == js.Undefined { return "/" } - goroot := process.Get("env").Get("GOROOT") - if goroot != js.Undefined { - return goroot.String() + if v := process.Get("env").Get("GOPHERJS_GOROOT"); v != js.Undefined && v.String() != "" { + // GopherJS-specific GOROOT value takes precedence. + return v.String() + } else if v := process.Get("env").Get("GOROOT"); v != js.Undefined && v.String() != "" { + return v.String() } // sys.DefaultGoroot is now gone, can't use it as fallback anymore. // TODO: See if a better solution is needed. return "/usr/local/go" } -func Breakpoint() { - js.Debugger() +func Breakpoint() { js.Debugger() } + +var ( + // JavaScript runtime doesn't provide access to low-level execution position + // counters, so we emulate them by recording positions we've encountered in + // Caller() and Callers() functions and assigning them arbitrary integer values. + // + // We use the map and the slice below to convert a "file:line" position + // into an integer position counter and then to a Func instance. + knownPositions = map[string]uintptr{} + positionCounters = []*Func{} +) + +func registerPosition(funcName string, file string, line int, col int) uintptr { + key := file + ":" + itoa(line) + ":" + itoa(col) + if pc, found := knownPositions[key]; found { + return pc + } + f := &Func{ + name: funcName, + file: file, + line: line, + } + pc := uintptr(len(positionCounters)) + positionCounters = append(positionCounters, f) + knownPositions[key] = pc + return pc +} + +// itoa converts an integer to a string. +// +// Can't use strconv.Itoa() in the `runtime` package due to a cyclic dependency. +func itoa(i int) string { + return js.Global.Get("String").New(i).String() +} + +// basicFrame contains stack trace information extracted from JS stack trace. +type basicFrame struct { + FuncName string + File string + Line int + Col int +} + +func callstack(skip, limit int) []basicFrame { + skip = skip + 1 /*skip error message*/ + 1 /*skip callstack's own frame*/ + lines := js.Global.Get("Error").New().Get("stack").Call("split", "\n").Call("slice", skip, skip+limit) + return parseCallstack(lines) +} + +var ( + // These functions are GopherJS-specific and don't have counterparts in + // upstream Go runtime. To improve interoperability, we filter them out from + // the stack trace. + hiddenFrames = map[string]bool{ + "$callDeferred": true, + } + // The following GopherJS prelude functions have differently-named + // counterparts in the upstream Go runtime. Some standard library code relies + // on the names matching, so we perform this substitution. + knownFrames = map[string]string{ + "$panic": "runtime.gopanic", + "$goroutine": "runtime.goexit", + } +) + +func parseCallstack(lines *js.Object) []basicFrame { + frames := []basicFrame{} + l := lines.Length() + for i := 0; i < l; i++ { + frame := ParseCallFrame(lines.Index(i)) + if hiddenFrames[frame.FuncName] { + continue + } + if alias, ok := knownFrames[frame.FuncName]; ok { + frame.FuncName = alias + } + frames = append(frames, frame) + if frame.FuncName == "runtime.goexit" { + break // We've reached the bottom of the goroutine stack. + } + } + return frames +} + +// ParseCallFrame is exported for the sake of testing. See this discussion for context https://github.com/gopherjs/gopherjs/pull/1097/files/561e6381406f04ccb8e04ef4effedc5c7887b70f#r776063799 +// +// TLDR; never use this function! +func ParseCallFrame(info *js.Object) basicFrame { + // FireFox + if info.Call("indexOf", "@").Int() >= 0 { + split := js.Global.Get("RegExp").New("[@:]") + parts := info.Call("split", split) + return basicFrame{ + File: parts.Call("slice", 1, parts.Length()-2).Call("join", ":").String(), + Line: parts.Index(parts.Length() - 2).Int(), + Col: parts.Index(parts.Length() - 1).Int(), + FuncName: parts.Index(0).String(), + } + } + + // Chrome / Node.js + openIdx := info.Call("lastIndexOf", "(").Int() + if openIdx == -1 { + parts := info.Call("split", ":") + + return basicFrame{ + File: parts.Call("slice", 0, parts.Length()-2).Call("join", ":"). + Call("replace", js.Global.Get("RegExp").New(`^\s*at `), "").String(), + Line: parts.Index(parts.Length() - 2).Int(), + Col: parts.Index(parts.Length() - 1).Int(), + FuncName: "", + } + } + + var file, funcName string + var line, col int + + pos := info.Call("substring", openIdx+1, info.Call("indexOf", ")").Int()) + parts := pos.Call("split", ":") + + if pos.String() == "" { + file = "" + } else { + file = parts.Call("slice", 0, parts.Length()-2).Call("join", ":").String() + line = parts.Index(parts.Length() - 2).Int() + col = parts.Index(parts.Length() - 1).Int() + } + fn := info.Call("substring", info.Call("indexOf", "at ").Int()+3, info.Call("indexOf", " (").Int()) + if idx := fn.Call("indexOf", "[as ").Int(); idx > 0 { + fn = fn.Call("substring", idx+4, fn.Call("indexOf", "]")) + } + funcName = fn.String() + + return basicFrame{ + File: file, + Line: line, + Col: col, + FuncName: funcName, + } } func Caller(skip int) (pc uintptr, file string, line int, ok bool) { - info := js.Global.Get("Error").New().Get("stack").Call("split", "\n").Index(skip + 2) - if info == js.Undefined { + skip = skip + 1 /*skip Caller's own frame*/ + frames := callstack(skip, 1) + if len(frames) != 1 { return 0, "", 0, false } - parts := info.Call("substring", info.Call("indexOf", "(").Int()+1, info.Call("indexOf", ")").Int()).Call("split", ":") - return 0, parts.Index(0).String(), parts.Index(1).Int(), true + pc = registerPosition(frames[0].FuncName, frames[0].File, frames[0].Line, frames[0].Col) + return pc, frames[0].File, frames[0].Line, true } +// Callers fills the slice pc with the return program counters of function +// invocations on the calling goroutine's stack. The argument skip is the number +// of stack frames to skip before recording in pc, with 0 identifying the frame +// for Callers itself and 1 identifying the caller of Callers. It returns the +// number of entries written to pc. +// +// The returned call stack represents the logical Go call stack, which excludes +// certain runtime-internal call frames that would be present in the raw +// JavaScript stack trace. This is done to improve interoperability with the +// upstream Go. Use JavaScript native APIs to access the raw call stack. +// +// To translate these PCs into symbolic information such as function names and +// line numbers, use CallersFrames. CallersFrames accounts for inlined functions +// and adjusts the return program counters into call program counters. Iterating +// over the returned slice of PCs directly is discouraged, as is using FuncForPC +// on any of the returned PCs, since these cannot account for inlining or return +// program counter adjustment. func Callers(skip int, pc []uintptr) int { - return 0 + frames := callstack(skip, len(pc)) + for i, frame := range frames { + pc[i] = registerPosition(frame.FuncName, frame.File, frame.Line, frame.Col) + } + return len(frames) } -// CallersFrames is not implemented for GOARCH=js. -// TODO: Implement if possible. -func CallersFrames(callers []uintptr) *Frames { return &Frames{} } +func CallersFrames(callers []uintptr) *Frames { + result := Frames{} + for _, pc := range callers { + fun := FuncForPC(pc) + result.frames = append(result.frames, Frame{ + PC: pc, + Func: fun, + Function: fun.name, + File: fun.file, + Line: fun.line, + Entry: fun.Entry(), + }) + } + return &result +} -type Frames struct{} +type Frames struct { + frames []Frame + current int +} -func (ci *Frames) Next() (frame Frame, more bool) { return } +func (ci *Frames) Next() (frame Frame, more bool) { + if ci.current >= len(ci.frames) { + return Frame{}, false + } + f := ci.frames[ci.current] + ci.current++ + return f, ci.current < len(ci.frames) +} type Frame struct { PC uintptr @@ -86,17 +310,14 @@ type Frame struct { Entry uintptr } -func GC() { -} +func GC() {} func Goexit() { js.Global.Get("$curGoroutine").Set("exit", true) js.Global.Call("$throw", nil) } -func GOMAXPROCS(n int) int { - return 1 -} +func GOMAXPROCS(int) int { return 1 } func Gosched() { c := make(chan struct{}) @@ -104,9 +325,7 @@ func Gosched() { <-c } -func NumCPU() int { - return 1 -} +func NumCPU() int { return 1 } func NumGoroutine() int { return js.Global.Get("$totalGoroutines").Int() @@ -163,21 +382,50 @@ type MemStats struct { } func ReadMemStats(m *MemStats) { + // TODO(nevkontakte): This function is effectively unimplemented and may + // lead to silent unexpected behaviors. Consider panicing explicitly. } func SetFinalizer(x, f interface{}) { + // TODO(nevkontakte): This function is effectively unimplemented and may + // lead to silent unexpected behaviors. Consider panicing explicitly. } type Func struct { + name string + file string + line int + opaque struct{} // unexported field to disallow conversions } -func (_ *Func) Entry() uintptr { return 0 } -func (_ *Func) FileLine(pc uintptr) (file string, line int) { return "", 0 } -func (_ *Func) Name() string { return "" } +func (_ *Func) Entry() uintptr { return 0 } + +func (f *Func) FileLine(pc uintptr) (file string, line int) { + if f == nil { + return "", 0 + } + return f.file, f.line +} + +func (f *Func) Name() string { + if f == nil || f.name == "" { + return "" + } + return f.name +} func FuncForPC(pc uintptr) *Func { - return nil + ipc := int(pc) + if ipc >= len(positionCounters) { + // Since we are faking position counters, the only valid way to obtain one + // is through a Caller() or Callers() function. If pc is out of positionCounters + // bounds it must have been obtained in some other way, which is unexpected. + // If a panic proves problematic, we can return a nil *Func, which will + // present itself as a generic "unknown" function. + panic("GopherJS: pc=" + itoa(ipc) + " is out of range of known position counters") + } + return positionCounters[ipc] } var MemProfileRate int = 512 * 1024 @@ -190,6 +438,12 @@ func SetMutexProfileFraction(rate int) int { return 0 } +// Stack formats a stack trace of the calling goroutine into buf and returns the +// number of bytes written to buf. If all is true, Stack formats stack traces of +// all other goroutines into buf after the trace for the current goroutine. +// +// Unlike runtime.Callers(), it returns an unprocessed, runtime-specific text +// representation of the JavaScript stack trace. func Stack(buf []byte, all bool) int { s := js.Global.Get("Error").New().Get("stack") if s == js.Undefined { @@ -202,8 +456,10 @@ func LockOSThread() {} func UnlockOSThread() {} +var buildVersion string // Set by init() + func Version() string { - return sys.TheVersion + return buildVersion } func StartTrace() error { return nil } @@ -215,20 +471,21 @@ func NumCgoCall() int64 { return 0 } -func efaceOf(ep *interface{}) *eface { - panic("efaceOf: not supported") -} - func KeepAlive(interface{}) {} +// An errorString represents a runtime error described by a single string. +type errorString string + +func (e errorString) RuntimeError() {} + +func (e errorString) Error() string { + return "runtime error: " + string(e) +} + func throw(s string) { panic(errorString(s)) } -// These are used by panicwrap. Not implemented for GOARCH=js. -// TODO: Implement if possible. -func getcallerpc() uintptr { return 0 } -func findfunc(pc uintptr) funcInfo { return funcInfo{} } -func funcname(f funcInfo) string { return "" } - -type funcInfo struct{} +func nanotime() int64 { + return js.Global.Get("Date").New().Call("getTime").Int64() * int64(1000_000) +} diff --git a/compiler/natives/src/strconv/atoi.go b/compiler/natives/src/strconv/atoi.go new file mode 100644 index 00000000..63ea9b73 --- /dev/null +++ b/compiler/natives/src/strconv/atoi.go @@ -0,0 +1,47 @@ +//go:build js +// +build js + +package strconv + +import ( + "github.com/gopherjs/gopherjs/js" +) + +const ( + maxInt32 float64 = 1<<31 - 1 + minInt32 float64 = -1 << 31 +) + +// Atoi returns the result of ParseInt(s, 10, 0) converted to type int. +func Atoi(s string) (int, error) { + const fnAtoi = "Atoi" + if len(s) == 0 { + return 0, syntaxError(fnAtoi, s) + } + // Investigate the bytes of the string + // Validate each byte is allowed in parsing + // Number allows some prefixes that Go does not: "0x" "0b", "0o" + // additionally Number accepts decimals where Go does not "10.2" + for i := 0; i < len(s); i++ { + v := s[i] + + if v < '0' || v > '9' { + if v != '+' && v != '-' { + return 0, syntaxError(fnAtoi, s) + } + } + } + jsValue := js.Global.Call("Number", s, 10) + if !js.Global.Call("isFinite", jsValue).Bool() { + return 0, syntaxError(fnAtoi, s) + } + // Bounds checking + floatval := jsValue.Float() + if floatval > maxInt32 { + return int(maxInt32), rangeError(fnAtoi, s) + } else if floatval < minInt32 { + return int(minInt32), rangeError(fnAtoi, s) + } + // Success! + return jsValue.Int(), nil +} diff --git a/compiler/natives/src/strconv/itoa.go b/compiler/natives/src/strconv/itoa.go new file mode 100644 index 00000000..c5440c78 --- /dev/null +++ b/compiler/natives/src/strconv/itoa.go @@ -0,0 +1,14 @@ +//go:build js +// +build js + +package strconv + +import ( + "github.com/gopherjs/gopherjs/js" +) + +// Itoa in gopherjs is always a 32bit int so the native toString +// always handles it successfully. +func Itoa(i int) string { + return js.InternalObject(i).Call("toString").String() +} diff --git a/compiler/natives/src/strings/strings.go b/compiler/natives/src/strings/strings.go index 09b1bb59..ebb1db1e 100644 --- a/compiler/natives/src/strings/strings.go +++ b/compiler/natives/src/strings/strings.go @@ -1,3 +1,4 @@ +//go:build js // +build js package strings @@ -65,3 +66,10 @@ func (b *Builder) copyCheck() { panic("strings: illegal use of non-zero Builder copied by value") } } + +func Clone(s string) string { + // Since in the JavaScript runtime we don't have access the the string's + // baking memory, we let the engine's garbage collector deal with substring + // memory overheads and simply return the string as-is. + return s +} diff --git a/compiler/natives/src/strings/strings_test.go b/compiler/natives/src/strings/strings_test.go index de6ccff3..fb9a4a57 100644 --- a/compiler/natives/src/strings/strings_test.go +++ b/compiler/natives/src/strings/strings_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package strings_test @@ -15,3 +16,7 @@ func TestBuilderGrow(t *testing.T) { func TestCompareStrings(t *testing.T) { t.Skip("unsafeString not supported in GopherJS") } + +func TestClone(t *testing.T) { + t.Skip("conversion to reflect.StringHeader is not supported in GopherJS") +} diff --git a/compiler/natives/src/sync/atomic/atomic.go b/compiler/natives/src/sync/atomic/atomic.go index 63601712..ebc98e91 100644 --- a/compiler/natives/src/sync/atomic/atomic.go +++ b/compiler/natives/src/sync/atomic/atomic.go @@ -1,3 +1,4 @@ +//go:build js // +build js package atomic @@ -174,12 +175,48 @@ func (v *Value) Load() (x interface{}) { return v.v } -func (v *Value) Store(x interface{}) { - if x == nil { - panic("sync/atomic: store of nil value into Value") +func (v *Value) Store(new interface{}) { + v.checkNew("store", new) + v.v = new +} + +func (v *Value) Swap(new interface{}) (old interface{}) { + v.checkNew("swap", new) + old, v.v = v.v, new + return old +} + +func (v *Value) CompareAndSwap(old, new interface{}) (swapped bool) { + v.checkNew("compare and swap", new) + + if !(v.v == nil && old == nil) && !sameType(old, new) { + panic("sync/atomic: compare and swap of inconsistently typed values into Value") + } + + if v.v != old { + return false } - if v.v != nil && js.InternalObject(x).Get("constructor") != js.InternalObject(v.v).Get("constructor") { - panic("sync/atomic: store of inconsistently typed value into Value") + + v.v = new + + return true +} + +func (v *Value) checkNew(op string, new interface{}) { + if new == nil { + panic("sync/atomic: " + op + " of nil value into Value") } - v.v = x + + if v.v != nil && !sameType(new, v.v) { + panic("sync/atomic: " + op + " of inconsistently typed value into Value") + } +} + +// sameType returns true if x and y contain the same concrete Go types. +func sameType(x, y interface{}) bool { + // This relies upon the fact that an interface in GopherJS is represented + // by the instance of the underlying Go type. Primitive values (e.g. bools) + // are still wrapped into a Go type object, so we can rely upon constructors + // existing and differing for different types. + return js.InternalObject(x).Get("constructor") == js.InternalObject(y).Get("constructor") } diff --git a/compiler/natives/src/sync/atomic/atomic_test.go b/compiler/natives/src/sync/atomic/atomic_test.go index 9ad30a2c..f4450cc6 100644 --- a/compiler/natives/src/sync/atomic/atomic_test.go +++ b/compiler/natives/src/sync/atomic/atomic_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package atomic_test @@ -7,3 +8,7 @@ import "testing" func TestHammerStoreLoad(t *testing.T) { t.Skip("use of unsafe") } + +func TestUnaligned64(t *testing.T) { + t.Skip("GopherJS emulates atomics, which makes alignment irrelevant.") +} diff --git a/compiler/natives/src/sync/cond.go b/compiler/natives/src/sync/cond.go index 829dac2e..916ace8f 100644 --- a/compiler/natives/src/sync/cond.go +++ b/compiler/natives/src/sync/cond.go @@ -1,3 +1,4 @@ +//go:build js // +build js package sync diff --git a/compiler/natives/src/sync/cond_test.go b/compiler/natives/src/sync/cond_test.go new file mode 100644 index 00000000..3b286ba4 --- /dev/null +++ b/compiler/natives/src/sync/cond_test.go @@ -0,0 +1,10 @@ +//go:build js +// +build js + +package sync_test + +import "testing" + +func TestCondCopy(t *testing.T) { + t.Skip("Copy checker requires raw pointers, which GopherJS doesn't fully support.") +} diff --git a/compiler/natives/src/sync/export_test.go b/compiler/natives/src/sync/export_test.go deleted file mode 100644 index f23a8733..00000000 --- a/compiler/natives/src/sync/export_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build js - -package sync - -// Referenced by tests, need to have no-op implementations. -var Runtime_procPin = func() int { return 0 } -var Runtime_procUnpin = func() {} diff --git a/compiler/natives/src/sync/map_test.go b/compiler/natives/src/sync/map_test.go new file mode 100644 index 00000000..43209607 --- /dev/null +++ b/compiler/natives/src/sync/map_test.go @@ -0,0 +1,10 @@ +//go:build js +// +build js + +package sync_test + +import "testing" + +func TestIssue40999(t *testing.T) { + t.Skip("test relies on runtime.SetFinalizer, which GopherJS does not implement") +} diff --git a/compiler/natives/src/sync/pool.go b/compiler/natives/src/sync/pool.go index 629010d9..9d3825e1 100644 --- a/compiler/natives/src/sync/pool.go +++ b/compiler/natives/src/sync/pool.go @@ -1,13 +1,27 @@ +//go:build js // +build js package sync -import "unsafe" - +// A Pool is a set of temporary objects that may be individually saved and +// retrieved. +// +// GopherJS provides a simpler, naive implementation with no synchronization at +// all. This is still correct for the GopherJS runtime because: +// +// 1. JavaScript is single-threaded, so it is impossible for two threads to be +// accessing the pool at the same moment in time. +// 2. GopherJS goroutine implementation uses cooperative multi-tasking model, +// which only allows passing control to other goroutines when the function +// might block. +// +// TODO(nevkontakte): Consider adding a mutex just to be safe if it doesn't +// create a large performance hit. +// +// Note: there is a special handling in the gopherjs/build package that filters +// out all original Pool implementation in order to avoid awkward unused fields +// referenced by dead code. type Pool struct { - local unsafe.Pointer - localSize uintptr - store []interface{} New func() interface{} } @@ -31,5 +45,6 @@ func (p *Pool) Put(x interface{}) { p.store = append(p.store, x) } -func runtime_registerPoolCleanup(cleanup func()) { -} +// These are referenced by tests, but are no-ops in GopherJS runtime. +func runtime_procPin() int { return 0 } +func runtime_procUnpin() {} diff --git a/compiler/natives/src/sync/pool_test.go b/compiler/natives/src/sync/pool_test.go new file mode 100644 index 00000000..21885265 --- /dev/null +++ b/compiler/natives/src/sync/pool_test.go @@ -0,0 +1,44 @@ +//go:build js +// +build js + +package sync_test + +import ( + . "sync" + "testing" +) + +func TestPool(t *testing.T) { + var p Pool + if p.Get() != nil { + t.Fatal("expected empty") + } + + p.Put("a") + p.Put("b") + + want := []interface{}{"b", "a", nil} + for i := range want { + got := p.Get() + if got != want[i] { + t.Fatalf("Got: p.Get() returned: %s. Want: %s.", got, want) + } + } + +} + +func TestPoolGC(t *testing.T) { + t.Skip("This test uses runtime.GC(), which GopherJS doesn't support.") +} + +func TestPoolRelease(t *testing.T) { + t.Skip("This test uses runtime.GC(), which GopherJS doesn't support.") +} + +func TestPoolDequeue(t *testing.T) { + t.Skip("This test targets upstream pool implementation, which is not used by GopherJS.") +} + +func TestPoolChain(t *testing.T) { + t.Skip("This test targets upstream pool implementation, which is not used by GopherJS.") +} diff --git a/compiler/natives/src/sync/sync.go b/compiler/natives/src/sync/sync.go index 2ae46e0a..58853775 100644 --- a/compiler/natives/src/sync/sync.go +++ b/compiler/natives/src/sync/sync.go @@ -1,3 +1,4 @@ +//go:build js // +build js package sync @@ -18,13 +19,13 @@ var semWaiters = make(map[*uint32][]chan bool) var semAwoken = make(map[*uint32]uint32) func runtime_Semacquire(s *uint32) { - runtime_SemacquireMutex(s, false) + runtime_SemacquireMutex(s, false, 1) } // SemacquireMutex is like Semacquire, but for profiling contended Mutexes. // Mutex profiling is not supported, so just use the same implementation as runtime_Semacquire. // TODO: Investigate this. If it's possible to implement, consider doing so, otherwise remove this comment. -func runtime_SemacquireMutex(s *uint32, lifo bool) { +func runtime_SemacquireMutex(s *uint32, lifo bool, skipframes int) { if (*s - semAwoken[s]) == 0 { ch := make(chan bool) if lifo { @@ -41,7 +42,7 @@ func runtime_SemacquireMutex(s *uint32, lifo bool) { *s-- } -func runtime_Semrelease(s *uint32, handoff bool) { +func runtime_Semrelease(s *uint32, handoff bool, skipframes int) { // TODO: Use handoff if needed/possible. *s++ diff --git a/compiler/natives/src/sync/sync_test.go b/compiler/natives/src/sync/sync_test.go deleted file mode 100644 index a61bcf3e..00000000 --- a/compiler/natives/src/sync/sync_test.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build js - -package sync_test - -import ( - "testing" -) - -func TestPool(t *testing.T) { - t.Skip() -} - -func TestPoolGC(t *testing.T) { - t.Skip() -} - -func TestPoolRelease(t *testing.T) { - t.Skip() -} - -func TestCondCopy(t *testing.T) { - t.Skip() -} diff --git a/compiler/natives/src/sync/waitgroup.go b/compiler/natives/src/sync/waitgroup.go index 0d4873d6..f24e8a06 100644 --- a/compiler/natives/src/sync/waitgroup.go +++ b/compiler/natives/src/sync/waitgroup.go @@ -1,4 +1,5 @@ -// +build js +//go:build js && go1.18 +// +build js,go1.18 package sync @@ -6,7 +7,8 @@ type WaitGroup struct { counter int ch chan struct{} - state1 [3]uint32 + state1 uint64 + state2 uint32 } func (wg *WaitGroup) Add(delta int) { diff --git a/compiler/natives/src/sync/waitgroup_go117.go b/compiler/natives/src/sync/waitgroup_go117.go new file mode 100644 index 00000000..affc6f24 --- /dev/null +++ b/compiler/natives/src/sync/waitgroup_go117.go @@ -0,0 +1,32 @@ +//go:build js && !go1.18 +// +build js,!go1.18 + +package sync + +type WaitGroup struct { + counter int + ch chan struct{} + + state1 [3]uint32 + sema uint32 +} + +func (wg *WaitGroup) Add(delta int) { + wg.counter += delta + if wg.counter < 0 { + panic("sync: negative WaitGroup counter") + } + if wg.counter > 0 && wg.ch == nil { + wg.ch = make(chan struct{}) + } + if wg.counter == 0 && wg.ch != nil { + close(wg.ch) + wg.ch = nil + } +} + +func (wg *WaitGroup) Wait() { + if wg.counter > 0 { + <-wg.ch + } +} diff --git a/compiler/natives/src/syscall/fs_js.go b/compiler/natives/src/syscall/fs_js.go new file mode 100644 index 00000000..f837d279 --- /dev/null +++ b/compiler/natives/src/syscall/fs_js.go @@ -0,0 +1,50 @@ +//go:build js +// +build js + +package syscall + +import ( + "syscall/js" +) + +// fsCall emulates a file system-related syscall via a corresponding NodeJS fs +// API. +// +// This version is similar to the upstream, but it gracefully handles missing fs +// methods (allowing for smaller prelude) and removes a workaround for an +// obsolete NodeJS version. +func fsCall(name string, args ...interface{}) (js.Value, error) { + type callResult struct { + val js.Value + err error + } + + c := make(chan callResult, 1) + f := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + var res callResult + + // Check that args has at least one element, then check both IsUndefined() and IsNull() on + // that element. In some situations, BrowserFS calls the callback without arguments or with + // an undefined argument: https://github.com/gopherjs/gopherjs/pull/1118 + if len(args) >= 1 { + if jsErr := args[0]; !jsErr.IsUndefined() && !jsErr.IsNull() { + res.err = mapJSError(jsErr) + } + } + + res.val = js.Undefined() + if len(args) >= 2 { + res.val = args[1] + } + + c <- res + return nil + }) + defer f.Release() + if jsFS.Get(name).IsUndefined() { + return js.Undefined(), ENOSYS + } + jsFS.Call(name, append(args, f)...) + res := <-c + return res.val, res.err +} diff --git a/compiler/natives/src/syscall/js/export_test.go b/compiler/natives/src/syscall/js/export_test.go new file mode 100644 index 00000000..25f6f683 --- /dev/null +++ b/compiler/natives/src/syscall/js/export_test.go @@ -0,0 +1,9 @@ +//go:build js +// +build js + +package js + +// Defined to avoid a compile error in the original TestGarbageCollection() +// body. Can't use gopherjs:prune-original on it, since it causes an unused +// import error. +var JSGo Value diff --git a/compiler/natives/src/syscall/js/js.go b/compiler/natives/src/syscall/js/js.go index 81af995b..55b5f878 100644 --- a/compiler/natives/src/syscall/js/js.go +++ b/compiler/natives/src/syscall/js/js.go @@ -1,9 +1,9 @@ +//go:build js // +build js package js import ( - "reflect" "unsafe" "github.com/gopherjs/gopherjs/js" @@ -45,6 +45,10 @@ func (t Type) String() string { } } +func (t Type) isObject() bool { + return t == TypeObject || t == TypeFunction +} + func Global() Value { return objectToValue(js.Global) } @@ -62,10 +66,14 @@ type Func struct { } func (f Func) Release() { + js.Global.Set("$exportedFunctions", js.Global.Get("$exportedFunctions").Int()-1) f.Value = Null() } func FuncOf(fn func(this Value, args []Value) interface{}) Func { + // Existence of a wrapped function means that an external event may awaken the + // program and we need to suppress deadlock detection. + js.Global.Set("$exportedFunctions", js.Global.Get("$exportedFunctions").Int()+1) return Func{ Value: objectToValue(js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} { vargs := make([]Value, len(args)) @@ -90,13 +98,15 @@ type Value struct { // inited represents whether Value is non-zero value. true represents the value is not 'undefined'. inited bool + + _ [0]func() // uncomparable; to make == not compile } func objectToValue(obj *js.Object) Value { if obj == js.Undefined { return Value{} } - return Value{obj, true} + return Value{v: obj, inited: true} } var ( @@ -142,14 +152,12 @@ func ValueOf(x interface{}) Value { return x case Func: return x.Value - case TypedArray: - return x.Value case nil: return Null() case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, unsafe.Pointer, string, map[string]interface{}, []interface{}: return objectToValue(id.Invoke(x)) default: - panic(`invalid arg: ` + reflect.TypeOf(x).String()) + panic("ValueOf: invalid value") } } @@ -177,6 +185,22 @@ func convertArgs(args ...interface{}) []interface{} { return newArgs } +func convertJSError() { + err := recover() + if err == nil { + return + } + if jsErr, ok := err.(*js.Error); ok { + // We expect that all panics caught by Value.Call() are in fact JavaScript + // exceptions intercepted by GopherJS runtime, which we convert to + // syscall/js.Error, which the callers would expect. + panic(Error{Value: objectToValue(jsErr.Object)}) + } + // Panics of other types are unexpected and should never happen. But if it + // does, we will just re-raise it as-is. + panic(err) +} + func (v Value) Call(m string, args ...interface{}) Value { if vType := v.Type(); vType != TypeObject && vType != TypeFunction { panic(&ValueError{"Value.Call", vType}) @@ -184,6 +208,7 @@ func (v Value) Call(m string, args ...interface{}) Value { if propType := v.Get(m).Type(); propType != TypeFunction { panic("js: Value.Call: property " + m + " is not a function, got " + propType.String()) } + defer convertJSError() return objectToValue(v.internal().Call(m, convertArgs(args...)...)) } @@ -195,10 +220,16 @@ func (v Value) Float() float64 { } func (v Value) Get(p string) Value { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.Get", vType}) + } return objectToValue(v.internal().Get(p)) } func (v Value) Index(i int) Value { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.Index", vType}) + } return objectToValue(v.internal().Index(i)) } @@ -229,19 +260,61 @@ func (v Value) Length() int { } func (v Value) New(args ...interface{}) Value { + defer func() { + err := recover() + if err == nil { + return + } + if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case + panic(&ValueError{"Value.New", vType}) + } + if jsErr, ok := err.(*js.Error); ok { + panic(Error{objectToValue(jsErr.Object)}) + } + panic(err) + }() return objectToValue(v.internal().New(convertArgs(args...)...)) } func (v Value) Set(p string, x interface{}) { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.Set", vType}) + } v.internal().Set(p, convertArgs(x)[0]) } func (v Value) SetIndex(i int, x interface{}) { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.SetIndex", vType}) + } v.internal().SetIndex(i, convertArgs(x)[0]) } +// String returns the value v as a string. +// String is a special case because of Go's String method convention. Unlike the other getters, +// it does not panic if v's Type is not TypeString. Instead, it returns a string of the form "" +// or "" where T is v's type and V is a string representation of v's value. func (v Value) String() string { - return v.internal().String() + switch v.Type() { + case TypeString: + return v.internal().String() + case TypeUndefined: + return "" + case TypeNull: + return "" + case TypeBoolean: + return "" + case TypeNumber: + return "" + case TypeSymbol: + return "" + case TypeObject: + return "" + case TypeFunction: + return "" + default: + panic("bad type") + } } func (v Value) Truthy() bool { @@ -252,21 +325,29 @@ func (v Value) Type() Type { return Type(getValueType.Invoke(v.internal()).Int()) } -type TypedArray struct { - Value +func (v Value) IsNull() bool { + return v.Type() == TypeNull } -func TypedArrayOf(slice interface{}) TypedArray { - switch slice := slice.(type) { - case []int8, []int16, []int32, []uint8, []uint16, []uint32, []float32, []float64: - return TypedArray{objectToValue(id.Invoke(slice))} - default: - panic("TypedArrayOf: not a supported slice") +func (v Value) IsUndefined() bool { + return !v.inited +} + +func (v Value) IsNaN() bool { + return js.Global.Call("isNaN", v.internal()).Bool() +} + +// Delete deletes the JavaScript property p of value v. +// It panics if v is not a JavaScript object. +func (v Value) Delete(p string) { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.Delete", vType}) } + v.internal().Delete(p) } -func (t *TypedArray) Release() { - t.Value = Value{} +func (v Value) Equal(w Value) bool { + return v.internal() == w.internal() } type ValueError struct { @@ -278,6 +359,25 @@ func (e *ValueError) Error() string { return "syscall/js: call of " + e.Method + " on " + e.Type.String() } -type Wrapper interface { - JSValue() Value +// CopyBytesToGo copies bytes from the Uint8Array src to dst. +// It returns the number of bytes copied, which will be the minimum of the lengths of src and dst. +// CopyBytesToGo panics if src is not an Uint8Array. +func CopyBytesToGo(dst []byte, src Value) int { + vlen := src.v.Length() + if dlen := len(dst); dlen < vlen { + vlen = dlen + } + copy(dst, src.v.Interface().([]byte)) + return vlen +} + +// CopyBytesToJS copies bytes from src to the Uint8Array dst. +// It returns the number of bytes copied, which will be the minimum of the lengths of src and dst. +// CopyBytesToJS panics if dst is not an Uint8Array. +func CopyBytesToJS(dst Value, src []byte) int { + dt, ok := dst.v.Interface().([]byte) + if !ok { + panic("syscall/js: CopyBytesToJS: expected dst to be an Uint8Array") + } + return copy(dt, src) } diff --git a/compiler/natives/src/syscall/js/js_test.go b/compiler/natives/src/syscall/js/js_test.go new file mode 100644 index 00000000..c95c2e76 --- /dev/null +++ b/compiler/natives/src/syscall/js/js_test.go @@ -0,0 +1,20 @@ +//go:build js +// +build js + +package js_test + +import "testing" + +//gopherjs:prune-original +func TestIntConversion(t *testing.T) { + // Same as upstream, but only test cases appropriate for a 32-bit environment. + testIntConversion(t, 0) + testIntConversion(t, 1) + testIntConversion(t, -1) + testIntConversion(t, 1<<20) + testIntConversion(t, -1<<20) +} + +func TestGarbageCollection(t *testing.T) { + t.Skip("GC is not supported by GopherJS") +} diff --git a/compiler/natives/src/syscall/syscall_unix.go b/compiler/natives/src/syscall/legacy.go similarity index 56% rename from compiler/natives/src/syscall/syscall_unix.go rename to compiler/natives/src/syscall/legacy.go index 528d342a..24bba4f3 100644 --- a/compiler/natives/src/syscall/syscall_unix.go +++ b/compiler/natives/src/syscall/legacy.go @@ -1,54 +1,28 @@ -// +build js,!windows +//go:build legacy_syscall +// +build legacy_syscall package syscall import ( - "runtime" - "unsafe" - "github.com/gopherjs/gopherjs/js" ) -func runtime_envs() []string { - process := js.Global.Get("process") - if process == js.Undefined { - return nil - } - jsEnv := process.Get("env") - envkeys := js.Global.Get("Object").Call("keys", jsEnv) - envs := make([]string, envkeys.Length()) - for i := 0; i < envkeys.Length(); i++ { - key := envkeys.Index(i).String() - envs[i] = key + "=" + jsEnv.Get(key).String() - } - return envs -} +var syscallModule *js.Object +var alreadyTriedToLoad = false +var minusOne = -1 -func setenv_c(k, v string) { - process := js.Global.Get("process") - if process == js.Undefined { - return - } - process.Get("env").Set(k, v) -} +var warningPrinted = false -func unsetenv_c(k string) { - process := js.Global.Get("process") - if process == js.Undefined { - return +func printWarning() { + if !warningPrinted { + js.Global.Get("console").Call("error", "warning: system calls not available, see https://github.com/gopherjs/gopherjs/blob/master/doc/syscalls.md") } - process.Get("env").Delete(k) + warningPrinted = true } -var syscallModule *js.Object -var alreadyTriedToLoad = false -var minusOne = -1 - func syscallByName(name string) *js.Object { - defer func() { - recover() - // return nil if recovered - }() + defer recover() // return nil if recovered + if syscallModule == nil { if alreadyTriedToLoad { return nil @@ -68,18 +42,8 @@ func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { r := f.Invoke(trap, a1, a2, a3) return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int()) } - if trap == SYS_WRITE && (a1 == 1 || a1 == 2) { - array := js.InternalObject(a2) - slice := make([]byte, array.Length()) - js.InternalObject(slice).Set("$array", array) - printToConsole(slice) - return uintptr(array.Length()), 0, 0 - } - if trap == exitTrap { - runtime.Goexit() - } printWarning() - return uintptr(minusOne), 0, EACCES + return uintptr(minusOne), 0, ENOSYS } func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { @@ -90,7 +54,7 @@ func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) if trap != 202 { // kern.osrelease on OS X, happens in init of "os" package printWarning() } - return uintptr(minusOne), 0, EACCES + return uintptr(minusOne), 0, ENOSYS } func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { @@ -99,7 +63,7 @@ func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int()) } printWarning() - return uintptr(minusOne), 0, EACCES + return uintptr(minusOne), 0, ENOSYS } func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) { @@ -117,17 +81,5 @@ func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errn return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int()) } printWarning() - return uintptr(minusOne), 0, EACCES -} - -func BytePtrFromString(s string) (*byte, error) { - array := js.Global.Get("Uint8Array").New(len(s) + 1) - for i, b := range []byte(s) { - if b == 0 { - return nil, EINVAL - } - array.SetIndex(i, b) - } - array.SetIndex(len(s), 0) - return (*byte)(unsafe.Pointer(array.Unsafe())), nil + return uintptr(minusOne), 0, ENOSYS } diff --git a/compiler/natives/src/syscall/syscall.go b/compiler/natives/src/syscall/syscall.go deleted file mode 100644 index 44c67ec9..00000000 --- a/compiler/natives/src/syscall/syscall.go +++ /dev/null @@ -1,64 +0,0 @@ -// +build js - -package syscall - -import ( - "unsafe" - - "github.com/gopherjs/gopherjs/js" -) - -var warningPrinted = false -var lineBuffer []byte - -func init() { - js.Global.Set("$flushConsole", js.InternalObject(func() { - if len(lineBuffer) != 0 { - js.Global.Get("console").Call("log", string(lineBuffer)) - lineBuffer = nil - } - })) -} - -func printWarning() { - if !warningPrinted { - js.Global.Get("console").Call("error", "warning: system calls not available, see https://github.com/gopherjs/gopherjs/blob/master/doc/syscalls.md") - } - warningPrinted = true -} - -func printToConsole(b []byte) { - goPrintToConsole := js.Global.Get("goPrintToConsole") - if goPrintToConsole != js.Undefined { - goPrintToConsole.Invoke(js.InternalObject(b)) - return - } - - lineBuffer = append(lineBuffer, b...) - for { - i := indexByte(lineBuffer, '\n') - if i == -1 { - break - } - js.Global.Get("console").Call("log", string(lineBuffer[:i])) // don't use println, since it does not externalize multibyte characters - lineBuffer = lineBuffer[i+1:] - } -} - -func use(p unsafe.Pointer) { - // no-op -} - -func Exit(code int) { - Syscall(exitTrap, uintptr(code), 0, 0) -} - -// indexByte is copied from bytes package to avoid importing it (since the real syscall package doesn't). -func indexByte(s []byte, c byte) int { - for i, b := range s { - if b == c { - return i - } - } - return -1 -} diff --git a/compiler/natives/src/syscall/syscall_darwin.go b/compiler/natives/src/syscall/syscall_darwin.go deleted file mode 100644 index dded41cf..00000000 --- a/compiler/natives/src/syscall/syscall_darwin.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build js - -package syscall - -import ( - "github.com/gopherjs/gopherjs/js" -) - -func funcPC(f func()) uintptr { - if js.InternalObject(f) == js.InternalObject(libc_write_trampoline) { - return SYS_WRITE - } - return uintptr(minusOne) -} - -func syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { - return Syscall(trap, a1, a2, a3) -} - -func syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { - return Syscall6(trap, a1, a2, a3, a4, a5, a6) -} - -func syscall6X(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { - panic("syscall6X is not implemented") -} - -func rawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { - return RawSyscall(trap, a1, a2, a3) -} - -func rawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { - return RawSyscall6(trap, a1, a2, a3, a4, a5, a6) -} diff --git a/compiler/natives/src/syscall/syscall_js_wasm.go b/compiler/natives/src/syscall/syscall_js_wasm.go new file mode 100644 index 00000000..c44c774c --- /dev/null +++ b/compiler/natives/src/syscall/syscall_js_wasm.go @@ -0,0 +1,78 @@ +//go:build js +// +build js + +package syscall + +import ( + "syscall/js" +) + +func runtime_envs() []string { + process := js.Global().Get("process") + if process.IsUndefined() { + return nil + } + jsEnv := process.Get("env") + envkeys := js.Global().Get("Object").Call("keys", jsEnv) + envs := make([]string, envkeys.Length()) + for i := 0; i < envkeys.Length(); i++ { + key := envkeys.Index(i).String() + envs[i] = key + "=" + jsEnv.Get(key).String() + } + return envs +} + +func setenv_c(k, v string) { + process := js.Global().Get("process") + if process.IsUndefined() { + return + } + process.Get("env").Set(k, v) +} + +func unsetenv_c(k string) { + process := js.Global().Get("process") + if process.IsUndefined() { + return + } + process.Get("env").Delete(k) +} + +func setStat(st *Stat_t, jsSt js.Value) { + // This method is an almost-exact copy of upstream, except for 4 places where + // time stamps are obtained as floats in lieu of int64. Upstream wasm emulates + // a 64-bit architecture and millisecond-based timestamps fit within an int + // type. GopherJS is 32-bit and use of 32-bit ints causes timestamp truncation. + // We get timestamps as float64 (which matches JS-native representation) and + // convert then to int64 manually, since syscall/js.Value doesn't have an + // Int64 method. + st.Dev = int64(jsSt.Get("dev").Int()) + st.Ino = uint64(jsSt.Get("ino").Int()) + st.Mode = uint32(jsSt.Get("mode").Int()) + st.Nlink = uint32(jsSt.Get("nlink").Int()) + st.Uid = uint32(jsSt.Get("uid").Int()) + st.Gid = uint32(jsSt.Get("gid").Int()) + st.Rdev = int64(jsSt.Get("rdev").Int()) + st.Size = int64(jsSt.Get("size").Int()) + st.Blksize = int32(jsSt.Get("blksize").Int()) + st.Blocks = int32(jsSt.Get("blocks").Int()) + atime := int64(jsSt.Get("atimeMs").Float()) // Int64 + st.Atime = atime / 1000 + st.AtimeNsec = (atime % 1000) * 1000000 + mtime := int64(jsSt.Get("mtimeMs").Float()) // Int64 + st.Mtime = mtime / 1000 + st.MtimeNsec = (mtime % 1000) * 1000000 + ctime := int64(jsSt.Get("ctimeMs").Float()) // Int64 + st.Ctime = ctime / 1000 + st.CtimeNsec = (ctime % 1000) * 1000000 +} + +func Exit(code int) { + if process := js.Global().Get("process"); !process.IsUndefined() { + process.Call("exit", code) + return + } + if code != 0 { + js.Global().Get("console").Call("warn", "Go program exited with non-zero code:", code) + } +} diff --git a/compiler/natives/src/syscall/syscall_linux.go b/compiler/natives/src/syscall/syscall_linux.go deleted file mode 100644 index 41024b62..00000000 --- a/compiler/natives/src/syscall/syscall_linux.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build js - -package syscall - -const exitTrap = SYS_EXIT_GROUP diff --git a/compiler/natives/src/syscall/syscall_nonlinux.go b/compiler/natives/src/syscall/syscall_nonlinux.go deleted file mode 100644 index 5b79fcfd..00000000 --- a/compiler/natives/src/syscall/syscall_nonlinux.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build js,!linux - -package syscall - -const exitTrap = SYS_EXIT diff --git a/compiler/natives/src/syscall/syscall_windows.go b/compiler/natives/src/syscall/syscall_windows.go deleted file mode 100644 index e0a5a522..00000000 --- a/compiler/natives/src/syscall/syscall_windows.go +++ /dev/null @@ -1,105 +0,0 @@ -// +build js - -package syscall - -import "runtime" - -var minusOne = -1 - -func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { - printWarning() - return uintptr(minusOne), 0, EACCES -} - -func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { - printWarning() - return uintptr(minusOne), 0, EACCES -} - -func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) { - printWarning() - return uintptr(minusOne), 0, EACCES -} - -func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno) { - printWarning() - return uintptr(minusOne), 0, EACCES -} - -func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno) { - printWarning() - return uintptr(minusOne), 0, EACCES -} - -func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno) { - printWarning() - return uintptr(minusOne), 0, EACCES -} - -func loadlibrary(filename *uint16) (handle uintptr, err Errno) { - printWarning() - return uintptr(minusOne), EACCES -} - -func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) { - printWarning() - return uintptr(minusOne), EACCES -} - -func (d *LazyDLL) Load() error { - return &DLLError{Msg: "system calls not available, see https://github.com/gopherjs/gopherjs/blob/master/doc/syscalls.md"} -} - -func (p *LazyProc) Find() error { - return &DLLError{Msg: "system calls not available, see https://github.com/gopherjs/gopherjs/blob/master/doc/syscalls.md"} -} - -func getStdHandle(h int) (fd Handle) { - if h == STD_OUTPUT_HANDLE { - return 1 - } - if h == STD_ERROR_HANDLE { - return 2 - } - return 0 -} - -func GetConsoleMode(console Handle, mode *uint32) (err error) { - return DummyError{} -} - -func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) { - if handle == 1 || handle == 2 { - printToConsole(buf) - *done = uint32(len(buf)) - return nil - } - printWarning() - return nil -} - -func ExitProcess(exitcode uint32) { - runtime.Goexit() -} - -func GetCommandLine() (cmd *uint16) { - return -} - -func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) { - return nil, DummyError{} -} - -func Getenv(key string) (value string, found bool) { - return "", false -} - -func GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) { - return 0, DummyError{} -} - -type DummyError struct{} - -func (e DummyError) Error() string { - return "" -} diff --git a/compiler/natives/src/testing/allocs_test.go b/compiler/natives/src/testing/allocs_test.go new file mode 100644 index 00000000..54c2f545 --- /dev/null +++ b/compiler/natives/src/testing/allocs_test.go @@ -0,0 +1,10 @@ +//go:build js +// +build js + +package testing_test + +import "testing" + +func TestAllocsPerRun(t *testing.T) { + t.Skip("runtime.ReadMemStats() is not supported by GopherJS.") +} diff --git a/compiler/natives/src/testing/example.go b/compiler/natives/src/testing/example.go index b44af132..bf8d0648 100644 --- a/compiler/natives/src/testing/example.go +++ b/compiler/natives/src/testing/example.go @@ -1,3 +1,4 @@ +//go:build js // +build js package testing @@ -16,7 +17,7 @@ func runExample(eg InternalExample) (ok bool) { // Capture stdout. stdout := os.Stdout - w, err := tempFile("." + eg.Name + ".stdout.") + w, err := os.CreateTemp("", "."+eg.Name+".stdout.") if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) @@ -33,7 +34,7 @@ func runExample(eg InternalExample) (ok bool) { // Close file, restore stdout, get output. w.Close() os.Stdout = stdout - out, readFileErr := readFile(w.Name()) + out, readFileErr := os.ReadFile(w.Name()) _ = os.Remove(w.Name()) if readFileErr != nil { fmt.Fprintf(os.Stderr, "testing: reading stdout file: %v\n", readFileErr) @@ -42,11 +43,11 @@ func runExample(eg InternalExample) (ok bool) { var fail string err := recover() - got := strings.TrimSpace(out) + got := strings.TrimSpace(string(out)) want := strings.TrimSpace(eg.Output) if eg.Unordered { if sortLines(got) != sortLines(want) && err == nil { - fail = fmt.Sprintf("got:\n%s\nwant (unordered):\n%s\n", out, eg.Output) + fail = fmt.Sprintf("got:\n%s\nwant (unordered):\n%s\n", string(out), eg.Output) } } else { if got != want && err == nil { diff --git a/compiler/natives/src/testing/helper_test.go b/compiler/natives/src/testing/helper_test.go new file mode 100644 index 00000000..b277fa31 --- /dev/null +++ b/compiler/natives/src/testing/helper_test.go @@ -0,0 +1,4 @@ +//go:build js +// +build js + +package testing diff --git a/compiler/natives/src/testing/ioutil.go b/compiler/natives/src/testing/ioutil.go deleted file mode 100644 index a1527f9c..00000000 --- a/compiler/natives/src/testing/ioutil.go +++ /dev/null @@ -1,66 +0,0 @@ -// +build js - -package testing - -import ( - "bytes" - "io" - "os" - "strconv" - "sync" - "time" -) - -var rand uint32 -var randmu sync.Mutex - -func reseed() uint32 { - return uint32(time.Now().UnixNano() + int64(os.Getpid())) -} - -func nextSuffix() string { - randmu.Lock() - r := rand - if r == 0 { - r = reseed() - } - r = r*1664525 + 1013904223 // constants from Numerical Recipes - rand = r - randmu.Unlock() - return strconv.Itoa(int(1e9 + r%1e9))[1:] -} - -// A functional copy of ioutil.TempFile, to avoid extra imports. -func tempFile(prefix string) (f *os.File, err error) { - dir := os.TempDir() - - nconflict := 0 - for i := 0; i < 10000; i++ { - name := dir + string(os.PathSeparator) + prefix + nextSuffix() - f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) - if os.IsExist(err) { - if nconflict++; nconflict > 10 { - randmu.Lock() - rand = reseed() - randmu.Unlock() - } - continue - } - break - } - return -} - -func readFile(filename string) (string, error) { - f, err := os.Open(filename) - if err != nil { - return "", err - } - defer f.Close() - var buf bytes.Buffer - _, err = io.Copy(&buf, f) - if err != nil { - return "", err - } - return buf.String(), nil -} diff --git a/compiler/natives/src/testing/sub_test.go b/compiler/natives/src/testing/sub_test.go new file mode 100644 index 00000000..1e9a79e4 --- /dev/null +++ b/compiler/natives/src/testing/sub_test.go @@ -0,0 +1,22 @@ +//go:build js +// +build js + +package testing + +func TestBenchmarkReadMemStatsBeforeFirstRun(t *T) { + t.Skip("runtime.ReadMemStats() is not supported by GopherJS.") +} + +func TestTRun(t *T) { + // TODO(nevkontakte): This test performs string comparisons expecting to find + // sub_test.go in the output, but GopherJS currently reports caller + // locations as "test." due to minimal caller and source map support. + t.Skip("GopherJS doesn't support source maps sufficiently.") +} + +func TestBRun(t *T) { + // TODO(nevkontakte): This test performs string comparisons expecting to find + // sub_test.go in the output, but GopherJS currently reports caller + // locations as "test." due to minimal caller and source map support. + t.Skip("GopherJS doesn't support source maps sufficiently.") +} diff --git a/compiler/natives/src/testing/testing.go b/compiler/natives/src/testing/testing.go deleted file mode 100644 index 392f1f87..00000000 --- a/compiler/natives/src/testing/testing.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build js - -package testing - -import "runtime" - -// The upstream callerName and frameSkip rely on runtime.Callers, -// and panic if there are zero callers found. However, runtime.Callers -// is not implemented for GopherJS at this time, so we can't use -// that implementation. Use these stubs instead. -func callerName(skip int) string { - // Upstream callerName requires a functional runtime.Callers. - // TODO: Implement if possible. - return "" -} - -func (*common) frameSkip(skip int) runtime.Frame { - _, file, line, ok := runtime.Caller(skip) - if !ok { - return runtime.Frame{} - } - return runtime.Frame{ - File: file, - Line: line, - } -} diff --git a/compiler/natives/src/text/template/template.go b/compiler/natives/src/text/template/template.go index 7fa211a5..056fe9c8 100644 --- a/compiler/natives/src/text/template/template.go +++ b/compiler/natives/src/text/template/template.go @@ -1,3 +1,4 @@ +//go:build js // +build js package template diff --git a/compiler/natives/src/time/time.go b/compiler/natives/src/time/time.go index e8988977..40c596e6 100644 --- a/compiler/natives/src/time/time.go +++ b/compiler/natives/src/time/time.go @@ -1,3 +1,4 @@ +//go:build js // +build js package time @@ -21,27 +22,11 @@ type runtimeTimer struct { period int64 f func(interface{}, uintptr) arg interface{} + seq uintptr timeout *js.Object active bool } -func initLocal() { - d := js.Global.Get("Date").New() - s := d.String() - i := indexByte(s, '(') - j := indexByte(s, ')') - if i == -1 || j == -1 { - localLoc.name = "UTC" - return - } - localLoc.name = s[i+1 : j] - localLoc.zone = []zone{{localLoc.name, d.Call("getTimezoneOffset").Int() * -60, false}} -} - -func runtimeNano() int64 { - return js.Global.Get("Date").New().Call("getTime").Int64() * int64(Millisecond) -} - func now() (sec int64, nsec int32, mono int64) { n := runtimeNano() return n / int64(Second), int32(n % int64(Second)), n @@ -79,6 +64,22 @@ func stopTimer(t *runtimeTimer) bool { return wasActive } +func modTimer(t *runtimeTimer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) { + stopTimer(t) + t.when = when + t.period = period + t.f = f + t.arg = arg + t.seq = seq + startTimer(t) +} + +func resetTimer(t *runtimeTimer, when int64) bool { + wasActive := t.active + modTimer(t, when, t.period, t.f, t.arg, t.seq) + return wasActive +} + func forceZipFileForTesting(zipOnly bool) { } diff --git a/compiler/natives/src/time/time_test.go b/compiler/natives/src/time/time_test.go index 410a6358..5d4119cc 100644 --- a/compiler/natives/src/time/time_test.go +++ b/compiler/natives/src/time/time_test.go @@ -1,3 +1,4 @@ +//go:build js // +build js package time_test @@ -9,3 +10,7 @@ import ( func TestSleep(t *testing.T) { t.Skip("time.Now() is not accurate enough for the test") } + +func TestEnvTZUsage(t *testing.T) { + t.Skip("TZ environment variable in not applicable in the browser context.") +} diff --git a/compiler/natives/src/time/zoneinfo_js.go b/compiler/natives/src/time/zoneinfo_js.go new file mode 100644 index 00000000..0101b95f --- /dev/null +++ b/compiler/natives/src/time/zoneinfo_js.go @@ -0,0 +1,57 @@ +//go:build js +// +build js + +package time + +import "github.com/gopherjs/gopherjs/js" + +// The code below is based on the upstream zoneinfo_js.go to closer match +// WebAssembly behavior. + +func initLocal() { + localLoc.name = "Local" + + z := zone{} + d := js.Global.Get("Date").New() + offset := d.Call("getTimezoneOffset").Int() * -1 + z.offset = offset * 60 + // According to https://tc39.github.io/ecma262/#sec-timezoneestring, + // the timezone name from (new Date()).toTimeString() is an implementation-dependent + // result, and in Google Chrome, it gives the fully expanded name rather than + // the abbreviation. + // Hence, we construct the name from the offset. + z.name = "UTC" + if offset < 0 { + z.name += "-" + offset *= -1 + } else { + z.name += "+" + } + z.name += itoa(offset / 60) + min := offset % 60 + if min != 0 { + z.name += ":" + itoa(min) + } + localLoc.zone = []zone{z} +} + +// itoa is like strconv.Itoa but only works for values of i in range [0,99]. +// It panics if i is out of range. +func itoa(i int) string { + if i < 10 { + return digits[i : i+1] + } + return smallsString[i*2 : i*2+2] +} + +const smallsString = "00010203040506070809" + + "10111213141516171819" + + "20212223242526272829" + + "30313233343536373839" + + "40414243444546474849" + + "50515253545556575859" + + "60616263646566676869" + + "70717273747576777879" + + "80818283848586878889" + + "90919293949596979899" +const digits = "0123456789" diff --git a/compiler/natives/src/unicode/unicode.go b/compiler/natives/src/unicode/unicode.go index e9231765..a622c32f 100644 --- a/compiler/natives/src/unicode/unicode.go +++ b/compiler/natives/src/unicode/unicode.go @@ -1,3 +1,4 @@ +//go:build js // +build js package unicode diff --git a/compiler/natives/src/vendor/golang.org/x/crypto/internal/subtle/aliasing.go b/compiler/natives/src/vendor/golang.org/x/crypto/internal/subtle/aliasing.go new file mode 100644 index 00000000..104ac82b --- /dev/null +++ b/compiler/natives/src/vendor/golang.org/x/crypto/internal/subtle/aliasing.go @@ -0,0 +1,20 @@ +//go:build js +// +build js + +package subtle + +// This file duplicated is these two locations: +// - src/crypto/internal/subtle/ +// - src/golang.org/x/crypto/internal/subtle/ + +import "github.com/gopherjs/gopherjs/js" + +// AnyOverlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +func AnyOverlap(x, y []byte) bool { + // GopherJS: We can't rely on pointer arithmetic, so use GopherJS slice internals. + return len(x) > 0 && len(y) > 0 && + js.InternalObject(x).Get("$array") == js.InternalObject(y).Get("$array") && + js.InternalObject(x).Get("$offset").Int() <= js.InternalObject(y).Get("$offset").Int()+len(y)-1 && + js.InternalObject(y).Get("$offset").Int() <= js.InternalObject(x).Get("$offset").Int()+len(x)-1 +} diff --git a/compiler/package.go b/compiler/package.go index f841b145..82afe90c 100644 --- a/compiler/package.go +++ b/compiler/package.go @@ -6,17 +6,23 @@ import ( "fmt" "go/ast" "go/constant" + "go/scanner" "go/token" "go/types" "sort" "strings" + "time" + + "github.com/gopherjs/gopherjs/internal/typeparams" "github.com/gopherjs/gopherjs/compiler/analysis" + "github.com/gopherjs/gopherjs/compiler/astutil" "github.com/neelance/astrewrite" "golang.org/x/tools/go/gcexportdata" "golang.org/x/tools/go/types/typeutil" ) +// pkgContext maintains compiler context for a specific package. type pkgContext struct { *analysis.Info additionalSelections map[*ast.SelectorExpr]selection @@ -67,9 +73,10 @@ func (sel *fakeSelection) Index() []int { return sel.index } func (sel *fakeSelection) Obj() types.Object { return sel.obj } func (sel *fakeSelection) Type() types.Type { return sel.typ } +// funcContext maintains compiler context for a specific function (lexical scope?). type funcContext struct { *analysis.FuncInfo - p *pkgContext + pkgCtx *pkgContext parent *funcContext sig *types.Signature allVars map[string]int @@ -118,7 +125,26 @@ func (pi packageImporter) Import(path string) (*types.Package, error) { return pi.importContext.Packages[a.ImportPath], nil } -func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, importContext *ImportContext, minify bool) (*Archive, error) { +func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, importContext *ImportContext, minify bool) (_ *Archive, err error) { + defer func() { + e := recover() + if e == nil { + return + } + if fe, ok := bailingOut(e); ok { + // Orderly bailout, return whatever clues we already have. + err = fe + return + } + // Some other unexpected panic, catch the stack trace and return as an error. + err = bailout(fmt.Errorf("unexpected compiler panic while building package %q: %v", importPath, e)) + }() + + // Files must be in the same order to get reproducible JS + sort.Slice(files, func(i, j int) bool { + return fileSet.File(files[i].Pos()).Name() > fileSet.File(files[j].Pos()).Name() + }) + typesInfo := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), @@ -128,8 +154,23 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor Scopes: make(map[ast.Node]*types.Scope), } - var importError error var errList ErrorList + + // Extract all go:linkname compiler directives from the package source. + goLinknames := []GoLinkname{} + for _, file := range files { + found, err := parseGoLinknames(fileSet, importPath, file) + if err != nil { + if errs, ok := err.(ErrorList); ok { + errList = append(errList, errs...) + } else { + errList = append(errList, err) + } + } + goLinknames = append(goLinknames, found...) + } + + var importError error var previousErr error config := &types.Config{ Importer: packageImporter{ @@ -192,9 +233,9 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor panic(fullName) } pkgInfo := analysis.AnalyzePkg(simplifiedFiles, fileSet, typesInfo, typesPkg, isBlocking) - c := &funcContext{ + funcCtx := &funcContext{ FuncInfo: pkgInfo.InitFuncInfo, - p: &pkgContext{ + pkgCtx: &pkgContext{ Info: pkgInfo, additionalSelections: make(map[*ast.SelectorExpr]selection), @@ -213,7 +254,7 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor labelCases: make(map[*types.Label]int), } for name := range reservedKeywords { - c.allVars[name] = 1 + funcCtx.allVars[name] = 1 } // imports @@ -225,19 +266,19 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor // but now we do it here to maintain previous behavior. continue } - c.p.pkgVars[importedPkg.Path()] = c.newVariableWithLevel(importedPkg.Name(), true) + funcCtx.pkgCtx.pkgVars[importedPkg.Path()] = funcCtx.newVariableWithLevel(importedPkg.Name(), true) importedPaths = append(importedPaths, importedPkg.Path()) } sort.Strings(importedPaths) for _, impPath := range importedPaths { - id := c.newIdent(fmt.Sprintf(`%s.$init`, c.p.pkgVars[impPath]), types.NewSignature(nil, nil, nil, false)) + id := funcCtx.newIdent(fmt.Sprintf(`%s.$init`, funcCtx.pkgCtx.pkgVars[impPath]), types.NewSignature(nil, nil, nil, false)) call := &ast.CallExpr{Fun: id} - c.Blocking[call] = true - c.Flattened[call] = true + funcCtx.Blocking[call] = true + funcCtx.Flattened[call] = true importDecls = append(importDecls, &Decl{ - Vars: []string{c.p.pkgVars[impPath]}, - DeclCode: []byte(fmt.Sprintf("\t%s = $packages[\"%s\"];\n", c.p.pkgVars[impPath], impPath)), - InitCode: c.CatchOutput(1, func() { c.translateStmt(&ast.ExprStmt{X: call}, nil) }), + Vars: []string{funcCtx.pkgCtx.pkgVars[impPath]}, + DeclCode: []byte(fmt.Sprintf("\t%s = $packages[\"%s\"];\n", funcCtx.pkgCtx.pkgVars[impPath], impPath)), + InitCode: funcCtx.CatchOutput(1, func() { funcCtx.translateStmt(&ast.ExprStmt{X: call}, nil) }), }) } @@ -247,7 +288,7 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: - sig := c.p.Defs[d.Name].(*types.Func).Type().(*types.Signature) + sig := funcCtx.pkgCtx.Defs[d.Name].(*types.Func).Type().(*types.Signature) var recvType types.Type if sig.Recv() != nil { recvType = sig.Recv().Type() @@ -256,7 +297,7 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor } } if sig.Recv() == nil { - c.objectName(c.p.Defs[d.Name].(*types.Func)) // register toplevel name + funcCtx.objectName(funcCtx.pkgCtx.Defs[d.Name].(*types.Func)) // register toplevel name } if !isBlank(d.Name) { functions = append(functions, d) @@ -265,17 +306,17 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor switch d.Tok { case token.TYPE: for _, spec := range d.Specs { - o := c.p.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) - c.p.typeNames = append(c.p.typeNames, o) - c.objectName(o) // register toplevel name + o := funcCtx.pkgCtx.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) + funcCtx.pkgCtx.typeNames = append(funcCtx.pkgCtx.typeNames, o) + funcCtx.objectName(o) // register toplevel name } case token.VAR: for _, spec := range d.Specs { for _, name := range spec.(*ast.ValueSpec).Names { if !isBlank(name) { - o := c.p.Defs[name].(*types.Var) + o := funcCtx.pkgCtx.Defs[name].(*types.Var) vars = append(vars, o) - c.objectName(o) // register toplevel name + funcCtx.objectName(o) // register toplevel name } } } @@ -287,10 +328,10 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor } collectDependencies := func(f func()) []string { - c.p.dependencies = make(map[types.Object]bool) + funcCtx.pkgCtx.dependencies = make(map[types.Object]bool) f() var deps []string - for o := range c.p.dependencies { + for o := range funcCtx.pkgCtx.dependencies { qualifiedName := o.Pkg().Path() + "." + o.Name() if f, ok := o.(*types.Func); ok && f.Type().(*types.Signature).Recv() != nil { deps = append(deps, qualifiedName+"~") @@ -305,7 +346,7 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor // variables var varDecls []*Decl varsWithInit := make(map[*types.Var]bool) - for _, init := range c.p.InitOrder { + for _, init := range funcCtx.pkgCtx.InitOrder { for _, o := range init.Lhs { varsWithInit[o] = true } @@ -313,41 +354,42 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor for _, o := range vars { var d Decl if !o.Exported() { - d.Vars = []string{c.objectName(o)} + d.Vars = []string{funcCtx.objectName(o)} } - if c.p.HasPointer[o] && !o.Exported() { - d.Vars = append(d.Vars, c.varPtrName(o)) + if funcCtx.pkgCtx.HasPointer[o] && !o.Exported() { + d.Vars = append(d.Vars, funcCtx.varPtrName(o)) } if _, ok := varsWithInit[o]; !ok { d.DceDeps = collectDependencies(func() { - d.InitCode = []byte(fmt.Sprintf("\t\t%s = %s;\n", c.objectName(o), c.translateExpr(c.zeroValue(o.Type())).String())) + d.InitCode = []byte(fmt.Sprintf("\t\t%s = %s;\n", funcCtx.objectName(o), funcCtx.translateExpr(funcCtx.zeroValue(o.Type())).String())) }) } d.DceObjectFilter = o.Name() varDecls = append(varDecls, &d) } - for _, init := range c.p.InitOrder { + for _, init := range funcCtx.pkgCtx.InitOrder { lhs := make([]ast.Expr, len(init.Lhs)) for i, o := range init.Lhs { ident := ast.NewIdent(o.Name()) - c.p.Defs[ident] = o - lhs[i] = c.setType(ident, o.Type()) + ident.NamePos = o.Pos() + funcCtx.pkgCtx.Defs[ident] = o + lhs[i] = funcCtx.setType(ident, o.Type()) varsWithInit[o] = true } var d Decl d.DceDeps = collectDependencies(func() { - c.localVars = nil - d.InitCode = c.CatchOutput(1, func() { - c.translateStmt(&ast.AssignStmt{ + funcCtx.localVars = nil + d.InitCode = funcCtx.CatchOutput(1, func() { + funcCtx.translateStmt(&ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: []ast.Expr{init.Rhs}, }, nil) }) - d.Vars = append(d.Vars, c.localVars...) + d.Vars = append(d.Vars, funcCtx.localVars...) }) if len(init.Lhs) == 1 { - if !analysis.HasSideEffect(init.Rhs, c.p.Info.Info) { + if !analysis.HasSideEffect(init.Rhs, funcCtx.pkgCtx.Info.Info) { d.DceObjectFilter = init.Lhs[0].Name() } } @@ -358,39 +400,47 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor var funcDecls []*Decl var mainFunc *types.Func for _, fun := range functions { - o := c.p.Defs[fun.Name].(*types.Func) - funcInfo := c.p.FuncDeclInfos[o] + o := funcCtx.pkgCtx.Defs[fun.Name].(*types.Func) + + if typeparams.FuncTypeHasTypeParam(fun.Type) { + return nil, scanner.Error{ + Pos: fileSet.Position(fun.Type.Pos()), + Msg: fmt.Sprintf("function %s: type parameters are not supported by GopherJS: https://github.com/gopherjs/gopherjs/issues/1013", o.Name()), + } + } + funcInfo := funcCtx.pkgCtx.FuncDeclInfos[o] d := Decl{ FullName: o.FullName(), Blocking: len(funcInfo.Blocking) != 0, } + d.LinkingName = newSymName(o) if fun.Recv == nil { - d.Vars = []string{c.objectName(o)} + d.Vars = []string{funcCtx.objectName(o)} d.DceObjectFilter = o.Name() switch o.Name() { case "main": mainFunc = o d.DceObjectFilter = "" case "init": - d.InitCode = c.CatchOutput(1, func() { - id := c.newIdent("", types.NewSignature(nil, nil, nil, false)) - c.p.Uses[id] = o + d.InitCode = funcCtx.CatchOutput(1, func() { + id := funcCtx.newIdent("", types.NewSignature(nil, nil, nil, false)) + funcCtx.pkgCtx.Uses[id] = o call := &ast.CallExpr{Fun: id} - if len(c.p.FuncDeclInfos[o].Blocking) != 0 { - c.Blocking[call] = true + if len(funcCtx.pkgCtx.FuncDeclInfos[o].Blocking) != 0 { + funcCtx.Blocking[call] = true } - c.translateStmt(&ast.ExprStmt{X: call}, nil) + funcCtx.translateStmt(&ast.ExprStmt{X: call}, nil) }) d.DceObjectFilter = "" } - } - if fun.Recv != nil { + } else { recvType := o.Type().(*types.Signature).Recv().Type() ptr, isPointer := recvType.(*types.Pointer) namedRecvType, _ := recvType.(*types.Named) if isPointer { namedRecvType = ptr.Elem().(*types.Named) } + d.NamedRecvType = funcCtx.objectName(namedRecvType.Obj()) d.DceObjectFilter = namedRecvType.Obj().Name() if !fun.Name.IsExported() { d.DceMethodFilter = o.Name() + "~" @@ -398,7 +448,7 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor } d.DceDeps = collectDependencies(func() { - d.DeclCode = c.translateToplevelFunction(fun, funcInfo) + d.DeclCode = funcCtx.translateToplevelFunction(fun, funcInfo) }) funcDecls = append(funcDecls, &d) } @@ -406,47 +456,55 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor if mainFunc == nil { return nil, fmt.Errorf("missing main function") } - id := c.newIdent("", types.NewSignature(nil, nil, nil, false)) - c.p.Uses[id] = mainFunc + id := funcCtx.newIdent("", types.NewSignature(nil, nil, nil, false)) + funcCtx.pkgCtx.Uses[id] = mainFunc call := &ast.CallExpr{Fun: id} ifStmt := &ast.IfStmt{ - Cond: c.newIdent("$pkg === $mainPkg", types.Typ[types.Bool]), + Cond: funcCtx.newIdent("$pkg === $mainPkg", types.Typ[types.Bool]), Body: &ast.BlockStmt{ List: []ast.Stmt{ &ast.ExprStmt{X: call}, &ast.AssignStmt{ - Lhs: []ast.Expr{c.newIdent("$mainFinished", types.Typ[types.Bool])}, + Lhs: []ast.Expr{funcCtx.newIdent("$mainFinished", types.Typ[types.Bool])}, Tok: token.ASSIGN, - Rhs: []ast.Expr{c.newConst(types.Typ[types.Bool], constant.MakeBool(true))}, + Rhs: []ast.Expr{funcCtx.newConst(types.Typ[types.Bool], constant.MakeBool(true))}, }, }, }, } - if len(c.p.FuncDeclInfos[mainFunc].Blocking) != 0 { - c.Blocking[call] = true - c.Flattened[ifStmt] = true + if len(funcCtx.pkgCtx.FuncDeclInfos[mainFunc].Blocking) != 0 { + funcCtx.Blocking[call] = true + funcCtx.Flattened[ifStmt] = true } funcDecls = append(funcDecls, &Decl{ - InitCode: c.CatchOutput(1, func() { - c.translateStmt(ifStmt, nil) + InitCode: funcCtx.CatchOutput(1, func() { + funcCtx.translateStmt(ifStmt, nil) }), }) } // named types var typeDecls []*Decl - for _, o := range c.p.typeNames { + for _, o := range funcCtx.pkgCtx.typeNames { if o.IsAlias() { continue } - typeName := c.objectName(o) + typeName := funcCtx.objectName(o) + + if named, ok := o.Type().(*types.Named); ok && typeparams.NamedHasTypeParam(named) { + return nil, scanner.Error{ + Pos: fileSet.Position(o.Pos()), + Msg: fmt.Sprintf("type %s: type parameters are not supported by GopherJS: https://github.com/gopherjs/gopherjs/issues/1013", o.Name()), + } + } + d := Decl{ Vars: []string{typeName}, DceObjectFilter: o.Name(), } d.DceDeps = collectDependencies(func() { - d.DeclCode = c.CatchOutput(0, func() { - typeName := c.objectName(o) + d.DeclCode = funcCtx.CatchOutput(0, func() { + typeName := funcCtx.objectName(o) lhs := typeName if isPkgLevel(o) { lhs += " = $pkg." + encodeIdent(o.Name()) @@ -461,7 +519,7 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor } constructor = fmt.Sprintf("function(%s) {\n\t\tthis.$val = this;\n\t\tif (arguments.length === 0) {\n", strings.Join(params, ", ")) for i := 0; i < t.NumFields(); i++ { - constructor += fmt.Sprintf("\t\t\tthis.%s = %s;\n", fieldName(t, i), c.translateExpr(c.zeroValue(t.Field(i).Type())).String()) + constructor += fmt.Sprintf("\t\t\tthis.%s = %s;\n", fieldName(t, i), funcCtx.translateExpr(funcCtx.zeroValue(t.Field(i).Type())).String()) } constructor += "\t\t\treturn;\n\t\t}\n" for i := 0; i < t.NumFields(); i++ { @@ -471,9 +529,16 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor case *types.Basic, *types.Array, *types.Slice, *types.Chan, *types.Signature, *types.Interface, *types.Pointer, *types.Map: size = sizes32.Sizeof(t) } - c.Printf(`%s = $newType(%d, %s, "%s.%s", %t, "%s", %t, %s);`, lhs, size, typeKind(o.Type()), o.Pkg().Name(), o.Name(), o.Name() != "", o.Pkg().Path(), o.Exported(), constructor) + if tPointer, ok := o.Type().Underlying().(*types.Pointer); ok { + if _, ok := tPointer.Elem().Underlying().(*types.Array); ok { + // Array pointers have non-default constructors to support wrapping + // of the native objects. + constructor = "$arrayPtrCtor()" + } + } + funcCtx.Printf(`%s = $newType(%d, %s, "%s.%s", %t, "%s", %t, %s);`, lhs, size, typeKind(o.Type()), o.Pkg().Name(), o.Name(), o.Name() != "", o.Pkg().Path(), o.Exported(), constructor) }) - d.MethodListCode = c.CatchOutput(0, func() { + d.MethodListCode = funcCtx.CatchOutput(0, func() { named := o.Type().(*types.Named) if _, ok := named.Underlying().(*types.Interface); ok { return @@ -491,7 +556,7 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor pkgPath = method.Pkg().Path() } t := method.Type().(*types.Signature) - entry := fmt.Sprintf(`{prop: "%s", name: %s, pkg: "%s", typ: $funcType(%s)}`, name, encodeString(method.Name()), pkgPath, c.initArgs(t)) + entry := fmt.Sprintf(`{prop: "%s", name: %s, pkg: "%s", typ: $funcType(%s)}`, name, encodeString(method.Name()), pkgPath, funcCtx.initArgs(t)) if _, isPtr := t.Recv().Type().(*types.Pointer); isPtr { ptrMethods = append(ptrMethods, entry) continue @@ -499,16 +564,16 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor methods = append(methods, entry) } if len(methods) > 0 { - c.Printf("%s.methods = [%s];", c.typeName(named), strings.Join(methods, ", ")) + funcCtx.Printf("%s.methods = [%s];", funcCtx.typeName(named), strings.Join(methods, ", ")) } if len(ptrMethods) > 0 { - c.Printf("%s.methods = [%s];", c.typeName(types.NewPointer(named)), strings.Join(ptrMethods, ", ")) + funcCtx.Printf("%s.methods = [%s];", funcCtx.typeName(types.NewPointer(named)), strings.Join(ptrMethods, ", ")) } }) switch t := o.Type().Underlying().(type) { case *types.Array, *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Slice, *types.Signature, *types.Struct: - d.TypeInitCode = c.CatchOutput(0, func() { - c.Printf("%s.init(%s);", c.objectName(o), c.initArgs(t)) + d.TypeInitCode = funcCtx.CatchOutput(0, func() { + funcCtx.Printf("%s.init(%s);", funcCtx.objectName(o), funcCtx.initArgs(t)) }) } }) @@ -516,13 +581,13 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor } // anonymous types - for _, t := range c.p.anonTypes { + for _, t := range funcCtx.pkgCtx.anonTypes { d := Decl{ Vars: []string{t.Name()}, DceObjectFilter: t.Name(), } d.DceDeps = collectDependencies(func() { - d.DeclCode = []byte(fmt.Sprintf("\t%s = $%sType(%s);\n", t.Name(), strings.ToLower(typeKind(t.Type())[5:]), c.initArgs(t.Type()))) + d.DeclCode = []byte(fmt.Sprintf("\t%s = $%sType(%s);\n", t.Name(), strings.ToLower(typeKind(t.Type())[5:]), funcCtx.initArgs(t.Type()))) }) typeDecls = append(typeDecls, &d) } @@ -536,8 +601,8 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor allDecls = append(allDecls, d) } - if len(c.p.errList) != 0 { - return nil, c.p.errList + if len(funcCtx.pkgCtx.errList) != 0 { + return nil, funcCtx.pkgCtx.errList } return &Archive{ @@ -548,15 +613,17 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor Declarations: allDecls, FileSet: encodedFileSet.Bytes(), Minified: minify, + GoLinknames: goLinknames, + BuildTime: time.Now(), }, nil } -func (c *funcContext) initArgs(ty types.Type) string { +func (fc *funcContext) initArgs(ty types.Type) string { switch t := ty.(type) { case *types.Array: - return fmt.Sprintf("%s, %d", c.typeName(t.Elem()), t.Len()) + return fmt.Sprintf("%s, %d", fc.typeName(t.Elem()), t.Len()) case *types.Chan: - return fmt.Sprintf("%s, %t, %t", c.typeName(t.Elem()), t.Dir()&types.SendOnly != 0, t.Dir()&types.RecvOnly != 0) + return fmt.Sprintf("%s, %t, %t", fc.typeName(t.Elem()), t.Dir()&types.SendOnly != 0, t.Dir()&types.RecvOnly != 0) case *types.Interface: methods := make([]string, t.NumMethods()) for i := range methods { @@ -565,23 +632,23 @@ func (c *funcContext) initArgs(ty types.Type) string { if !method.Exported() { pkgPath = method.Pkg().Path() } - methods[i] = fmt.Sprintf(`{prop: "%s", name: "%s", pkg: "%s", typ: $funcType(%s)}`, method.Name(), method.Name(), pkgPath, c.initArgs(method.Type())) + methods[i] = fmt.Sprintf(`{prop: "%s", name: "%s", pkg: "%s", typ: $funcType(%s)}`, method.Name(), method.Name(), pkgPath, fc.initArgs(method.Type())) } return fmt.Sprintf("[%s]", strings.Join(methods, ", ")) case *types.Map: - return fmt.Sprintf("%s, %s", c.typeName(t.Key()), c.typeName(t.Elem())) + return fmt.Sprintf("%s, %s", fc.typeName(t.Key()), fc.typeName(t.Elem())) case *types.Pointer: - return fmt.Sprintf("%s", c.typeName(t.Elem())) + return fmt.Sprintf("%s", fc.typeName(t.Elem())) case *types.Slice: - return fmt.Sprintf("%s", c.typeName(t.Elem())) + return fmt.Sprintf("%s", fc.typeName(t.Elem())) case *types.Signature: params := make([]string, t.Params().Len()) for i := range params { - params[i] = c.typeName(t.Params().At(i).Type()) + params[i] = fc.typeName(t.Params().At(i).Type()) } results := make([]string, t.Results().Len()) for i := range results { - results[i] = c.typeName(t.Results().At(i).Type()) + results[i] = fc.typeName(t.Results().At(i).Type()) } return fmt.Sprintf("[%s], [%s], %t", strings.Join(params, ", "), strings.Join(results, ", "), t.Variadic()) case *types.Struct: @@ -592,16 +659,17 @@ func (c *funcContext) initArgs(ty types.Type) string { if !field.Exported() { pkgPath = field.Pkg().Path() } - fields[i] = fmt.Sprintf(`{prop: "%s", name: %s, embedded: %t, exported: %t, typ: %s, tag: %s}`, fieldName(t, i), encodeString(field.Name()), field.Anonymous(), field.Exported(), c.typeName(field.Type()), encodeString(t.Tag(i))) + fields[i] = fmt.Sprintf(`{prop: "%s", name: %s, embedded: %t, exported: %t, typ: %s, tag: %s}`, fieldName(t, i), encodeString(field.Name()), field.Anonymous(), field.Exported(), fc.typeName(field.Type()), encodeString(t.Tag(i))) } return fmt.Sprintf(`"%s", [%s]`, pkgPath, strings.Join(fields, ", ")) default: - panic("invalid type") + err := bailout(fmt.Errorf("%v has unexpected type %T", ty, ty)) + panic(err) } } -func (c *funcContext) translateToplevelFunction(fun *ast.FuncDecl, info *analysis.FuncInfo) []byte { - o := c.p.Defs[fun.Name].(*types.Func) +func (fc *funcContext) translateToplevelFunction(fun *ast.FuncDecl, info *analysis.FuncInfo) []byte { + o := fc.pkgCtx.Defs[fun.Name].(*types.Func) sig := o.Type().(*types.Signature) var recv *ast.Ident if fun.Recv != nil && fun.Recv.List[0].Names != nil { @@ -614,7 +682,7 @@ func (c *funcContext) translateToplevelFunction(fun *ast.FuncDecl, info *analysi return []byte(fmt.Sprintf("\t%s = function() {\n\t\t$throwRuntimeError(\"native function not implemented: %s\");\n\t};\n", funcRef, o.FullName())) } - params, fun := translateFunction(fun.Type, recv, fun.Body, c, sig, info, funcRef) + params, fun := translateFunction(fun.Type, recv, fun.Body, fc, sig, info, funcRef) joinedParams = strings.Join(params, ", ") return []byte(fmt.Sprintf("\t%s = %s;\n", funcRef, fun)) } @@ -622,7 +690,7 @@ func (c *funcContext) translateToplevelFunction(fun *ast.FuncDecl, info *analysi code := bytes.NewBuffer(nil) if fun.Recv == nil { - funcRef := c.objectName(o) + funcRef := fc.objectName(o) code.Write(primaryFunction(funcRef)) if fun.Name.IsExported() { fmt.Fprintf(code, "\t$pkg.%s = %s;\n", encodeIdent(fun.Name.Name), funcRef) @@ -636,7 +704,7 @@ func (c *funcContext) translateToplevelFunction(fun *ast.FuncDecl, info *analysi if isPointer { namedRecvType = ptr.Elem().(*types.Named) } - typeName := c.objectName(namedRecvType.Obj()) + typeName := fc.objectName(namedRecvType.Obj()) funName := fun.Name.Name if reservedKeywords[funName] { funName += "$" @@ -673,7 +741,7 @@ func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, c := &funcContext{ FuncInfo: info, - p: outerContext.p, + pkgCtx: outerContext.pkgCtx, parent: outerContext, sig: sig, allVars: make(map[string]int, len(outerContext.allVars)), @@ -685,7 +753,7 @@ func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, for k, v := range outerContext.allVars { c.allVars[k] = v } - prevEV := c.p.escapingVars + prevEV := c.pkgCtx.escapingVars var params []string for _, param := range typ.Params.List { @@ -698,13 +766,13 @@ func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, params = append(params, c.newVariable("param")) continue } - params = append(params, c.objectName(c.p.Defs[ident])) + params = append(params, c.objectName(c.pkgCtx.Defs[ident])) } } bodyOutput := string(c.CatchOutput(1, func() { if len(c.Blocking) != 0 { - c.p.Scopes[body] = c.p.Scopes[typ] + c.pkgCtx.Scopes[body] = c.pkgCtx.Scopes[typ] c.handleEscapingVars(body) } @@ -714,21 +782,21 @@ func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, result := c.sig.Results().At(i) c.Printf("%s = %s;", c.objectName(result), c.translateExpr(c.zeroValue(result.Type())).String()) id := ast.NewIdent("") - c.p.Uses[id] = result + c.pkgCtx.Uses[id] = result c.resultNames[i] = c.setType(id, result.Type()) } } if recv != nil && !isBlank(recv) { this := "this" - if isWrapped(c.p.TypeOf(recv)) { - this = "this.$val" + if isWrapped(c.pkgCtx.TypeOf(recv)) { + this = "this.$val" // Unwrap receiver value. } c.Printf("%s = %s;", c.translateExpr(recv), this) } c.translateStmtList(body.List) - if len(c.Flattened) != 0 && !endsWithReturn(body.List) { + if len(c.Flattened) != 0 && !astutil.EndsWithReturn(body.List) { c.translateStmt(&ast.ReturnStmt{}, nil) } })) @@ -739,7 +807,7 @@ func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, if len(c.Flattened) != 0 { c.localVars = append(c.localVars, "$s") - prefix = prefix + " $s = 0;" + prefix = prefix + " $s = $s || 0;" } if c.HasDefer { @@ -750,19 +818,29 @@ func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, } } + localVarDefs := "" // Function-local var declaration at the top. + if len(c.Blocking) != 0 { - c.localVars = append(c.localVars, "$r") if funcRef == "" { funcRef = "$b" functionName = " $b" } - var stores, loads string - for _, v := range c.localVars { - loads += fmt.Sprintf("%s = $f.%s; ", v, v) - stores += fmt.Sprintf("$f.%s = %s; ", v, v) - } - prefix = prefix + " var $f, $c = false; if (this !== undefined && this.$blk !== undefined) { $f = this; $c = true; " + loads + "}" - suffix = " if ($f === undefined) { $f = { $blk: " + funcRef + " }; } " + stores + "return $f;" + suffix + + localVars := append([]string{}, c.localVars...) + // There are several special variables involved in handling blocking functions: + // $r is sometimes used as a temporary variable to store blocking call result. + // $c indicates that a function is being resumed after a blocking call when set to true. + // $f is an object used to save and restore function context for blocking calls. + localVars = append(localVars, "$r") + // If a blocking function is being resumed, initialize local variables from the saved context. + localVarDefs = fmt.Sprintf("var {%s, $c} = $restore(this, {%s});\n", strings.Join(localVars, ", "), strings.Join(params, ", ")) + // If the function gets blocked, save local variables for future. + saveContext := fmt.Sprintf("var $f = {$blk: "+funcRef+", $c: true, $r, %s};", strings.Join(c.localVars, ", ")) + + suffix = " " + saveContext + "return $f;" + suffix + } else if len(c.localVars) > 0 { + // Non-blocking functions simply declare local variables with no need for restore support. + localVarDefs = fmt.Sprintf("var %s;\n", strings.Join(c.localVars, ", ")) } if c.HasDefer { @@ -790,20 +868,20 @@ func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, } if c.HasDefer { - prefix = prefix + " $deferred = []; $deferred.index = $curGoroutine.deferStack.length; $curGoroutine.deferStack.push($deferred);" + prefix = prefix + " $deferred = []; $curGoroutine.deferStack.push($deferred);" } if prefix != "" { - bodyOutput = strings.Repeat("\t", c.p.indentation+1) + "/* */" + prefix + "\n" + bodyOutput + bodyOutput = strings.Repeat("\t", c.pkgCtx.indentation+1) + "/* */" + prefix + "\n" + bodyOutput } if suffix != "" { - bodyOutput = bodyOutput + strings.Repeat("\t", c.p.indentation+1) + "/* */" + suffix + "\n" + bodyOutput = bodyOutput + strings.Repeat("\t", c.pkgCtx.indentation+1) + "/* */" + suffix + "\n" } - if len(c.localVars) != 0 { - bodyOutput = fmt.Sprintf("%svar %s;\n", strings.Repeat("\t", c.p.indentation+1), strings.Join(c.localVars, ", ")) + bodyOutput + if localVarDefs != "" { + bodyOutput = strings.Repeat("\t", c.pkgCtx.indentation+1) + localVarDefs + bodyOutput } - c.p.escapingVars = prevEV + c.pkgCtx.escapingVars = prevEV - return params, fmt.Sprintf("function%s(%s) {\n%s%s}", functionName, strings.Join(params, ", "), bodyOutput, strings.Repeat("\t", c.p.indentation)) + return params, fmt.Sprintf("function%s(%s) {\n%s%s}", functionName, strings.Join(params, ", "), bodyOutput, strings.Repeat("\t", c.pkgCtx.indentation)) } diff --git a/compiler/prelude/genmin.go b/compiler/prelude/genmin.go index 739dbf21..6e46e058 100644 --- a/compiler/prelude/genmin.go +++ b/compiler/prelude/genmin.go @@ -1,3 +1,4 @@ +//go:build ignore // +build ignore package main diff --git a/compiler/prelude/goroutines.go b/compiler/prelude/goroutines.go index d9780b65..6478aba7 100644 --- a/compiler/prelude/goroutines.go +++ b/compiler/prelude/goroutines.go @@ -12,18 +12,16 @@ var $getStackDepth = function() { var $panicStackDepth = null, $panicValue; var $callDeferred = function(deferred, jsErr, fromPanic) { - if (!fromPanic && deferred !== null && deferred.index >= $curGoroutine.deferStack.length) { + if (!fromPanic && deferred !== null && $curGoroutine.deferStack.indexOf(deferred) == -1) { throw jsErr; } if (jsErr !== null) { var newErr = null; try { - $curGoroutine.deferStack.push(deferred); $panic(new $jsErrorPtr(jsErr)); } catch (err) { newErr = err; } - $curGoroutine.deferStack.pop(); $callDeferred(deferred, newErr); return; } @@ -83,9 +81,25 @@ var $callDeferred = function(deferred, jsErr, fromPanic) { } if (localPanicValue !== undefined && $panicStackDepth === null) { - throw null; /* error was recovered */ + /* error was recovered */ + if (fromPanic) { + throw null; + } + return; } } + } catch(e) { + // Deferred function threw a JavaScript exception or tries to unwind stack + // to the point where a panic was handled. + if (fromPanic) { + // Re-throw the exception to reach deferral execution call at the end + // of the function. + throw e; + } + // We are at the end of the function, handle the error or re-throw to + // continue unwinding if necessary, or simply stop unwinding if we got far + // enough. + $callDeferred(deferred, e, fromPanic); } finally { if (localPanicValue !== undefined) { if ($panicStackDepth !== null) { @@ -112,7 +126,7 @@ var $recover = function() { var $throw = function(err) { throw err; }; var $noGoroutine = { asleep: false, exit: false, deferStack: [], panicStack: [] }; -var $curGoroutine = $noGoroutine, $totalGoroutines = 0, $awakeGoroutines = 0, $checkForDeadlock = true; +var $curGoroutine = $noGoroutine, $totalGoroutines = 0, $awakeGoroutines = 0, $checkForDeadlock = true, $exportedFunctions = 0; var $mainFinished = false; var $go = function(fun, args) { $totalGoroutines++; @@ -139,7 +153,7 @@ var $go = function(fun, args) { } if ($goroutine.asleep) { $awakeGoroutines--; - if (!$mainFinished && $awakeGoroutines === 0 && $checkForDeadlock) { + if (!$mainFinished && $awakeGoroutines === 0 && $checkForDeadlock && $exportedFunctions === 0) { console.error("fatal error: all goroutines are asleep - deadlock!"); if ($global.process !== undefined) { $global.process.exit(2); @@ -157,14 +171,28 @@ var $go = function(fun, args) { var $scheduled = []; var $runScheduled = function() { + // For nested setTimeout calls browsers enforce 4ms minimum delay. We minimize + // the effect of this penalty by queueing the timer preemptively before we run + // the goroutines, and later cancelling it if it turns out unneeded. See: + // https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#nested_timeouts + var nextRun = setTimeout($runScheduled); try { + var start = Date.now(); var r; while ((r = $scheduled.shift()) !== undefined) { r(); + // We need to interrupt this loop in order to allow the event loop to + // process timers, IO, etc. However, invoking scheduling through + // setTimeout is ~1000 times more expensive, so we amortize this cost by + // looping until the 4ms minimal delay has elapsed (assuming there are + // scheduled goroutines to run), and then yield to the event loop. + var elapsed = Date.now() - start; + if (elapsed > 4 || elapsed < 0) { break; } } } finally { - if ($scheduled.length > 0) { - setTimeout($runScheduled, 0); + if ($scheduled.length == 0) { + // Cancel scheduling pass if there's nothing to run. + clearTimeout(nextRun); } } }; @@ -195,6 +223,13 @@ var $block = function() { $curGoroutine.asleep = true; }; +var $restore = function(context, params) { + if (context !== undefined && context.$blk !== undefined) { + return context; + } + return params; +} + var $send = function(chan, value) { if (chan.$closed) { $throwRuntimeError("send on closed channel"); diff --git a/compiler/prelude/jsmapping.go b/compiler/prelude/jsmapping.go index dc29cba6..075f6681 100644 --- a/compiler/prelude/jsmapping.go +++ b/compiler/prelude/jsmapping.go @@ -23,7 +23,7 @@ var $needsExternalization = function(t) { } }; -var $externalize = function(v, t) { +var $externalize = function(v, t, makeWrapper) { if (t === $jsObjectPtr) { return v; } @@ -46,11 +46,11 @@ var $externalize = function(v, t) { return $flatten64(v); case $kindArray: if ($needsExternalization(t.elem)) { - return $mapArray(v, function(e) { return $externalize(e, t.elem); }); + return $mapArray(v, function(e) { return $externalize(e, t.elem, makeWrapper); }); } return v; case $kindFunc: - return $externalizeFunction(v, t, false); + return $externalizeFunction(v, t, false, makeWrapper); case $kindInterface: if (v === $ifaceNil) { return null; @@ -58,25 +58,25 @@ var $externalize = function(v, t) { if (v.constructor === $jsObjectPtr) { return v.$val.object; } - return $externalize(v.$val, v.constructor); + return $externalize(v.$val, v.constructor, makeWrapper); case $kindMap: var m = {}; - var keys = $keys(v); + var keys = Array.from(v.keys()); for (var i = 0; i < keys.length; i++) { - var entry = v[keys[i]]; - m[$externalize(entry.k, t.key)] = $externalize(entry.v, t.elem); + var entry = v.get(keys[i]); + m[$externalize(entry.k, t.key, makeWrapper)] = $externalize(entry.v, t.elem, makeWrapper); } return m; case $kindPtr: if (v === t.nil) { return null; } - return $externalize(v.$get(), t.elem); + return $externalize(v.$get(), t.elem, makeWrapper); case $kindSlice: if ($needsExternalization(t.elem)) { - return $mapArray($sliceToArray(v), function(e) { return $externalize(e, t.elem); }); + return $mapArray($sliceToNativeArray(v), function(e) { return $externalize(e, t.elem, makeWrapper); }); } - return $sliceToArray(v); + return $sliceToNativeArray(v); case $kindString: if ($isASCII(v)) { return v; @@ -126,20 +126,24 @@ var $externalize = function(v, t) { return o; } + if (makeWrapper !== undefined) { + return makeWrapper(v); + } + o = {}; for (var i = 0; i < t.fields.length; i++) { var f = t.fields[i]; if (!f.exported) { continue; } - o[f.name] = $externalize(v[f.prop], f.typ); + o[f.name] = $externalize(v[f.prop], f.typ, makeWrapper); } return o; } $throwRuntimeError("cannot externalize " + t.string); }; -var $externalizeFunction = function(v, t, passThis) { +var $externalizeFunction = function(v, t, passThis, makeWrapper) { if (v === $throwNilPointerError) { return null; } @@ -151,22 +155,22 @@ var $externalizeFunction = function(v, t, passThis) { if (t.variadic && i === t.params.length - 1) { var vt = t.params[i].elem, varargs = []; for (var j = i; j < arguments.length; j++) { - varargs.push($internalize(arguments[j], vt)); + varargs.push($internalize(arguments[j], vt, makeWrapper)); } args.push(new (t.params[i])(varargs)); break; } - args.push($internalize(arguments[i], t.params[i])); + args.push($internalize(arguments[i], t.params[i], makeWrapper)); } var result = v.apply(passThis ? this : undefined, args); switch (t.results.length) { case 0: return; case 1: - return $externalize(result, t.results[0]); + return $externalize($copyIfRequired(result, t.results[0]), t.results[0], makeWrapper); default: for (var i = 0; i < t.results.length; i++) { - result[i] = $externalize(result[i], t.results[i]); + result[i] = $externalize($copyIfRequired(result[i], t.results[i]), t.results[i], makeWrapper); } return result; } @@ -175,7 +179,7 @@ var $externalizeFunction = function(v, t, passThis) { return v.$externalizeWrapper; }; -var $internalize = function(v, t, recv) { +var $internalize = function(v, t, recv, seen, makeWrapper) { if (t === $jsObjectPtr) { return v; } @@ -192,6 +196,13 @@ var $internalize = function(v, t, recv) { } return timePkg.Unix(new $Int64(0, 0), new $Int64(0, v.getTime() * 1000000)); } + + // Cache for values we've already internalized in order to deal with circular + // references. + if (seen === undefined) { seen = new Map(); } + if (!seen.has(t)) { seen.set(t, new Map()); } + if (seen.get(t).has(v)) { return seen.get(t).get(v); } + switch (t.kind) { case $kindBool: return !!v; @@ -222,7 +233,7 @@ var $internalize = function(v, t, recv) { if (v.length !== t.len) { $throwRuntimeError("got array with wrong size from JavaScript native"); } - return $mapArray(v, function(e) { return $internalize(e, t.elem); }); + return $mapArray(v, function(e) { return $internalize(e, t.elem, makeWrapper); }); case $kindFunc: return function() { var args = []; @@ -230,21 +241,21 @@ var $internalize = function(v, t, recv) { if (t.variadic && i === t.params.length - 1) { var vt = t.params[i].elem, varargs = arguments[i]; for (var j = 0; j < varargs.$length; j++) { - args.push($externalize(varargs.$array[varargs.$offset + j], vt)); + args.push($externalize(varargs.$array[varargs.$offset + j], vt, makeWrapper)); } break; } - args.push($externalize(arguments[i], t.params[i])); + args.push($externalize(arguments[i], t.params[i], makeWrapper)); } var result = v.apply(recv, args); switch (t.results.length) { case 0: return; case 1: - return $internalize(result, t.results[0]); + return $internalize(result, t.results[0], makeWrapper); default: for (var i = 0; i < t.results.length; i++) { - result[i] = $internalize(result[i], t.results[i]); + result[i] = $internalize(result[i], t.results[i], makeWrapper); } return result; } @@ -277,7 +288,7 @@ var $internalize = function(v, t, recv) { case Float64Array: return new ($sliceType($Float64))(v); case Array: - return $internalize(v, $sliceType($emptyInterface)); + return $internalize(v, $sliceType($emptyInterface), makeWrapper); case Boolean: return new $Bool(!!v); case Date: @@ -285,35 +296,36 @@ var $internalize = function(v, t, recv) { /* time package is not present, internalize as &js.Object{Date} so it can be externalized into original Date. */ return new $jsObjectPtr(v); } - return new timePkg.Time($internalize(v, timePkg.Time)); - case Function: + return new timePkg.Time($internalize(v, timePkg.Time, makeWrapper)); + case (function () { }).constructor: // is usually Function, but in Chrome extensions it is something else var funcType = $funcType([$sliceType($emptyInterface)], [$jsObjectPtr], true); - return new funcType($internalize(v, funcType)); + return new funcType($internalize(v, funcType, makeWrapper)); case Number: return new $Float64(parseFloat(v)); case String: - return new $String($internalize(v, $String)); + return new $String($internalize(v, $String, makeWrapper)); default: if ($global.Node && v instanceof $global.Node) { return new $jsObjectPtr(v); } var mapType = $mapType($String, $emptyInterface); - return new mapType($internalize(v, mapType)); + return new mapType($internalize(v, mapType, recv, seen, makeWrapper)); } case $kindMap: - var m = {}; + var m = new Map(); + seen.get(t).set(v, m); var keys = $keys(v); for (var i = 0; i < keys.length; i++) { - var k = $internalize(keys[i], t.key); - m[t.key.keyFor(k)] = { k: k, v: $internalize(v[keys[i]], t.elem) }; + var k = $internalize(keys[i], t.key, recv, seen, makeWrapper); + m.set(t.key.keyFor(k), { k: k, v: $internalize(v[keys[i]], t.elem, recv, seen, makeWrapper) }); } return m; case $kindPtr: if (t.elem.kind === $kindStruct) { - return $internalize(v, t.elem); + return $internalize(v, t.elem, makeWrapper); } case $kindSlice: - return new t($mapArray(v, function(e) { return $internalize(e, t.elem); })); + return new t($mapArray(v, function(e) { return $internalize(e, t.elem, makeWrapper); })); case $kindString: v = String(v); if ($isASCII(v)) { @@ -367,6 +379,20 @@ var $internalize = function(v, t, recv) { $throwRuntimeError("cannot internalize " + t.string); }; +var $copyIfRequired = function(v, typ) { + // interface values + if (v && v.constructor && v.constructor.copy) { + return new v.constructor($clone(v.$val, v.constructor)) + } + // array and struct values + if (typ.copy) { + var clone = typ.zero(); + typ.copy(clone, v); + return clone; + } + return v; +} + /* $isASCII reports whether string s contains only ASCII characters. */ var $isASCII = function(s) { for (var i = 0; i < s.length; i++) { diff --git a/compiler/prelude/numeric.go b/compiler/prelude/numeric.go index 063d09f4..76bc30a2 100644 --- a/compiler/prelude/numeric.go +++ b/compiler/prelude/numeric.go @@ -80,23 +80,43 @@ var $shiftRightUint64 = function(x, y) { }; var $mul64 = function(x, y) { - var high = 0, low = 0; - if ((y.$low & 1) !== 0) { - high = x.$high; - low = x.$low; - } - for (var i = 1; i < 32; i++) { - if ((y.$low & 1<>> (32 - i); - low += (x.$low << i) >>> 0; - } - } - for (var i = 0; i < 32; i++) { - if ((y.$high & 1<>> 16; + var x32 = x.$high & 0xFFFF; + var x16 = x.$low >>> 16; + var x00 = x.$low & 0xFFFF; + + var y48 = y.$high >>> 16; + var y32 = y.$high & 0xFFFF; + var y16 = y.$low >>> 16; + var y00 = y.$low & 0xFFFF; + + var z48 = 0, z32 = 0, z16 = 0, z00 = 0; + z00 += x00 * y00; + z16 += z00 >>> 16; + z00 &= 0xFFFF; + z16 += x16 * y00; + z32 += z16 >>> 16; + z16 &= 0xFFFF; + z16 += x00 * y16; + z32 += z16 >>> 16; + z16 &= 0xFFFF; + z32 += x32 * y00; + z48 += z32 >>> 16; + z32 &= 0xFFFF; + z32 += x16 * y16; + z48 += z32 >>> 16; + z32 &= 0xFFFF; + z32 += x00 * y32; + z48 += z32 >>> 16; + z32 &= 0xFFFF; + z48 += x48 * y00 + x32 * y16 + x16 * y32 + x00 * y48; + z48 &= 0xFFFF; + + var hi = ((z48 << 16) | z32) >>> 0; + var lo = ((z16 << 16) | z00) >>> 0; + + var r = new x.constructor(hi, lo); + return r; }; var $div64 = function(x, y, returnRemainder) { diff --git a/compiler/prelude/prelude.go b/compiler/prelude/prelude.go index c27601e8..fad69e7b 100644 --- a/compiler/prelude/prelude.go +++ b/compiler/prelude/prelude.go @@ -7,6 +7,7 @@ const Prelude = prelude + numeric + types + goroutines + jsmapping const prelude = `Error.stackTraceLimit = Infinity; +var $NaN = NaN; var $global, $module; if (typeof window !== "undefined") { /* web page */ $global = window; @@ -26,6 +27,41 @@ if (typeof module !== "undefined") { $module = module; } +if (!$global.fs && $global.require) { + try { + var fs = $global.require('fs'); + if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) { + $global.fs = fs; + } + } catch(e) { /* Ignore if the module couldn't be loaded. */ } +} + +if (!$global.fs) { + var outputBuf = ""; + var decoder = new TextDecoder("utf-8"); + $global.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + writeSync: function writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + var nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substr(0, nl)); + outputBuf = outputBuf.substr(nl + 1); + } + return buf.length; + }, + write: function write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()); + return; + } + var n = this.writeSync(fd, buf); + callback(null, n); + } + }; +} + +var $linknames = {} // Collection of functions referenced by a go:linkname directive. var $packages = {}, $idCounter = 0; var $keys = function(m) { return m ? Object.keys(m) : []; }; var $flushConsole = function() {}; @@ -34,6 +70,27 @@ var $throwNilPointerError = function() { $throwRuntimeError("invalid memory addr var $call = function(fn, rcvr, args) { return fn.apply(rcvr, args); }; var $makeFunc = function(fn) { return function() { return $externalize(fn(this, new ($sliceType($jsObjectPtr))($global.Array.prototype.slice.call(arguments, []))), $emptyInterface); }; }; var $unused = function(v) {}; +var $print = console.log; +// Under Node we can emulate print() more closely by avoiding a newline. +if (($global.process !== undefined) && $global.require) { + try { + var util = $global.require('util'); + $print = function() { $global.process.stderr.write(util.format.apply(this, arguments)); }; + } catch (e) { + // Failed to require util module, keep using console.log(). + } +} +var $println = console.log + +var $initAllLinknames = function() { + var names = $keys($packages); + for (var i = 0; i < names.length; i++) { + var f = $packages[names[i]]["$initLinknames"]; + if (typeof f == 'function') { + f(); + } + } +} var $mapArray = function(array, f) { var newArray = new array.constructor(array.length); @@ -43,6 +100,16 @@ var $mapArray = function(array, f) { return newArray; }; +// $mapIndex returns the value of the given key in m, or undefined if m is nil/undefined or not a map +var $mapIndex = function(m, key) { + return typeof m.get === "function" ? m.get(key) : undefined; +}; +// $mapDelete deletes the key and associated value from m. If m is nil/undefined or not a map, $mapDelete is a no-op +var $mapDelete = function(m, key) { + typeof m.delete === "function" && m.delete(key) +}; +// Returns a method bound to the receiver instance, safe to invoke as a +// standalone function. Bound function is cached for later reuse. var $methodVal = function(recv, name) { var vals = recv.$methodVals || {}; recv.$methodVals = vals; /* noop for primitives */ @@ -51,14 +118,7 @@ var $methodVal = function(recv, name) { return f; } var method = recv[name]; - f = function() { - $stackDepthOffset--; - try { - return method.apply(recv, arguments); - } finally { - $stackDepthOffset++; - } - }; + f = method.bind(recv); vals[name] = f; return f; }; @@ -124,13 +184,53 @@ var $substring = function(str, low, high) { return str.substring(low, high); }; -var $sliceToArray = function(slice) { +// Convert Go slice to an equivalent JS array type. +var $sliceToNativeArray = function(slice) { if (slice.$array.constructor !== Array) { return slice.$array.subarray(slice.$offset, slice.$offset + slice.$length); } return slice.$array.slice(slice.$offset, slice.$offset + slice.$length); }; +// Convert Go slice to a pointer to an underlying Go array. +// +// Note that an array pointer can be represented by an "unwrapped" native array +// type, and it will be wrapped back into its Go type when necessary. +var $sliceToGoArray = function(slice, arrayPtrType) { + var arrayType = arrayPtrType.elem; + if (arrayType !== undefined && slice.$length < arrayType.len) { + $throwRuntimeError("cannot convert slice with length " + slice.$length + " to pointer to array with length " + arrayType.len); + } + if (slice == slice.constructor.nil) { + return arrayPtrType.nil; // Nil slice converts to nil array pointer. + } + if (slice.$array.constructor !== Array) { + return slice.$array.subarray(slice.$offset, slice.$offset + arrayType.len); + } + if (slice.$offset == 0 && slice.$length == slice.$capacity && slice.$length == arrayType.len) { + return slice.$array; + } + if (arrayType.len == 0) { + return new arrayType([]); + } + + // Array.slice (unlike TypedArray.subarray) returns a copy of an array range, + // which is not sharing memory with the original one, which violates the spec + // for slice to array conversion. This is incompatible with the Go spec, in + // particular that the assignments to the array elements would be visible in + // the slice. Prefer to fail explicitly instead of creating subtle bugs. + $throwRuntimeError("gopherjs: non-numeric slice to underlying array conversion is not supported for subslices"); +}; + +// Convert between compatible slice types (e.g. native and names). +var $convertSliceType = function(slice, desiredType) { + if (slice == slice.constructor.nil) { + return desiredType.nil; // Preserve nil value. + } + + return $subslice(new desiredType(slice.$array), slice.$offset, slice.$offset + slice.$length); +} + var $decodeRune = function(str, pos) { var c0 = str.charCodeAt(pos); @@ -422,4 +522,46 @@ var $interfaceIsEqual = function(a, b) { } return $equal(a.$val, b.$val, a.constructor); }; + +var $unsafeMethodToFunction = function(typ, name, isPtr) { + if (isPtr) { + return function(r, ...args) { + var ptrType = $ptrType(typ); + if (r.constructor != ptrType) { + switch (typ.kind) { + case $kindStruct: + r = $pointerOfStructConversion(r, ptrType); + break; + case $kindArray: + r = new ptrType(r); + break; + default: + r = new ptrType(r.$get,r.$set,r.$target); + } + } + return r[name](...args); + } + } else { + return function(r, ...args) { + var ptrType = $ptrType(typ); + if (r.constructor != ptrType) { + switch (typ.kind) { + case $kindStruct: + r = $clone(r, typ); + break; + case $kindSlice: + r = $convertSliceType(r, typ); + break; + case $kindComplex64: + case $kindComplex128: + r = new typ(r.$real, r.$imag); + break; + default: + r = new typ(r); + } + } + return r[name](...args); + } + } +}; ` diff --git a/compiler/prelude/prelude_min.go b/compiler/prelude/prelude_min.go index 0918ffa0..9ca9f167 100644 --- a/compiler/prelude/prelude_min.go +++ b/compiler/prelude/prelude_min.go @@ -3,4 +3,4 @@ package prelude // Minified is an uglifyjs-minified version of Prelude. -const Minified = "var $global,$module;if(Error.stackTraceLimit=1/0,\"undefined\"!=typeof window?$global=window:\"undefined\"!=typeof self?$global=self:\"undefined\"!=typeof global?($global=global).require=require:$global=this,void 0===$global||void 0===$global.Array)throw new Error(\"no global object found\");\"undefined\"!=typeof module&&($module=module);var $throwRuntimeError,$packages={},$idCounter=0,$keys=function(e){return e?Object.keys(e):[]},$flushConsole=function(){},$throwNilPointerError=function(){$throwRuntimeError(\"invalid memory address or nil pointer dereference\")},$call=function(e,n,r){return e.apply(n,r)},$makeFunc=function(e){return function(){return $externalize(e(this,new($sliceType($jsObjectPtr))($global.Array.prototype.slice.call(arguments,[]))),$emptyInterface)}},$unused=function(e){},$mapArray=function(e,n){for(var r=new e.constructor(e.length),t=0;te.$capacity||t>e.$capacity)&&$throwRuntimeError(\"slice bounds out of range\"),e===e.constructor.nil)return e;var i=new e.constructor(e.$array);return i.$offset=e.$offset+n,i.$length=r-n,i.$capacity=t-n,i},$substring=function(e,n,r){return(n<0||re.length)&&$throwRuntimeError(\"slice bounds out of range\"),e.substring(n,r)},$sliceToArray=function(e){return e.$array.constructor!==Array?e.$array.subarray(e.$offset,e.$offset+e.$length):e.$array.slice(e.$offset,e.$offset+e.$length)},$decodeRune=function(e,n){var r=e.charCodeAt(n);if(r<128)return[r,1];if(r!=r||r<192)return[65533,1];var t=e.charCodeAt(n+1);if(t!=t||t<128||192<=t)return[65533,1];if(r<224)return(a=(31&r)<<6|63&t)<=127?[65533,1]:[a,2];var i=e.charCodeAt(n+2);if(i!=i||i<128||192<=i)return[65533,1];if(r<240)return(a=(15&r)<<12|(63&t)<<6|63&i)<=2047?[65533,1]:55296<=a&&a<=57343?[65533,1]:[a,3];var a,o=e.charCodeAt(n+3);return o!=o||o<128||192<=o?[65533,1]:r<248?(a=(7&r)<<18|(63&t)<<12|(63&i)<<6|63&o)<=65535||11141111114111||55296<=e&&e<=57343)&&(e=65533),e<=127?String.fromCharCode(e):e<=2047?String.fromCharCode(192|e>>6,128|63&e):e<=65535?String.fromCharCode(224|e>>12,128|e>>6&63,128|63&e):String.fromCharCode(240|e>>18,128|e>>12&63,128|e>>6&63,128|63&e)},$stringToBytes=function(e){for(var n=new Uint8Array(e.length),r=0;rt){for(var o=i-1;o>=0;o--)a.copy(e[r+o],n[t+o]);return}for(o=0;ot)for(o=i-1;o>=0;o--)e[r+o]=n[t+o];else for(o=0;o$)if(a=0,$=Math.max(o,e.$capacity<1024?2*e.$capacity:Math.floor(5*e.$capacity/4)),e.$array.constructor===Array){(i=e.$array.slice(e.$offset,e.$offset+e.$length)).length=$;for(var c=e.constructor.elem.zero,u=e.$length;u<$;u++)i[u]=c()}else(i=new e.$array.constructor($)).set(e.$array.subarray(e.$offset,e.$offset+e.$length));$copyArray(i,n,a+e.$length,r,t,e.constructor.elem);var l=new e.constructor(i);return l.$offset=a,l.$length=o,l.$capacity=$,l},$equal=function(e,n,r){if(r===$jsObjectPtr)return e===n;switch(r.kind){case $kindComplex64:case $kindComplex128:return e.$real===n.$real&&e.$imag===n.$imag;case $kindInt64:case $kindUint64:return e.$high===n.$high&&e.$low===n.$low;case $kindArray:if(e.length!==n.length)return!1;for(var t=0;t>>16&65535)*t+r*(n>>>16&65535)<<16>>>0)>>0},$floatKey=function(e){return e!=e?\"NaN$\"+ ++$idCounter:String(e)},$flatten64=function(e){return 4294967296*e.$high+e.$low},$shiftLeft64=function(e,n){return 0===n?e:n<32?new e.constructor(e.$high<>>32-n,e.$low<>>0):n<64?new e.constructor(e.$low<>n,(e.$low>>>n|e.$high<<32-n)>>>0):n<64?new e.constructor(e.$high>>31,e.$high>>n-32>>>0):e.$high<0?new e.constructor(-1,4294967295):new e.constructor(0,0)},$shiftRightUint64=function(e,n){return 0===n?e:n<32?new e.constructor(e.$high>>>n,(e.$low>>>n|e.$high<<32-n)>>>0):n<64?new e.constructor(0,e.$high>>>n-32):new e.constructor(0,0)},$mul64=function(e,n){var r=0,t=0;0!=(1&n.$low)&&(r=e.$high,t=e.$low);for(var i=1;i<32;i++)0!=(n.$low&1<>>32-i,t+=e.$low<>>0);for(i=0;i<32;i++)0!=(n.$high&1<$||a===$&&o>c);)$=($<<1|c>>>31)>>>0,c=c<<1>>>0,s++;for(var f=0;f<=s;f++)u=u<<1|l>>>31,l=l<<1>>>0,(a>$||a===$&&o>=c)&&(a-=$,(o-=c)<0&&(a--,o+=4294967296),4294967296===++l&&(u++,l=0)),c=(c>>>1|$<<31)>>>0,$>>>=1;return r?new e.constructor(a*i,o*i):new e.constructor(u*t,l*t)},$divComplex=function(e,n){var r=e.$real===1/0||e.$real===-1/0||e.$imag===1/0||e.$imag===-1/0,t=n.$real===1/0||n.$real===-1/0||n.$imag===1/0||n.$imag===-1/0,i=!r&&(e.$real!=e.$real||e.$imag!=e.$imag),a=!t&&(n.$real!=n.$real||n.$imag!=n.$imag);if(i||a)return new e.constructor(NaN,NaN);if(r&&!t)return new e.constructor(1/0,1/0);if(!r&&t)return new e.constructor(0,0);if(0===n.$real&&0===n.$imag)return 0===e.$real&&0===e.$imag?new e.constructor(NaN,NaN):new e.constructor(1/0,1/0);if(Math.abs(n.$real)<=Math.abs(n.$imag)){var o=n.$real/n.$imag,$=n.$real*o+n.$imag;return new e.constructor((e.$real*o+e.$imag)/$,(e.$imag*o-e.$real)/$)}o=n.$imag/n.$real,$=n.$imag*o+n.$real;return new e.constructor((e.$imag*o+e.$real)/$,(e.$imag-e.$real*o)/$)},$kindBool=1,$kindInt=2,$kindInt8=3,$kindInt16=4,$kindInt32=5,$kindInt64=6,$kindUint=7,$kindUint8=8,$kindUint16=9,$kindUint32=10,$kindUint64=11,$kindUintptr=12,$kindFloat32=13,$kindFloat64=14,$kindComplex64=15,$kindComplex128=16,$kindArray=17,$kindChan=18,$kindFunc=19,$kindInterface=20,$kindMap=21,$kindPtr=22,$kindSlice=23,$kindString=24,$kindStruct=25,$kindUnsafePointer=26,$methodSynthesizers=[],$addMethodSynthesizer=function(e){null!==$methodSynthesizers?$methodSynthesizers.push(e):e()},$synthesizeMethods=function(){$methodSynthesizers.forEach(function(e){e()}),$methodSynthesizers=null},$ifaceKeyFor=function(e){if(e===$ifaceNil)return\"nil\";var n=e.constructor;return n.string+\"$\"+n.keyFor(e.$val)},$identity=function(e){return e},$typeIDCounter=0,$idKey=function(e){return void 0===e.$id&&($idCounter++,e.$id=$idCounter),String(e.$id)},$newType=function(e,n,r,t,i,a,o){var $;switch(n){case $kindBool:case $kindInt:case $kindInt8:case $kindInt16:case $kindInt32:case $kindUint:case $kindUint8:case $kindUint16:case $kindUint32:case $kindUintptr:case $kindUnsafePointer:($=function(e){this.$val=e}).wrapped=!0,$.keyFor=$identity;break;case $kindString:($=function(e){this.$val=e}).wrapped=!0,$.keyFor=function(e){return\"$\"+e};break;case $kindFloat32:case $kindFloat64:($=function(e){this.$val=e}).wrapped=!0,$.keyFor=function(e){return $floatKey(e)};break;case $kindInt64:($=function(e,n){this.$high=e+Math.floor(Math.ceil(n)/4294967296)>>0,this.$low=n>>>0,this.$val=this}).keyFor=function(e){return e.$high+\"$\"+e.$low};break;case $kindUint64:($=function(e,n){this.$high=e+Math.floor(Math.ceil(n)/4294967296)>>>0,this.$low=n>>>0,this.$val=this}).keyFor=function(e){return e.$high+\"$\"+e.$low};break;case $kindComplex64:($=function(e,n){this.$real=$fround(e),this.$imag=$fround(n),this.$val=this}).keyFor=function(e){return e.$real+\"$\"+e.$imag};break;case $kindComplex128:($=function(e,n){this.$real=e,this.$imag=n,this.$val=this}).keyFor=function(e){return e.$real+\"$\"+e.$imag};break;case $kindArray:($=function(e){this.$val=e}).wrapped=!0,$.ptr=$newType(4,$kindPtr,\"*\"+r,!1,\"\",!1,function(e){this.$get=function(){return e},this.$set=function(e){$.copy(this,e)},this.$val=e}),$.init=function(e,n){$.elem=e,$.len=n,$.comparable=e.comparable,$.keyFor=function(n){return Array.prototype.join.call($mapArray(n,function(n){return String(e.keyFor(n)).replace(/\\\\/g,\"\\\\\\\\\").replace(/\\$/g,\"\\\\$\")}),\"$\")},$.copy=function(n,r){$copyArray(n,r,0,0,r.length,e)},$.ptr.init($),Object.defineProperty($.ptr.nil,\"nilCheck\",{get:$throwNilPointerError})};break;case $kindChan:($=function(e){this.$val=e}).wrapped=!0,$.keyFor=$idKey,$.init=function(e,n,r){$.elem=e,$.sendOnly=n,$.recvOnly=r};break;case $kindFunc:($=function(e){this.$val=e}).wrapped=!0,$.init=function(e,n,r){$.params=e,$.results=n,$.variadic=r,$.comparable=!1};break;case $kindInterface:($={implementedBy:{},missingMethodFor:{}}).keyFor=$ifaceKeyFor,$.init=function(e){$.methods=e,e.forEach(function(e){$ifaceNil[e.prop]=$throwNilPointerError})};break;case $kindMap:($=function(e){this.$val=e}).wrapped=!0,$.init=function(e,n){$.key=e,$.elem=n,$.comparable=!1};break;case $kindPtr:($=o||function(e,n,r){this.$get=e,this.$set=n,this.$target=r,this.$val=this}).keyFor=$idKey,$.init=function(e){$.elem=e,$.wrapped=e.kind===$kindArray,$.nil=new $($throwNilPointerError,$throwNilPointerError)};break;case $kindSlice:($=function(e){e.constructor!==$.nativeArray&&(e=new $.nativeArray(e)),this.$array=e,this.$offset=0,this.$length=e.length,this.$capacity=e.length,this.$val=this}).init=function(e){$.elem=e,$.comparable=!1,$.nativeArray=$nativeArray(e.kind),$.nil=new $([])};break;case $kindStruct:($=function(e){this.$val=e}).wrapped=!0,$.ptr=$newType(4,$kindPtr,\"*\"+r,!1,i,a,o),$.ptr.elem=$,$.ptr.prototype.$get=function(){return this},$.ptr.prototype.$set=function(e){$.copy(this,e)},$.init=function(e,n){$.pkgPath=e,$.fields=n,n.forEach(function(e){e.typ.comparable||($.comparable=!1)}),$.keyFor=function(e){var r=e.$val;return $mapArray(n,function(e){return String(e.typ.keyFor(r[e.prop])).replace(/\\\\/g,\"\\\\\\\\\").replace(/\\$/g,\"\\\\$\")}).join(\"$\")},$.copy=function(e,r){for(var t=0;t0;){var a=[],o=[];t.forEach(function(e){if(!i[e.typ.string])switch(i[e.typ.string]=!0,e.typ.named&&(o=o.concat(e.typ.methods),e.indirect&&(o=o.concat($ptrType(e.typ).methods))),e.typ.kind){case $kindStruct:e.typ.fields.forEach(function(n){if(n.embedded){var r=n.typ,t=r.kind===$kindPtr;a.push({typ:t?r.elem:r,indirect:e.indirect||t})}});break;case $kindInterface:o=o.concat(e.typ.methods)}}),o.forEach(function(e){void 0===n[e.name]&&(n[e.name]=e)}),t=a}return e.methodSetCache=[],Object.keys(n).sort().forEach(function(r){e.methodSetCache.push(n[r])}),e.methodSetCache},$Bool=$newType(1,$kindBool,\"bool\",!0,\"\",!1,null),$Int=$newType(4,$kindInt,\"int\",!0,\"\",!1,null),$Int8=$newType(1,$kindInt8,\"int8\",!0,\"\",!1,null),$Int16=$newType(2,$kindInt16,\"int16\",!0,\"\",!1,null),$Int32=$newType(4,$kindInt32,\"int32\",!0,\"\",!1,null),$Int64=$newType(8,$kindInt64,\"int64\",!0,\"\",!1,null),$Uint=$newType(4,$kindUint,\"uint\",!0,\"\",!1,null),$Uint8=$newType(1,$kindUint8,\"uint8\",!0,\"\",!1,null),$Uint16=$newType(2,$kindUint16,\"uint16\",!0,\"\",!1,null),$Uint32=$newType(4,$kindUint32,\"uint32\",!0,\"\",!1,null),$Uint64=$newType(8,$kindUint64,\"uint64\",!0,\"\",!1,null),$Uintptr=$newType(4,$kindUintptr,\"uintptr\",!0,\"\",!1,null),$Float32=$newType(4,$kindFloat32,\"float32\",!0,\"\",!1,null),$Float64=$newType(8,$kindFloat64,\"float64\",!0,\"\",!1,null),$Complex64=$newType(8,$kindComplex64,\"complex64\",!0,\"\",!1,null),$Complex128=$newType(16,$kindComplex128,\"complex128\",!0,\"\",!1,null),$String=$newType(8,$kindString,\"string\",!0,\"\",!1,null),$UnsafePointer=$newType(4,$kindUnsafePointer,\"unsafe.Pointer\",!0,\"\",!1,null),$nativeArray=function(e){switch(e){case $kindInt:return Int32Array;case $kindInt8:return Int8Array;case $kindInt16:return Int16Array;case $kindInt32:return Int32Array;case $kindUint:return Uint32Array;case $kindUint8:return Uint8Array;case $kindUint16:return Uint16Array;case $kindUint32:case $kindUintptr:return Uint32Array;case $kindFloat32:return Float32Array;case $kindFloat64:return Float64Array;default:return Array}},$toNativeArray=function(e,n){var r=$nativeArray(e);return r===Array?n:new r(n)},$arrayTypes={},$arrayType=function(e,n){var r=e.id+\"$\"+n,t=$arrayTypes[r];return void 0===t&&(t=$newType(12,$kindArray,\"[\"+n+\"]\"+e.string,!1,\"\",!1,null),$arrayTypes[r]=t,t.init(e,n)),t},$chanType=function(e,n,r){var t=(r?\"<-\":\"\")+\"chan\"+(n?\"<- \":\" \")+e.string,i=n?\"SendChan\":r?\"RecvChan\":\"Chan\",a=e[i];return void 0===a&&(a=$newType(4,$kindChan,t,!1,\"\",!1,null),e[i]=a,a.init(e,n,r)),a},$Chan=function(e,n){(n<0||n>2147483647)&&$throwRuntimeError(\"makechan: size out of range\"),this.$elem=e,this.$capacity=n,this.$buffer=[],this.$sendQueue=[],this.$recvQueue=[],this.$closed=!1},$chanNil=new $Chan(null,0);$chanNil.$sendQueue=$chanNil.$recvQueue={length:0,push:function(){},shift:function(){},indexOf:function(){return-1}};var $funcTypes={},$funcType=function(e,n,r){var t=$mapArray(e,function(e){return e.id}).join(\",\")+\"$\"+$mapArray(n,function(e){return e.id}).join(\",\")+\"$\"+r,i=$funcTypes[t];if(void 0===i){var a=$mapArray(e,function(e){return e.string});r&&(a[a.length-1]=\"...\"+a[a.length-1].substr(2));var o=\"func(\"+a.join(\", \")+\")\";1===n.length?o+=\" \"+n[0].string:n.length>1&&(o+=\" (\"+$mapArray(n,function(e){return e.string}).join(\", \")+\")\"),i=$newType(4,$kindFunc,o,!1,\"\",!1,null),$funcTypes[t]=i,i.init(e,n,r)}return i},$interfaceTypes={},$interfaceType=function(e){var n=$mapArray(e,function(e){return e.pkg+\",\"+e.name+\",\"+e.typ.id}).join(\"$\"),r=$interfaceTypes[n];if(void 0===r){var t=\"interface {}\";0!==e.length&&(t=\"interface { \"+$mapArray(e,function(e){return(\"\"!==e.pkg?e.pkg+\".\":\"\")+e.name+e.typ.string.substr(4)}).join(\"; \")+\" }\"),r=$newType(8,$kindInterface,t,!1,\"\",!1,null),$interfaceTypes[n]=r,r.init(e)}return r},$emptyInterface=$interfaceType([]),$ifaceNil={},$error=$newType(8,$kindInterface,\"error\",!0,\"\",!1,null);$error.init([{prop:\"Error\",name:\"Error\",pkg:\"\",typ:$funcType([],[$String],!1)}]);var $panicValue,$jsObjectPtr,$jsErrorPtr,$mapTypes={},$mapType=function(e,n){var r=e.id+\"$\"+n.id,t=$mapTypes[r];return void 0===t&&(t=$newType(4,$kindMap,\"map[\"+e.string+\"]\"+n.string,!1,\"\",!1,null),$mapTypes[r]=t,t.init(e,n)),t},$makeMap=function(e,n){for(var r={},t=0;t2147483647)&&$throwRuntimeError(\"makeslice: len out of range\"),(r<0||r2147483647)&&$throwRuntimeError(\"makeslice: cap out of range\");var t=new e.nativeArray(r);if(e.nativeArray===Array)for(var i=0;i=$curGoroutine.deferStack.length)throw n;if(null!==n){var t=null;try{$curGoroutine.deferStack.push(e),$panic(new $jsErrorPtr(n))}catch(e){t=e}return $curGoroutine.deferStack.pop(),void $callDeferred(e,t)}if(!$curGoroutine.asleep){$stackDepthOffset--;var i=$panicStackDepth,a=$panicValue,o=$curGoroutine.panicStack.pop();void 0!==o&&($panicStackDepth=$getStackDepth(),$panicValue=o);try{for(;;){if(null===e&&void 0===(e=$curGoroutine.deferStack[$curGoroutine.deferStack.length-1])){if($panicStackDepth=null,o.Object instanceof Error)throw o.Object;var $;throw $=o.constructor===$String?o.$val:void 0!==o.Error?o.Error():void 0!==o.String?o.String():o,new Error($)}var c=e.pop();if(void 0===c){if($curGoroutine.deferStack.pop(),void 0!==o){e=null;continue}return}var u=c[0].apply(c[2],c[1]);if(u&&void 0!==u.$blk){if(e.push([u.$blk,[],u]),r)throw null;return}if(void 0!==o&&null===$panicStackDepth)throw null}}finally{void 0!==o&&(null!==$panicStackDepth&&$curGoroutine.panicStack.push(o),$panicStackDepth=i,$panicValue=a),$stackDepthOffset++}}},$panic=function(e){$curGoroutine.panicStack.push(e),$callDeferred(null,null,!0)},$recover=function(){return null===$panicStackDepth||void 0!==$panicStackDepth&&$panicStackDepth!==$getStackDepth()-2?$ifaceNil:($panicStackDepth=null,$panicValue)},$throw=function(e){throw e},$noGoroutine={asleep:!1,exit:!1,deferStack:[],panicStack:[]},$curGoroutine=$noGoroutine,$totalGoroutines=0,$awakeGoroutines=0,$checkForDeadlock=!0,$mainFinished=!1,$go=function(e,n){$totalGoroutines++,$awakeGoroutines++;var r=function(){try{$curGoroutine=r;var t=e.apply(void 0,n);if(t&&void 0!==t.$blk)return e=function(){return t.$blk()},void(n=[]);r.exit=!0}catch(e){if(!r.exit)throw e}finally{$curGoroutine=$noGoroutine,r.exit&&($totalGoroutines--,r.asleep=!0),r.asleep&&($awakeGoroutines--,!$mainFinished&&0===$awakeGoroutines&&$checkForDeadlock&&(console.error(\"fatal error: all goroutines are asleep - deadlock!\"),void 0!==$global.process&&$global.process.exit(2)))}};r.asleep=!1,r.exit=!1,r.deferStack=[],r.panicStack=[],$schedule(r)},$scheduled=[],$runScheduled=function(){try{for(var e;void 0!==(e=$scheduled.shift());)e()}finally{$scheduled.length>0&&setTimeout($runScheduled,0)}},$schedule=function(e){e.asleep&&(e.asleep=!1,$awakeGoroutines++),$scheduled.push(e),$curGoroutine===$noGoroutine&&$runScheduled()},$setTimeout=function(e,n){return $awakeGoroutines++,setTimeout(function(){$awakeGoroutines--,e()},n)},$block=function(){$curGoroutine===$noGoroutine&&$throwRuntimeError(\"cannot block in JavaScript callback, fix by wrapping code in goroutine\"),$curGoroutine.asleep=!0},$send=function(e,n){e.$closed&&$throwRuntimeError(\"send on closed channel\");var r=e.$recvQueue.shift();if(void 0===r){if(!(e.$buffer.length65535){var u=Math.floor((c-65536)/1024)+55296,l=(c-65536)%1024+56320;$+=String.fromCharCode(u,l)}else $+=String.fromCharCode(c)}return $;case $kindStruct:var s=$packages.time;if(void 0!==s&&e.constructor===s.Time.ptr){var f=$div64(e.UnixNano(),new $Int64(0,1e6));return new Date($flatten64(f))}var d={},p=function(e,n){if(n===$jsObjectPtr)return e;switch(n.kind){case $kindPtr:return e===n.nil?d:p(e.$get(),n.elem);case $kindStruct:var r=n.fields[0];return p(e[r.prop],r.typ);case $kindInterface:return p(e.$val,e.constructor);default:return d}},h=p(e,n);if(h!==d)return h;h={};for(i=0;i>24;case $kindInt16:return parseInt(e)<<16>>16;case $kindInt32:return parseInt(e)>>0;case $kindUint:return parseInt(e);case $kindUint8:return parseInt(e)<<24>>>24;case $kindUint16:return parseInt(e)<<16>>>16;case $kindUint32:case $kindUintptr:return parseInt(e)>>>0;case $kindInt64:case $kindUint64:return new n(0,e);case $kindFloat32:case $kindFloat64:return parseFloat(e);case $kindArray:return e.length!==n.len&&$throwRuntimeError(\"got array with wrong size from JavaScript native\"),$mapArray(e,function(e){return $internalize(e,n.elem)});case $kindFunc:return function(){for(var t=[],i=0;i=128)return!1;return!0};\n" +const Minified = "Error.stackTraceLimit=1/0;var $global,$module,$NaN=NaN;if(\"undefined\"!=typeof window?$global=window:\"undefined\"!=typeof self?$global=self:\"undefined\"!=typeof global?($global=global).require=require:$global=this,void 0===$global||void 0===$global.Array)throw new Error(\"no global object found\");if(\"undefined\"!=typeof module&&($module=module),!$global.fs&&$global.require)try{var fs=$global.require(\"fs\");\"object\"==typeof fs&&null!==fs&&0!==Object.keys(fs).length&&($global.fs=fs)}catch(e){}if(!$global.fs){var outputBuf=\"\",decoder=new TextDecoder(\"utf-8\");$global.fs={constants:{O_WRONLY:-1,O_RDWR:-1,O_CREAT:-1,O_TRUNC:-1,O_APPEND:-1,O_EXCL:-1},writeSync:function(e,n){var r=(outputBuf+=decoder.decode(n)).lastIndexOf(\"\\n\");return-1!=r&&(console.log(outputBuf.substr(0,r)),outputBuf=outputBuf.substr(r+1)),n.length},write:function(e,n,r,t,i,a){0===r&&t===n.length&&null===i?a(null,this.writeSync(e,n)):a(enosys())}}}var $throwRuntimeError,$linknames={},$packages={},$idCounter=0,$keys=function(e){return e?Object.keys(e):[]},$flushConsole=function(){},$throwNilPointerError=function(){$throwRuntimeError(\"invalid memory address or nil pointer dereference\")},$call=function(e,n,r){return e.apply(n,r)},$makeFunc=function(e){return function(){return $externalize(e(this,new($sliceType($jsObjectPtr))($global.Array.prototype.slice.call(arguments,[]))),$emptyInterface)}},$unused=function(e){},$print=console.log;if(void 0!==$global.process&&$global.require)try{var util=$global.require(\"util\");$print=function(){$global.process.stderr.write(util.format.apply(this,arguments))}}catch(e){}var $println=console.log,$initAllLinknames=function(){for(var e=$keys($packages),n=0;ne.$capacity||t>e.$capacity)&&$throwRuntimeError(\"slice bounds out of range\"),e===e.constructor.nil)return e;var i=new e.constructor(e.$array);return i.$offset=e.$offset+n,i.$length=r-n,i.$capacity=t-n,i},$substring=function(e,n,r){return(n<0||re.length)&&$throwRuntimeError(\"slice bounds out of range\"),e.substring(n,r)},$sliceToNativeArray=function(e){return e.$array.constructor!==Array?e.$array.subarray(e.$offset,e.$offset+e.$length):e.$array.slice(e.$offset,e.$offset+e.$length)},$sliceToGoArray=function(e,n){var r=n.elem;return void 0!==r&&e.$length1114111||55296<=e&&e<=57343)&&(e=65533),e<=127?String.fromCharCode(e):e<=2047?String.fromCharCode(192|e>>6,128|63&e):e<=65535?String.fromCharCode(224|e>>12,128|e>>6&63,128|63&e):String.fromCharCode(240|e>>18,128|e>>12&63,128|e>>6&63,128|63&e)},$stringToBytes=function(e){for(var n=new Uint8Array(e.length),r=0;rt){for(var o=i-1;o>=0;o--)a.copy(e[r+o],n[t+o]);return}for(o=0;ot)for(o=i-1;o>=0;o--)e[r+o]=n[t+o];else for(o=0;oc)if(a=0,c=Math.max(o,e.$capacity<1024?2*e.$capacity:Math.floor(5*e.$capacity/4)),e.$array.constructor===Array){(i=e.$array.slice(e.$offset,e.$offset+e.$length)).length=c;for(var $=e.constructor.elem.zero,u=e.$length;u>>16&65535)*t+r*(n>>>16&65535)<<16>>>0)>>0},$floatKey=function(e){return e!=e?\"NaN$\"+ ++$idCounter:String(e)},$flatten64=function(e){return 4294967296*e.$high+e.$low},$shiftLeft64=function(e,n){return 0===n?e:n<32?new e.constructor(e.$high<>>32-n,e.$low<>>0):n<64?new e.constructor(e.$low<>n,(e.$low>>>n|e.$high<<32-n)>>>0):n<64?new e.constructor(e.$high>>31,e.$high>>n-32>>>0):e.$high<0?new e.constructor(-1,4294967295):new e.constructor(0,0)},$shiftRightUint64=function(e,n){return 0===n?e:n<32?new e.constructor(e.$high>>>n,(e.$low>>>n|e.$high<<32-n)>>>0):n<64?new e.constructor(0,e.$high>>>n-32):new e.constructor(0,0)},$mul64=function(e,n){var r=e.$high>>>16,t=65535&e.$high,i=e.$low>>>16,a=65535&e.$low,o=n.$high>>>16,c=65535&n.$high,$=n.$low>>>16,u=65535&n.$low,l=0,s=0,f=0,d=0;f+=(d+=a*u)>>>16,s+=(f+=i*u)>>>16,f&=65535,s+=(f+=a*$)>>>16,l+=(s+=t*u)>>>16,s&=65535,l+=(s+=i*$)>>>16,s&=65535,l+=(s+=a*c)>>>16,l+=r*u+t*$+i*c+a*o;var p=((l&=65535)<<16|(s&=65535))>>>0,h=((f&=65535)<<16|(d&=65535))>>>0;return new e.constructor(p,h)},$div64=function(e,n,r){0===n.$high&&0===n.$low&&$throwRuntimeError(\"integer divide by zero\");var t=1,i=1,a=e.$high,o=e.$low;a<0&&(t=-1,i=-1,a=-a,0!==o&&(a--,o=4294967296-o));var c=n.$high,$=n.$low;n.$high<0&&(t*=-1,c=-c,0!==$&&(c--,$=4294967296-$));for(var u=0,l=0,s=0;c<2147483648&&(a>c||a===c&&o>$);)c=(c<<1|$>>>31)>>>0,$=$<<1>>>0,s++;for(var f=0;f<=s;f++)u=u<<1|l>>>31,l=l<<1>>>0,(a>c||a===c&&o>=$)&&(a-=c,(o-=$)<0&&(a--,o+=4294967296),4294967296===++l&&(u++,l=0)),$=($>>>1|c<<31)>>>0,c>>>=1;return r?new e.constructor(a*i,o*i):new e.constructor(u*t,l*t)},$divComplex=function(e,n){var r=e.$real===1/0||e.$real===-1/0||e.$imag===1/0||e.$imag===-1/0,t=n.$real===1/0||n.$real===-1/0||n.$imag===1/0||n.$imag===-1/0,i=!r&&(e.$real!=e.$real||e.$imag!=e.$imag),a=!t&&(n.$real!=n.$real||n.$imag!=n.$imag);if(i||a)return new e.constructor(NaN,NaN);if(r&&!t)return new e.constructor(1/0,1/0);if(!r&&t)return new e.constructor(0,0);if(0===n.$real&&0===n.$imag)return 0===e.$real&&0===e.$imag?new e.constructor(NaN,NaN):new e.constructor(1/0,1/0);if(Math.abs(n.$real)<=Math.abs(n.$imag)){var o=n.$real/n.$imag,c=n.$real*o+n.$imag;return new e.constructor((e.$real*o+e.$imag)/c,(e.$imag*o-e.$real)/c)}o=n.$imag/n.$real,c=n.$imag*o+n.$real;return new e.constructor((e.$imag*o+e.$real)/c,(e.$imag-e.$real*o)/c)},$kindBool=1,$kindInt=2,$kindInt8=3,$kindInt16=4,$kindInt32=5,$kindInt64=6,$kindUint=7,$kindUint8=8,$kindUint16=9,$kindUint32=10,$kindUint64=11,$kindUintptr=12,$kindFloat32=13,$kindFloat64=14,$kindComplex64=15,$kindComplex128=16,$kindArray=17,$kindChan=18,$kindFunc=19,$kindInterface=20,$kindMap=21,$kindPtr=22,$kindSlice=23,$kindString=24,$kindStruct=25,$kindUnsafePointer=26,$methodSynthesizers=[],$addMethodSynthesizer=function(e){null!==$methodSynthesizers?$methodSynthesizers.push(e):e()},$synthesizeMethods=function(){$methodSynthesizers.forEach(function(e){e()}),$methodSynthesizers=null},$ifaceKeyFor=function(e){if(e===$ifaceNil)return\"nil\";var n=e.constructor;return n.string+\"$\"+n.keyFor(e.$val)},$identity=function(e){return e},$typeIDCounter=0,$idKey=function(e){return void 0===e.$id&&($idCounter++,e.$id=$idCounter),String(e.$id)},$arrayPtrCtor=function(){return function(e){this.$get=function(){return e},this.$set=function(e){typ.copy(this,e)},this.$val=e}},$newType=function(e,n,r,t,i,a,o){var c;switch(n){case $kindBool:case $kindInt:case $kindInt8:case $kindInt16:case $kindInt32:case $kindUint:case $kindUint8:case $kindUint16:case $kindUint32:case $kindUintptr:case $kindUnsafePointer:(c=function(e){this.$val=e}).wrapped=!0,c.keyFor=$identity;break;case $kindString:(c=function(e){this.$val=e}).wrapped=!0,c.keyFor=function(e){return\"$\"+e};break;case $kindFloat32:case $kindFloat64:(c=function(e){this.$val=e}).wrapped=!0,c.keyFor=function(e){return $floatKey(e)};break;case $kindInt64:(c=function(e,n){this.$high=e+Math.floor(Math.ceil(n)/4294967296)>>0,this.$low=n>>>0,this.$val=this}).keyFor=function(e){return e.$high+\"$\"+e.$low};break;case $kindUint64:(c=function(e,n){this.$high=e+Math.floor(Math.ceil(n)/4294967296)>>>0,this.$low=n>>>0,this.$val=this}).keyFor=function(e){return e.$high+\"$\"+e.$low};break;case $kindComplex64:(c=function(e,n){this.$real=$fround(e),this.$imag=$fround(n),this.$val=this}).keyFor=function(e){return e.$real+\"$\"+e.$imag};break;case $kindComplex128:(c=function(e,n){this.$real=e,this.$imag=n,this.$val=this}).keyFor=function(e){return e.$real+\"$\"+e.$imag};break;case $kindArray:(c=function(e){this.$val=e}).wrapped=!0,c.ptr=$newType(4,$kindPtr,\"*\"+r,!1,\"\",!1,$arrayPtrCtor()),c.init=function(e,n){c.elem=e,c.len=n,c.comparable=e.comparable,c.keyFor=function(n){return Array.prototype.join.call($mapArray(n,function(n){return String(e.keyFor(n)).replace(/\\\\/g,\"\\\\\\\\\").replace(/\\$/g,\"\\\\$\")}),\"$\")},c.copy=function(n,r){$copyArray(n,r,0,0,r.length,e)},c.ptr.init(c),Object.defineProperty(c.ptr.nil,\"nilCheck\",{get:$throwNilPointerError})};break;case $kindChan:(c=function(e){this.$val=e}).wrapped=!0,c.keyFor=$idKey,c.init=function(e,n,r){c.elem=e,c.sendOnly=n,c.recvOnly=r};break;case $kindFunc:(c=function(e){this.$val=e}).wrapped=!0,c.init=function(e,n,r){c.params=e,c.results=n,c.variadic=r,c.comparable=!1};break;case $kindInterface:(c={implementedBy:{},missingMethodFor:{}}).keyFor=$ifaceKeyFor,c.init=function(e){c.methods=e,e.forEach(function(e){$ifaceNil[e.prop]=$throwNilPointerError})};break;case $kindMap:(c=function(e){this.$val=e}).wrapped=!0,c.init=function(e,n){c.key=e,c.elem=n,c.comparable=!1};break;case $kindPtr:(c=o||function(e,n,r){this.$get=e,this.$set=n,this.$target=r,this.$val=this}).keyFor=$idKey,c.init=function(e){c.elem=e,c.wrapped=e.kind===$kindArray,c.nil=new c($throwNilPointerError,$throwNilPointerError)};break;case $kindSlice:(c=function(e){e.constructor!==c.nativeArray&&(e=new c.nativeArray(e)),this.$array=e,this.$offset=0,this.$length=e.length,this.$capacity=e.length,this.$val=this}).init=function(e){c.elem=e,c.comparable=!1,c.nativeArray=$nativeArray(e.kind),c.nil=new c([])};break;case $kindStruct:(c=function(e){this.$val=e}).wrapped=!0,c.ptr=$newType(4,$kindPtr,\"*\"+r,!1,i,a,o),c.ptr.elem=c,c.ptr.prototype.$get=function(){return this},c.ptr.prototype.$set=function(e){c.copy(this,e)},c.init=function(e,n){c.pkgPath=e,c.fields=n,n.forEach(function(e){e.typ.comparable||(c.comparable=!1)}),c.keyFor=function(e){var r=e.$val;return $mapArray(n,function(e){return String(e.typ.keyFor(r[e.prop])).replace(/\\\\/g,\"\\\\\\\\\").replace(/\\$/g,\"\\\\$\")}).join(\"$\")},c.copy=function(e,r){for(var t=0;t0;){var a=[],o=[];t.forEach(function(e){if(!i[e.typ.string])switch(i[e.typ.string]=!0,e.typ.named&&(o=o.concat(e.typ.methods),e.indirect&&(o=o.concat($ptrType(e.typ).methods))),e.typ.kind){case $kindStruct:e.typ.fields.forEach(function(n){if(n.embedded){var r=n.typ,t=r.kind===$kindPtr;a.push({typ:t?r.elem:r,indirect:e.indirect||t})}});break;case $kindInterface:o=o.concat(e.typ.methods)}}),o.forEach(function(e){void 0===n[e.name]&&(n[e.name]=e)}),t=a}return e.methodSetCache=[],Object.keys(n).sort().forEach(function(r){e.methodSetCache.push(n[r])}),e.methodSetCache},$Bool=$newType(1,$kindBool,\"bool\",!0,\"\",!1,null),$Int=$newType(4,$kindInt,\"int\",!0,\"\",!1,null),$Int8=$newType(1,$kindInt8,\"int8\",!0,\"\",!1,null),$Int16=$newType(2,$kindInt16,\"int16\",!0,\"\",!1,null),$Int32=$newType(4,$kindInt32,\"int32\",!0,\"\",!1,null),$Int64=$newType(8,$kindInt64,\"int64\",!0,\"\",!1,null),$Uint=$newType(4,$kindUint,\"uint\",!0,\"\",!1,null),$Uint8=$newType(1,$kindUint8,\"uint8\",!0,\"\",!1,null),$Uint16=$newType(2,$kindUint16,\"uint16\",!0,\"\",!1,null),$Uint32=$newType(4,$kindUint32,\"uint32\",!0,\"\",!1,null),$Uint64=$newType(8,$kindUint64,\"uint64\",!0,\"\",!1,null),$Uintptr=$newType(4,$kindUintptr,\"uintptr\",!0,\"\",!1,null),$Float32=$newType(4,$kindFloat32,\"float32\",!0,\"\",!1,null),$Float64=$newType(8,$kindFloat64,\"float64\",!0,\"\",!1,null),$Complex64=$newType(8,$kindComplex64,\"complex64\",!0,\"\",!1,null),$Complex128=$newType(16,$kindComplex128,\"complex128\",!0,\"\",!1,null),$String=$newType(8,$kindString,\"string\",!0,\"\",!1,null),$UnsafePointer=$newType(4,$kindUnsafePointer,\"unsafe.Pointer\",!0,\"unsafe\",!1,null),$nativeArray=function(e){switch(e){case $kindInt:return Int32Array;case $kindInt8:return Int8Array;case $kindInt16:return Int16Array;case $kindInt32:return Int32Array;case $kindUint:return Uint32Array;case $kindUint8:return Uint8Array;case $kindUint16:return Uint16Array;case $kindUint32:case $kindUintptr:return Uint32Array;case $kindFloat32:return Float32Array;case $kindFloat64:return Float64Array;default:return Array}},$toNativeArray=function(e,n){var r=$nativeArray(e);return r===Array?n:new r(n)},$arrayTypes={},$arrayType=function(e,n){var r=e.id+\"$\"+n,t=$arrayTypes[r];return void 0===t&&(t=$newType(e.size*n,$kindArray,\"[\"+n+\"]\"+e.string,!1,\"\",!1,null),$arrayTypes[r]=t,t.init(e,n)),t},$chanType=function(e,n,r){var t=(r?\"<-\":\"\")+\"chan\"+(n?\"<- \":\" \");n||r||\"<\"!=e.string[0]?t+=e.string:t+=\"(\"+e.string+\")\";var i=n?\"SendChan\":r?\"RecvChan\":\"Chan\",a=e[i];return void 0===a&&(a=$newType(4,$kindChan,t,!1,\"\",!1,null),e[i]=a,a.init(e,n,r)),a},$Chan=function(e,n){(n<0||n>2147483647)&&$throwRuntimeError(\"makechan: size out of range\"),this.$elem=e,this.$capacity=n,this.$buffer=[],this.$sendQueue=[],this.$recvQueue=[],this.$closed=!1},$chanNil=new $Chan(null,0);$chanNil.$sendQueue=$chanNil.$recvQueue={length:0,push:function(){},shift:function(){},indexOf:function(){return-1}};var $funcTypes={},$funcType=function(e,n,r){var t=$mapArray(e,function(e){return e.id}).join(\",\")+\"$\"+$mapArray(n,function(e){return e.id}).join(\",\")+\"$\"+r,i=$funcTypes[t];if(void 0===i){var a=$mapArray(e,function(e){return e.string});r&&(a[a.length-1]=\"...\"+a[a.length-1].substr(2));var o=\"func(\"+a.join(\", \")+\")\";1===n.length?o+=\" \"+n[0].string:n.length>1&&(o+=\" (\"+$mapArray(n,function(e){return e.string}).join(\", \")+\")\"),i=$newType(4,$kindFunc,o,!1,\"\",!1,null),$funcTypes[t]=i,i.init(e,n,r)}return i},$interfaceTypes={},$interfaceType=function(e){var n=$mapArray(e,function(e){return e.pkg+\",\"+e.name+\",\"+e.typ.id}).join(\"$\"),r=$interfaceTypes[n];if(void 0===r){var t=\"interface {}\";0!==e.length&&(t=\"interface { \"+$mapArray(e,function(e){return(\"\"!==e.pkg?e.pkg+\".\":\"\")+e.name+e.typ.string.substr(4)}).join(\"; \")+\" }\"),r=$newType(8,$kindInterface,t,!1,\"\",!1,null),$interfaceTypes[n]=r,r.init(e)}return r},$emptyInterface=$interfaceType([]),$ifaceNil={},$error=$newType(8,$kindInterface,\"error\",!0,\"\",!1,null);$error.init([{prop:\"Error\",name:\"Error\",pkg:\"\",typ:$funcType([],[$String],!1)}]);var $panicValue,$jsObjectPtr,$jsErrorPtr,$mapTypes={},$mapType=function(e,n){var r=e.id+\"$\"+n.id,t=$mapTypes[r];return void 0===t&&(t=$newType(4,$kindMap,\"map[\"+e.string+\"]\"+n.string,!1,\"\",!1,null),$mapTypes[r]=t,t.init(e,n)),t},$makeMap=function(e,n){for(var r=new Map,t=0;t2147483647)&&$throwRuntimeError(\"makeslice: len out of range\"),(r<0||r2147483647)&&$throwRuntimeError(\"makeslice: cap out of range\");var t=new e.nativeArray(r);if(e.nativeArray===Array)for(var i=0;i4||t<0)break}}finally{0==$scheduled.length&&clearTimeout(e)}},$schedule=function(e){e.asleep&&(e.asleep=!1,$awakeGoroutines++),$scheduled.push(e),$curGoroutine===$noGoroutine&&$runScheduled()},$setTimeout=function(e,n){return $awakeGoroutines++,setTimeout(function(){$awakeGoroutines--,e()},n)},$block=function(){$curGoroutine===$noGoroutine&&$throwRuntimeError(\"cannot block in JavaScript callback, fix by wrapping code in goroutine\"),$curGoroutine.asleep=!0},$restore=function(e,n){return void 0!==e&&void 0!==e.$blk?e:n},$send=function(e,n){e.$closed&&$throwRuntimeError(\"send on closed channel\");var r=e.$recvQueue.shift();if(void 0===r){if(!(e.$buffer.length65535){var l=Math.floor((u-65536)/1024)+55296,s=(u-65536)%1024+56320;$+=String.fromCharCode(l,s)}else $+=String.fromCharCode(u)}return $;case $kindStruct:var f=$packages.time;if(void 0!==f&&e.constructor===f.Time.ptr){var d=$div64(e.UnixNano(),new $Int64(0,1e6));return new Date($flatten64(d))}var p={},h=function(e,n){if(n===$jsObjectPtr)return e;switch(n.kind){case $kindPtr:return e===n.nil?p:h(e.$get(),n.elem);case $kindStruct:var r=n.fields[0];return h(e[r.prop],r.typ);case $kindInterface:return h(e.$val,e.constructor);default:return p}},k=h(e,n);if(k!==p)return k;if(void 0!==r)return r(e);k={};for(a=0;a>24;case $kindInt16:return parseInt(e)<<16>>16;case $kindInt32:return parseInt(e)>>0;case $kindUint:return parseInt(e);case $kindUint8:return parseInt(e)<<24>>>24;case $kindUint16:return parseInt(e)<<16>>>16;case $kindUint32:case $kindUintptr:return parseInt(e)>>>0;case $kindInt64:case $kindUint64:return new n(0,e);case $kindFloat32:case $kindFloat64:return parseFloat(e);case $kindArray:return e.length!==n.len&&$throwRuntimeError(\"got array with wrong size from JavaScript native\"),$mapArray(e,function(e){return $internalize(e,n.elem,i)});case $kindFunc:return function(){for(var t=[],a=0;a=128)return!1;return!0};\n" diff --git a/compiler/prelude/types.go b/compiler/prelude/types.go index 0d37509b..69afe8af 100644 --- a/compiler/prelude/types.go +++ b/compiler/prelude/types.go @@ -61,6 +61,16 @@ var $idKey = function(x) { return String(x.$id); }; +// Creates constructor functions for array pointer types. Returns a new function +// instace each time to make sure each type is independent of the other. +var $arrayPtrCtor = function() { + return function(array) { + this.$get = function() { return array; }; + this.$set = function(v) { typ.copy(this, v); }; + this.$val = array; + } +} + var $newType = function(size, kind, string, named, pkg, exported, constructor) { var typ; switch(kind) { @@ -132,11 +142,7 @@ var $newType = function(size, kind, string, named, pkg, exported, constructor) { case $kindArray: typ = function(v) { this.$val = v; }; typ.wrapped = true; - typ.ptr = $newType(4, $kindPtr, "*" + string, false, "", false, function(array) { - this.$get = function() { return array; }; - this.$set = function(v) { typ.copy(this, v); }; - this.$val = array; - }); + typ.ptr = $newType(4, $kindPtr, "*" + string, false, "", false, $arrayPtrCtor()); typ.init = function(elem, len) { typ.elem = elem; typ.len = len; @@ -476,7 +482,7 @@ var $Float64 = $newType( 8, $kindFloat64, "float64", true, "" var $Complex64 = $newType( 8, $kindComplex64, "complex64", true, "", false, null); var $Complex128 = $newType(16, $kindComplex128, "complex128", true, "", false, null); var $String = $newType( 8, $kindString, "string", true, "", false, null); -var $UnsafePointer = $newType( 4, $kindUnsafePointer, "unsafe.Pointer", true, "", false, null); +var $UnsafePointer = $newType( 4, $kindUnsafePointer, "unsafe.Pointer", true, "unsafe", false, null); var $nativeArray = function(elemKind) { switch (elemKind) { @@ -518,7 +524,7 @@ var $arrayType = function(elem, len) { var typeKey = elem.id + "$" + len; var typ = $arrayTypes[typeKey]; if (typ === undefined) { - typ = $newType(12, $kindArray, "[" + len + "]" + elem.string, false, "", false, null); + typ = $newType(elem.size*len, $kindArray, "[" + len + "]" + elem.string, false, "", false, null); $arrayTypes[typeKey] = typ; typ.init(elem, len); } @@ -526,7 +532,12 @@ var $arrayType = function(elem, len) { }; var $chanType = function(elem, sendOnly, recvOnly) { - var string = (recvOnly ? "<-" : "") + "chan" + (sendOnly ? "<- " : " ") + elem.string; + var string = (recvOnly ? "<-" : "") + "chan" + (sendOnly ? "<- " : " "); + if (!sendOnly && !recvOnly && (elem.string[0] == "<")) { + string += "(" + elem.string + ")"; + } else { + string += elem.string; + } var field = sendOnly ? "SendChan" : (recvOnly ? "RecvChan" : "Chan"); var typ = elem[field]; if (typ === undefined) { @@ -606,10 +617,10 @@ var $mapType = function(key, elem) { return typ; }; var $makeMap = function(keyForFunc, entries) { - var m = {}; + var m = new Map(); for (var i = 0; i < entries.length; i++) { var e = entries[i]; - m[keyForFunc(e.k)] = e; + m.set(keyForFunc(e.k), e); } return m; }; @@ -632,8 +643,17 @@ var $newDataPointer = function(data, constructor) { }; var $indexPtr = function(array, index, constructor) { - array.$ptr = array.$ptr || {}; - return array.$ptr[index] || (array.$ptr[index] = new constructor(function() { return array[index]; }, function(v) { array[index] = v; })); + if (array.buffer) { + // Pointers to the same underlying ArrayBuffer share cache. + var cache = array.buffer.$ptr = array.buffer.$ptr || {}; + // Pointers of different primitive types are non-comparable and stored in different caches. + var typeCache = cache[array.name] = cache[array.name] || {}; + var cacheIdx = array.BYTES_PER_ELEMENT * index + array.byteOffset; + return typeCache[cacheIdx] || (typeCache[cacheIdx] = new constructor(function() { return array[index]; }, function(v) { array[index] = v; })); + } else { + array.$ptr = array.$ptr || {}; + return array.$ptr[index] || (array.$ptr[index] = new constructor(function() { return array[index]; }, function(v) { array[index] = v; })); + } }; var $sliceType = function(elem) { @@ -670,7 +690,11 @@ var $structType = function(pkgPath, fields) { var typ = $structTypes[typeKey]; if (typ === undefined) { var string = "struct { " + $mapArray(fields, function(f) { - return f.name + " " + f.typ.string + (f.tag !== "" ? (" \"" + f.tag.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"") : ""); + var str = f.typ.string + (f.tag !== "" ? (" \"" + f.tag.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"") : ""); + if (f.embedded) { + return str; + } + return f.name + " " + str; }).join("; ") + " }"; if (fields.length === 0) { string = "struct {}"; @@ -679,6 +703,9 @@ var $structType = function(pkgPath, fields) { this.$val = this; for (var i = 0; i < fields.length; i++) { var f = fields[i]; + if (f.name == '_') { + continue; + } var arg = arguments[i]; this[f.prop] = arg !== undefined ? arg : f.typ.zero(); } diff --git a/compiler/prelude/uglifyjs_options.json b/compiler/prelude/uglifyjs_options.json index b603add9..7e7bef1a 100644 --- a/compiler/prelude/uglifyjs_options.json +++ b/compiler/prelude/uglifyjs_options.json @@ -56,7 +56,7 @@ "keep_classnames": false, "keep_fnames": false, "properties": false, - "reserved": [], + "reserved": ["$goroutine", "$panic"], "safari10": false, "toplevel": false }, diff --git a/compiler/statements.go b/compiler/statements.go index b8339623..fc36453a 100644 --- a/compiler/statements.go +++ b/compiler/statements.go @@ -4,6 +4,7 @@ import ( "fmt" "go/ast" "go/constant" + "go/printer" "go/token" "go/types" "strings" @@ -14,22 +15,45 @@ import ( "github.com/gopherjs/gopherjs/compiler/typesutil" ) -func (c *funcContext) translateStmtList(stmts []ast.Stmt) { +func (fc *funcContext) translateStmtList(stmts []ast.Stmt) { for _, stmt := range stmts { - c.translateStmt(stmt, nil) + fc.translateStmt(stmt, nil) } - c.SetPos(token.NoPos) + fc.SetPos(token.NoPos) } -func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { - c.SetPos(stmt.Pos()) +func (fc *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { + defer func() { + err := recover() + if err == nil { + return + } + if _, yes := bailingOut(err); yes { + panic(err) // Continue orderly bailout. + } + + // Oh noes, we've tried to compile something so bad that compiler paniced + // and ran away. Let's gather some debugging clues. + bail := bailout(err) + pos := stmt.Pos() + if fc.posAvailable && fc.pos.IsValid() { + pos = fc.pos + } + fmt.Fprintf(bail, "Occurred while compiling statement at %s:\n", fc.pkgCtx.fileSet.Position(pos)) + (&printer.Config{Tabwidth: 2, Indent: 1, Mode: printer.UseSpaces}).Fprint(bail, fc.pkgCtx.fileSet, stmt) + fmt.Fprintf(bail, "\n\nDetailed AST:\n") + ast.Fprint(bail, fc.pkgCtx.fileSet, stmt, ast.NotNilFilter) + panic(bail) // Initiate orderly bailout. + }() - stmt = filter.IncDecStmt(stmt, c.p.Info.Info) - stmt = filter.Assign(stmt, c.p.Info.Info, c.p.Info.Pkg) + fc.SetPos(stmt.Pos()) + + stmt = filter.IncDecStmt(stmt, fc.pkgCtx.Info.Info) + stmt = filter.Assign(stmt, fc.pkgCtx.Info.Info, fc.pkgCtx.Info.Pkg) switch s := stmt.(type) { case *ast.BlockStmt: - c.translateStmtList(s.List) + fc.translateStmtList(s.List) case *ast.IfStmt: var caseClauses []*ast.CaseClause @@ -49,7 +73,7 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { if block, ok := ifStmt.Else.(*ast.BlockStmt); ok { defaultClause = &ast.CaseClause{Body: block.List} } - c.translateBranchingStmt(caseClauses, defaultClause, false, c.translateExpr, nil, c.Flattened[s]) + fc.translateBranchingStmt(caseClauses, defaultClause, false, fc.translateExpr, nil, fc.Flattened[s]) case *ast.SwitchStmt: if s.Init != nil || s.Tag != nil || len(s.Body.List) != 1 { @@ -60,48 +84,48 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { panic("simplification error") } - prevFlowData := c.flowDatas[nil] + prevFlowData := fc.flowDatas[nil] data := &flowData{ postStmt: prevFlowData.postStmt, // for "continue" of outer loop beginCase: prevFlowData.beginCase, // same } - c.flowDatas[nil] = data - c.flowDatas[label] = data + fc.flowDatas[nil] = data + fc.flowDatas[label] = data defer func() { - delete(c.flowDatas, label) - c.flowDatas[nil] = prevFlowData + delete(fc.flowDatas, label) + fc.flowDatas[nil] = prevFlowData }() - if c.Flattened[s] { - data.endCase = c.caseCounter - c.caseCounter++ + if fc.Flattened[s] { + data.endCase = fc.caseCounter + fc.caseCounter++ - c.Indent(func() { - c.translateStmtList(clause.Body) + fc.Indent(func() { + fc.translateStmtList(clause.Body) }) - c.Printf("case %d:", data.endCase) + fc.Printf("case %d:", data.endCase) return } if label != nil || analysis.HasBreak(clause) { if label != nil { - c.Printf("%s:", label.Name()) + fc.Printf("%s:", label.Name()) } - c.Printf("switch (0) { default:") - c.Indent(func() { - c.translateStmtList(clause.Body) + fc.Printf("switch (0) { default:") + fc.Indent(func() { + fc.translateStmtList(clause.Body) }) - c.Printf("}") + fc.Printf("}") return } - c.translateStmtList(clause.Body) + fc.translateStmtList(clause.Body) case *ast.TypeSwitchStmt: if s.Init != nil { - c.translateStmt(s.Init, nil) + fc.translateStmt(s.Init, nil) } - refVar := c.newVariable("_ref") + refVar := fc.newVariable("_ref") var expr ast.Expr switch a := s.Assign.(type) { case *ast.AssignStmt: @@ -109,19 +133,19 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { case *ast.ExprStmt: expr = a.X.(*ast.TypeAssertExpr).X } - c.Printf("%s = %s;", refVar, c.translateExpr(expr)) + fc.Printf("%s = %s;", refVar, fc.translateExpr(expr)) translateCond := func(cond ast.Expr) *expression { - if types.Identical(c.p.TypeOf(cond), types.Typ[types.UntypedNil]) { - return c.formatExpr("%s === $ifaceNil", refVar) + if types.Identical(fc.pkgCtx.TypeOf(cond), types.Typ[types.UntypedNil]) { + return fc.formatExpr("%s === $ifaceNil", refVar) } - return c.formatExpr("$assertType(%s, %s, true)[1]", refVar, c.typeName(c.p.TypeOf(cond))) + return fc.formatExpr("$assertType(%s, %s, true)[1]", refVar, fc.typeName(fc.pkgCtx.TypeOf(cond))) } var caseClauses []*ast.CaseClause var defaultClause *ast.CaseClause for _, cc := range s.Body.List { clause := cc.(*ast.CaseClause) var bodyPrefix []ast.Stmt - if implicit := c.p.Implicits[clause]; implicit != nil { + if implicit := fc.pkgCtx.Implicits[clause]; implicit != nil { value := refVar if typesutil.IsJsObject(implicit.Type().Underlying()) { value += ".$val.object" @@ -129,9 +153,9 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { value += ".$val" } bodyPrefix = []ast.Stmt{&ast.AssignStmt{ - Lhs: []ast.Expr{c.newIdent(c.objectName(implicit), implicit.Type())}, + Lhs: []ast.Expr{fc.newIdent(fc.objectName(implicit), implicit.Type())}, Tok: token.DEFINE, - Rhs: []ast.Expr{c.newIdent(value, implicit.Type())}, + Rhs: []ast.Expr{fc.newIdent(value, implicit.Type())}, }} } c := &ast.CaseClause{ @@ -144,66 +168,71 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { } caseClauses = append(caseClauses, c) } - c.translateBranchingStmt(caseClauses, defaultClause, true, translateCond, label, c.Flattened[s]) + fc.translateBranchingStmt(caseClauses, defaultClause, true, translateCond, label, fc.Flattened[s]) case *ast.ForStmt: if s.Init != nil { - c.translateStmt(s.Init, nil) + fc.translateStmt(s.Init, nil) } cond := func() string { if s.Cond == nil { return "true" } - return c.translateExpr(s.Cond).String() + return fc.translateExpr(s.Cond).String() } - c.translateLoopingStmt(cond, s.Body, nil, func() { + fc.translateLoopingStmt(cond, s.Body, nil, func() { if s.Post != nil { - c.translateStmt(s.Post, nil) + fc.translateStmt(s.Post, nil) } - }, label, c.Flattened[s]) + }, label, fc.Flattened[s]) case *ast.RangeStmt: - refVar := c.newVariable("_ref") - c.Printf("%s = %s;", refVar, c.translateExpr(s.X)) + refVar := fc.newVariable("_ref") + fc.Printf("%s = %s;", refVar, fc.translateExpr(s.X)) - switch t := c.p.TypeOf(s.X).Underlying().(type) { + switch t := fc.pkgCtx.TypeOf(s.X).Underlying().(type) { case *types.Basic: - iVar := c.newVariable("_i") - c.Printf("%s = 0;", iVar) - runeVar := c.newVariable("_rune") - c.translateLoopingStmt(func() string { return iVar + " < " + refVar + ".length" }, s.Body, func() { - c.Printf("%s = $decodeRune(%s, %s);", runeVar, refVar, iVar) + iVar := fc.newVariable("_i") + fc.Printf("%s = 0;", iVar) + runeVar := fc.newVariable("_rune") + fc.translateLoopingStmt(func() string { return iVar + " < " + refVar + ".length" }, s.Body, func() { + fc.Printf("%s = $decodeRune(%s, %s);", runeVar, refVar, iVar) if !isBlank(s.Key) { - c.Printf("%s", c.translateAssign(s.Key, c.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE)) + fc.Printf("%s", fc.translateAssign(s.Key, fc.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { - c.Printf("%s", c.translateAssign(s.Value, c.newIdent(runeVar+"[0]", types.Typ[types.Rune]), s.Tok == token.DEFINE)) + fc.Printf("%s", fc.translateAssign(s.Value, fc.newIdent(runeVar+"[0]", types.Typ[types.Rune]), s.Tok == token.DEFINE)) } }, func() { - c.Printf("%s += %s[1];", iVar, runeVar) - }, label, c.Flattened[s]) + fc.Printf("%s += %s[1];", iVar, runeVar) + }, label, fc.Flattened[s]) case *types.Map: - iVar := c.newVariable("_i") - c.Printf("%s = 0;", iVar) - keysVar := c.newVariable("_keys") - c.Printf("%s = $keys(%s);", keysVar, refVar) - c.translateLoopingStmt(func() string { return iVar + " < " + keysVar + ".length" }, s.Body, func() { - entryVar := c.newVariable("_entry") - c.Printf("%s = %s[%s[%s]];", entryVar, refVar, keysVar, iVar) - c.translateStmt(&ast.IfStmt{ - Cond: c.newIdent(entryVar+" === undefined", types.Typ[types.Bool]), + iVar := fc.newVariable("_i") + fc.Printf("%s = 0;", iVar) + keysVar := fc.newVariable("_keys") + fc.Printf("%s = %s ? %s.keys() : undefined;", keysVar, refVar, refVar) + + sizeVar := fc.newVariable("_size") + fc.Printf("%s = %s ? %s.size : 0;", sizeVar, refVar, refVar) + fc.translateLoopingStmt(func() string { return iVar + " < " + sizeVar }, s.Body, func() { + keyVar := fc.newVariable("_key") + entryVar := fc.newVariable("_entry") + fc.Printf("%s = %s.next().value;", keyVar, keysVar) + fc.Printf("%s = %s.get(%s);", entryVar, refVar, keyVar) + fc.translateStmt(&ast.IfStmt{ + Cond: fc.newIdent(entryVar+" === undefined", types.Typ[types.Bool]), Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.CONTINUE}}}, }, nil) if !isBlank(s.Key) { - c.Printf("%s", c.translateAssign(s.Key, c.newIdent(entryVar+".k", t.Key()), s.Tok == token.DEFINE)) + fc.Printf("%s", fc.translateAssign(s.Key, fc.newIdent(entryVar+".k", t.Key()), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { - c.Printf("%s", c.translateAssign(s.Value, c.newIdent(entryVar+".v", t.Elem()), s.Tok == token.DEFINE)) + fc.Printf("%s", fc.translateAssign(s.Value, fc.newIdent(entryVar+".v", t.Elem()), s.Tok == token.DEFINE)) } }, func() { - c.Printf("%s++;", iVar) - }, label, c.Flattened[s]) + fc.Printf("%s++;", iVar) + }, label, fc.Flattened[s]) case *types.Array, *types.Pointer, *types.Slice: var length string @@ -219,24 +248,24 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { length = refVar + ".$length" elemType = t2.Elem() } - iVar := c.newVariable("_i") - c.Printf("%s = 0;", iVar) - c.translateLoopingStmt(func() string { return iVar + " < " + length }, s.Body, func() { + iVar := fc.newVariable("_i") + fc.Printf("%s = 0;", iVar) + fc.translateLoopingStmt(func() string { return iVar + " < " + length }, s.Body, func() { if !isBlank(s.Key) { - c.Printf("%s", c.translateAssign(s.Key, c.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE)) + fc.Printf("%s", fc.translateAssign(s.Key, fc.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { - c.Printf("%s", c.translateAssign(s.Value, c.setType(&ast.IndexExpr{ - X: c.newIdent(refVar, t), - Index: c.newIdent(iVar, types.Typ[types.Int]), + fc.Printf("%s", fc.translateAssign(s.Value, fc.setType(&ast.IndexExpr{ + X: fc.newIdent(refVar, t), + Index: fc.newIdent(iVar, types.Typ[types.Int]), }, elemType), s.Tok == token.DEFINE)) } }, func() { - c.Printf("%s++;", iVar) - }, label, c.Flattened[s]) + fc.Printf("%s++;", iVar) + }, label, fc.Flattened[s]) case *types.Chan: - okVar := c.newIdent(c.newVariable("_ok"), types.Typ[types.Bool]) + okVar := fc.newIdent(fc.newVariable("_ok"), types.Typ[types.Bool]) key := s.Key tok := s.Tok if key == nil { @@ -252,7 +281,7 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { okVar, }, Rhs: []ast.Expr{ - c.setType(&ast.UnaryExpr{X: c.newIdent(refVar, t), Op: token.ARROW}, types.NewTuple(types.NewVar(0, nil, "", t.Elem()), types.NewVar(0, nil, "", types.Typ[types.Bool]))), + fc.setType(&ast.UnaryExpr{X: fc.newIdent(refVar, t), Op: token.ARROW}, types.NewTuple(types.NewVar(0, nil, "", t.Elem()), types.NewVar(0, nil, "", types.Typ[types.Bool]))), }, Tok: tok, }, @@ -264,8 +293,8 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { }, }, } - c.Flattened[forStmt] = true - c.translateStmt(forStmt, label) + fc.Flattened[forStmt] = true + fc.translateStmt(forStmt, label) default: panic("") @@ -274,78 +303,73 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { case *ast.BranchStmt: normalLabel := "" blockingLabel := "" - data := c.flowDatas[nil] + data := fc.flowDatas[nil] if s.Label != nil { normalLabel = " " + s.Label.Name blockingLabel = " s" // use explicit label "s", because surrounding loop may not be flattened - data = c.flowDatas[c.p.Uses[s.Label].(*types.Label)] + data = fc.flowDatas[fc.pkgCtx.Uses[s.Label].(*types.Label)] } switch s.Tok { case token.BREAK: - c.PrintCond(data.endCase == 0, fmt.Sprintf("break%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.endCase, blockingLabel)) + fc.PrintCond(data.endCase == 0, fmt.Sprintf("break%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.endCase, blockingLabel)) case token.CONTINUE: data.postStmt() - c.PrintCond(data.beginCase == 0, fmt.Sprintf("continue%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.beginCase, blockingLabel)) + fc.PrintCond(data.beginCase == 0, fmt.Sprintf("continue%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.beginCase, blockingLabel)) case token.GOTO: - c.PrintCond(false, "goto "+s.Label.Name, fmt.Sprintf("$s = %d; continue;", c.labelCase(c.p.Uses[s.Label].(*types.Label)))) + fc.PrintCond(false, "goto "+s.Label.Name, fmt.Sprintf("$s = %d; continue;", fc.labelCase(fc.pkgCtx.Uses[s.Label].(*types.Label)))) case token.FALLTHROUGH: // handled in CaseClause default: - panic("Unhandled branch statment: " + s.Tok.String()) + panic("Unhandled branch statement: " + s.Tok.String()) } case *ast.ReturnStmt: results := s.Results - if c.resultNames != nil { + if fc.resultNames != nil { if len(s.Results) != 0 { - c.translateStmt(&ast.AssignStmt{ - Lhs: c.resultNames, + fc.translateStmt(&ast.AssignStmt{ + Lhs: fc.resultNames, Tok: token.ASSIGN, Rhs: s.Results, }, nil) } - results = c.resultNames + results = fc.resultNames } - rVal := c.translateResults(results) - if len(c.Flattened) != 0 { - c.Printf("$s = -1; return%s;", rVal) + rVal := fc.translateResults(results) + + if len(fc.Flattened) == 0 { + // The function is not flattened and we don't have to worry about + // resumption. A plain return statement is sufficient. + fc.Printf("return%s;", rVal) return } - c.Printf("return%s;", rVal) - - case *ast.DeferStmt: - isBuiltin := false - isJs := false - switch fun := s.Call.Fun.(type) { - case *ast.Ident: - var builtin *types.Builtin - builtin, isBuiltin = c.p.Uses[fun].(*types.Builtin) - if isBuiltin && builtin.Name() == "recover" { - c.Printf("$deferred.push([$recover, []]);") - return - } - case *ast.SelectorExpr: - isJs = typesutil.IsJsPackage(c.p.Uses[fun.Sel].Pkg()) - } - sig := c.p.TypeOf(s.Call.Fun).Underlying().(*types.Signature) - args := c.translateArgs(sig, s.Call.Args, s.Call.Ellipsis.IsValid()) - if isBuiltin || isJs { - vars := make([]string, len(s.Call.Args)) - callArgs := make([]ast.Expr, len(s.Call.Args)) - for i, arg := range s.Call.Args { - v := c.newVariable("_arg") - vars[i] = v - callArgs[i] = c.newIdent(v, c.p.TypeOf(arg)) - } - call := c.translateExpr(&ast.CallExpr{ - Fun: s.Call.Fun, - Args: callArgs, - Ellipsis: s.Call.Ellipsis, - }) - c.Printf("$deferred.push([function(%s) { %s; }, [%s]]);", strings.Join(vars, ", "), call, strings.Join(args, ", ")) + if !fc.Blocking[s] { + // The function is flattened, but the return statement is non-blocking + // (i.e. doesn't lead to blocking deferred calls). A regular return + // is sufficient, but we also make sure to not resume function body. + fc.Printf("$s = -1; return%s;", rVal) return } - c.Printf("$deferred.push([%s, [%s]]);", c.translateExpr(s.Call.Fun), strings.Join(args, ", ")) + + if rVal != "" { + // If returned expression is non empty, evaluate and store it in a + // variable to avoid double-execution in case a deferred function blocks. + rVar := fc.newVariable("$r") + fc.Printf("%s =%s;", rVar, rVal) + rVal = " " + rVar + } + + // If deferred function is blocking, we need to re-execute return statement + // upon resumption to make sure the returned value is not lost. + // See: https://github.com/gopherjs/gopherjs/issues/603. + nextCase := fc.caseCounter + fc.caseCounter++ + fc.Printf("$s = %[1]d; case %[1]d: return%[2]s;", nextCase, rVal) + return + + case *ast.DeferStmt: + callable, arglist := fc.delegatedCall(s.Call) + fc.Printf("$deferred.push([%s, %s]);", callable, arglist) case *ast.AssignStmt: if s.Tok != token.ASSIGN && s.Tok != token.DEFINE { @@ -356,35 +380,35 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { case len(s.Lhs) == 1 && len(s.Rhs) == 1: lhs := astutil.RemoveParens(s.Lhs[0]) if isBlank(lhs) { - c.Printf("$unused(%s);", c.translateExpr(s.Rhs[0])) + fc.Printf("$unused(%s);", fc.translateImplicitConversion(s.Rhs[0], fc.pkgCtx.TypeOf(s.Lhs[0]))) return } - c.Printf("%s", c.translateAssign(lhs, s.Rhs[0], s.Tok == token.DEFINE)) + fc.Printf("%s", fc.translateAssign(lhs, s.Rhs[0], s.Tok == token.DEFINE)) case len(s.Lhs) > 1 && len(s.Rhs) == 1: - tupleVar := c.newVariable("_tuple") - c.Printf("%s = %s;", tupleVar, c.translateExpr(s.Rhs[0])) - tuple := c.p.TypeOf(s.Rhs[0]).(*types.Tuple) + tupleVar := fc.newVariable("_tuple") + fc.Printf("%s = %s;", tupleVar, fc.translateExpr(s.Rhs[0])) + tuple := fc.pkgCtx.TypeOf(s.Rhs[0]).(*types.Tuple) for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { - c.Printf("%s", c.translateAssign(lhs, c.newIdent(fmt.Sprintf("%s[%d]", tupleVar, i), tuple.At(i).Type()), s.Tok == token.DEFINE)) + fc.Printf("%s", fc.translateAssign(lhs, fc.newIdent(fmt.Sprintf("%s[%d]", tupleVar, i), tuple.At(i).Type()), s.Tok == token.DEFINE)) } } case len(s.Lhs) == len(s.Rhs): tmpVars := make([]string, len(s.Rhs)) for i, rhs := range s.Rhs { - tmpVars[i] = c.newVariable("_tmp") + tmpVars[i] = fc.newVariable("_tmp") if isBlank(astutil.RemoveParens(s.Lhs[i])) { - c.Printf("$unused(%s);", c.translateExpr(rhs)) + fc.Printf("$unused(%s);", fc.translateExpr(rhs)) continue } - c.Printf("%s", c.translateAssign(c.newIdent(tmpVars[i], c.p.TypeOf(s.Lhs[i])), rhs, true)) + fc.Printf("%s", fc.translateAssign(fc.newIdent(tmpVars[i], fc.pkgCtx.TypeOf(s.Lhs[i])), rhs, true)) } for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { - c.Printf("%s", c.translateAssign(lhs, c.newIdent(tmpVars[i], c.p.TypeOf(lhs)), s.Tok == token.DEFINE)) + fc.Printf("%s", fc.translateAssign(lhs, fc.newIdent(tmpVars[i], fc.pkgCtx.TypeOf(lhs)), s.Tok == token.DEFINE)) } } @@ -407,10 +431,10 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { if len(rhs) == 0 { rhs = make([]ast.Expr, len(lhs)) for i, e := range lhs { - rhs[i] = c.zeroValue(c.p.TypeOf(e)) + rhs[i] = fc.zeroValue(fc.pkgCtx.TypeOf(e)) } } - c.translateStmt(&ast.AssignStmt{ + fc.translateStmt(&ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: rhs, @@ -418,42 +442,43 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { } case token.TYPE: for _, spec := range decl.Specs { - o := c.p.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) - c.p.typeNames = append(c.p.typeNames, o) - c.p.objectNames[o] = c.newVariableWithLevel(o.Name(), true) - c.p.dependencies[o] = true + o := fc.pkgCtx.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) + fc.pkgCtx.typeNames = append(fc.pkgCtx.typeNames, o) + fc.pkgCtx.objectNames[o] = fc.newVariableWithLevel(o.Name(), true) + fc.pkgCtx.dependencies[o] = true } case token.CONST: // skip, constants are inlined } case *ast.ExprStmt: - expr := c.translateExpr(s.X) + expr := fc.translateExpr(s.X) if expr != nil && expr.String() != "" { - c.Printf("%s;", expr) + fc.Printf("%s;", expr) } case *ast.LabeledStmt: - label := c.p.Defs[s.Label].(*types.Label) - if c.GotoLabel[label] { - c.PrintCond(false, s.Label.Name+":", fmt.Sprintf("case %d:", c.labelCase(label))) + label := fc.pkgCtx.Defs[s.Label].(*types.Label) + if fc.GotoLabel[label] { + fc.PrintCond(false, s.Label.Name+":", fmt.Sprintf("case %d:", fc.labelCase(label))) } - c.translateStmt(s.Stmt, label) + fc.translateStmt(s.Stmt, label) case *ast.GoStmt: - c.Printf("$go(%s, [%s]);", c.translateExpr(s.Call.Fun), strings.Join(c.translateArgs(c.p.TypeOf(s.Call.Fun).Underlying().(*types.Signature), s.Call.Args, s.Call.Ellipsis.IsValid()), ", ")) + callable, arglist := fc.delegatedCall(s.Call) + fc.Printf("$go(%s, %s);", callable, arglist) case *ast.SendStmt: - chanType := c.p.TypeOf(s.Chan).Underlying().(*types.Chan) + chanType := fc.pkgCtx.TypeOf(s.Chan).Underlying().(*types.Chan) call := &ast.CallExpr{ - Fun: c.newIdent("$send", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", chanType), types.NewVar(0, nil, "", chanType.Elem())), nil, false)), - Args: []ast.Expr{s.Chan, c.newIdent(c.translateImplicitConversionWithCloning(s.Value, chanType.Elem()).String(), chanType.Elem())}, + Fun: fc.newIdent("$send", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", chanType), types.NewVar(0, nil, "", chanType.Elem())), nil, false)), + Args: []ast.Expr{s.Chan, fc.newIdent(fc.translateImplicitConversionWithCloning(s.Value, chanType.Elem()).String(), chanType.Elem())}, } - c.Blocking[call] = true - c.translateStmt(&ast.ExprStmt{X: call}, label) + fc.Blocking[call] = true + fc.translateStmt(&ast.ExprStmt{X: call}, label) case *ast.SelectStmt: - selectionVar := c.newVariable("_selection") + selectionVar := fc.newVariable("_selection") var channels []string var caseClauses []*ast.CaseClause flattened := false @@ -465,26 +490,26 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { channels = append(channels, "[]") hasDefault = true case *ast.ExprStmt: - channels = append(channels, c.formatExpr("[%e]", astutil.RemoveParens(comm.X).(*ast.UnaryExpr).X).String()) + channels = append(channels, fc.formatExpr("[%e]", astutil.RemoveParens(comm.X).(*ast.UnaryExpr).X).String()) case *ast.AssignStmt: - channels = append(channels, c.formatExpr("[%e]", astutil.RemoveParens(comm.Rhs[0]).(*ast.UnaryExpr).X).String()) + channels = append(channels, fc.formatExpr("[%e]", astutil.RemoveParens(comm.Rhs[0]).(*ast.UnaryExpr).X).String()) case *ast.SendStmt: - chanType := c.p.TypeOf(comm.Chan).Underlying().(*types.Chan) - channels = append(channels, c.formatExpr("[%e, %s]", comm.Chan, c.translateImplicitConversionWithCloning(comm.Value, chanType.Elem())).String()) + chanType := fc.pkgCtx.TypeOf(comm.Chan).Underlying().(*types.Chan) + channels = append(channels, fc.formatExpr("[%e, %s]", comm.Chan, fc.translateImplicitConversionWithCloning(comm.Value, chanType.Elem())).String()) default: panic(fmt.Sprintf("unhandled: %T", comm)) } indexLit := &ast.BasicLit{Kind: token.INT} - c.p.Types[indexLit] = types.TypeAndValue{Type: types.Typ[types.Int], Value: constant.MakeInt64(int64(i))} + fc.pkgCtx.Types[indexLit] = types.TypeAndValue{Type: types.Typ[types.Int], Value: constant.MakeInt64(int64(i))} var bodyPrefix []ast.Stmt if assign, ok := clause.Comm.(*ast.AssignStmt); ok { - switch rhsType := c.p.TypeOf(assign.Rhs[0]).(type) { + switch rhsType := fc.pkgCtx.TypeOf(assign.Rhs[0]).(type) { case *types.Tuple: - bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1]", rhsType)}, Tok: assign.Tok}} + bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{fc.newIdent(selectionVar+"[1]", rhsType)}, Tok: assign.Tok}} default: - bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}} + bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{fc.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}} } } @@ -493,21 +518,23 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { Body: append(bodyPrefix, clause.Body...), }) - flattened = flattened || c.Flattened[clause] + flattened = flattened || fc.Flattened[clause] } - selectCall := c.setType(&ast.CallExpr{ - Fun: c.newIdent("$select", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", types.NewInterface(nil, nil))), types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])), false)), - Args: []ast.Expr{c.newIdent(fmt.Sprintf("[%s]", strings.Join(channels, ", ")), types.NewInterface(nil, nil))}, + selectCall := fc.setType(&ast.CallExpr{ + Fun: fc.newIdent("$select", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", types.NewInterface(nil, nil))), types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])), false)), + Args: []ast.Expr{fc.newIdent(fmt.Sprintf("[%s]", strings.Join(channels, ", ")), types.NewInterface(nil, nil))}, }, types.Typ[types.Int]) - c.Blocking[selectCall] = !hasDefault - c.Printf("%s = %s;", selectionVar, c.translateExpr(selectCall)) + if !hasDefault { + fc.Blocking[selectCall] = true + } + fc.Printf("%s = %s;", selectionVar, fc.translateExpr(selectCall)) if len(caseClauses) != 0 { translateCond := func(cond ast.Expr) *expression { - return c.formatExpr("%s[0] === %e", selectionVar, cond) + return fc.formatExpr("%s[0] === %e", selectionVar, cond) } - c.translateBranchingStmt(caseClauses, nil, true, translateCond, label, flattened) + fc.translateBranchingStmt(caseClauses, nil, true, translateCond, label, flattened) } case *ast.EmptyStmt: @@ -519,31 +546,31 @@ func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { } } -func (c *funcContext) translateBranchingStmt(caseClauses []*ast.CaseClause, defaultClause *ast.CaseClause, canBreak bool, translateCond func(ast.Expr) *expression, label *types.Label, flatten bool) { +func (fc *funcContext) translateBranchingStmt(caseClauses []*ast.CaseClause, defaultClause *ast.CaseClause, canBreak bool, translateCond func(ast.Expr) *expression, label *types.Label, flatten bool) { var caseOffset, defaultCase, endCase int if flatten { - caseOffset = c.caseCounter + caseOffset = fc.caseCounter defaultCase = caseOffset + len(caseClauses) endCase = defaultCase if defaultClause != nil { endCase++ } - c.caseCounter = endCase + 1 + fc.caseCounter = endCase + 1 } hasBreak := false if canBreak { - prevFlowData := c.flowDatas[nil] + prevFlowData := fc.flowDatas[nil] data := &flowData{ postStmt: prevFlowData.postStmt, // for "continue" of outer loop beginCase: prevFlowData.beginCase, // same endCase: endCase, } - c.flowDatas[nil] = data - c.flowDatas[label] = data + fc.flowDatas[nil] = data + fc.flowDatas[label] = data defer func() { - delete(c.flowDatas, label) - c.flowDatas[nil] = prevFlowData + delete(fc.flowDatas, label) + fc.flowDatas[nil] = prevFlowData }() for _, child := range caseClauses { @@ -558,7 +585,7 @@ func (c *funcContext) translateBranchingStmt(caseClauses []*ast.CaseClause, defa } if label != nil && !flatten { - c.Printf("%s:", label.Name()) + fc.Printf("%s:", label.Name()) } condStrs := make([]string, len(caseClauses)) @@ -569,12 +596,12 @@ func (c *funcContext) translateBranchingStmt(caseClauses []*ast.CaseClause, defa } condStrs[i] = strings.Join(conds, " || ") if flatten { - c.Printf("/* */ if (%s) { $s = %d; continue; }", condStrs[i], caseOffset+i) + fc.Printf("/* */ if (%s) { $s = %d; continue; }", condStrs[i], caseOffset+i) } } if flatten { - c.Printf("/* */ $s = %d; continue;", defaultCase) + fc.Printf("/* */ $s = %d; continue;", defaultCase) } prefix := "" @@ -585,62 +612,62 @@ func (c *funcContext) translateBranchingStmt(caseClauses []*ast.CaseClause, defa } for i, clause := range caseClauses { - c.SetPos(clause.Pos()) - c.PrintCond(!flatten, fmt.Sprintf("%sif (%s) {", prefix, condStrs[i]), fmt.Sprintf("case %d:", caseOffset+i)) - c.Indent(func() { - c.translateStmtList(clause.Body) - if flatten && (i < len(caseClauses)-1 || defaultClause != nil) && !endsWithReturn(clause.Body) { - c.Printf("$s = %d; continue;", endCase) + fc.SetPos(clause.Pos()) + fc.PrintCond(!flatten, fmt.Sprintf("%sif (%s) {", prefix, condStrs[i]), fmt.Sprintf("case %d:", caseOffset+i)) + fc.Indent(func() { + fc.translateStmtList(clause.Body) + if flatten && (i < len(caseClauses)-1 || defaultClause != nil) && !astutil.EndsWithReturn(clause.Body) { + fc.Printf("$s = %d; continue;", endCase) } }) prefix = "} else " } if defaultClause != nil { - c.PrintCond(!flatten, prefix+"{", fmt.Sprintf("case %d:", caseOffset+len(caseClauses))) - c.Indent(func() { - c.translateStmtList(defaultClause.Body) + fc.PrintCond(!flatten, prefix+"{", fmt.Sprintf("case %d:", caseOffset+len(caseClauses))) + fc.Indent(func() { + fc.translateStmtList(defaultClause.Body) }) } - c.PrintCond(!flatten, "}"+suffix, fmt.Sprintf("case %d:", endCase)) + fc.PrintCond(!flatten, "}"+suffix, fmt.Sprintf("case %d:", endCase)) } -func (c *funcContext) translateLoopingStmt(cond func() string, body *ast.BlockStmt, bodyPrefix, post func(), label *types.Label, flatten bool) { - prevFlowData := c.flowDatas[nil] +func (fc *funcContext) translateLoopingStmt(cond func() string, body *ast.BlockStmt, bodyPrefix, post func(), label *types.Label, flatten bool) { + prevFlowData := fc.flowDatas[nil] data := &flowData{ postStmt: post, } if flatten { - data.beginCase = c.caseCounter - data.endCase = c.caseCounter + 1 - c.caseCounter += 2 + data.beginCase = fc.caseCounter + data.endCase = fc.caseCounter + 1 + fc.caseCounter += 2 } - c.flowDatas[nil] = data - c.flowDatas[label] = data + fc.flowDatas[nil] = data + fc.flowDatas[label] = data defer func() { - delete(c.flowDatas, label) - c.flowDatas[nil] = prevFlowData + delete(fc.flowDatas, label) + fc.flowDatas[nil] = prevFlowData }() if !flatten && label != nil { - c.Printf("%s:", label.Name()) + fc.Printf("%s:", label.Name()) } - c.PrintCond(!flatten, "while (true) {", fmt.Sprintf("case %d:", data.beginCase)) - c.Indent(func() { + isTerminated := false + fc.PrintCond(!flatten, "while (true) {", fmt.Sprintf("case %d:", data.beginCase)) + fc.Indent(func() { condStr := cond() if condStr != "true" { - c.PrintCond(!flatten, fmt.Sprintf("if (!(%s)) { break; }", condStr), fmt.Sprintf("if(!(%s)) { $s = %d; continue; }", condStr, data.endCase)) + fc.PrintCond(!flatten, fmt.Sprintf("if (!(%s)) { break; }", condStr), fmt.Sprintf("if(!(%s)) { $s = %d; continue; }", condStr, data.endCase)) } - prevEV := c.p.escapingVars - c.handleEscapingVars(body) + prevEV := fc.pkgCtx.escapingVars + fc.handleEscapingVars(body) if bodyPrefix != nil { bodyPrefix() } - c.translateStmtList(body.List) - isTerminated := false + fc.translateStmtList(body.List) if len(body.List) != 0 { switch body.List[len(body.List)-1].(type) { case *ast.ReturnStmt, *ast.BranchStmt: @@ -651,31 +678,50 @@ func (c *funcContext) translateLoopingStmt(cond func() string, body *ast.BlockSt post() } - c.p.escapingVars = prevEV + fc.pkgCtx.escapingVars = prevEV }) - c.PrintCond(!flatten, "}", fmt.Sprintf("$s = %d; continue; case %d:", data.beginCase, data.endCase)) + if flatten { + // If the last statement of the loop is a return or unconditional branching + // statement, there's no need for an instruction to go back to the beginning + // of the loop. + if !isTerminated { + fc.Printf("$s = %d; continue;", data.beginCase) + } + fc.Printf("case %d:", data.endCase) + } else { + fc.Printf("}") + } } -func (c *funcContext) translateAssign(lhs, rhs ast.Expr, define bool) string { +func (fc *funcContext) translateAssign(lhs, rhs ast.Expr, define bool) string { lhs = astutil.RemoveParens(lhs) if isBlank(lhs) { panic("translateAssign with blank lhs") } if l, ok := lhs.(*ast.IndexExpr); ok { - if t, ok := c.p.TypeOf(l.X).Underlying().(*types.Map); ok { - if typesutil.IsJsObject(c.p.TypeOf(l.Index)) { - c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: l.Index.Pos(), Msg: "cannot use js.Object as map key"}) - } - keyVar := c.newVariable("_key") - return fmt.Sprintf(`%s = %s; (%s || $throwRuntimeError("assignment to entry in nil map"))[%s.keyFor(%s)] = { k: %s, v: %s };`, keyVar, c.translateImplicitConversionWithCloning(l.Index, t.Key()), c.translateExpr(l.X), c.typeName(t.Key()), keyVar, keyVar, c.translateImplicitConversionWithCloning(rhs, t.Elem())) + if t, ok := fc.pkgCtx.TypeOf(l.X).Underlying().(*types.Map); ok { + if typesutil.IsJsObject(fc.pkgCtx.TypeOf(l.Index)) { + fc.pkgCtx.errList = append(fc.pkgCtx.errList, types.Error{Fset: fc.pkgCtx.fileSet, Pos: l.Index.Pos(), Msg: "cannot use js.Object as map key"}) + } + keyVar := fc.newVariable("_key") + return fmt.Sprintf( + `%s = %s; (%s || $throwRuntimeError("assignment to entry in nil map")).set(%s.keyFor(%s), { k: %s, v: %s });`, + keyVar, + fc.translateImplicitConversionWithCloning(l.Index, t.Key()), + fc.translateExpr(l.X), + fc.typeName(t.Key()), + keyVar, + keyVar, + fc.translateImplicitConversionWithCloning(rhs, t.Elem()), + ) } } - lhsType := c.p.TypeOf(lhs) - rhsExpr := c.translateImplicitConversion(rhs, lhsType) + lhsType := fc.pkgCtx.TypeOf(lhs) + rhsExpr := fc.translateImplicitConversion(rhs, lhsType) if _, ok := rhs.(*ast.CompositeLit); ok && define { - return fmt.Sprintf("%s = %s;", c.translateExpr(lhs), rhsExpr) // skip $copy + return fmt.Sprintf("%s = %s;", fc.translateExpr(lhs), rhsExpr) // skip $copy } isReflectValue := false @@ -686,38 +732,38 @@ func (c *funcContext) translateAssign(lhs, rhs ast.Expr, define bool) string { switch lhsType.Underlying().(type) { case *types.Array, *types.Struct: if define { - return fmt.Sprintf("%s = $clone(%s, %s);", c.translateExpr(lhs), rhsExpr, c.typeName(lhsType)) + return fmt.Sprintf("%s = $clone(%s, %s);", fc.translateExpr(lhs), rhsExpr, fc.typeName(lhsType)) } - return fmt.Sprintf("%s.copy(%s, %s);", c.typeName(lhsType), c.translateExpr(lhs), rhsExpr) + return fmt.Sprintf("%s.copy(%s, %s);", fc.typeName(lhsType), fc.translateExpr(lhs), rhsExpr) } } switch l := lhs.(type) { case *ast.Ident: - return fmt.Sprintf("%s = %s;", c.objectName(c.p.ObjectOf(l)), rhsExpr) + return fmt.Sprintf("%s = %s;", fc.objectName(fc.pkgCtx.ObjectOf(l)), rhsExpr) case *ast.SelectorExpr: - sel, ok := c.p.SelectionOf(l) + sel, ok := fc.pkgCtx.SelectionOf(l) if !ok { // qualified identifier - return fmt.Sprintf("%s = %s;", c.objectName(c.p.Uses[l.Sel]), rhsExpr) + return fmt.Sprintf("%s = %s;", fc.objectName(fc.pkgCtx.Uses[l.Sel]), rhsExpr) } - fields, jsTag := c.translateSelection(sel, l.Pos()) + fields, jsTag := fc.translateSelection(sel, l.Pos()) if jsTag != "" { - return fmt.Sprintf("%s.%s%s = %s;", c.translateExpr(l.X), strings.Join(fields, "."), formatJSStructTagVal(jsTag), c.externalize(rhsExpr.String(), sel.Type())) + return fmt.Sprintf("%s.%s%s = %s;", fc.translateExpr(l.X), strings.Join(fields, "."), formatJSStructTagVal(jsTag), fc.externalize(rhsExpr.String(), sel.Type())) } - return fmt.Sprintf("%s.%s = %s;", c.translateExpr(l.X), strings.Join(fields, "."), rhsExpr) + return fmt.Sprintf("%s.%s = %s;", fc.translateExpr(l.X), strings.Join(fields, "."), rhsExpr) case *ast.StarExpr: - return fmt.Sprintf("%s.$set(%s);", c.translateExpr(l.X), rhsExpr) + return fmt.Sprintf("%s.$set(%s);", fc.translateExpr(l.X), rhsExpr) case *ast.IndexExpr: - switch t := c.p.TypeOf(l.X).Underlying().(type) { + switch t := fc.pkgCtx.TypeOf(l.X).Underlying().(type) { case *types.Array, *types.Pointer: - pattern := rangeCheck("%1e[%2f] = %3s", c.p.Types[l.Index].Value != nil, true) + pattern := rangeCheck("%1e[%2f] = %3s", fc.pkgCtx.Types[l.Index].Value != nil, true) if _, ok := t.(*types.Pointer); ok { // check pointer for nil (attribute getter causes a panic) pattern = `%1e.nilCheck, ` + pattern } - return c.formatExpr(pattern, l.X, l.Index, rhsExpr).String() + ";" + return fc.formatExpr(pattern, l.X, l.Index, rhsExpr).String() + ";" case *types.Slice: - return c.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f] = %3s", c.p.Types[l.Index].Value != nil, false), l.X, l.Index, rhsExpr).String() + ";" + return fc.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f] = %3s", fc.pkgCtx.Types[l.Index].Value != nil, false), l.X, l.Index, rhsExpr).String() + ";" default: panic(fmt.Sprintf("Unhandled lhs type: %T\n", t)) } @@ -726,61 +772,61 @@ func (c *funcContext) translateAssign(lhs, rhs ast.Expr, define bool) string { } } -func (c *funcContext) translateResults(results []ast.Expr) string { - tuple := c.sig.Results() +func (fc *funcContext) translateResults(results []ast.Expr) string { + tuple := fc.sig.Results() switch tuple.Len() { case 0: return "" case 1: - result := c.zeroValue(tuple.At(0).Type()) + result := fc.zeroValue(tuple.At(0).Type()) if results != nil { result = results[0] } - v := c.translateImplicitConversion(result, tuple.At(0).Type()) - c.delayedOutput = nil + v := fc.translateImplicitConversion(result, tuple.At(0).Type()) + fc.delayedOutput = nil return " " + v.String() default: if len(results) == 1 { - resultTuple := c.p.TypeOf(results[0]).(*types.Tuple) + resultTuple := fc.pkgCtx.TypeOf(results[0]).(*types.Tuple) if resultTuple.Len() != tuple.Len() { panic("invalid tuple return assignment") } - resultExpr := c.translateExpr(results[0]).String() + resultExpr := fc.translateExpr(results[0]).String() if types.Identical(resultTuple, tuple) { return " " + resultExpr } - tmpVar := c.newVariable("_returncast") - c.Printf("%s = %s;", tmpVar, resultExpr) + tmpVar := fc.newVariable("_returncast") + fc.Printf("%s = %s;", tmpVar, resultExpr) // Not all the return types matched, map everything out for implicit casting results = make([]ast.Expr, resultTuple.Len()) for i := range results { - results[i] = c.newIdent(fmt.Sprintf("%s[%d]", tmpVar, i), resultTuple.At(i).Type()) + results[i] = fc.newIdent(fmt.Sprintf("%s[%d]", tmpVar, i), resultTuple.At(i).Type()) } } values := make([]string, tuple.Len()) for i := range values { - result := c.zeroValue(tuple.At(i).Type()) + result := fc.zeroValue(tuple.At(i).Type()) if results != nil { result = results[i] } - values[i] = c.translateImplicitConversion(result, tuple.At(i).Type()).String() + values[i] = fc.translateImplicitConversion(result, tuple.At(i).Type()).String() } - c.delayedOutput = nil + fc.delayedOutput = nil return " [" + strings.Join(values, ", ") + "]" } } -func (c *funcContext) labelCase(label *types.Label) int { - labelCase, ok := c.labelCases[label] +func (fc *funcContext) labelCase(label *types.Label) int { + labelCase, ok := fc.labelCases[label] if !ok { - labelCase = c.caseCounter - c.caseCounter++ - c.labelCases[label] = labelCase + labelCase = fc.caseCounter + fc.caseCounter++ + fc.labelCases[label] = labelCase } return labelCase } diff --git a/compiler/utils.go b/compiler/utils.go index d5452e0a..5cde7b3c 100644 --- a/compiler/utils.go +++ b/compiler/utils.go @@ -3,12 +3,15 @@ package compiler import ( "bytes" "encoding/binary" + "errors" "fmt" "go/ast" "go/constant" "go/token" "go/types" "net/url" + "regexp" + "runtime/debug" "sort" "strconv" "strings" @@ -19,119 +22,142 @@ import ( "github.com/gopherjs/gopherjs/compiler/typesutil" ) -func (c *funcContext) Write(b []byte) (int, error) { - c.writePos() - c.output = append(c.output, b...) +func (fc *funcContext) Write(b []byte) (int, error) { + fc.writePos() + fc.output = append(fc.output, b...) return len(b), nil } -func (c *funcContext) Printf(format string, values ...interface{}) { - c.Write([]byte(strings.Repeat("\t", c.p.indentation))) - fmt.Fprintf(c, format, values...) - c.Write([]byte{'\n'}) - c.Write(c.delayedOutput) - c.delayedOutput = nil +func (fc *funcContext) Printf(format string, values ...interface{}) { + fc.Write([]byte(strings.Repeat("\t", fc.pkgCtx.indentation))) + fmt.Fprintf(fc, format, values...) + fc.Write([]byte{'\n'}) + fc.Write(fc.delayedOutput) + fc.delayedOutput = nil } -func (c *funcContext) PrintCond(cond bool, onTrue, onFalse string) { +func (fc *funcContext) PrintCond(cond bool, onTrue, onFalse string) { if !cond { - c.Printf("/* %s */ %s", strings.Replace(onTrue, "*/", "/", -1), onFalse) + fc.Printf("/* %s */ %s", strings.Replace(onTrue, "*/", "/", -1), onFalse) return } - c.Printf("%s", onTrue) + fc.Printf("%s", onTrue) } -func (c *funcContext) SetPos(pos token.Pos) { - c.posAvailable = true - c.pos = pos +func (fc *funcContext) SetPos(pos token.Pos) { + fc.posAvailable = true + fc.pos = pos } -func (c *funcContext) writePos() { - if c.posAvailable { - c.posAvailable = false - c.Write([]byte{'\b'}) - binary.Write(c, binary.BigEndian, uint32(c.pos)) +func (fc *funcContext) writePos() { + if fc.posAvailable { + fc.posAvailable = false + fc.Write([]byte{'\b'}) + binary.Write(fc, binary.BigEndian, uint32(fc.pos)) } } -func (c *funcContext) Indent(f func()) { - c.p.indentation++ +func (fc *funcContext) Indent(f func()) { + fc.pkgCtx.indentation++ f() - c.p.indentation-- + fc.pkgCtx.indentation-- } -func (c *funcContext) CatchOutput(indent int, f func()) []byte { - origoutput := c.output - c.output = nil - c.p.indentation += indent +func (fc *funcContext) CatchOutput(indent int, f func()) []byte { + origoutput := fc.output + fc.output = nil + fc.pkgCtx.indentation += indent f() - c.writePos() - catched := c.output - c.output = origoutput - c.p.indentation -= indent - return catched + fc.writePos() + caught := fc.output + fc.output = origoutput + fc.pkgCtx.indentation -= indent + return caught } -func (c *funcContext) Delayed(f func()) { - c.delayedOutput = c.CatchOutput(0, f) +func (fc *funcContext) Delayed(f func()) { + fc.delayedOutput = fc.CatchOutput(0, f) } -func (c *funcContext) translateArgs(sig *types.Signature, argExprs []ast.Expr, ellipsis bool) []string { - if len(argExprs) == 1 { - if tuple, isTuple := c.p.TypeOf(argExprs[0]).(*types.Tuple); isTuple { - tupleVar := c.newVariable("_tuple") - c.Printf("%s = %s;", tupleVar, c.translateExpr(argExprs[0])) - argExprs = make([]ast.Expr, tuple.Len()) - for i := range argExprs { - argExprs[i] = c.newIdent(c.formatExpr("%s[%d]", tupleVar, i).String(), tuple.At(i).Type()) - } - } +// expandTupleArgs converts a function call which argument is a tuple returned +// by another function into a set of individual call arguments corresponding to +// tuple elements. +// +// For example, for functions defined as: +// func a() (int, string) {return 42, "foo"} +// func b(a1 int, a2 string) {} +// ...the following statement: +// b(a()) +// ...will be transformed into: +// _tuple := a() +// b(_tuple[0], _tuple[1]) +func (fc *funcContext) expandTupleArgs(argExprs []ast.Expr) []ast.Expr { + if len(argExprs) != 1 { + return argExprs + } + + tuple, isTuple := fc.pkgCtx.TypeOf(argExprs[0]).(*types.Tuple) + if !isTuple { + return argExprs } - paramsLen := sig.Params().Len() + tupleVar := fc.newVariable("_tuple") + fc.Printf("%s = %s;", tupleVar, fc.translateExpr(argExprs[0])) + argExprs = make([]ast.Expr, tuple.Len()) + for i := range argExprs { + argExprs[i] = fc.newIdent(fc.formatExpr("%s[%d]", tupleVar, i).String(), tuple.At(i).Type()) + } + return argExprs +} - var varargType *types.Slice - if sig.Variadic() && !ellipsis { - varargType = sig.Params().At(paramsLen - 1).Type().(*types.Slice) +func (fc *funcContext) translateArgs(sig *types.Signature, argExprs []ast.Expr, ellipsis bool) []string { + argExprs = fc.expandTupleArgs(argExprs) + + sigTypes := signatureTypes{Sig: sig} + + if sig.Variadic() && len(argExprs) == 0 { + return []string{fmt.Sprintf("%s.nil", fc.typeName(sigTypes.VariadicType()))} } preserveOrder := false for i := 1; i < len(argExprs); i++ { - preserveOrder = preserveOrder || c.Blocking[argExprs[i]] + preserveOrder = preserveOrder || fc.Blocking[argExprs[i]] } args := make([]string, len(argExprs)) for i, argExpr := range argExprs { - var argType types.Type - switch { - case varargType != nil && i >= paramsLen-1: - argType = varargType.Elem() - default: - argType = sig.Params().At(i).Type() - } - - arg := c.translateImplicitConversionWithCloning(argExpr, argType).String() + arg := fc.translateImplicitConversionWithCloning(argExpr, sigTypes.Param(i, ellipsis)).String() - if preserveOrder && c.p.Types[argExpr].Value == nil { - argVar := c.newVariable("_arg") - c.Printf("%s = %s;", argVar, arg) + if preserveOrder && fc.pkgCtx.Types[argExpr].Value == nil { + argVar := fc.newVariable("_arg") + fc.Printf("%s = %s;", argVar, arg) arg = argVar } args[i] = arg } - if varargType != nil { - return append(args[:paramsLen-1], fmt.Sprintf("new %s([%s])", c.typeName(varargType), strings.Join(args[paramsLen-1:], ", "))) + // If variadic arguments were passed in as individual elements, regroup them + // into a slice and pass it as a single argument. + if sig.Variadic() && !ellipsis { + required := args[:sigTypes.RequiredParams()] + var variadic string + if len(args) == sigTypes.RequiredParams() { + // If no variadic parameters were passed, the slice value defaults to nil. + variadic = fmt.Sprintf("%s.nil", fc.typeName(sigTypes.VariadicType())) + } else { + variadic = fmt.Sprintf("new %s([%s])", fc.typeName(sigTypes.VariadicType()), strings.Join(args[sigTypes.RequiredParams():], ", ")) + } + return append(required, variadic) } return args } -func (c *funcContext) translateSelection(sel selection, pos token.Pos) ([]string, string) { +func (fc *funcContext) translateSelection(sel selection, pos token.Pos) ([]string, string) { var fields []string t := sel.Recv() for _, index := range sel.Index() { - if ptr, isPtr := t.(*types.Pointer); isPtr { + if ptr, isPtr := t.Underlying().(*types.Pointer); isPtr { t = ptr.Elem() } s := t.Underlying().(*types.Struct) @@ -150,7 +176,7 @@ func (c *funcContext) translateSelection(sel selection, pos token.Pos) ([]string var ok bool s, ok = ft.(*types.Struct) if !ok || s.NumFields() == 0 { - c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: pos, Msg: fmt.Sprintf("could not find field with type *js.Object for 'js' tag of field '%s'", jsFieldName), Soft: true}) + fc.pkgCtx.errList = append(fc.pkgCtx.errList, types.Error{Fset: fc.pkgCtx.fileSet, Pos: pos, Msg: fmt.Sprintf("could not find field with type *js.Object for 'js' tag of field '%s'", jsFieldName), Soft: true}) return nil, "" } } @@ -163,16 +189,16 @@ func (c *funcContext) translateSelection(sel selection, pos token.Pos) ([]string var nilObj = types.Universe.Lookup("nil") -func (c *funcContext) zeroValue(ty types.Type) ast.Expr { +func (fc *funcContext) zeroValue(ty types.Type) ast.Expr { switch t := ty.Underlying().(type) { case *types.Basic: switch { case isBoolean(t): - return c.newConst(ty, constant.MakeBool(false)) + return fc.newConst(ty, constant.MakeBool(false)) case isNumeric(t): - return c.newConst(ty, constant.MakeInt64(0)) + return fc.newConst(ty, constant.MakeInt64(0)) case isString(t): - return c.newConst(ty, constant.MakeString("")) + return fc.newConst(ty, constant.MakeString("")) case t.Kind() == types.UnsafePointer: // fall through to "nil" case t.Kind() == types.UntypedNil: @@ -181,33 +207,33 @@ func (c *funcContext) zeroValue(ty types.Type) ast.Expr { panic(fmt.Sprintf("Unhandled basic type: %v\n", t)) } case *types.Array, *types.Struct: - return c.setType(&ast.CompositeLit{}, ty) + return fc.setType(&ast.CompositeLit{}, ty) case *types.Chan, *types.Interface, *types.Map, *types.Signature, *types.Slice, *types.Pointer: // fall through to "nil" default: panic(fmt.Sprintf("Unhandled type: %T\n", t)) } - id := c.newIdent("nil", ty) - c.p.Uses[id] = nilObj + id := fc.newIdent("nil", ty) + fc.pkgCtx.Uses[id] = nilObj return id } -func (c *funcContext) newConst(t types.Type, value constant.Value) ast.Expr { +func (fc *funcContext) newConst(t types.Type, value constant.Value) ast.Expr { id := &ast.Ident{} - c.p.Types[id] = types.TypeAndValue{Type: t, Value: value} + fc.pkgCtx.Types[id] = types.TypeAndValue{Type: t, Value: value} return id } -func (c *funcContext) newVariable(name string) string { - return c.newVariableWithLevel(name, false) +func (fc *funcContext) newVariable(name string) string { + return fc.newVariableWithLevel(name, false) } -func (c *funcContext) newVariableWithLevel(name string, pkgLevel bool) string { +func (fc *funcContext) newVariableWithLevel(name string, pkgLevel bool) string { if name == "" { panic("newVariable: empty name") } name = encodeIdent(name) - if c.p.minify { + if fc.pkgCtx.minify { i := 0 for { offset := int('a') @@ -217,56 +243,56 @@ func (c *funcContext) newVariableWithLevel(name string, pkgLevel bool) string { j := i name = "" for { - name = string(offset+(j%26)) + name + name = string(rune(offset+(j%26))) + name j = j/26 - 1 if j == -1 { break } } - if c.allVars[name] == 0 { + if fc.allVars[name] == 0 { break } i++ } } - n := c.allVars[name] - c.allVars[name] = n + 1 + n := fc.allVars[name] + fc.allVars[name] = n + 1 varName := name if n > 0 { varName = fmt.Sprintf("%s$%d", name, n) } if pkgLevel { - for c2 := c.parent; c2 != nil; c2 = c2.parent { + for c2 := fc.parent; c2 != nil; c2 = c2.parent { c2.allVars[name] = n + 1 } return varName } - c.localVars = append(c.localVars, varName) + fc.localVars = append(fc.localVars, varName) return varName } -func (c *funcContext) newIdent(name string, t types.Type) *ast.Ident { +func (fc *funcContext) newIdent(name string, t types.Type) *ast.Ident { ident := ast.NewIdent(name) - c.setType(ident, t) - obj := types.NewVar(0, c.p.Pkg, name, t) - c.p.Uses[ident] = obj - c.p.objectNames[obj] = name + fc.setType(ident, t) + obj := types.NewVar(0, fc.pkgCtx.Pkg, name, t) + fc.pkgCtx.Uses[ident] = obj + fc.pkgCtx.objectNames[obj] = name return ident } -func (c *funcContext) setType(e ast.Expr, t types.Type) ast.Expr { - c.p.Types[e] = types.TypeAndValue{Type: t} +func (fc *funcContext) setType(e ast.Expr, t types.Type) ast.Expr { + fc.pkgCtx.Types[e] = types.TypeAndValue{Type: t} return e } -func (c *funcContext) pkgVar(pkg *types.Package) string { - if pkg == c.p.Pkg { +func (fc *funcContext) pkgVar(pkg *types.Package) string { + if pkg == fc.pkgCtx.Pkg { return "$pkg" } - pkgVar, found := c.p.pkgVars[pkg.Path()] + pkgVar, found := fc.pkgCtx.pkgVars[pkg.Path()] if !found { pkgVar = fmt.Sprintf(`$packages["%s"]`, pkg.Path()) } @@ -285,41 +311,41 @@ func isPkgLevel(o types.Object) bool { return o.Parent() != nil && o.Parent().Parent() == types.Universe } -func (c *funcContext) objectName(o types.Object) string { +func (fc *funcContext) objectName(o types.Object) string { if isPkgLevel(o) { - c.p.dependencies[o] = true + fc.pkgCtx.dependencies[o] = true - if o.Pkg() != c.p.Pkg || (isVarOrConst(o) && o.Exported()) { - return c.pkgVar(o.Pkg()) + "." + o.Name() + if o.Pkg() != fc.pkgCtx.Pkg || (isVarOrConst(o) && o.Exported()) { + return fc.pkgVar(o.Pkg()) + "." + o.Name() } } - name, ok := c.p.objectNames[o] + name, ok := fc.pkgCtx.objectNames[o] if !ok { - name = c.newVariableWithLevel(o.Name(), isPkgLevel(o)) - c.p.objectNames[o] = name + name = fc.newVariableWithLevel(o.Name(), isPkgLevel(o)) + fc.pkgCtx.objectNames[o] = name } - if v, ok := o.(*types.Var); ok && c.p.escapingVars[v] { + if v, ok := o.(*types.Var); ok && fc.pkgCtx.escapingVars[v] { return name + "[0]" } return name } -func (c *funcContext) varPtrName(o *types.Var) string { +func (fc *funcContext) varPtrName(o *types.Var) string { if isPkgLevel(o) && o.Exported() { - return c.pkgVar(o.Pkg()) + "." + o.Name() + "$ptr" + return fc.pkgVar(o.Pkg()) + "." + o.Name() + "$ptr" } - name, ok := c.p.varPtrNames[o] + name, ok := fc.pkgCtx.varPtrNames[o] if !ok { - name = c.newVariableWithLevel(o.Name()+"$ptr", isPkgLevel(o)) - c.p.varPtrNames[o] = name + name = fc.newVariableWithLevel(o.Name()+"$ptr", isPkgLevel(o)) + fc.pkgCtx.varPtrNames[o] = name } return name } -func (c *funcContext) typeName(ty types.Type) string { +func (fc *funcContext) typeName(ty types.Type) string { switch t := ty.(type) { case *types.Basic: return "$" + toJavaScriptType(t) @@ -327,26 +353,26 @@ func (c *funcContext) typeName(ty types.Type) string { if t.Obj().Name() == "error" { return "$error" } - return c.objectName(t.Obj()) + return fc.objectName(t.Obj()) case *types.Interface: if t.Empty() { return "$emptyInterface" } } - anonType, ok := c.p.anonTypeMap.At(ty).(*types.TypeName) + anonType, ok := fc.pkgCtx.anonTypeMap.At(ty).(*types.TypeName) if !ok { - c.initArgs(ty) // cause all embedded types to be registered - varName := c.newVariableWithLevel(strings.ToLower(typeKind(ty)[5:])+"Type", true) - anonType = types.NewTypeName(token.NoPos, c.p.Pkg, varName, ty) // fake types.TypeName - c.p.anonTypes = append(c.p.anonTypes, anonType) - c.p.anonTypeMap.Set(ty, anonType) + fc.initArgs(ty) // cause all embedded types to be registered + varName := fc.newVariableWithLevel(strings.ToLower(typeKind(ty)[5:])+"Type", true) + anonType = types.NewTypeName(token.NoPos, fc.pkgCtx.Pkg, varName, ty) // fake types.TypeName + fc.pkgCtx.anonTypes = append(fc.pkgCtx.anonTypes, anonType) + fc.pkgCtx.anonTypeMap.Set(ty, anonType) } - c.p.dependencies[anonType] = true + fc.pkgCtx.dependencies[anonType] = true return anonType.Name() } -func (c *funcContext) externalize(s string, t types.Type) string { +func (fc *funcContext) externalize(s string, t types.Type) string { if typesutil.IsJsObject(t) { return s } @@ -359,18 +385,18 @@ func (c *funcContext) externalize(s string, t types.Type) string { return "null" } } - return fmt.Sprintf("$externalize(%s, %s)", s, c.typeName(t)) + return fmt.Sprintf("$externalize(%s, %s)", s, fc.typeName(t)) } -func (c *funcContext) handleEscapingVars(n ast.Node) { +func (fc *funcContext) handleEscapingVars(n ast.Node) { newEscapingVars := make(map[*types.Var]bool) - for escaping := range c.p.escapingVars { + for escaping := range fc.pkgCtx.escapingVars { newEscapingVars[escaping] = true } - c.p.escapingVars = newEscapingVars + fc.pkgCtx.escapingVars = newEscapingVars var names []string - objs := analysis.EscapingObjects(n, c.p.Info.Info) + objs := analysis.EscapingObjects(n, fc.pkgCtx.Info.Info) sort.Slice(objs, func(i, j int) bool { if objs[i].Name() == objs[j].Name() { return objs[i].Pos() < objs[j].Pos() @@ -378,12 +404,12 @@ func (c *funcContext) handleEscapingVars(n ast.Node) { return objs[i].Name() < objs[j].Name() }) for _, obj := range objs { - names = append(names, c.objectName(obj)) - c.p.escapingVars[obj] = true + names = append(names, fc.objectName(obj)) + fc.pkgCtx.escapingVars[obj] = true } sort.Strings(names) for _, name := range names { - c.Printf("%s = [%s];", name, name) + fc.Printf("%s = [%s];", name, name) } } @@ -478,6 +504,36 @@ func isBlank(expr ast.Expr) bool { return false } +// isWrapped returns true for types that may need to be boxed to access full +// functionality of the Go type. +// +// For efficiency or interoperability reasons certain Go types can be represented +// by JavaScript values that weren't constructed by the corresponding Go type +// constructor. +// +// For example, consider a Go type: +// +// type SecretInt int +// func (_ SecretInt) String() string { return "" } +// +// func main() { +// var i SecretInt = 1 +// println(i.String()) +// } +// +// For this example the compiler will generate code similar to the snippet below: +// +// SecretInt = $pkg.SecretInt = $newType(4, $kindInt, "main.SecretInt", true, "main", true, null); +// SecretInt.prototype.String = function() { +// return ""; +// }; +// main = function() { +// var i = 1; +// console.log(new SecretInt(i).String()); +// }; +// +// Note that the generated code assigns a primitive "number" value into i, and +// only boxes it into an object when it's necessary to access its methods. func isWrapped(ty types.Type) bool { switch t := ty.Underlying().(type) { case *types.Basic: @@ -633,15 +689,6 @@ func rangeCheck(pattern string, constantIndex, array bool) string { return "(" + check + ` ? ($throwRuntimeError("index out of range"), undefined) : ` + pattern + ")" } -func endsWithReturn(stmts []ast.Stmt) bool { - if len(stmts) > 0 { - if _, ok := stmts[len(stmts)-1].(*ast.ReturnStmt); ok { - return true - } - } - return false -} - func encodeIdent(name string) string { return strings.Replace(url.QueryEscape(name), "%", "$", -1) } @@ -671,3 +718,112 @@ func formatJSStructTagVal(jsTag string) string { // Safe to use dot notation without any escaping. return "." + jsTag } + +// signatureTypes is a helper that provides convenient access to function +// signature type information. +type signatureTypes struct { + Sig *types.Signature +} + +// RequiredParams returns the number of required parameters in the function signature. +func (st signatureTypes) RequiredParams() int { + l := st.Sig.Params().Len() + if st.Sig.Variadic() { + return l - 1 // Last parameter is a slice of variadic params. + } + return l +} + +// VariadicType returns the slice-type corresponding to the signature's variadic +// parameter, or nil of the signature is not variadic. With the exception of +// the special-case `append([]byte{}, "string"...)`, the returned type is +// `*types.Slice` and `.Elem()` method can be used to get the type of individual +// arguments. +func (st signatureTypes) VariadicType() types.Type { + if !st.Sig.Variadic() { + return nil + } + return st.Sig.Params().At(st.Sig.Params().Len() - 1).Type() +} + +// Returns the expected argument type for the i'th argument position. +// +// This function is able to return correct expected types for variadic calls +// both when ellipsis syntax (e.g. myFunc(requiredArg, optionalArgSlice...)) +// is used and when optional args are passed individually. +// +// The returned types may differ from the actual argument expression types if +// there is an implicit type conversion involved (e.g. passing a struct into a +// function that expects an interface). +func (st signatureTypes) Param(i int, ellipsis bool) types.Type { + if i < st.RequiredParams() { + return st.Sig.Params().At(i).Type() + } + if !st.Sig.Variadic() { + // This should never happen if the code was type-checked successfully. + panic(fmt.Errorf("Tried to access parameter %d of a non-variadic signature %s", i, st.Sig)) + } + if ellipsis { + return st.VariadicType() + } + return st.VariadicType().(*types.Slice).Elem() +} + +// ErrorAt annotates an error with a position in the source code. +func ErrorAt(err error, fset *token.FileSet, pos token.Pos) error { + return fmt.Errorf("%s: %w", fset.Position(pos), err) +} + +// FatalError is an error compiler panics with when it encountered a fatal error. +// +// FatalError implements io.Writer, which can be used to record any free-form +// debugging details for human consumption. This information will be included +// into String() result along with the rest. +type FatalError struct { + cause interface{} + stack []byte + clues strings.Builder +} + +func (b FatalError) Unwrap() error { + if b.cause == nil { + return nil + } + if err, ok := b.cause.(error); ok { + return err + } + if s, ok := b.cause.(string); ok { + return errors.New(s) + } + return fmt.Errorf("[%T]: %v", b.cause, b.cause) +} + +// Write implements io.Writer and can be used to store free-form debugging clues. +func (b *FatalError) Write(p []byte) (n int, err error) { return b.clues.Write(p) } + +func (b FatalError) Error() string { + buf := &strings.Builder{} + fmt.Fprintln(buf, "[compiler panic] ", strings.TrimSpace(b.Unwrap().Error())) + if b.clues.Len() > 0 { + fmt.Fprintln(buf, "\n"+b.clues.String()) + } + if len(b.stack) > 0 { + // Shift stack track by 2 spaces for better readability. + stack := regexp.MustCompile("(?m)^").ReplaceAll(b.stack, []byte(" ")) + fmt.Fprintln(buf, "\nOriginal stack trace:\n", string(stack)) + } + return buf.String() +} + +func bailout(cause interface{}) *FatalError { + b := &FatalError{ + cause: cause, + stack: debug.Stack(), + } + return b +} + +func bailingOut(err interface{}) (*FatalError, bool) { + fe, ok := err.(*FatalError) + return fe, ok +} diff --git a/compiler/version_check.go b/compiler/version_check.go index 48bb27ae..6306fc12 100644 --- a/compiler/version_check.go +++ b/compiler/version_check.go @@ -1,9 +1,79 @@ -// +build go1.12 -// +build !go1.13 +//go:build go1.16 && !go1.19 +// +build go1.16,!go1.19 package compiler -const ___GOPHERJS_REQUIRES_GO_VERSION_1_12___ = true +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" +) // Version is the GopherJS compiler version string. -const Version = "1.12-2" +const Version = "1.18.0+go1.18.5" + +// GoVersion is the current Go 1.x version that GopherJS is compatible with. +var GoVersion = runtime.Version()[:6] + +// CheckGoVersion checks the version of the Go distribution +// at goroot, and reports an error if it's not compatible +// with this version of the GopherJS compiler. +func CheckGoVersion(goroot string) error { + if nvc, err := strconv.ParseBool(os.Getenv("GOPHERJS_SKIP_VERSION_CHECK")); err == nil && nvc { + return nil + } + v, err := goRootVersion(goroot) + if err != nil { + return fmt.Errorf("unable to detect Go version for %q: %w", goroot, err) + } + if !strings.HasPrefix(v, GoVersion) { + return fmt.Errorf("GopherJS %s requires a %v distribution, but found version %s", Version, GoVersion, v) + } + return nil +} + +// goRootVersion defermines Go release for the given GOROOT installation. +func goRootVersion(goroot string) (string, error) { + v, err := os.ReadFile(filepath.Join(goroot, "VERSION")) + if err == nil { + // Standard Go distribution has VERSION file inside its GOROOT, checking it + // is the most efficient option. + return string(v), nil + } + + // Fall back to the "go version" command. + cmd := exec.Command(filepath.Join(goroot, "bin", "go"), "version") + out, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("`go version` command failed: %w", err) + } + // Expected output: go version go1.18.1 linux/amd64 + parts := strings.Split(string(out), " ") + if len(parts) != 4 { + return "", fmt.Errorf("unexpected `go version` output %q, expected 4 words", string(out)) + } + return parts[2], nil +} + +// GoRelease does a best-effort to identify Go release we are building with. +// If unable to determin the precise version for the given GOROOT, falls back +// to the best guess available. +func GoRelease(goroot string) string { + v, err := goRootVersion(goroot) + if err == nil { + // Prefer using the actual version of the GOROOT we are working with. + return v + } + // Use Go version GopherJS release was tested against as a fallback. By + // convention, it is included in the GopherJS version after the plus sign. + parts := strings.Split(Version, "+") + if len(parts) == 2 { + return parts[1] + } + // If everything else fails, return just the Go version without patch level. + return runtime.Version() +} diff --git a/compiler/version_check_test.go b/compiler/version_check_test.go new file mode 100644 index 00000000..aa2f4d1f --- /dev/null +++ b/compiler/version_check_test.go @@ -0,0 +1,28 @@ +package compiler + +import ( + "runtime" + "strings" + "testing" +) + +func TestGoRelease(t *testing.T) { + t.Run("goroot", func(t *testing.T) { + got := GoRelease(runtime.GOROOT()) + want := runtime.Version() + if got != want { + t.Fatalf("Got: goRelease(%q) returned %q. Want %s.", runtime.GOROOT(), got, want) + } + }) + + t.Run("fallback", func(t *testing.T) { + const goroot = "./invalid goroot" + got := GoRelease(goroot) + if got == "" { + t.Fatalf("Got: goRelease(%q) returned \"\". Want: a Go version.", goroot) + } + if !strings.HasSuffix(Version, "+"+got) { + t.Fatalf("Got: goRelease(%q) returned %q. Want: a fallback to GopherJS version suffix %q.", goroot, got, Version) + } + }) +} diff --git a/doc/compatibility.md b/doc/compatibility.md new file mode 100644 index 00000000..8bb41c4a --- /dev/null +++ b/doc/compatibility.md @@ -0,0 +1,68 @@ +# GopherJS compatibility + +_TL;DR: GopherJS aims to provide full compatibility with regular Go, but JavaScript runtime introduces unavoidable differences._ + +Go ecosystem is broad and complex, which means there are several dimensions in which different levels of compatibility can be achieved: + +1. **[Go Language Specification](https://golang.org/ref/spec)**: full compatibility. With the exception of several minor differences documented below, GopherJS _should_ be fully compliant with the language specification (e.g. type system, goroutines, operations, built-ins, etc.). +2. **[Go Standard Library](https://pkg.go.dev/std)**: mostly compatible. GopherJS attempts to support as much of standard library as possible, but certain functionality is impossible or difficult to implement within the JavaScript runtime, most of which is related to os interaction, low-level runtime manipulation or `unsafe`. See [package compatibility table](packages.md) and [syscall support](syscalls.md) for details. +3. **Build system and tooling**: partially compatible. The `gopherjs` CLI tool is used to build and test GopherJS code. It currently supports building `GOPATH` projects, but Go Modules support is missing (see https://github.com/gopherjs/gopherjs/issues/855). Our goal is to reach complete feature parity with the `go` tool, but there is a large amount of work required to get there. Other notable challenges include: + - Limited [compiler directive](pragma.md) (a.k.a. "pragma") support. Those are considered compiler implementation-specific and are generally not portable. + - GopherJS ships with [standard library augmentations](../compiler/natives/src/), that are required to make it work in a browser. Those are applied on-the-fly during the build process and are generally invisible to any third-party tooling such as linters. In most cases that shouldn't matter, since they never change public interfaces of the standard library packages, but this is something to be aware of. + - Runtime debuggers and profilers. Since GopherJS compiles Go to JavaScript, one must use JavaScript debuggers and profilers (e.g. browser dev tools) instead of the normal Go ones (e.g. delve or pprof). Unfortunately, limited sourcemap support makes this experience less than ideal at the moment. + +## Go version compatibility + +In general, for a given release of GopherJS the following statements _should_ be true: + +- GopherJS compiler can be built from source with the latest stable Go release at the time when the GopherJS release is created, or any newer Go release. + + Example: you can build GopherJS `1.12-3` with Go `1.12` or newer. + +- GopherJS compiler can build code using standard library of a specific Go version, normally the latest stable at the time of GopherJS release. In most cases, it should be compatible with all patch versions within the minor Go version, but this is not guaranteed. + + Example: GopherJS `1.16.0+go1.16.2` (see [developer documentation](https://github.com/gopherjs/gopherjs/wiki/Developer-Guidelines#versions) about GopherJS versioning schema) can build code with GOROOT pointing at Go `1.16.0` or `1.16.2`, but not at Go `1.15.x` or `1.17.x`. + +- Users can use older GopherJS releases if they need to target older Go versions, but only the latest GopherJS release is officially supported at this time. + +_Note_: we would love to make GopherJS compatible with more Go releases, but the amount of effort required to support that exceeds amount of time we currently have available. If you wish to lend your help to make that possible, please reach out to us! + +## How to report a incompatibility issue? + +First of all, please check the list of known issues below, [package support table](packages.md), as well as [open issues](https://github.com/gopherjs/gopherjs/issues) on GitHub. If the issue is already known, great! You've saved yourself a bit of time. Feel free to add any extra details you think are relevant, though. + +If the issue is not known yet, please open a new issue on GitHub and include the following information: + +1. Go and GopherJS versions you are using. +2. In which environment do you see the issue (browser, nodejs, etc.). +3. A minimal program that behaves differently when compiled with the regular Go compiler and GopherJS. + +Now that the issue exists, we (GopherJS maintainers) will do our best to address it as promptly as we can. Note, however, that all of us are working on GopherJS in our spare time after our job and family responsibilities, so we can't guarantee an immediate fix. + +🚧 If you would like to help, please consider [submitting a pull request](https://github.com/gopherjs/gopherjs/wiki/Developer-Guidelines) with a fix. If you are unsure of the best way to approach the issue, we will be happy to share whatever knowledge we can! 😃 + +## How to write portable code + +For the most part, GopherJS shouldn't require any special support for the code that only uses [supported standard packages](packages.md). + +However, if you do need to provide different implementations depending on the target architecture, you can use [build constraints](https://golang.org/cmd/go/#hdr-Build_constraints) to do so. By default, GopherJS uses `GOOS=js GOARCH=ecmascript`, which can be used in build constraints: + +- `//go:build js` — the source will be used for GopherJS and Go WebAssembly, but not for native builds. +- `//go:build js && ecmascript` — the source will be used for GopherJS only, and not WebAssembly or native builds. +- `//go:build js && wasm` — the source will be used for Go WebAssembly, and not GopherJS or native builds. +- `//go:build gopherjs` — the source will be used only by the GopherJS compiler, regardless of the `GOOS` and `GOARCH`. Use this constraint for sources that are not portable to other Go implementations. + +Also be careful about using GopherJS-specific packages (e.g. `github.com/gopherjs/gopherjs/js`) or features (e.g. [wrapping JavaScript objects](https://github.com/gopherjs/gopherjs/wiki/JavaScript-Tips-and-Gotchas#tips) into Go structs), since those won't work outside of GopherJS. + +### Portability between Go and TinyGo WebAssembly implementations + +GopherJS implements `syscall/js` package, so it _should_ be able to run most code written for WebAssembly. However, in practice this topic is largely unexplored at this time. + +It is worth noting that GopherJS emulates 32-bit environment, whereas Go WebAssembly is 64 bit, so you should use fixed-size types if you need to guarantee consistent behavior between the two architectures. + +🚧 If you have first-hand experience with this, please consider adding it to this section! + +## Known Go specification violations + +- Bit shifts of a negative amount (e.g. `42 << -1`) panic in Go, but not in GopherJS. +- See also [open issues](https://github.com/gopherjs/gopherjs/issues) and [known failing compiler tests](https://github.com/gopherjs/gopherjs/blob/master/tests/run.go). diff --git a/doc/packages.md b/doc/packages.md index c86c36f3..b49db731 100644 --- a/doc/packages.md +++ b/doc/packages.md @@ -4,154 +4,164 @@ On each commit, Circle CI automatically compiles all supported packages with Gop [![Circle CI](https://circleci.com/gh/gopherjs/gopherjs.svg?style=svg)](https://circleci.com/gh/gopherjs/gopherjs) -Name | Supported | Comment ------------------- | ------------ | ---------------------------------------------------------------------------------- -archive | | --- tar | ✅ yes | --- zip | ✅ yes | -bufio | ✅ yes | -builtin | ✅ yes | -bytes | ✅ yes | -compress | | --- bzip2 | ✅ yes | --- flate | ✅ yes | --- gzip | ✅ yes | --- lzw | ✅ yes | --- zlib | ✅ yes | -container | | --- heap | ✅ yes | --- list | ✅ yes | --- ring | ✅ yes | -crypto | | --- aes | ✅ yes | --- cipher | ✅ yes | --- des | ✅ yes | --- dsa | ✅ yes | --- ecdsa | ✅ yes | --- elliptic | ✅ yes | --- hmac | ✅ yes | --- md5 | ✅ yes | --- rand | ✅ yes | --- rc4 | ✅ yes | --- rsa | ✅ yes | --- sha1 | ✅ yes | --- sha256 | ✅ yes | --- sha512 | ✅ yes | --- subtle | ✅ yes | --- tls | ❌ no | --- x509 | ✅ yes | --- -- pkix | ✅ yes | -database | | --- sql | ✅ yes | --- -- driver | ✅ yes | -debug | | --- dwarf | ✅ yes | --- elf | ✅ yes | --- gosym | ☑️ partially | on binaries generated by gc --- macho | ✅ yes | --- pe | ✅ yes | -encoding | | --- ascii85 | ✅ yes | --- asn1 | ✅ yes | --- base32 | ✅ yes | --- base64 | ✅ yes | --- binary | ✅ yes | --- csv | ✅ yes | --- gob | ✅ yes | --- hex | ✅ yes | --- json | ✅ yes | --- pem | ✅ yes | --- xml | ✅ yes | -errors | ✅ yes | -expvar | ✅ yes | -flag | ✅ yes | -fmt | ✅ yes | -go | | --- ast | ✅ yes | --- build | ❌ no | --- constant | ✅ yes | --- doc | ✅ yes | --- format | ✅ yes | --- importer | ❌ no | --- parser | ✅ yes | --- printer | ✅ yes | --- scanner | ✅ yes | --- token | ✅ yes | --- types | ❌ no | -hash | | --- adler32 | ✅ yes | --- crc32 | ✅ yes | --- crc64 | ✅ yes | --- fnv | ✅ yes | -html | ✅ yes | --- template | ✅ yes | -image | ✅ yes | --- color | ✅ yes | --- -- palette | ✅ yes | --- draw | ✅ yes | --- gif | ✅ yes | --- jpeg | ✅ yes | --- png | ✅ yes | -index | | --- suffixarray | ✅ yes | -io | ✅ yes | --- ioutil | ✅ yes | -log | ✅ yes | --- syslog | ❌ no | -math | ✅ yes | --- big | ✅ yes | --- bits | ✅ yes | --- cmplx | ✅ yes | --- rand | ✅ yes | -mime | ✅ yes | --- multipart | ✅ yes | --- quotedprintable | ✅ yes | -net | ❌ no | --- http | ☑️ partially | client only, emulated via Fetch/XMLHttpRequest APIs;
node.js requires polyfill --- -- cgi | ❌ no | --- -- cookiejar | ✅ yes | --- -- fcgi | ✅ yes | --- -- httptest | ☑️ partially | --- -- httputil | ☑️ partially | --- -- pprof | ❌ no | --- mail | ✅ yes | --- rpc | ☑️ partially | data structures only (no net) --- -- jsonrpc | ✅ yes | --- smtp | ☑️ partially | data structures only (no net) --- textproto | ✅ yes | --- url | ✅ yes | -os | ☑️ partially | node.js only --- exec | ☑️ partially | node.js only --- signal | ☑️ partially | node.js only --- user | ☑️ partially | node.js only -path | ✅ yes | --- filepath | ✅ yes | -plugin | ❌ no | -reflect | ✅ yes | except StructOf (pending) -regexp | ✅ yes | --- syntax | ✅ yes | -runtime | ☑️ partially | SetMutexProfileFraction, SetFinalizer, ReadMemStats, Callers, CallersFrames unsupported --- cgo | ❌ no | --- debug | ❌ no | --- pprof | ❌ no | --- race | ❌ no | --- trace | ❌ no | -sort | ✅ yes | -strconv | ✅ yes | -strings | ✅ yes | -sync | ✅ yes | --- atomic | ✅ yes | -syscall | ☑️ partially | node.js only -testing | ☑️ partially | AllocsPerRun unsupported --- iotest | ✅ yes | --- quick | ✅ yes | -text | | --- scanner | ✅ yes | --- tabwriter | ✅ yes | --- template | ✅ yes | --- -- parse | ✅ yes | -time | ✅ yes | UTC and Local only (see [issue](https://github.com/gopherjs/gopherjs/issues/64)) -unicode | ✅ yes | --- utf16 | ✅ yes | --- utf8 | ✅ yes | -unsafe | ❌ no | +| Name | Supported | Comment | +| ------------------- | ------------ | --------------------------------------------------------------------------------- | +| archive | | +| -- tar | ✅ yes | +| -- zip | ✅ yes | +| bufio | ✅ yes | +| builtin | ✅ yes | +| bytes | ✅ yes | +| compress | | +| -- bzip2 | ✅ yes | +| -- flate | ✅ yes | +| -- gzip | ✅ yes | +| -- lzw | ✅ yes | +| -- zlib | ✅ yes | +| container | | +| -- heap | ✅ yes | +| -- list | ✅ yes | +| -- ring | ✅ yes | +| context | ✅ yes | +| crypto | ✅ yes | +| -- aes | ✅ yes | +| -- cipher | ✅ yes | +| -- des | ✅ yes | +| -- dsa | ✅ yes | +| -- ecdsa | ✅ yes | +| -- ed25519 | ✅ yes | +| -- elliptic | ✅ yes | +| -- hmac | ✅ yes | +| -- md5 | ✅ yes | +| -- rand | ✅ yes | +| -- rc4 | ✅ yes | +| -- rsa | ✅ yes | +| -- sha1 | ✅ yes | +| -- sha256 | ✅ yes | +| -- sha512 | ✅ yes | +| -- subtle | ✅ yes | +| -- tls | ❌ no | +| -- x509 | ✅ yes | +| -- -- pkix | ✅ yes | +| database | | +| -- sql | ✅ yes | +| -- -- driver | ✅ yes | +| debug | | +| -- dwarf | ✅ yes | +| -- elf | ✅ yes | +| -- gosym | ☑️ partially | on binaries generated by gc | +| -- macho | ✅ yes | +| -- pe | ✅ yes | +| -- plan9obj | ✅ yes | +| embed | ❌ no | Not implemented yet: https://github.com/gopherjs/gopherjs/issues/997. | +| encoding | | +| -- ascii85 | ✅ yes | +| -- asn1 | ✅ yes | +| -- base32 | ✅ yes | +| -- base64 | ✅ yes | +| -- binary | ✅ yes | +| -- csv | ✅ yes | +| -- gob | ✅ yes | +| -- hex | ✅ yes | +| -- json | ✅ yes | +| -- pem | ✅ yes | +| -- xml | ✅ yes | +| errors | ✅ yes | +| expvar | ✅ yes | +| flag | ✅ yes | +| fmt | ✅ yes | +| go | | +| -- ast | ✅ yes | +| -- build | ❌ no | +| -- build/constraint | ✅ yes | +| -- constant | ✅ yes | +| -- doc | ✅ yes | +| -- format | ✅ yes | +| -- importer | ❌ no | +| -- parser | ✅ yes | +| -- printer | ✅ yes | +| -- scanner | ✅ yes | +| -- token | ✅ yes | +| -- types | ❌ no | +| hash | ✅ yes | +| -- adler32 | ✅ yes | +| -- crc32 | ✅ yes | +| -- crc64 | ✅ yes | +| -- fnv | ✅ yes | +| -- maphash | ✅ yes | +| html | ✅ yes | +| -- template | ✅ yes | +| image | ✅ yes | +| -- color | ✅ yes | +| -- -- palette | ✅ yes | +| -- draw | ✅ yes | +| -- gif | ✅ yes | +| -- jpeg | ✅ yes | +| -- png | ✅ yes | +| index | | +| -- suffixarray | ✅ yes | +| io | ✅ yes | +| -- fs | ✅ yes | +| -- ioutil | ✅ yes | +| log | ✅ yes | +| -- syslog | ❌ no | +| math | ✅ yes | +| -- big | ✅ yes | +| -- bits | ✅ yes | +| -- cmplx | ✅ yes | +| -- rand | ✅ yes | +| mime | ✅ yes | +| -- multipart | ✅ yes | +| -- quotedprintable | ✅ yes | +| net | ☑️ partially | network is simulated, supports only localhost connections | +| -- http | ☑️ partially | client only, emulated via Fetch/XMLHttpRequest APIs;
node.js requires polyfill | +| -- -- cgi | ❌ no | +| -- -- cookiejar | ✅ yes | +| -- -- fcgi | ✅ yes | +| -- -- httptest | ☑️ partially | +| -- -- httputil | ☑️ partially | +| -- -- pprof | ❌ no | +| -- mail | ✅ yes | +| -- rpc | ☑️ partially | data structures only (no net) | +| -- -- jsonrpc | ✅ yes | +| -- smtp | ☑️ partially | data structures only (no net) | +| -- textproto | ✅ yes | +| -- url | ✅ yes | +| os | ☑️ partially | node.js only | +| -- exec | ☑️ partially | node.js only | +| -- signal | ☑️ partially | node.js only | +| -- user | ☑️ partially | node.js only | +| path | ✅ yes | +| -- filepath | ✅ yes | +| plugin | ❌ no | +| reflect | ✅ yes | +| regexp | ✅ yes | +| -- syntax | ✅ yes | +| runtime | ☑️ partially | SetMutexProfileFraction, SetFinalizer, ReadMemStats unsupported | +| -- metrics | ☑️ partially | Same as runtime. | +| -- cgo | ❌ no | +| -- debug | ❌ no | +| -- pprof | ❌ no | +| -- race | ❌ no | +| -- trace | ❌ no | +| sort | ✅ yes | +| strconv | ✅ yes | +| strings | ✅ yes | +| sync | ✅ yes | +| -- atomic | ✅ yes | +| syscall | ☑️ partially | node.js only | +| testing | ☑️ partially | AllocsPerRun and T.Helper are unsupported. | +| -- iotest | ✅ yes | +| -- fstest | ✅ yes | +| -- quick | ✅ yes | +| text | | +| -- scanner | ✅ yes | +| -- tabwriter | ✅ yes | +| -- template | ✅ yes | +| -- -- parse | ✅ yes | +| time | ✅ yes | UTC and Local only (see [issue](https://github.com/gopherjs/gopherjs/issues/64)) | +| -- tzdata | ✅ yes | +| unicode | ✅ yes | +| -- utf16 | ✅ yes | +| -- utf8 | ✅ yes | +| unsafe | ❌ no | diff --git a/doc/pargma.md b/doc/pargma.md new file mode 100644 index 00000000..a13c64ce --- /dev/null +++ b/doc/pargma.md @@ -0,0 +1,40 @@ +# Compiler directives + +Compiler directives allow to provide low-level instructions to the GopherJS +compiler, which are outside of the Go language itself. Compiler directives are +specific to each Go compiler implementation and may be a source of portability +issues, so it is recommended to avoid using them if possible. + +GopherJS compiler supports the following directives: + +## `go:linkname` + +This is a limited version of the `go:linkname` directive the upstream Go +compiler implements. Usage: + +```go +import _ "unsafe" // for go:linkname + +//go:linkname localname import/path.remotename +func localname(arg1 type1, arg2 type2) (returnType, error) +``` + +This directive has an effect of making a `remotename` function from +`import/path` package available to the current package as `localname`. +Signatures of `remotename` and `localname` must be identical. Since this +directive can subvert package incapsulation, the source file that uses the +directive must also import `unsafe`. + +The following directive format is supported: +//go:linkname . +//go:linkname .. +//go:linkname .<(*type)>. + +Compared to the upstream Go, the following limitations exist in GopherJS: + + - The directive only works on package-level functions or methods (variables + are not supported). + - The directive can only be used to "import" implementation from another + package, and not to "provide" local implementation to another package. + +See https://github.com/gopherjs/gopherjs/issues/1000 for details. diff --git a/doc/syscalls.md b/doc/syscalls.md index 59366267..eb75c148 100644 --- a/doc/syscalls.md +++ b/doc/syscalls.md @@ -1,8 +1,9 @@ -System Calls ------------- +## System Calls System calls are the bridge between your application and your operating system. They are used whenever you access something outside of your application's memory, for example when you write to the console, when you read or write files or when you access the network. In Go, system calls are mostly used by the `os` package, hence the name. When using GopherJS you need to consider if system calls are available or not. +Starting with 1.18, GopherJS provides the same [set of cross-platform](https://pkg.go.dev/syscall?GOOS=js) syscalls as standard Go WebAssembly, emulating them via JavaScript APIs available in the runtime (browser or Node.js). + ### Output redirection to console If system calls are not available in your environment (see below), then a special redirection of `os.Stdout` and `os.Stderr` is applied. It buffers a line until it is terminated by a line break and then prints it via JavaScript's `console.log` to your browser's JavaScript console or your system console. That way, `fmt.Println` etc. work as expected, even if system calls are not available. @@ -11,24 +12,53 @@ If system calls are not available in your environment (see below), then a specia The JavaScript environment of a web browser is completely isolated from your operating system to protect your machine. You don't want any web page to read or write files on your disk without your consent. That is why system calls are not and will never be available when running your code in a web browser. -### Node.js on Linux and macOS +However, certain subsets of syscalls can be emulated using third-party libraries. For example, [BrowserFS](https://github.com/jvilk/BrowserFS) library can be used to emulate Node.js file system API in a browser using HTML5 LocalStorage or other fallbacks. + +### Node.js on all platforms + +GopherJS emulates syscalls for accessing file system (and a few others) using Node.js standard [`fs`](https://nodejs.org/api/fs.html) and [`process`](https://nodejs.org/api/process.html) APIs. No additional extensions are required for this in GopherJS 1.18 and newer. + +### Node.js with the legacy node-syscall extension. + +Prior to 1.18 GopherJS required a custom Node extension to be installed that provided access to system calls on Linux and MacOS. Currently this extension is deprecated and its support will be removed entirely in a future release. This decision is motivated by several factors: + +- This extension has been developed before Go had WebAssembly support and at the time there was no easier way to provide file system access. Today standard library for `js/wasm` provides most of the relevant functionality without the need for custom extensions. +- It required GopherJS to support building Go standard library with multiple different GOOS/GOARCH combinations, which significantly increased maintenance effort and slowed down support for new Go versions. It was not supported on Windows entirely. +- Using this extension required non-trivial setup for the users who needed file system access. +- The extension itself contained significant technical debt and potential memory leaks. +- File system syscalls use asynchronous Node.js API, so other goroutines doesn't get blocked. -GopherJS has support for system calls on Linux and macOS. Before running your code with Node.js, you need to install the system calls module. The module is compatible with Node.js version 10.0.0 (or newer). If you want to use an older version you can opt to not install the module, but then system calls are not available. +Issue [#693](https://github.com/gopherjs/gopherjs/issues/693) has a detailed discussion of this. + +In GopherJS 1.18 support for this extension is disabled by default to reduce the output size. It can be enabled with a build tag `legacy_syscall` (for example, `gopherjs build --tags legacy_syscall pkg/name`) with the following caveats: + +- `node-syscall` extension must be built and installed according to instructions below. +- Functions `syscall.Syscall`, `syscall.Syscall6`, `syscall.RawSyscall` and `syscall.RawSyscall6` will be changed to use the extension API and can be called from the third-party code. +- Standard library is still built for `js/wasm` regardless of the host OS, so the syscall package API will remain reduced compared to `linux` or `darwin`. +- All functions in the `syscall` package that GopherJS emulates via Node.js APIs will continue using those APIs. +- While executing a legacy syscall, all goroutines get blocked. This may cause some programs not to behave as expected. + +We strongly recommend upgrading your package to not use `syscall` package directly and use higher-level APIs in the `os` package, which will continue working. + +The module is compatible with Node.js version 10.0.0 (or newer). If you want to use an older version you can opt to not install the module, but then system calls are not available. Compile and install the module with: ``` -cd $GOPATH/src/github.com/gopherjs/gopherjs/node-syscall/ -npm install --global node-gyp -node-gyp rebuild -mkdir -p ~/.node_libraries/ -cp build/Release/syscall.node ~/.node_libraries/syscall.node +cd gopherjs/node-syscall/ +npm install ``` -### Node.js on Windows +You can copy build/Release/syscall.node into you `node_modules` directory and run `node -r syscall` to make sure the module can be loaded successfully. -When running your code with Node.js on Windows, it is theoretically possible to use system calls. To do so, you would need a special Node.js module that provides direct access to system calls. However, since the interface is quite different from the one used on Linux and macOS, the system calls module included in GopherJS currently does not support Windows. Sorry. Get in contact if you feel like you want to change this situation. +Alternatively, in _your_ `package.json` you can do something like this: -### Caveats +``` +{ + "dependencies": { + "syscall": "file:path/to/gopherjs/node-syscall" + } +} +``` -Note that even with syscalls enabled in Node.js, some programs may not behave as expected due to the fact that the current implementation blocks other goroutines during a syscall, which can lead to a deadlock in some situations. This is not considered a bug, as it is considered sufficient for most test cases (which is all Node.js should be used for). Get in contact if you feel like you want to change this situation. +Which will make `npm install` in your project capable of building the extension. You may need to set `export NODE_PATH="$(npm root)"` to ensure that node can load modules from any working directory, for example when running `gopherjs test`. diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..b3b3f91e --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module github.com/gopherjs/gopherjs + +go 1.16 + +require ( + github.com/fsnotify/fsnotify v1.5.1 + github.com/google/go-cmp v0.5.7 + github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 + github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c + github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636 + github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 + github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 + github.com/sirupsen/logrus v1.8.1 + github.com/spf13/cobra v1.2.1 + github.com/spf13/pflag v1.0.5 + github.com/visualfc/goembed v0.3.3 + golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20220412211240-33da011f77ad + golang.org/x/tools v0.1.10 +) + +require ( + golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect + golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..b90e10e0 --- /dev/null +++ b/go.sum @@ -0,0 +1,612 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wud7xg83MaEFyjxQB1W5bz5d0IFppr+ymk= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c h1:bY6ktFuJkt+ZXkX0RChQch2FtHpWQLVS8Qo1YasiIVk= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636 h1:aSISeOcal5irEhJd1M+IrApc0PdcN7e7Aj4yuEnOrfQ= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/visualfc/goembed v0.3.3 h1:pOL02L715tHKsLQVMcZz06tTzRDAHkJKJLRnCA22G9Q= +github.com/visualfc/goembed v0.3.3/go.mod h1:jCVCz/yTJGyslo6Hta+pYxWWBuq9ADCcIVZBTQ0/iVI= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= +golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/srctesting/srctesting.go b/internal/srctesting/srctesting.go new file mode 100644 index 00000000..4831e014 --- /dev/null +++ b/internal/srctesting/srctesting.go @@ -0,0 +1,81 @@ +// Package srctesting contains common helpers for unit testing source code +// analysis and transformation. +package srctesting + +import ( + "bytes" + "go/ast" + "go/format" + "go/parser" + "go/token" + "go/types" + "testing" +) + +// Parse source from the string and return complete AST. +// +// Assumes source file name `test.go`. Fails the test on parsing error. +func Parse(t *testing.T, fset *token.FileSet, src string) *ast.File { + t.Helper() + f, err := parser.ParseFile(fset, "test.go", src, parser.ParseComments) + if err != nil { + t.Fatalf("Failed to parse test source: %s", err) + } + return f +} + +// Check type correctness of the provided AST. +// +// Assumes "test" package import path. Fails the test if type checking fails. +// Provided AST is expected not to have any imports. +func Check(t *testing.T, fset *token.FileSet, files ...*ast.File) (*types.Info, *types.Package) { + t.Helper() + typesInfo := &types.Info{ + Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), + Uses: make(map[*ast.Ident]types.Object), + Implicits: make(map[ast.Node]types.Object), + Selections: make(map[*ast.SelectorExpr]*types.Selection), + Scopes: make(map[ast.Node]*types.Scope), + } + config := &types.Config{ + Sizes: &types.StdSizes{WordSize: 4, MaxAlign: 8}, + } + typesPkg, err := config.Check("test", fset, files, typesInfo) + if err != nil { + t.Fatalf("Filed to type check test source: %s", err) + } + return typesInfo, typesPkg +} + +// ParseFuncDecl parses source with a single function defined and returns the +// function AST. +// +// Fails the test if there isn't exactly one function declared in the source. +func ParseFuncDecl(t *testing.T, src string) *ast.FuncDecl { + t.Helper() + fset := token.NewFileSet() + file := Parse(t, fset, src) + if l := len(file.Decls); l != 1 { + t.Fatalf("Got %d decls in the sources, expected exactly 1", l) + } + fdecl, ok := file.Decls[0].(*ast.FuncDecl) + if !ok { + t.Fatalf("Got %T decl, expected *ast.FuncDecl", file.Decls[0]) + } + return fdecl +} + +// Format AST node into a string. +// +// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl, +// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or +// ast.Stmt. +func Format(t *testing.T, fset *token.FileSet, node interface{}) string { + t.Helper() + buf := &bytes.Buffer{} + if err := format.Node(buf, fset, node); err != nil { + t.Fatalf("Failed to format AST node %T: %s", node, err) + } + return buf.String() +} diff --git a/internal/sysutil/sysutil.go b/internal/sysutil/sysutil.go index c3631f2b..e19eb02e 100644 --- a/internal/sysutil/sysutil.go +++ b/internal/sysutil/sysutil.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows // Package sysutil contains system-specific utilities. diff --git a/internal/testmain/testdata/testpkg/external_test.go b/internal/testmain/testdata/testpkg/external_test.go new file mode 100644 index 00000000..5d9a4ba1 --- /dev/null +++ b/internal/testmain/testdata/testpkg/external_test.go @@ -0,0 +1,16 @@ +package testpkg_test + +import ( + "fmt" + "testing" +) + +func TestYyy(t *testing.T) {} + +func BenchmarkYyy(b *testing.B) {} + +func FuzzYyy(f *testing.F) { f.Skip() } + +func ExampleYyy() { + fmt.Println("hello") // Output: hello +} diff --git a/internal/testmain/testdata/testpkg/inpackage_test.go b/internal/testmain/testdata/testpkg/inpackage_test.go new file mode 100644 index 00000000..dd387828 --- /dev/null +++ b/internal/testmain/testdata/testpkg/inpackage_test.go @@ -0,0 +1,13 @@ +package testpkg + +import "testing" + +func TestXxx(t *testing.T) {} + +func BenchmarkXxx(b *testing.B) {} + +func FuzzXxx(f *testing.F) { f.Skip() } + +func ExampleXxx() {} + +func TestMain(m *testing.M) { m.Run() } diff --git a/internal/testmain/testdata/testpkg/testpkg.go b/internal/testmain/testdata/testpkg/testpkg.go new file mode 100644 index 00000000..cdeafab4 --- /dev/null +++ b/internal/testmain/testdata/testpkg/testpkg.go @@ -0,0 +1,4 @@ +package testpkg + +// Xxx is an sample function. +func Xxx() {} diff --git a/internal/testmain/testmain.go b/internal/testmain/testmain.go new file mode 100644 index 00000000..c3bf6007 --- /dev/null +++ b/internal/testmain/testmain.go @@ -0,0 +1,254 @@ +package testmain + +import ( + "bytes" + "errors" + "fmt" + "go/ast" + gobuild "go/build" + "go/doc" + "go/parser" + "go/token" + "path" + "sort" + "strings" + "text/template" + "unicode" + "unicode/utf8" + + "github.com/gopherjs/gopherjs/build" + "golang.org/x/tools/go/buildutil" +) + +// FuncLocation describes whether a test function is in-package or external +// (i.e. in the xxx_test package). +type FuncLocation uint8 + +const ( + // LocUnknown is the default, invalid value of the PkgType. + LocUnknown FuncLocation = iota + // LocInPackage is an in-package test. + LocInPackage + // LocExternal is an external test (i.e. in the xxx_test package). + LocExternal +) + +func (tl FuncLocation) String() string { + switch tl { + case LocInPackage: + return "_test" + case LocExternal: + return "_xtest" + default: + return "" + } +} + +// TestFunc describes a single test/benchmark/fuzz function in a package. +type TestFunc struct { + Location FuncLocation // Where the function is defined. + Name string // Function name. +} + +// ExampleFunc describes an example. +type ExampleFunc struct { + Location FuncLocation // Where the function is defined. + Name string // Function name. + Output string // Expected output. + Unordered bool // Output is allowed to be unordered. + EmptyOutput bool // Whether the output is expected to be empty. +} + +// Executable returns true if the example function should be executed with tests. +func (ef ExampleFunc) Executable() bool { + return ef.EmptyOutput || ef.Output != "" +} + +// TestMain is a helper type responsible for generation of the test main package. +type TestMain struct { + Package *build.PackageData + Tests []TestFunc + Benchmarks []TestFunc + Fuzz []TestFunc + Examples []ExampleFunc + TestMain *TestFunc +} + +// Scan package for tests functions. +func (tm *TestMain) Scan(fset *token.FileSet) error { + if err := tm.scanPkg(fset, tm.Package.TestGoFiles, LocInPackage); err != nil { + return err + } + if err := tm.scanPkg(fset, tm.Package.XTestGoFiles, LocExternal); err != nil { + return err + } + return nil +} + +func (tm *TestMain) scanPkg(fset *token.FileSet, files []string, loc FuncLocation) error { + for _, name := range files { + srcPath := path.Join(tm.Package.Dir, name) + f, err := buildutil.OpenFile(tm.Package.InternalBuildContext(), srcPath) + if err != nil { + return fmt.Errorf("failed to open source file %q: %w", srcPath, err) + } + defer f.Close() + parsed, err := parser.ParseFile(fset, srcPath, f, parser.ParseComments) + if err != nil { + return fmt.Errorf("failed to parse %q: %w", srcPath, err) + } + + if err := tm.scanFile(parsed, loc); err != nil { + return err + } + } + return nil +} + +func (tm *TestMain) scanFile(f *ast.File, loc FuncLocation) error { + for _, d := range f.Decls { + n, ok := d.(*ast.FuncDecl) + if !ok { + continue + } + if n.Recv != nil { + continue + } + name := n.Name.String() + switch { + case isTestMain(n): + if tm.TestMain != nil { + return errors.New("multiple definitions of TestMain") + } + tm.TestMain = &TestFunc{ + Location: loc, + Name: name, + } + case isTest(name, "Test"): + tm.Tests = append(tm.Tests, TestFunc{ + Location: loc, + Name: name, + }) + case isTest(name, "Benchmark"): + tm.Benchmarks = append(tm.Benchmarks, TestFunc{ + Location: loc, + Name: name, + }) + case isTest(name, "Fuzz"): + tm.Fuzz = append(tm.Fuzz, TestFunc{ + Location: loc, + Name: name, + }) + } + } + + ex := doc.Examples(f) + sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order }) + for _, e := range ex { + tm.Examples = append(tm.Examples, ExampleFunc{ + Location: loc, + Name: "Example" + e.Name, + Output: e.Output, + Unordered: e.Unordered, + EmptyOutput: e.EmptyOutput, + }) + } + + return nil +} + +// Synthesize main package for the tests. +func (tm *TestMain) Synthesize(fset *token.FileSet) (*build.PackageData, *ast.File, error) { + buf := &bytes.Buffer{} + if err := testmainTmpl.Execute(buf, tm); err != nil { + return nil, nil, fmt.Errorf("failed to generate testmain source for package %s: %w", tm.Package.ImportPath, err) + } + src, err := parser.ParseFile(fset, "_testmain.go", buf, 0) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse testmain source for package %s: %w", tm.Package.ImportPath, err) + } + pkg := &build.PackageData{ + Package: &gobuild.Package{ + ImportPath: tm.Package.ImportPath + ".testmain", + Name: "main", + GoFiles: []string{"_testmain.go"}, + }, + } + return pkg, src, nil +} + +func (tm *TestMain) hasTests(loc FuncLocation, executableOnly bool) bool { + if tm.TestMain != nil && tm.TestMain.Location == loc { + return true + } + // Tests, Benchmarks and Fuzz targets are always executable. + all := []TestFunc{} + all = append(all, tm.Tests...) + all = append(all, tm.Benchmarks...) + + for _, t := range all { + if t.Location == loc { + return true + } + } + + for _, e := range tm.Examples { + if e.Location == loc && (e.Executable() || !executableOnly) { + return true + } + } + return false +} + +// ImportTest returns true if in-package test package needs to be imported. +func (tm *TestMain) ImportTest() bool { return tm.hasTests(LocInPackage, false) } + +// ImportXTest returns true if external test package needs to be imported. +func (tm *TestMain) ImportXTest() bool { return tm.hasTests(LocExternal, false) } + +// ExecutesTest returns true if in-package test package has executable tests. +func (tm *TestMain) ExecutesTest() bool { return tm.hasTests(LocInPackage, true) } + +// ExecutesXTest returns true if external package test package has executable tests. +func (tm *TestMain) ExecutesXTest() bool { return tm.hasTests(LocExternal, true) } + +// isTestMain tells whether fn is a TestMain(m *testing.M) function. +func isTestMain(fn *ast.FuncDecl) bool { + if fn.Name.String() != "TestMain" || + fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || + fn.Type.Params == nil || + len(fn.Type.Params.List) != 1 || + len(fn.Type.Params.List[0].Names) > 1 { + return false + } + ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) + if !ok { + return false + } + // We can't easily check that the type is *testing.M + // because we don't know how testing has been imported, + // but at least check that it's *M or *something.M. + if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" { + return true + } + if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" { + return true + } + return false +} + +// isTest tells whether name looks like a test (or benchmark, according to prefix). +// It is a Test (say) if there is a character after Test that is not a lower-case letter. +// We don't want TesticularCancer. +func isTest(name, prefix string) bool { + if !strings.HasPrefix(name, prefix) { + return false + } + if len(name) == len(prefix) { // "Test" is ok + return true + } + rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(rune) +} + +var testmainTmpl = template.Must(template.New("main").Parse(testmainData)) diff --git a/internal/testmain/testmain_test.go b/internal/testmain/testmain_test.go new file mode 100644 index 00000000..01c92cc7 --- /dev/null +++ b/internal/testmain/testmain_test.go @@ -0,0 +1,210 @@ +package testmain_test + +import ( + gobuild "go/build" + "go/token" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/gopherjs/gopherjs/build" + "github.com/gopherjs/gopherjs/internal/srctesting" + . "github.com/gopherjs/gopherjs/internal/testmain" +) + +func TestScan(t *testing.T) { + xctx := build.NewBuildContext("", nil) + pkg, err := xctx.Import("github.com/gopherjs/gopherjs/internal/testmain/testdata/testpkg", "", 0) + if err != nil { + t.Fatalf("Failed to import package: %s", err) + } + + fset := token.NewFileSet() + + got := TestMain{Package: pkg} + if err := got.Scan(fset); err != nil { + t.Fatalf("Got: tm.Scan() returned error: %s. Want: no error.", err) + } + + want := TestMain{ + TestMain: &TestFunc{Location: LocInPackage, Name: "TestMain"}, + Tests: []TestFunc{ + {Location: LocInPackage, Name: "TestXxx"}, + {Location: LocExternal, Name: "TestYyy"}, + }, + Benchmarks: []TestFunc{ + {Location: LocInPackage, Name: "BenchmarkXxx"}, + {Location: LocExternal, Name: "BenchmarkYyy"}, + }, + Fuzz: []TestFunc{ + {Location: LocInPackage, Name: "FuzzXxx"}, + {Location: LocExternal, Name: "FuzzYyy"}, + }, + Examples: []ExampleFunc{ + {Location: LocInPackage, Name: "ExampleXxx"}, + {Location: LocExternal, Name: "ExampleYyy", Output: "hello\n"}, + }, + } + opts := cmp.Options{ + cmpopts.IgnoreFields(TestMain{}, "Package"), // Inputs. + } + if diff := cmp.Diff(want, got, opts...); diff != "" { + t.Errorf("List of test function is different from expected (-want,+got):\n%s", diff) + } +} + +func TestSynthesize(t *testing.T) { + pkg := &build.PackageData{ + Package: &gobuild.Package{ImportPath: "foo/bar"}, + } + + tests := []struct { + descr string + tm TestMain + wantSrc string + }{ + { + descr: "all tests", + tm: TestMain{ + Package: pkg, + Tests: []TestFunc{ + {Location: LocInPackage, Name: "TestXxx"}, + {Location: LocExternal, Name: "TestYyy"}, + }, + Benchmarks: []TestFunc{ + {Location: LocInPackage, Name: "BenchmarkXxx"}, + {Location: LocExternal, Name: "BenchmarkYyy"}, + }, + Fuzz: []TestFunc{ + {Location: LocInPackage, Name: "FuzzXxx"}, + {Location: LocExternal, Name: "FuzzYyy"}, + }, + Examples: []ExampleFunc{ + {Location: LocInPackage, Name: "ExampleXxx", EmptyOutput: true}, + {Location: LocExternal, Name: "ExampleYyy", EmptyOutput: true}, + }, + }, + wantSrc: allTests, + }, { + descr: "testmain", + tm: TestMain{ + Package: pkg, + TestMain: &TestFunc{Location: LocInPackage, Name: "TestMain"}, + }, + wantSrc: testmain, + }, { + descr: "import only", + tm: TestMain{ + Package: pkg, + Examples: []ExampleFunc{ + {Location: LocInPackage, Name: "ExampleXxx"}, + }, + }, + wantSrc: importOnly, + }, + } + + for _, test := range tests { + t.Run(test.descr, func(t *testing.T) { + fset := token.NewFileSet() + _, src, err := test.tm.Synthesize(fset) + if err != nil { + t.Fatalf("Got: tm.Synthesize() returned error: %s. Want: no error.", err) + } + got := srctesting.Format(t, fset, src) + if diff := cmp.Diff(test.wantSrc, got); diff != "" { + t.Errorf("Different _testmain.go source (-want,+got):\n%s", diff) + t.Logf("Got source:\n%s", got) + } + }) + } +} + +const allTests = `package main + +import ( + "os" + + "testing" + "testing/internal/testdeps" + + _test "foo/bar" + _xtest "foo/bar_test" +) + +var tests = []testing.InternalTest{ + {"TestXxx", _test.TestXxx}, + {"TestYyy", _xtest.TestYyy}, +} + +var benchmarks = []testing.InternalBenchmark{ + {"BenchmarkXxx", _test.BenchmarkXxx}, + {"BenchmarkYyy", _xtest.BenchmarkYyy}, +} + +var fuzzTargets = []testing.InternalFuzzTarget{ + {"FuzzXxx", _test.FuzzXxx}, + {"FuzzYyy", _xtest.FuzzYyy}, +} + +var examples = []testing.InternalExample{ + {"ExampleXxx", _test.ExampleXxx, "", false}, + {"ExampleYyy", _xtest.ExampleYyy, "", false}, +} + +func main() { + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) + + os.Exit(m.Run()) +} +` + +const testmain = `package main + +import ( + "testing" + "testing/internal/testdeps" + + _test "foo/bar" +) + +var tests = []testing.InternalTest{} + +var benchmarks = []testing.InternalBenchmark{} + +var fuzzTargets = []testing.InternalFuzzTarget{} + +var examples = []testing.InternalExample{} + +func main() { + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) + + _test.TestMain(m) +} +` + +const importOnly = `package main + +import ( + "os" + + "testing" + "testing/internal/testdeps" + + _ "foo/bar" +) + +var tests = []testing.InternalTest{} + +var benchmarks = []testing.InternalBenchmark{} + +var fuzzTargets = []testing.InternalFuzzTarget{} + +var examples = []testing.InternalExample{} + +func main() { + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) + + os.Exit(m.Run()) +} +` diff --git a/internal/testmain/tmpl_go117.go b/internal/testmain/tmpl_go117.go new file mode 100644 index 00000000..2f669d49 --- /dev/null +++ b/internal/testmain/tmpl_go117.go @@ -0,0 +1,53 @@ +//go:build !go1.18 +// +build !go1.18 + +package testmain + +var testmainData = ` +package main + +import ( +{{if not .TestMain}} + "os" +{{end}} + "testing" + "testing/internal/testdeps" + +{{if .ImportTest}} + {{if .ExecutesTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} +{{end -}} +{{- if .ImportXTest -}} + {{if .ExecutesXTest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} +{{end}} +) + +var tests = []testing.InternalTest{ +{{- range .Tests}} + {"{{.Name}}", {{.Location}}.{{.Name}}}, +{{- end}} +} + +var benchmarks = []testing.InternalBenchmark{ +{{- range .Benchmarks}} + {"{{.Name}}", {{.Location}}.{{.Name}}}, +{{- end}} +} + +var examples = []testing.InternalExample{ +{{- range .Examples }} +{{- if .Executable }} + {"{{.Name}}", {{.Location}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, +{{- end }} +{{- end }} +} + +func main() { + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples) +{{with .TestMain}} + {{.Location}}.{{.Name}}(m) +{{else}} + os.Exit(m.Run()) +{{end -}} +} + +` diff --git a/internal/testmain/tmpl_go118.go b/internal/testmain/tmpl_go118.go new file mode 100644 index 00000000..f1a10a05 --- /dev/null +++ b/internal/testmain/tmpl_go118.go @@ -0,0 +1,59 @@ +//go:build go1.18 +// +build go1.18 + +package testmain + +var testmainData = ` +package main + +import ( +{{if not .TestMain}} + "os" +{{end}} + "testing" + "testing/internal/testdeps" + +{{if .ImportTest}} + {{if .ExecutesTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} +{{end -}} +{{- if .ImportXTest -}} + {{if .ExecutesXTest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} +{{end}} +) + +var tests = []testing.InternalTest{ +{{- range .Tests}} + {"{{.Name}}", {{.Location}}.{{.Name}}}, +{{- end}} +} + +var benchmarks = []testing.InternalBenchmark{ +{{- range .Benchmarks}} + {"{{.Name}}", {{.Location}}.{{.Name}}}, +{{- end}} +} + +var fuzzTargets = []testing.InternalFuzzTarget{ +{{- range .Fuzz}} + {"{{.Name}}", {{.Location}}.{{.Name}}}, +{{- end}} +} + +var examples = []testing.InternalExample{ +{{- range .Examples }} +{{- if .Executable }} + {"{{.Name}}", {{.Location}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, +{{- end }} +{{- end }} +} + +func main() { + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) +{{with .TestMain}} + {{.Location}}.{{.Name}}(m) +{{else}} + os.Exit(m.Run()) +{{end -}} +} + +` diff --git a/internal/typeparams/typeparams_go117.go b/internal/typeparams/typeparams_go117.go new file mode 100644 index 00000000..2edece02 --- /dev/null +++ b/internal/typeparams/typeparams_go117.go @@ -0,0 +1,24 @@ +//go:build !go1.18 +// +build !go1.18 + +package typeparams + +import ( + "go/ast" + "go/types" +) + +func HasTypeParam(t types.Type) bool { + return false +} + +func NamedHasTypeParam(t *types.Named) bool { + return false +} + +func FuncTypeHasTypeParam(t *ast.FuncType) bool { + return false +} + +func TypeSpecRemoveTypeParam(spec *ast.TypeSpec) { +} diff --git a/internal/typeparams/typeparams_go118.go b/internal/typeparams/typeparams_go118.go new file mode 100644 index 00000000..0c5fe8c3 --- /dev/null +++ b/internal/typeparams/typeparams_go118.go @@ -0,0 +1,33 @@ +//go:build go1.18 +// +build go1.18 + +package typeparams + +import ( + "go/ast" + "go/types" +) + +func HasTypeParam(t types.Type) bool { + switch t := t.(type) { + case *types.TypeParam: + return true + case *types.Named: + return t.TypeParams() != nil + case *types.Signature: + return t.TypeParams() != nil + } + return false +} + +func NamedHasTypeParam(t *types.Named) bool { + return t.TypeParams() != nil +} + +func FuncTypeHasTypeParam(t *ast.FuncType) bool { + return t.TypeParams != nil +} + +func TypeSpecRemoveTypeParam(spec *ast.TypeSpec) { + spec.TypeParams = nil +} diff --git a/js/js.go b/js/js.go index 3fbf1d88..a1ded9ee 100644 --- a/js/js.go +++ b/js/js.go @@ -1,6 +1,6 @@ // Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their corresponding JavaScript syntax. // -// Use MakeWrapper to expose methods to JavaScript. When passing values directly, the following type conversions are performed: +// Use MakeWrapper to expose methods to JavaScript. Use MakeFullWrapper to expose methods AND fields to JavaScript. When passing values directly, the following type conversions are performed: // // | Go type | JavaScript type | Conversions back to interface{} | // | --------------------- | --------------------- | ------------------------------- | @@ -97,7 +97,13 @@ func (err *Error) Stack() string { // Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js). var Global *Object -// Module gives the value of the "module" variable set by Node.js. Hint: Set a module export with 'js.Module.Get("exports").Set("exportName", ...)'. +// Module gives the value of the "module" variable set by Node.js. Hint: Set a +// module export with 'js.Module.Get("exports").Set("exportName", ...)'. +// +// Note that js.Module is only defined in runtimes which support CommonJS +// modules (https://nodejs.org/api/modules.html). NodeJS supports it natively, +// but in browsers it can only be used if GopherJS output is passed through a +// bundler which implements CommonJS (for example, webpack or esbuild). var Module *Object // Undefined gives the JavaScript value "undefined". @@ -147,6 +153,99 @@ func MakeWrapper(i interface{}) *Object { return o } +// MakeFullWrapper creates a JavaScript object which has wrappers for the exported +// methods of i, and, where i is a (pointer to a) struct value, wrapped getters +// and setters +// (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) +// for the non-embedded exported fields of i. Values accessed via these methods +// and getters are themselves wrapped when accessed, but an important point to +// note is that a new wrapped value is created on each access. +func MakeFullWrapper(i interface{}) *Object { + internalObj := InternalObject(i) + constructor := internalObj.Get("constructor") + + wrapperObj := Global.Get("Object").New() + + defineProperty := func(key string, descriptor M) { + Global.Get("Object").Call("defineProperty", wrapperObj, key, descriptor) + } + + defineProperty("__internal_object__", M{ + "value": internalObj, + }) + + { + // Calculate a sensible type string. + + // We don't want to import any packages in this package, + // so we do some string operations by hand. + + typ := constructor.Get("string").String() + pkg := constructor.Get("pkg").String() + + ptr := "" + if typ[0] == '*' { + ptr = "*" + } + + for i := 0; i < len(typ); i++ { + if typ[i] == '.' { + typ = typ[i+1:] + break + } + } + + pkgTyp := pkg + "." + ptr + typ + defineProperty("$type", M{ + "value": pkgTyp, + }) + } + + var fields *Object + methods := Global.Get("Array").New() + if ms := constructor.Get("methods"); ms != Undefined { + methods = methods.Call("concat", ms) + } + // If we are a pointer value then add fields from element, + // else the constructor itself will have them. + if e := constructor.Get("elem"); e != Undefined { + fields = e.Get("fields") + methods = methods.Call("concat", e.Get("methods")) + } else { + fields = constructor.Get("fields") + } + for i := 0; i < methods.Length(); i++ { + m := methods.Index(i) + if m.Get("pkg").String() != "" { // not exported + continue + } + defineProperty(m.Get("prop").String(), M{ + "value": func(args ...*Object) *Object { + return Global.Call("$externalizeFunction", internalObj.Get(m.Get("prop").String()), m.Get("typ"), true, InternalObject(MakeFullWrapper)).Call("apply", internalObj, args) + }, + }) + } + if fields != Undefined { + for i := 0; i < fields.Length(); i++ { + f := fields.Index(i) + if !f.Get("exported").Bool() { + continue + } + defineProperty(f.Get("prop").String(), M{ + "get": func() *Object { + vc := Global.Call("$copyIfRequired", internalObj.Get("$val").Get(f.Get("prop").String()), f.Get("typ")) + return Global.Call("$externalize", vc, f.Get("typ"), InternalObject(MakeFullWrapper)) + }, + "set": func(jv *Object) { + gv := Global.Call("$internalize", jv, f.Get("typ"), InternalObject(MakeFullWrapper)) + internalObj.Get("$val").Set(f.Get("prop").String(), gv) + }, + }) + } + } + return wrapperObj +} + // NewArrayBuffer creates a JavaScript ArrayBuffer from a byte slice. func NewArrayBuffer(b []byte) *Object { slice := InternalObject(b) @@ -162,7 +261,7 @@ type M map[string]interface{} type S []interface{} func init() { - // avoid dead code elimination + // Avoid dead code elimination. e := Error{} _ = e } diff --git a/js/js_test.go b/js/js_test.go new file mode 100644 index 00000000..d7a90495 --- /dev/null +++ b/js/js_test.go @@ -0,0 +1,19 @@ +//go:build js +// +build js + +package js_test + +import ( + "testing" + + "github.com/gopherjs/gopherjs/js" +) + +func TestInternalizeCircularReference(t *testing.T) { + // See https://github.com/gopherjs/gopherjs/issues/968. + js.Global.Call("eval", ` + var issue968a = {}; + var issue968b = {'a': issue968a}; + issue968a.b = issue968b;`) + _ = js.Global.Get("issue968a").Interface() +} diff --git a/node-syscall/.gitignore b/node-syscall/.gitignore new file mode 100644 index 00000000..c2658d7d --- /dev/null +++ b/node-syscall/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/node-syscall/binding.gyp b/node-syscall/binding.gyp index 9f3211d1..3ee74b35 100644 --- a/node-syscall/binding.gyp +++ b/node-syscall/binding.gyp @@ -2,7 +2,18 @@ 'targets': [ { 'target_name': 'syscall', - 'sources': [ 'syscall.cc' ] - } - ] -} \ No newline at end of file + 'sources': [ 'syscall.cc' ], + + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], + 'xcode_settings': { + 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', + 'CLANG_CXX_LIBRARY': 'libc++', + 'MACOSX_DEPLOYMENT_TARGET': '10.7', + }, + 'msvs_settings': { + 'VCCLCompilerTool': { 'ExceptionHandling': 1 }, + }, + }, + ], +} diff --git a/node-syscall/index.js b/node-syscall/index.js new file mode 100644 index 00000000..1e956d2a --- /dev/null +++ b/node-syscall/index.js @@ -0,0 +1 @@ +module.exports = require('./build/Release/syscall') diff --git a/node-syscall/package-lock.json b/node-syscall/package-lock.json new file mode 100644 index 00000000..302d9e5c --- /dev/null +++ b/node-syscall/package-lock.json @@ -0,0 +1,694 @@ +{ + "name": "syscall", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", + "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "cacache": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz", + "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==", + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-fetch-happen": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz", + "integrity": "sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==", + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.4.tgz", + "integrity": "sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ==", + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-gyp": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.1.0.tgz", + "integrity": "sha512-o2elh1qt7YUp3lkMwY3/l4KF3j/A3fI/Qt4NH+CQQgPJdqGE9y7qnP84cjIWN27Q0jJkrSAhCVDg+wBVNBYdBg==", + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^8.0.14", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.0", + "which": "^2.0.2" + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" + }, + "socks": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", + "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "requires": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "requires": { + "minipass": "^3.1.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/node-syscall/package.json b/node-syscall/package.json new file mode 100644 index 00000000..d77c950f --- /dev/null +++ b/node-syscall/package.json @@ -0,0 +1,15 @@ +{ + "name": "syscall", + "description": "Native syscall support for GopherJS.", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "install": "node-gyp rebuild" + }, + "main": "index.js", + "author": "", + "license": "BSD-2-Clause", + "gypfile": true, + "dependencies": { + "node-gyp": "^8.1.0" + } +} diff --git a/node-syscall/syscall.cc b/node-syscall/syscall.cc index 7d432d9e..9f6469c0 100644 --- a/node-syscall/syscall.cc +++ b/node-syscall/syscall.cc @@ -1,3 +1,6 @@ +#include +#include +#include #include #include #include @@ -13,7 +16,37 @@ using namespace v8; #define ARRAY_BUFFER_DATA_OFFSET 31 #endif -intptr_t toNative(Local value) { +// arena stores buffers we allocate for data passed to syscalls. +// +// This object lives for the duration of Syscall() or Syscall6() and correctly +// frees all allocated buffers at the end. This is necessary to avoid memory +// leaks on each call. +class arena { + std::vector> allocs_; +public: + arena() = default; + virtual ~arena() = default; + arena(const arena& a) = delete; + + intptr_t* allocate(size_t n) { + allocs_.emplace_back(new intptr_t[n]); + return allocs_.end()->get(); + } +}; + +// Cast value to integer or throw an exception. +// +// The exception must be explicitly caught up the call stack, since the Node API +// this extension is using doesn't seem to handle exceptions. +Local integerOrDie(Local ctx, Local value) { + Local integer; + if (value->ToInteger(ctx).ToLocal(&integer)) { + return integer; + } + throw std::runtime_error("expected integer, got something else"); +} + +intptr_t toNative(Local ctx, arena& a, Local value) { if (value.IsEmpty()) { return 0; } @@ -23,76 +56,95 @@ intptr_t toNative(Local value) { } if (value->IsArray()) { Local array = Local::Cast(value); - intptr_t* native = reinterpret_cast(malloc(array->Length() * sizeof(intptr_t))); // TODO memory leak + intptr_t* native = a.allocate(array->Length()); for (uint32_t i = 0; i < array->Length(); i++) { - native[i] = toNative(array->Get(i)); + native[i] = toNative(ctx, a, array->Get(ctx, i).ToLocalChecked()); } return reinterpret_cast(native); } - return static_cast(static_cast(value->ToInteger()->Value())); + + return static_cast(static_cast(integerOrDie(ctx, value)->Value())); } void Syscall(const FunctionCallbackInfo& info) { - int trap = info[0]->ToInteger()->Value(); - int r1 = 0, r2 = 0; - switch (trap) { - case SYS_fork: - r1 = fork(); - break; - case SYS_pipe: - int fd[2]; - r1 = pipe(fd); - if (r1 == 0) { - r1 = fd[0]; - r2 = fd[1]; + arena a; + Isolate* isolate = info.GetIsolate(); + Local ctx = isolate->GetCurrentContext(); + + try { + int trap = integerOrDie(ctx, info[0])->Value(); + int r1 = 0, r2 = 0; + switch (trap) { + case SYS_fork: + r1 = fork(); + break; + case SYS_pipe: + int fd[2]; + r1 = pipe(fd); + if (r1 == 0) { + r1 = fd[0]; + r2 = fd[1]; + } + break; + default: + r1 = syscall( + trap, + toNative(ctx, a, info[1]), + toNative(ctx, a, info[2]), + toNative(ctx, a, info[3]) + ); + break; } - break; - default: - r1 = syscall( - trap, - toNative(info[1]), - toNative(info[2]), - toNative(info[3]) - ); - break; - } - int err = 0; - if (r1 < 0) { - err = errno; + int err = 0; + if (r1 < 0) { + err = errno; + } + Local res = Array::New(isolate, 3); + res->Set(ctx, 0, Integer::New(isolate, r1)).ToChecked(); + res->Set(ctx, 1, Integer::New(isolate, r2)).ToChecked(); + res->Set(ctx, 2, Integer::New(isolate, err)).ToChecked(); + info.GetReturnValue().Set(res); + } catch (std::exception& e) { + auto message = String::NewFromUtf8(isolate, e.what(), NewStringType::kNormal).ToLocalChecked(); + isolate->ThrowException(Exception::TypeError(message)); + return; } - Isolate* isolate = info.GetIsolate(); - Local res = Array::New(isolate, 3); - res->Set(0, Integer::New(isolate, r1)); - res->Set(1, Integer::New(isolate, r2)); - res->Set(2, Integer::New(isolate, err)); - info.GetReturnValue().Set(res); } void Syscall6(const FunctionCallbackInfo& info) { - int r = syscall( - info[0]->ToInteger()->Value(), - toNative(info[1]), - toNative(info[2]), - toNative(info[3]), - toNative(info[4]), - toNative(info[5]), - toNative(info[6]) - ); - int err = 0; - if (r < 0) { - err = errno; - } + arena a; Isolate* isolate = info.GetIsolate(); - Local res = Array::New(isolate, 3); - res->Set(0, Integer::New(isolate, r)); - res->Set(1, Integer::New(isolate, 0)); - res->Set(2, Integer::New(isolate, err)); - info.GetReturnValue().Set(res); + Local ctx = Context::New(isolate); + + try { + int r = syscall( + integerOrDie(ctx, info[0])->Value(), + toNative(ctx, a, info[1]), + toNative(ctx, a, info[2]), + toNative(ctx, a, info[3]), + toNative(ctx, a, info[4]), + toNative(ctx, a, info[5]), + toNative(ctx, a, info[6]) + ); + int err = 0; + if (r < 0) { + err = errno; + } + Local res = Array::New(isolate, 3); + res->Set(ctx, 0, Integer::New(isolate, r)).ToChecked(); + res->Set(ctx, 1, Integer::New(isolate, 0)).ToChecked(); + res->Set(ctx, 2, Integer::New(isolate, err)).ToChecked(); + info.GetReturnValue().Set(res); + } catch (std::exception& e) { + auto message = String::NewFromUtf8(isolate, e.what(), NewStringType::kNormal).ToLocalChecked(); + isolate->ThrowException(Exception::TypeError(message)); + return; + } } -void init(Handle target) { - NODE_SET_METHOD(target, "Syscall", Syscall); - NODE_SET_METHOD(target, "Syscall6", Syscall6); +void init(Local exports) { + NODE_SET_METHOD(exports, "Syscall", Syscall); + NODE_SET_METHOD(exports, "Syscall6", Syscall6); } NODE_MODULE(syscall, init); diff --git a/package-lock.json b/package-lock.json index ed79b7a9..21b8f586 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,1841 @@ { "name": "gopherjs", + "lockfileVersion": 2, "requires": true, - "lockfileVersion": 1, + "packages": { + "": { + "name": "gopherjs", + "license": "BSD-2-Clause", + "dependencies": { + "source-map-support": "^0.5.19" + }, + "devDependencies": { + "uglify-es": "^3.3.9" + }, + "optionalDependencies": { + "syscall": "file:./node-syscall" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/syscall": { + "resolved": "node-syscall", + "link": true + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "optional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "deprecated": "support for ECMAScript is superseded by `uglify-js` as of v3.13.0", + "dev": true, + "dependencies": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true + }, + "node-syscall": { + "name": "syscall", + "hasInstallScript": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "node-gyp": "^8.1.0" + } + }, + "node-syscall/node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node-syscall/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node-syscall/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "node-syscall/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node-syscall/node_modules/agentkeepalive": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", + "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", + "optional": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node-syscall/node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node-syscall/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node-syscall/node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, + "node-syscall/node_modules/are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node-syscall/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "optional": true + }, + "node-syscall/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node-syscall/node_modules/cacache": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz", + "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==", + "optional": true, + "dependencies": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node-syscall/node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node-syscall/node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node-syscall/node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true + }, + "node-syscall/node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, + "node-syscall/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "optional": true + }, + "node-syscall/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node-syscall/node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "node-syscall/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node-syscall/node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node-syscall/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node-syscall/node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, + "node-syscall/node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "optional": true + }, + "node-syscall/node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "optional": true, + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node-syscall/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node-syscall/node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "optional": true + }, + "node-syscall/node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, + "node-syscall/node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "optional": true + }, + "node-syscall/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node-syscall/node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node-syscall/node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node-syscall/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node-syscall/node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node-syscall/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node-syscall/node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, + "node-syscall/node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node-syscall/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "optional": true + }, + "node-syscall/node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "optional": true + }, + "node-syscall/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node-syscall/node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "optional": true + }, + "node-syscall/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true + }, + "node-syscall/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "optional": true + }, + "node-syscall/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node-syscall/node_modules/make-fetch-happen": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz", + "integrity": "sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node-syscall/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node-syscall/node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node-syscall/node_modules/minipass-fetch": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.4.tgz", + "integrity": "sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ==", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node-syscall/node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node-syscall/node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node-syscall/node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node-syscall/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "node-syscall/node_modules/node-gyp": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.1.0.tgz", + "integrity": "sha512-o2elh1qt7YUp3lkMwY3/l4KF3j/A3fI/Qt4NH+CQQgPJdqGE9y7qnP84cjIWN27Q0jJkrSAhCVDg+wBVNBYdBg==", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^8.0.14", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.0", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node-syscall/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node-syscall/node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node-syscall/node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node-syscall/node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node-syscall/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, + "dependencies": { + "wrappy": "1" + } + }, + "node-syscall/node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node-syscall/node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node-syscall/node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "optional": true + }, + "node-syscall/node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "optional": true + }, + "node-syscall/node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node-syscall/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node-syscall/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node-syscall/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node-syscall/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "node-syscall/node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, + "node-syscall/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node-syscall/node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true + }, + "node-syscall/node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "optional": true + }, + "node-syscall/node_modules/smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node-syscall/node_modules/socks": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", + "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "optional": true, + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node-syscall/node_modules/socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node-syscall/node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node-syscall/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node-syscall/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node-syscall/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node-syscall/node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node-syscall/node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node-syscall/node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true + }, + "node-syscall/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node-syscall/node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node-syscall/node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "optional": true + } + }, "dependencies": { + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true + }, "commander": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", "dev": true }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "syscall": { + "version": "file:node-syscall", + "optional": true, + "requires": { + "node-gyp": "^8.1.0" + }, + "dependencies": { + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "optional": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", + "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", + "optional": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "cacache": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz", + "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==", + "optional": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "optional": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "optional": true + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "optional": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "optional": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "optional": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "optional": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "optional": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "optional": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "optional": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "optional": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "optional": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "optional": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-fetch-happen": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz", + "integrity": "sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==", + "optional": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.4.tgz", + "integrity": "sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ==", + "optional": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "node-gyp": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.1.0.tgz", + "integrity": "sha512-o2elh1qt7YUp3lkMwY3/l4KF3j/A3fI/Qt4NH+CQQgPJdqGE9y7qnP84cjIWN27Q0jJkrSAhCVDg+wBVNBYdBg==", + "optional": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^8.0.14", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.0", + "which": "^2.0.2" + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "optional": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "optional": true + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "optional": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "optional": true + }, + "smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "optional": true + }, + "socks": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", + "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "optional": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "optional": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "optional": true + } + } + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "optional": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } }, "uglify-es": { "version": "3.3.9", @@ -21,9 +1843,15 @@ "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", "dev": true, "requires": { - "commander": "2.13.0", - "source-map": "0.6.1" + "commander": "~2.13.0", + "source-map": "~0.6.1" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true } } } diff --git a/package.json b/package.json index e7426958..f276a4eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,13 @@ { "name": "gopherjs", + "license": "BSD-2-Clause", "devDependencies": { - "uglify-es": "3.3.9" + "uglify-es": "^3.3.9" + }, + "dependencies": { + "source-map-support": "^0.5.19" + }, + "optionalDependencies": { + "syscall": "file:./node-syscall" } } diff --git a/tests/arrays_test.go b/tests/arrays_test.go new file mode 100644 index 00000000..c202f2e9 --- /dev/null +++ b/tests/arrays_test.go @@ -0,0 +1,86 @@ +package tests + +import ( + "reflect" + "testing" + "unsafe" +) + +func TestArrayPointer(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var p1 *[1]int + if p1 != nil { + t.Errorf("Zero-value array pointer is not equal to nil: %v", p1) + } + + var p2 *[1]int = nil + if p2 != nil { + t.Errorf("Nil array pointer is not equal to nil: %v", p2) + } + + p3 := func() *[1]int { return nil }() + if p3 != nil { + t.Errorf("Nil array pointer returned from function is not equal to nil: %v", p3) + } + + if p1 != p3 || p1 != p2 || p2 != p3 { + t.Errorf("Nil pointers are not equal to each other: %v %v %v", p1, p2, p3) + } + + if v := reflect.ValueOf(p1); !v.IsNil() { + t.Errorf("reflect.Value.IsNil() is false for a nil pointer: %v %v", p1, v) + } + + type arr *[1]int + var p4 arr = nil + + if v := reflect.ValueOf(p4); !v.IsNil() { + t.Errorf("reflect.Value.IsNil() is false for a nil pointer: %v %v", p4, v) + } + }) + + t.Run("pointer-dereference", func(t *testing.T) { + a1 := [1]int{42} + aPtr := &a1 + a2 := *aPtr + if !reflect.DeepEqual(a1, a2) { + t.Errorf("Array after pointer dereferencing is not equal to the original: %v != %v", a1, a2) + t.Logf("Pointer: %v", aPtr) + } + }) + + t.Run("interface-and-back", func(t *testing.T) { + type arr *[1]int + tests := []struct { + name string + a arr + }{{ + name: "not nil", + a: &[1]int{42}, + }, { + name: "nil", + a: nil, + }} + for _, test := range tests { + a1 := test.a + i := interface{}(a1) + a2 := i.(arr) + + if a1 != a2 { + t.Errorf("Array pointer is not equal to itself after interface conversion: %v != %v", a1, a2) + println(a1, a2) + } + } + }) + + t.Run("reflect.IsNil", func(t *testing.T) { + + }) +} + +func TestReflectArraySize(t *testing.T) { + want := unsafe.Sizeof(int(0)) * 8 + if got := reflect.TypeOf([8]int{}).Size(); got != want { + t.Errorf("array type size gave %v, want %v", got, want) + } +} diff --git a/tests/compiler_test.go b/tests/compiler_test.go new file mode 100644 index 00000000..7c72e353 --- /dev/null +++ b/tests/compiler_test.go @@ -0,0 +1,54 @@ +package tests + +import ( + "testing" +) + +func TestVariadicNil(t *testing.T) { + t.Run("only variadic", func(t *testing.T) { + printVari := func(strs ...string) []string { + return strs + } + + if got := printVari(); got != nil { + t.Errorf("printVari(): got: %#v; want %#v.", got, nil) + } + + { + var want []string + if got := printVari(want...); got != nil { + t.Errorf("printVari(want...): got: %#v; want %#v.", got, nil) + } + } + + { + want := []string{} + if got := printVari(want...); got == nil || len(got) != len(want) { + t.Errorf("printVari(want...): got: %#v; want %#v.", got, want) + } + } + }) + t.Run("mixed", func(t *testing.T) { + printVari := func(_ int, strs ...string) []string { + return strs + } + + if got := printVari(0); got != nil { + t.Errorf("printVari(): got: %#v; want %#v.", got, nil) + } + + { + var want []string + if got := printVari(0, want...); got != nil { + t.Errorf("printVari(want...): got: %#v; want %#v.", got, nil) + } + } + + { + want := []string{} + if got := printVari(0, want...); got == nil || len(got) != len(want) { + t.Errorf("printVari(want...): got: %#v; want %#v.", got, want) + } + } + }) +} diff --git a/tests/copy_test.go b/tests/copy_test.go index c03a3a83..b0ea8e09 100644 --- a/tests/copy_test.go +++ b/tests/copy_test.go @@ -1,6 +1,7 @@ package tests import ( + "reflect" "testing" ) @@ -131,3 +132,17 @@ func TestExplicitConversion(t *testing.T) { t.Fail() } } + +func TestCopyStructByReflect(t *testing.T) { + // https://github.com/gopherjs/gopherjs/issues/1156 + type Info struct { + name string + } + a := []Info{Info{"A"}, Info{"B"}, Info{"C"}} + v := reflect.ValueOf(a) + i := v.Index(0).Interface() + a[0] = Info{"X"} + if got := i.(Info).name; got != "A" { + t.Fatalf(`bad copy struct got %q, want "A"`, got) + } +} diff --git a/tests/deferblock_test.go b/tests/deferblock_test.go index 89fe0c9c..7667f6d8 100644 --- a/tests/deferblock_test.go +++ b/tests/deferblock_test.go @@ -1,6 +1,8 @@ package tests import ( + "errors" + "fmt" "testing" "time" ) @@ -40,3 +42,75 @@ func TestBlockingInDefer(t *testing.T) { outer(ch, b) } + +func TestIssue1083(t *testing.T) { + // https://github.com/gopherjs/gopherjs/issues/1083 + var block = make(chan bool) + + recoverCompleted := false + + recoverAndBlock := func() { + defer func() {}() + recover() + block <- true + recoverCompleted = true + } + + handle := func() { + defer func() {}() + panic("expected panic") + } + + serve := func() { + defer recoverAndBlock() + handle() + t.Fatal("This line must never execute.") + } + + go func() { <-block }() + + serve() + if !recoverCompleted { + t.Fatal("Recovery function did not execute fully.") + } +} + +func TestIssue780(t *testing.T) { + // https://github.com/gopherjs/gopherjs/issues/780 + want := errors.New("expected error") + var got error + + catch := func() { + if r := recover(); r != nil { + got = r.(error) + } + } + throw := func() { panic(want) } + + catchAndThrow := func() { + t.Logf("catchAndThrow: %v", recover()) + panic(want) + } + + execute := func(x int) (err error) { + defer catch() // Final recovery. + + for i := 0; i < x; i++ { + // Test that several deferred panics can be handled. + defer catchAndThrow() + } + + defer throw() // Emulates a panicing cleanup. + + return nil + } + + for _, x := range []int{0, 1, 2, 5, 10} { + t.Run(fmt.Sprint(x), func(t *testing.T) { + execute(x) + if !errors.Is(got, want) { + t.Errorf("process() returned error %v, want %v", got, want) + } + }) + } +} diff --git a/tests/gopherjsvendored_test.sh b/tests/gopherjsvendored_test.sh index a9ef4105..4597a796 100755 --- a/tests/gopherjsvendored_test.sh +++ b/tests/gopherjsvendored_test.sh @@ -37,9 +37,12 @@ func main() { for pkg in $(go list -f '{{if not .Goroot}}{{.ImportPath}}{{end}}' $(go list -f '{{.ImportPath}} {{join .Deps " "}}' github.com/gopherjs/gopherjs)); do copyGoPackage "$pkg" "$tmp/src/example.org/hello/vendor/$pkg" done +cp -r "$(go list -f '{{.Dir}}' 'github.com/gopherjs/gopherjs/compiler/natives')/src" \ + "$tmp/src/example.org/hello/vendor/github.com/gopherjs/gopherjs/compiler/natives/src" # Make $tmp our GOPATH workspace. export GOPATH="$tmp" +export GO111MODULE=off # Build the vendored copy of GopherJS. go install example.org/hello/vendor/github.com/gopherjs/gopherjs diff --git a/tests/gorepo/gorepo_test.go b/tests/gorepo/gorepo_test.go new file mode 100644 index 00000000..d8071594 --- /dev/null +++ b/tests/gorepo/gorepo_test.go @@ -0,0 +1,40 @@ +package gorepo_test + +import ( + "os" + "os/exec" + "runtime" + "testing" +) + +// Go repository basic compiler tests, and regression tests for fixed compiler bugs. +func TestGoRepositoryCompilerTests(t *testing.T) { + if testing.Short() { + t.Skip("skipping Go repository tests in the short mode") + } + if runtime.GOOS == "js" { + t.Skip("test meant to be run using normal Go compiler (needs os/exec)") + } + + args := []string{"go", "run", "run.go", "-summary"} + if testing.Verbose() { + args = append(args, "-v") + } + + shards := os.Getenv("CIRCLE_NODE_TOTAL") + shard := os.Getenv("CIRCLE_NODE_INDEX") + if shards != "" && shard != "" { + // We are running under CircleCI parallel test job, so we need to shard execution. + args = append(args, "-shard="+shard, "-shards="+shards) + // CircleCI reports a lot more cores than we can actually use, so we have to limit concurrency. + args = append(args, "-n=2", "-l=2") + } + + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stdout + err := cmd.Run() + if err != nil { + t.Fatal(err) + } +} diff --git a/tests/run.go b/tests/gorepo/run.go similarity index 86% rename from tests/run.go rename to tests/gorepo/run.go index 172cd310..dd6b392f 100644 --- a/tests/run.go +++ b/tests/gorepo/run.go @@ -1,3 +1,4 @@ +//go:build ignore // +build ignore // skip @@ -36,6 +37,8 @@ import ( "strings" "time" "unicode" + + gbuild "github.com/gopherjs/gopherjs/build" ) // ----------------------------------------------------------------------------- @@ -55,7 +58,6 @@ var knownFails = map[string]failReason{ "fixedbugs/bug352.go": {desc: "BUG: bug352 struct{}"}, "fixedbugs/bug409.go": {desc: "1 2 3 4"}, "fixedbugs/bug433.go": {desc: "Error: [object Object]"}, - "fixedbugs/issue10353.go": {desc: "incorrect output"}, "fixedbugs/issue11656.go": {desc: "Error: Native function not implemented: runtime/debug.setPanicOnFault"}, "fixedbugs/issue4085b.go": {desc: "Error: got panic JavaScript error: Invalid typed array length, want len out of range"}, "fixedbugs/issue4316.go": {desc: "Error: runtime error: invalid memory address or nil pointer dereference"}, @@ -64,9 +66,8 @@ var knownFails = map[string]failReason{ "fixedbugs/issue4620.go": {desc: "map[0:1 1:2], Error: m[i] != 2"}, "fixedbugs/issue5856.go": {category: requiresSourceMapSupport}, "fixedbugs/issue6899.go": {desc: "incorrect output -0"}, - "fixedbugs/issue7550.go": {desc: "FATAL ERROR: invalid table size Allocation failed - process out of memory"}, + "fixedbugs/issue7550.go": {category: neverTerminates, desc: "FATAL ERROR: invalid table size Allocation failed - process out of memory"}, "fixedbugs/issue7690.go": {desc: "Error: runtime error: slice bounds out of range"}, - "fixedbugs/issue8047.go": {desc: "null"}, "fixedbugs/issue8047b.go": {desc: "Error: [object Object]"}, // Failing due to use of os/exec.Command, which is unsupported. Now skipped via !nacl build tag. @@ -84,7 +85,6 @@ var knownFails = map[string]failReason{ "fixedbugs/issue14646.go": {category: unsureIfGopherJSSupportsThisFeature, desc: "tests runtime.Caller behavior in a deferred func in SSA backend... does GopherJS even support runtime.Caller?"}, "fixedbugs/issue15039.go": {desc: "valid bug but deal with after Go 1.7 support is out? it's likely not a regression"}, "fixedbugs/issue15281.go": {desc: "also looks valid but deal with after Go 1.7 support is out? it's likely not a regression"}, - "fixedbugs/issue15975.go": {desc: "also looks valid but deal with after Go 1.7 support is out?"}, // These are new tests in Go 1.8. "fixedbugs/issue17381.go": {category: unsureIfGopherJSSupportsThisFeature, desc: "tests runtime.{Callers,FuncForPC} behavior in a deferred func with garbage on stack... does GopherJS even support runtime.{Callers,FuncForPC}?"}, @@ -101,7 +101,6 @@ var knownFails = map[string]failReason{ // These are new tests in Go 1.10. "fixedbugs/issue21879.go": {desc: "incorrect output related to runtime.Callers, runtime.CallersFrames, etc."}, "fixedbugs/issue21887.go": {desc: "incorrect output (although within spec, not worth fixing) for println(^uint64(0)). got: { '$high': 4294967295, '$low': 4294967295, '$val': [Circular] } want: 18446744073709551615"}, - "fixedbugs/issue22083.go": {category: requiresSourceMapSupport}, // Technically, added in Go 1.9.2. "fixedbugs/issue22660.go": {category: notApplicable, desc: "test of gc compiler, uses os/exec.Command"}, "fixedbugs/issue23305.go": {desc: "GopherJS fails to compile println(0xffffffff), maybe because 32-bit arch"}, @@ -119,7 +118,45 @@ var knownFails = map[string]failReason{ "fixedbugs/issue23837.go": {desc: "missing panic on nil pointer-to-empty-struct dereference"}, "fixedbugs/issue27201.go": {desc: "incorrect stack trace for nil dereference in inlined function"}, "fixedbugs/issue27518b.go": {desc: "sigpanic can make dead pointer live again"}, - "fixedbugs/issue29190.go": {desc: "append does not fail when length overflows"}, + "fixedbugs/issue29190.go": {desc: "append does not fail when length overflows", category: neverTerminates}, + + // These are new tests in Go 1.12.9. + "fixedbugs/issue30977.go": {category: neverTerminates, desc: "does for { runtime.GC() }"}, + "fixedbugs/issue32477.go": {category: notApplicable, desc: "uses runtime.SetFinalizer and runtime.GC"}, + "fixedbugs/issue32680.go": {category: notApplicable, desc: "uses -gcflags=-d=ssa/check/on flag"}, + + // These are new tests in Go 1.13-1.16. + "fixedbugs/issue19113.go": {category: lowLevelRuntimeDifference, desc: "JavaScript bit shifts by negative amount don't cause an exception"}, + "fixedbugs/issue24491a.go": {category: notApplicable, desc: "tests interaction between unsafe and GC; uses runtime.SetFinalizer()"}, + "fixedbugs/issue24491b.go": {category: notApplicable, desc: "tests interaction between unsafe and GC; uses runtime.SetFinalizer()"}, + "fixedbugs/issue29504.go": {category: notApplicable, desc: "requires source map support beyond what GopherJS currently provides"}, + // This test incorrectly passes because main function's name is returned as "main" and not "main.main". Even number of bugs cancel each other out ¯\_(ツ)_/¯ + // "fixedbugs/issue29735.go": {category: usesUnsupportedPackage, desc: "GopherJS only supports runtime.FuncForPC() with position counters previously returned by runtime.Callers() or runtime.Caller()"}, + "fixedbugs/issue30116.go": {desc: "GopherJS doesn't specify the array/slice index selector in the out-of-bounds message"}, + "fixedbugs/issue30116u.go": {desc: "GopherJS doesn't specify the array/slice index selector in the out-of-bounds message"}, + "fixedbugs/issue34395.go": {category: neverTerminates, desc: "https://github.com/gopherjs/gopherjs/issues/1007"}, + "fixedbugs/issue35027.go": {category: usesUnsupportedPackage, desc: "uses unsupported conversion to reflect.SliceHeader and -gcflags=-d=checkptr"}, + "fixedbugs/issue35073.go": {category: usesUnsupportedPackage, desc: "uses unsupported flag -gcflags=-d=checkptr"}, + "fixedbugs/issue35576.go": {category: lowLevelRuntimeDifference, desc: "GopherJS print/println format for floats differs from Go's"}, + "fixedbugs/issue40917.go": {category: notApplicable, desc: "uses pointer arithmetic and unsupported flag -gcflags=-d=checkptr"}, + + // These are new tests in Go 1.17 + "fixedbugs/issue45045.go": {category: notApplicable, desc: "GC related, not relevant to GopherJS"}, + "fixedbugs/issue5493.go": {category: notApplicable, desc: "GC related, not relevant to GopherJS"}, + "fixedbugs/issue46725.go": {category: notApplicable, desc: "GC related, not relevant to GopherJS"}, + "fixedbugs/issue43444.go": {category: lowLevelRuntimeDifference, desc: "GopherJS println format is different from Go's"}, + "fixedbugs/issue23017.go": {desc: "https://github.com/gopherjs/gopherjs/issues/1063"}, + + // These are new tests in Go 1.17.8 + "fixedbugs/issue50854.go": {category: lowLevelRuntimeDifference, desc: "negative int32 overflow behaves differently in JS"}, + + // These are new tests in Go 1.18 + "fixedbugs/issue46938.go": {category: notApplicable, desc: "tests -d=checkptr compiler mode, which GopherJS doesn't support"}, + "fixedbugs/issue47928.go": {category: notApplicable, desc: "//go:nointerface is a part of GOEXPERIMENT=fieldtrack and is not supported by GopherJS"}, + "fixedbugs/issue49665.go": {category: other, desc: "attempts to pass -gcflags=-G=3 to enable generics, GopherJS doesn't expect the flag; re-enable in Go 1.19 where the flag is removed"}, + "fixedbugs/issue48898.go": {category: other, desc: "https://github.com/gopherjs/gopherjs/issues/1128"}, + "fixedbugs/issue48536.go": {category: usesUnsupportedPackage, desc: "https://github.com/gopherjs/gopherjs/issues/1130"}, + "fixedbugs/issue53600.go": {category: lowLevelRuntimeDifference, desc: "GopherJS println format is different from Go's"}, } type failCategory uint8 @@ -131,7 +168,8 @@ const ( requiresSourceMapSupport // Test fails without source map support (as configured in CI), because it tries to check filename/line number via runtime.Caller. compilerPanic unsureIfGopherJSSupportsThisFeature - notApplicable // Test that doesn't need to run under GopherJS; it doesn't apply to the Go language in a general way. + lowLevelRuntimeDifference // JavaScript runtime behaves differently from Go in ways that are difficult to work around. + notApplicable // Test that doesn't need to run under GopherJS; it doesn't apply to the Go language in a general way. ) type failReason struct { @@ -181,27 +219,31 @@ func main() { flag.Parse() // GOPHERJS. - err := os.Chdir(filepath.Join(runtime.GOROOT(), "test")) + err := os.Chdir(filepath.Join(gbuild.DefaultGOROOT, "test")) if err != nil { log.Fatalln(err) } - goos = getenv("GOOS", runtime.GOOS) - //goarch = getenv("GOARCH", runtime.GOARCH) - // GOPHERJS. - goarch = getenv("GOARCH", "js") // We're running this script natively, but the tests are executed with js architecture. - - if *verbose { - fmt.Printf("goos: %q, goarch: %q\n", goos, goarch) - } + // GOPHERJS: We're running this script natively, but the tests are executed with js architecture. + goos = getenv("GOOS", "js") + goarch = getenv("GOARCH", "ecmascript") findExecCmd() - // Disable parallelism if printing or if using a simulator. - if *verbose || len(findExecCmd()) > 0 { + // Disable parallelism if using a simulator. + // Do not disable parallelism in verbose mode, since Go's file IO had internal + // r/w locking, which should make significant output garbling very unlikely. + // GopherJS CI setup runs these tests in verbose mode, but it can benefit from + // parallelism a lot. + if len(findExecCmd()) > 0 { *numParallel = 1 } + if *verbose { + fmt.Printf("goos: %q, goarch: %q\n", goos, goarch) + fmt.Printf("parallel: %d\n", *numParallel) + } + ratec = make(chan bool, *numParallel) rungatec = make(chan bool, *runoutputLimit) @@ -306,14 +348,6 @@ func main() { } } -func toolPath(name string) string { - p := filepath.Join(os.Getenv("GOROOT"), "bin", "tool", name) - if _, err := os.Stat(p); err != nil { - log.Fatalf("didn't find binary at %s", p) - } - return p -} - func shardMatch(name string) bool { if *shards == 0 { return true @@ -654,10 +688,10 @@ func (t *test) run() { // A few tests (of things like the environment) require these to be set. if os.Getenv("GOOS") == "" { - os.Setenv("GOOS", runtime.GOOS) + os.Setenv("GOOS", goos) } if os.Getenv("GOARCH") == "" { - os.Setenv("GOARCH", runtime.GOARCH) + os.Setenv("GOARCH", goarch) } useTmp := true @@ -800,7 +834,7 @@ func (t *test) run() { case "run": useTmp = false // GOPHERJS. - out, err := runcmd(append([]string{"gopherjs", "run", "-q", t.goFileName()}, args...)...) + out, err := runcmd(append([]string{"gopherjs", "run", t.goFileName()}, args...)...) if err != nil { t.err = err return diff --git a/tests/goroutine_test.go b/tests/goroutine_test.go index 962a6456..011e3e8a 100644 --- a/tests/goroutine_test.go +++ b/tests/goroutine_test.go @@ -1,9 +1,14 @@ package tests import ( + "context" "fmt" + "runtime" + "sync/atomic" "testing" "time" + + "github.com/gopherjs/gopherjs/js" ) var expectedI int @@ -98,6 +103,50 @@ func testPanicAdvanced2(t *testing.T) { checkI(t, 4) } +func TestPanicIssue1030(t *testing.T) { + throwException := func() { + t.Log("Will throw now...") + js.Global.Call("eval", "throw 'original panic';") + } + + wrapException := func() { + defer func() { + err := recover() + if err == nil { + t.Fatal("Should never happen: no original panic.") + } + t.Log("Got original panic: ", err) + panic("replacement panic") + }() + + throwException() + } + + panicing := false + + expectPanic := func() { + defer func() { + t.Log("No longer panicing.") + panicing = false + }() + defer func() { + err := recover() + if err == nil { + t.Fatal("Should never happen: no wrapped panic.") + } + t.Log("Got wrapped panic: ", err) + }() + + wrapException() + } + + expectPanic() + + if panicing { + t.Fatal("Deferrals were not executed correctly!") + } +} + func TestSelect(t *testing.T) { expectedI = 1 a := make(chan int) @@ -140,3 +189,126 @@ func TestDeferWithBlocking(t *testing.T) { fmt.Print("") return } + +// counter, sideEffect and withBlockingDeferral are defined as top-level symbols +// to make compiler generate simplest code possible without any closures. +var counter = 0 + +func sideEffect() int { + counter++ + return 42 +} + +func withBlockingDeferral() int { + defer time.Sleep(0) + return sideEffect() +} + +func TestReturnWithBlockingDefer(t *testing.T) { + // See: https://github.com/gopherjs/gopherjs/issues/603. + counter = 0 + + got := withBlockingDeferral() + if got != 42 { + t.Errorf("Unexpected return value %v. Want: 42.", got) + } + if counter != 1 { + t.Errorf("Return value was computed %d times. Want: exactly 1.", counter) + } +} + +func BenchmarkGoroutineSwitching(b *testing.B) { + // This benchmark is designed to measure the cost of goroutine switching. + // The two goroutines communicate through an unbuffered channel, which forces + // the control to be passed between them on each iteraction of the benchmark. + // Although the cost of channel operations is also included in the measurement, + // it still allows relative comparison of changes to goroutine scheduling + // performance. + c := make(chan bool) + go func() { + for i := 0; i < b.N; i++ { + c <- true + } + close(c) + }() + + b.ResetTimer() + count := 0 + for range c { + count++ + } +} + +func TestEventLoopStarvation(t *testing.T) { + // See: https://github.com/gopherjs/gopherjs/issues/1078. + c := make(chan bool) + ctx, cancel := context.WithCancel(context.Background()) + go func() { + time.Sleep(100 * time.Millisecond) + cancel() + }() + go func() { + for { + select { + case c <- true: + case <-ctx.Done(): + return + } + } + }() + go func() { + for { + select { + case <-c: + case <-ctx.Done(): + return + } + } + }() + <-ctx.Done() +} + +func TestGoroutineBuiltin(t *testing.T) { + // Test that a built-in function can be a goroutine body. + // https://github.com/gopherjs/gopherjs/issues/547. + c := make(chan bool) + go close(c) + <-c // Wait until goroutine executes successfully. +} + +func TestGoroutineJsObject(t *testing.T) { + // Test that js.Object methods can be a goroutine body. + // https://github.com/gopherjs/gopherjs/issues/547. + if !(runtime.GOOS == "js" || runtime.GOARCH == "js") { + t.Skip("Test requires GopherJS") + } + o := js.Global.Get("Object").New() + go o.Set("x", "y") + // Wait until the goroutine executes successfully. Can't use locks here + // because goroutine body must be a bare js.Object method call. + for o.Get("x").String() != "y" { + runtime.Gosched() + } +} + +func issue1106() { + select { + default: + } +} + +func TestIssue1106(t *testing.T) { + // https://github.com/gopherjs/gopherjs/issues/1106#issuecomment-1046323374 + var done int32 = 0 + go func() { + f := issue1106 + f() + atomic.AddInt32(&done, 1) + }() + + // Will get stuck here if #1106 is not fixed. + for !atomic.CompareAndSwapInt32(&done, 1, 1) { + // Maintain one active goroutine to prevent Node from exiting. + runtime.Gosched() + } +} diff --git a/tests/js_test.go b/tests/js_test.go index a51ee276..03f0e1d7 100644 --- a/tests/js_test.go +++ b/tests/js_test.go @@ -1,4 +1,5 @@ -// +build js +//go:build js && !wasm +// +build js,!wasm package tests_test @@ -391,6 +392,14 @@ type F struct { Field int } +func (f F) NonPoint() int { + return 10 +} + +func (f *F) Point() int { + return 20 +} + func TestExternalizeField(t *testing.T) { if dummys.Call("testField", map[string]int{"Field": 42}).Int() != 42 { t.Fail() @@ -423,7 +432,12 @@ func TestMakeFunc(t *testing.T) { } type M struct { - f int + Struct F + Pointer *F + Array [1]F + Slice []*F + Name string + f int } func (m *M) Method(a interface{}) map[string]string { @@ -435,8 +449,28 @@ func (m *M) Method(a interface{}) map[string]string { } } +func (m *M) GetF() F { + return m.Struct +} + +func (m *M) GetFPointer() *F { + return m.Pointer +} + +func (m *M) ParamMethod(v *M) string { + return v.Name +} + +func (m *M) Field() string { + return "rubbish" +} + +func (m M) NonPointField() string { + return "sensible" +} + func TestMakeWrapper(t *testing.T) { - m := &M{42} + m := &M{f: 42} if !js.Global.Call("eval", `(function(m) { return m.Method({x: 1})["y"] === "z"; })`).Invoke(js.MakeWrapper(m)).Bool() { t.Fail() } @@ -445,12 +479,140 @@ func TestMakeWrapper(t *testing.T) { t.Fail() } +} + +func TestMakeFullWrapperType(t *testing.T) { + m := &M{f: 42} f := func(m *M) { if m.f != 42 { t.Fail() } } - js.Global.Call("eval", `(function(f, m) { f(m); })`).Invoke(f, js.MakeWrapper(m)) + + js.Global.Call("eval", `(function(f, m) { f(m); })`).Invoke(f, js.MakeFullWrapper(m)) + want := "github.com/gopherjs/gopherjs/tests_test.*M" + if got := js.MakeFullWrapper(m).Get("$type").String(); got != want { + t.Errorf("wanted type string %q; got %q", want, got) + } +} + +func TestMakeFullWrapperGettersAndSetters(t *testing.T) { + f := &F{Field: 50} + m := &M{ + Name: "Gopher", + Struct: F{Field: 42}, + Pointer: f, + Array: [1]F{F{Field: 42}}, + Slice: []*F{f}, + } + + const globalVar = "TestMakeFullWrapper_w1" + + eval := func(s string, v ...interface{}) *js.Object { + return js.Global.Call("eval", s).Invoke(v...) + } + call := func(s string, v ...interface{}) *js.Object { + return eval(fmt.Sprintf(`(function(g) { return g["%v"]%v; })`, globalVar, s), js.Global).Invoke(v...) + } + get := func(s string) *js.Object { + return eval(fmt.Sprintf(`(function(g) { return g["%v"]%v; })`, globalVar, s), js.Global) + } + set := func(s string, v interface{}) { + eval(fmt.Sprintf(`(function(g, v) { g["%v"]%v = v; })`, globalVar, s), js.Global, v) + } + + w1 := js.MakeFullWrapper(m) + { + w2 := js.MakeFullWrapper(m) + + // we expect that MakeFullWrapper produces a different value each time + if eval(`(function(o, p) { return o === p; })`, w1, w2).Bool() { + t.Fatalf("w1 equalled w2 when we didn't expect it to") + } + } + + set("", w1) + + { + prop := ".Name" + want := m.Name + if got := get(prop).String(); got != want { + t.Fatalf("wanted w1%v to be %v; got %v", prop, want, got) + } + newVal := "JS" + set(prop, newVal) + if got := m.Name; got != newVal { + t.Fatalf("wanted m%v to be %v; got %v", prop, newVal, got) + } + } + { + prop := ".Struct.Field" + want := m.Struct.Field + if got := get(prop).Int(); got != want { + t.Fatalf("wanted w1%v to be %v; got %v", prop, want, got) + } + newVal := 40 + set(prop, newVal) + if got := m.Struct.Field; got == newVal { + t.Fatalf("wanted m%v not to be %v; but was", prop, newVal) + } + } + { + prop := ".Pointer.Field" + want := m.Pointer.Field + if got := get(prop).Int(); got != want { + t.Fatalf("wanted w1%v to be %v; got %v", prop, want, got) + } + newVal := 40 + set(prop, newVal) + if got := m.Pointer.Field; got != newVal { + t.Fatalf("wanted m%v to be %v; got %v", prop, newVal, got) + } + } + { + prop := ".Array[0].Field" + want := m.Array[0].Field + if got := get(prop).Int(); got != want { + t.Fatalf("wanted w1%v to be %v; got %v", prop, want, got) + } + newVal := 40 + set(prop, newVal) + if got := m.Array[0].Field; got == newVal { + t.Fatalf("wanted m%v not to be %v; but was", prop, newVal) + } + } + { + prop := ".Slice[0].Field" + want := m.Slice[0].Field + if got := get(prop).Int(); got != want { + t.Fatalf("wanted w1%v to be %v; got %v", prop, want, got) + } + newVal := 40 + set(prop, newVal) + if got := m.Slice[0].Field; got != newVal { + t.Fatalf("wanted m%v to be %v; got %v", prop, newVal, got) + } + } + { + prop := ".GetF().Field" + want := m.Struct.Field + if got := get(prop).Int(); got != want { + t.Fatalf("wanted w1%v to be %v; got %v", prop, want, got) + } + newVal := 105 + set(prop, newVal) + if got := m.Struct.Field; got == newVal { + t.Fatalf("wanted m%v not to be %v; but was", prop, newVal) + } + } + { + method := ".ParamMethod" + want := method + m.Name = want + if got := call(method, get("")).String(); got != want { + t.Fatalf("wanted w1%v() to be %v; got %v", method, want, got) + } + } } func TestCallWithNull(t *testing.T) { diff --git a/tests/linkname_test.go b/tests/linkname_test.go new file mode 100644 index 00000000..c1646d0a --- /dev/null +++ b/tests/linkname_test.go @@ -0,0 +1,36 @@ +package tests + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/gopherjs/gopherjs/tests/testdata/linkname/method" + "github.com/gopherjs/gopherjs/tests/testdata/linkname/one" +) + +func TestLinknames(t *testing.T) { + defer func() { + if err := recover(); err != nil { + t.Fatalf("one.DoAll() paniced: %s", err) + } + }() + want := "doing one\n" + + "doing two\n" + + "doing imported one: doing internal one: one secret\n" + + "doing three\n" + + "doing imported three: doing internal three: three secret\n" + got := one.DoAll() + + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Callink linknamed functions returned a diff (-want,+got):\n%s", diff) + } +} + +func TestLinknameMethods(t *testing.T) { + defer func() { + if err := recover(); err != nil { + t.Fatalf("method.TestLinkname() paniced: %s", err) + } + }() + method.TestLinkname(t) +} diff --git a/tests/lowlevel_test.go b/tests/lowlevel_test.go index 2d0e7fb9..e966eba7 100644 --- a/tests/lowlevel_test.go +++ b/tests/lowlevel_test.go @@ -1,12 +1,14 @@ package tests_test import ( - "bytes" "io/ioutil" "os/exec" "path/filepath" "runtime" + "strings" "testing" + + "github.com/google/go-cmp/cmp" ) // Test for internalization/externalization of time.Time/Date when time package is imported @@ -14,21 +16,36 @@ import ( // // See https://github.com/gopherjs/gopherjs/issues/279. func TestTimeInternalizationExternalization(t *testing.T) { - if runtime.GOARCH == "js" { + if runtime.GOOS == "js" { t.Skip("test meant to be run using normal Go compiler (needs os/exec)") } - got, err := exec.Command("gopherjs", "run", filepath.Join("testdata", "time_inexternalization.go")).Output() + gotb, err := exec.Command("gopherjs", "run", filepath.Join("testdata", "time_inexternalization.go")).Output() + got := string(gotb) if err != nil { t.Fatalf("%v:\n%s", err, got) } - want, err := ioutil.ReadFile(filepath.Join("testdata", "time_inexternalization.out")) + wantb, err := ioutil.ReadFile(filepath.Join("testdata", "time_inexternalization.out")) + want := string(wantb) if err != nil { t.Fatalf("error reading .out file: %v", err) } + got = strings.ReplaceAll(got, "\r\n", "\n") + want = strings.ReplaceAll(want, "\r\n", "\n") - if !bytes.Equal(got, want) { - t.Fatalf("got != want:\ngot:\n%s\nwant:\n%s", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Got diff (-want,+got):\n%s", diff) + } +} + +func TestDeferBuiltin(t *testing.T) { + if runtime.GOOS == "js" { + t.Skip("test meant to be run using normal Go compiler (needs os/exec)") + } + + got, err := exec.Command("gopherjs", "run", filepath.Join("testdata", "defer_builtin.go")).CombinedOutput() + if err != nil { + t.Fatalf("%v:\n%s", err, got) } } diff --git a/tests/map_js_test.go b/tests/map_js_test.go new file mode 100644 index 00000000..26d55e20 --- /dev/null +++ b/tests/map_js_test.go @@ -0,0 +1,115 @@ +//go:build js && !wasm +// +build js,!wasm + +package tests + +import ( + "github.com/gopherjs/gopherjs/js" + "testing" +) + +func Test_MapWrapper(t *testing.T) { + // This tests that various map types, and a map as a function argument and return, + // wrap and unwrap correctly. + type Dummy struct { + Msg string + } + + type StructWithMap struct { + StringMap map[string]string + IntMap map[int]int + DummyMap map[string]*Dummy + MapFunc func(map[string]string) map[string]string + } + + dummyMap := map[string]*Dummy{"key": {Msg: "value"}} + swm := &StructWithMap{ + StringMap: map[string]string{"key": "value"}, + IntMap: map[int]int{1: 2}, + DummyMap: dummyMap, + MapFunc: func(m map[string]string) map[string]string { + return m + }, + } + swmWrapper := js.MakeFullWrapper(swm) + swmUnwrapped := swmWrapper.Interface().(*StructWithMap) + mapFuncArg := map[string]string{"key2": "value2"} + + if got := swmWrapper.Get("StringMap").Get("key").String(); got != swm.StringMap["key"] { + t.Errorf("StringMap Got: %s, Want: %s", got, swm.StringMap["key"]) + } + if got := swmWrapper.Get("IntMap").Get("1").Int(); got != swm.IntMap[1] { + t.Errorf("IntMap Got: %d, Want: %d", got, swm.IntMap[1]) + } + if got := swmWrapper.Get("DummyMap").Get("key").Get("Msg").String(); got != swm.DummyMap["key"].Msg { + t.Errorf("DummyMap Got: %s, Want: %s", got, swm.DummyMap["key"].Msg) + + } + if got := swmWrapper.Call("MapFunc", mapFuncArg).Get("key2").String(); got != mapFuncArg["key2"] { + t.Errorf("MapFunc Got: %s, Want: %s", got, mapFuncArg["key2"]) + } + + if got := swmUnwrapped.StringMap["key"]; got != swm.StringMap["key"] { + t.Errorf("Unwrapped StringMap Got: %s, Want: %s", got, swm.StringMap["key"]) + } + if got := swmUnwrapped.IntMap[1]; got != swm.IntMap[1] { + t.Errorf("Unwrapped IntMap Got: %d, Want: %d", got, swm.IntMap[1]) + } + if got := swmUnwrapped.DummyMap["key"].Msg; got != swm.DummyMap["key"].Msg { + t.Errorf("Unwrapped DummyMap Got: %s, Want: %s", got, swm.DummyMap["key"].Msg) + } + if got := swmUnwrapped.MapFunc(mapFuncArg)["key2"]; got != swm.MapFunc(mapFuncArg)["key2"] { + t.Errorf("Unwrapped MapFunc Got: %s, Want: %s", got, swm.MapFunc(mapFuncArg)["key2"]) + } +} + +func Test_MapStructObjectWrapper(t *testing.T) { + // This tests that maps work as expected when wrapping a Struct with js.Object field containing a map. + // js.Object fields' content should be passed to JS, so this is basically doubly-wrapping a map in two structs. + + stringMap := map[string]string{"key": "value"} + + // You cannot wrap a map directly, so put it in a stuct. + type StructWithMap struct { + Map map[string]string + } + + swm := &StructWithMap{Map: stringMap} + swmWrapped := js.MakeFullWrapper(swm) + + // Now that map is wrapped in a struct, wrap the js.object in *another* struct. + type StructWithObject struct { + Wrappedswm *js.Object // This Object contains StructWithMap. + } + + swo := &StructWithObject{Wrappedswm: swmWrapped} + swoWrapper := js.MakeFullWrapper(swo) + swmUnwrapped := swoWrapper.Interface().(*StructWithObject) + + // Using "Get("Map")" shows that the first wrapping was unchanged. + if got := swoWrapper.Get("Wrappedswm").Get("Map").Get("key").String(); got != stringMap["key"] { + t.Errorf("Wrapped Wrappedswm value Got: %s, Want: %s", got, stringMap["key"]) + } + + if got := swmUnwrapped.Wrappedswm.Get("Map").Get("key").String(); got != stringMap["key"] { + t.Errorf("Unwrapped Wrappedswm value Got: %s, Want: %s", got, stringMap["key"]) + } +} + +func Test_MapEmbeddedObject(t *testing.T) { + o := js.Global.Get("JSON").Call("parse", `{"props": {"one": 1, "two": 2}}`) + + type data struct { + *js.Object + Props map[string]int `js:"props"` + } + + d := data{Object: o} + if d.Props["one"] != 1 { + t.Errorf("key 'one' value Got: %d, Want: %d", d.Props["one"], 1) + } + if d.Props["two"] != 2 { + t.Errorf("key 'two' value Got: %d, Want: %d", d.Props["two"], 2) + } + +} diff --git a/tests/map_test.go b/tests/map_test.go new file mode 100644 index 00000000..56b9e635 --- /dev/null +++ b/tests/map_test.go @@ -0,0 +1,299 @@ +package tests + +import ( + "strings" + "testing" +) + +// These tests exercise the api of maps and built-in functions that operate on maps +func Test_MapLiteral(t *testing.T) { + myMap := map[string]int{"test": 0, "key": 1, "charm": 2} + + assertMapApi(t, myMap) +} + +func Test_MapLiteralAssign(t *testing.T) { + myMap := map[string]int{} + myMap["test"] = 0 + myMap["key"] = 1 + myMap["charm"] = 2 + + assertMapApi(t, myMap) +} + +func Test_MapMake(t *testing.T) { + myMap := make(map[string]int) + myMap["test"] = 0 + myMap["key"] = 1 + myMap["charm"] = 2 + + assertMapApi(t, myMap) +} + +func Test_MapMakeSizeHint(t *testing.T) { + myMap := make(map[string]int, 3) + myMap["test"] = 0 + myMap["key"] = 1 + myMap["charm"] = 2 + + assertMapApi(t, myMap) +} + +func Test_MapNew(t *testing.T) { + myMap := new(map[string]int) + if *myMap != nil { + t.Errorf("Got: %v, Want: nil when made with new()", *myMap) + } +} + +func Test_MapType(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Error("assignment on nil map should panic") + } else { + str := err.(error).Error() + if !strings.Contains(str, "assignment to entry in nil map") { + t.Errorf("nil map assignment Got: %s, Want: assigning to a nil map", str) + } + } + }() + + var myMap map[string]int + if myMap != nil { + t.Errorf("map declared with var, Got: %v, Want: nil", myMap) + } + + myMap["key"] = 666 +} + +func Test_MapLenPrecedence(t *testing.T) { + // This test verifies that the expression len(m) compiles to is evaluated + // correctly in the context of the enclosing expression. + m := make(map[string]string) + + if len(m) != 0 { + t.Errorf("inline len Got: %d, Want: 0", len(m)) + } + + i := len(m) + if i != 0 { + t.Errorf("assigned len Got: %d, Want: 0", i) + } +} + +func Test_MapRangeMutation(t *testing.T) { + // This test verifies that all of a map is iterated, even if the map is modified during iteration. + + myMap := map[string]int{"one": 1, "two": 2, "three": 3} + + var seenKeys []string + + for k := range myMap { + seenKeys = append(seenKeys, k) + if k == "two" { + delete(myMap, k) + } + } + + if len(seenKeys) != 3 { + t.Errorf("iteration seenKeys len Got: %d, Want: 3", len(seenKeys)) + } +} + +func Test_MapRangeNil(t *testing.T) { + // Tests that loops on nil maps do not error. + i := 0 + var m map[string]int + for k, v := range m { + _, _ = k, v + i++ + } + + if i > 0 { + t.Error("Got: Loops happened on a nil map, Want: no looping") + } +} + +func Test_MapDelete(t *testing.T) { + var nilMap map[string]string + m := map[string]string{"key": "value"} + + delete(nilMap, "key") // noop + delete(m, "key") + if m["key"] == "value" { + t.Error("Got: entry still set, Want: should have been deleted") + } + delete(m, "key") // noop +} + +func assertMapApi(t *testing.T, myMap map[string]int) { + if len(myMap) != 3 { + t.Errorf("initial len of map Got: %d, Want: 3", len(myMap)) + } + + var keys []string + var values []int + + for k, v := range myMap { + keys = append(keys, k) + values = append(values, v) + } + + if len(keys) != 3 || !containsString(keys, "test") || !containsString(keys, "key") || !containsString(keys, "charm") { + t.Error("range did not contain the correct keys") + } + + if len(values) != 3 || !containsInt(values, 0) || !containsInt(values, 1) || !containsInt(values, 2) { + t.Error("range did not contain the correct values") + } + + if myMap["test"] != 0 { + t.Errorf("test value Got: %d, Want: 0", myMap["test"]) + } + if myMap["key"] != 1 { + t.Errorf("key value Got: %d, Want: 1", myMap["key"]) + } + if myMap["missing"] != 0 { + t.Errorf("missing value Got: %d, Want: 0", myMap["missing"]) + } + + charm, found := myMap["charm"] + if charm != 2 { + t.Errorf("charm value Got: %d, Want: 2", charm) + } + if !found { + t.Error("charm should be found") + } + + missing2, found := myMap["missing"] + if missing2 != 0 { + t.Errorf("missing value Got: %d, Want: 0", missing2) + } + if found { + t.Error("absent key should not be found") + } + + delete(myMap, "missing") + if len(myMap) != 3 { + t.Errorf("len after noop delete Got: %d , Want: 3", len(myMap)) + } + + delete(myMap, "charm") + if len(myMap) != 2 { + t.Errorf("len after delete Got: %d, Want: 2", len(myMap)) + } + + myMap["add"] = 3 + if len(myMap) != 3 { + t.Errorf("len after assign by key Got: %d, Want: 3", len(myMap)) + } + if myMap["add"] != 3 { + t.Errorf("add value Got: %d, Want: 3", myMap["add"]) + } + + myMap["add"] = 10 + if len(myMap) != 3 { + t.Errorf("len after update by key Got: %d, Want: 3", len(myMap)) + } + if myMap["add"] != 10 { + t.Errorf("add value Got: %d, Want: 10", myMap["add"]) + } + + myMap2 := myMap + if len(myMap2) != len(myMap) { + t.Errorf("copy len Got: %d, Want: %d", len(myMap2), len(myMap)) + } +} + +func containsInt(s []int, e int) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} + +func containsString(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} + +// These benchmarks test various Map operations, and include a slice benchmark for reference. +const size = 10000 + +func makeMap(size int) map[int]string { + myMap := make(map[int]string, size) + for i := 0; i < size; i++ { + myMap[i] = "data" + } + + return myMap +} + +func makeSlice(size int) []int { + slice := make([]int, size) + for i := 0; i < size; i++ { + slice[i] = i + } + + return slice +} + +func BenchmarkSliceLen(b *testing.B) { + slice := makeSlice(size) + + for i := 0; i < b.N; i++ { + if len(slice) > 0 { + } + } +} + +func BenchmarkMapLen(b *testing.B) { + myMap := makeMap(size) + + for i := 0; i < b.N; i++ { + if len(myMap) > 0 { + } + } +} + +func BenchmarkMapNilCheck(b *testing.B) { + myMap := makeMap(size) + + for i := 0; i < b.N; i++ { + if myMap != nil { + } + } +} + +func BenchmarkMapNilElementCheck(b *testing.B) { + myMap := makeMap(size) + + for i := 0; i < b.N; i++ { + if myMap[0] != "" { + } + } +} + +func BenchmarkSliceRange(b *testing.B) { + slice := makeSlice(size) + + for i := 0; i < b.N; i++ { + for range slice { + } + } +} + +func BenchmarkMapRange(b *testing.B) { + myMap := makeMap(size) + + for i := 0; i < b.N; i++ { + for range myMap { + } + } +} diff --git a/tests/misc_test.go b/tests/misc_test.go index c3d7835c..b6f41922 100644 --- a/tests/misc_test.go +++ b/tests/misc_test.go @@ -7,7 +7,6 @@ import ( "strings" "testing" "time" - "vendored" "github.com/gopherjs/gopherjs/tests/otherpkg" ) @@ -504,12 +503,6 @@ func TestGoexit(t *testing.T) { }() } -func TestVendoring(t *testing.T) { - if vendored.Answer != 42 { - t.Fail() - } -} - func TestShift(t *testing.T) { if x := uint(32); uint32(1)< 0 { + z += x << i + } + } + return z +} + +func TestMul64(t *testing.T) { + cfg := &quick.Config{ + MaxCountScale: 10000, + Rand: rand.New(rand.NewSource(0x5EED)), // Fixed seed for reproducibility. + } + if testing.Short() { + cfg.MaxCountScale = 1000 + } + + t.Run("unsigned", func(t *testing.T) { + err := quick.CheckEqual( + func(x, y uint64) uint64 { return x * y }, + naiveMul64, + cfg) + if err != nil { + t.Error(err) + } + }) + t.Run("signed", func(t *testing.T) { + // GopherJS represents 64-bit signed integers in a two-complement form, + // so bitwise multiplication looks identical for signed and unsigned integers + // and we can reuse naiveMul64() as a reference implementation for both with + // appropriate type conversions. + err := quick.CheckEqual( + func(x, y int64) int64 { return x * y }, + func(x, y int64) int64 { return int64(naiveMul64(uint64(x), uint64(y))) }, + cfg) + if err != nil { + t.Error(err) + } + }) +} + +func BenchmarkMul64(b *testing.B) { + // Prepare a randomized set of multipliers to make sure the benchmark doesn't + // get too specific for a single value. The trade-off is that the cost of + // loading from an array gets mixed into the result, but it is good enough for + // relative comparisons. + r := rand.New(rand.NewSource(0x5EED)) + const size = 1024 + xU := [size]uint64{} + yU := [size]uint64{} + xS := [size]int64{} + yS := [size]int64{} + for i := 0; i < size; i++ { + xU[i] = r.Uint64() + yU[i] = r.Uint64() + xS[i] = r.Int63() | (r.Int63n(2) << 63) + yS[i] = r.Int63() | (r.Int63n(2) << 63) + } + + b.Run("noop", func(b *testing.B) { + // This benchmark allows to gauge the cost of array load operations without + // the multiplications. + for i := 0; i < b.N; i++ { + runtime.KeepAlive(yU[i%size]) + runtime.KeepAlive(xU[i%size]) + } + }) + b.Run("unsigned", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z := xU[i%size] * yU[i%size] + runtime.KeepAlive(z) + } + }) + b.Run("signed", func(b *testing.B) { + for i := 0; i < b.N; i++ { + z := xS[i%size] * yS[i%size] + runtime.KeepAlive(z) + } + }) +} diff --git a/tests/runtime_test.go b/tests/runtime_test.go new file mode 100644 index 00000000..24d9e88d --- /dev/null +++ b/tests/runtime_test.go @@ -0,0 +1,163 @@ +//go:build js && gopherjs +// +build js,gopherjs + +package tests + +import ( + "fmt" + "runtime" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/gopherjs/gopherjs/js" +) + +func Test_parseCallFrame(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + { + name: "Chrome 96.0.4664.110 on Linux #1", + input: "at foo (eval at $b (https://gopherjs.github.io/playground/playground.js:102:11836), :25887:60)", + want: "foo https://gopherjs.github.io/playground/playground.js 102 11836", + }, + { + name: "Chrome 96, anonymous eval", + input: " at eval ()", + want: "eval 0 0", + }, + { + name: "Chrome 96, anonymous Array.forEach", + input: " at Array.forEach ()", + want: "Array.forEach 0 0", + }, + { + name: "Chrome 96, file location only", + input: "at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js:31:225", + want: " https://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js 31 225", + }, + { + name: "Chrome 96, aliased function", + input: "at k.e.$externalizeWrapper.e.$externalizeWrapper [as run] (https://gopherjs.github.io/playground/playground.js:5:30547)", + want: "run https://gopherjs.github.io/playground/playground.js 5 30547", + }, + { + name: "Node.js v12.22.5", + input: " at Script.runInThisContext (vm.js:120:18)", + want: "Script.runInThisContext vm.js 120 18", + }, + { + name: "Node.js v12.22.5, aliased function", + input: "at REPLServer.runBound [as eval] (domain.js:440:12)", + want: "eval domain.js 440 12", + }, + { + name: "Firefox 78.15.0esr Linux", + input: "getEvalResult@resource://devtools/server/actors/webconsole/eval-with-debugger.js:231:24", + want: "getEvalResult resource://devtools/server/actors/webconsole/eval-with-debugger.js 231 24", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + lines := js.Global.Get("String").New(tt.input) + frame := runtime.ParseCallFrame(lines) + got := fmt.Sprintf("%v %v %v %v", frame.FuncName, frame.File, frame.Line, frame.Col) + if tt.want != got { + t.Errorf("Unexpected result: %s", got) + } + }) + } +} + +func TestBuildPlatform(t *testing.T) { + if runtime.GOOS != "js" { + t.Errorf("Got runtime.GOOS=%q. Want: %q.", runtime.GOOS, "js") + } + if runtime.GOARCH != "ecmascript" { + t.Errorf("Got runtime.GOARCH=%q. Want: %q.", runtime.GOARCH, "ecmascript") + } +} + +type funcName string + +func masked(_ funcName) funcName { return "" } + +type callStack []funcName + +func (c *callStack) capture() { + *c = nil + pc := [100]uintptr{} + depth := runtime.Callers(0, pc[:]) + frames := runtime.CallersFrames(pc[:depth]) + for true { + frame, more := frames.Next() + *c = append(*c, funcName(frame.Function)) + if !more { + break + } + } +} + +func TestCallers(t *testing.T) { + got := callStack{} + + // Some of the GopherJS function names don't match upstream Go, or even the + // function names in the Go source when minified. + // Until https://github.com/gopherjs/gopherjs/issues/1085 is resolved, the + // mismatch is difficult to avoid, but we can at least use "masked" frames to + // make sure the number of frames matches expected. + want := callStack{ + masked("runtime.Callers"), + masked("github.com/gopherjs/gopherjs/tests.(*callerNames).capture"), + masked("github.com/gopherjs/gopherjs/tests.TestCallers.func{1,2}"), + masked("testing.tRunner"), + "runtime.goexit", + } + + opts := cmp.Comparer(func(a, b funcName) bool { + if a == masked("") || b == masked("") { + return true + } + return a == b + }) + + t.Run("Normal", func(t *testing.T) { + got.capture() + if diff := cmp.Diff(want, got, opts); diff != "" { + t.Errorf("runtime.Callers() returned a diff (-want,+got):\n%s", diff) + } + }) + + t.Run("Deferred", func(t *testing.T) { + defer func() { + if diff := cmp.Diff(want, got, opts); diff != "" { + t.Errorf("runtime.Callers() returned a diff (-want,+got):\n%s", diff) + } + }() + defer got.capture() + }) + + t.Run("Recover", func(t *testing.T) { + defer func() { + recover() + got.capture() + + want := callStack{ + masked("runtime.Callers"), + masked("github.com/gopherjs/gopherjs/tests.(*callerNames).capture"), + masked("github.com/gopherjs/gopherjs/tests.TestCallers.func3.1"), + "runtime.gopanic", + masked("github.com/gopherjs/gopherjs/tests.TestCallers.func{1,2}"), + masked("testing.tRunner"), + "runtime.goexit", + } + if diff := cmp.Diff(want, got, opts); diff != "" { + t.Errorf("runtime.Callers() returned a diff (-want,+got):\n%s", diff) + } + }() + panic("panic") + }) +} diff --git a/tests/slice_to_array_ptr_test.go b/tests/slice_to_array_ptr_test.go new file mode 100644 index 00000000..5f5f8223 --- /dev/null +++ b/tests/slice_to_array_ptr_test.go @@ -0,0 +1,170 @@ +//go:build ignore +// +build ignore + +package tests + +import ( + "runtime" + "testing" +) + +// https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer +func TestSliceToArrayPointerConversion(t *testing.T) { + // GopherJS uses TypedArray for numeric types and Array for everything else + // since those are substantially different types, the tests are repeated + // for both. + expectOutOfBoundsPanic := func(t *testing.T) { + t.Helper() + if recover() == nil { + t.Error("out-of-bounds conversion of s should panic") + } + } + + t.Run("Numeric", func(t *testing.T) { + s := make([]byte, 2, 4) + t.Run("NotNil", func(t *testing.T) { + s0 := (*[0]byte)(s) + if s0 == nil { + t.Error("s0 should not be nil") + } + }) + + t.Run("ElementPointerEquality", func(t *testing.T) { + s2 := (*[2]byte)(s) + if &s2[0] != &s[0] { + t.Error("&s2[0] should match &s[0]") + } + s3 := (*[1]byte)(s[1:]) + if &s3[0] != &s[1] { + t.Error("&s3[0] should match &s[1]") + } + }) + + t.Run("SliceToLargerArray", func(t *testing.T) { + defer expectOutOfBoundsPanic(t) + s4 := (*[4]byte)(s) + _ = s4 + }) + + t.Run("SharedMemory", func(t *testing.T) { + s2 := (*[2]byte)(s) + (*s2)[0] = 'x' + if s[0] != 'x' { + t.Errorf("s[0] should be changed") + } + + s3 := (*[1]byte)(s[1:]) + (*s3)[0] = 'y' + if s[1] != 'y' { + t.Errorf("s[1] should be changed") + } + }) + + var q []byte + t.Run("NilSlice", func(t *testing.T) { + q0 := (*[0]byte)(q) + if q0 != nil { + t.Error("q0 should be nil") + } + }) + + t.Run("NilSliceToLargerArray", func(t *testing.T) { + defer expectOutOfBoundsPanic(t) + q1 := (*[1]byte)(q) + _ = q1 + }) + + t.Run("ZeroLenSlice", func(t *testing.T) { + u := make([]byte, 0) + u0 := (*[0]byte)(u) + if u0 == nil { + t.Error("u0 should not be nil") + } + }) + + t.Run("SliceToShorterArray", func(t *testing.T) { + s[0] = 'x' + s[1] = 'y' + s4 := (*[1]byte)(s[:]) + if got := s4[0]; got != 'x' { + t.Errorf("Got s0[0] = %q, want 'x'", got) + } + if got := len(s4); got != 1 { + t.Errorf("Got len(s0) = %d, want 1.", got) + } + + // Verify that the backing array size has been reduced to match the Go + // type. If not, a "source too large" runtime exception will be thrown + // upon the copy attempt. + s5 := [1]byte{} + s5 = *s4 + runtime.KeepAlive(s5) + }) + }) + + t.Run("String", func(t *testing.T) { + s := make([]string, 2, 2) + t.Run("NotNil", func(t *testing.T) { + s0 := (*[0]string)(s) + if s0 == nil { + t.Error("s0 should not be nil") + } + }) + + t.Run("ElementPointerEquality", func(t *testing.T) { + s2 := (*[2]string)(s) + if &s2[0] != &s[0] { + t.Error("&s2[0] should match &s[0]") + } + + t.Skip("non-numeric slice to underlying array conversion is not supported for subslices") + s3 := (*[1]string)(s[1:]) + if &s3[0] != &s[1] { + t.Error("&s3[0] should match &s[1]") + } + }) + + t.Run("SliceToLargerArray", func(t *testing.T) { + defer expectOutOfBoundsPanic(t) + s4 := (*[4]string)(s) + _ = s4 + }) + + t.Run("SharedMemory", func(t *testing.T) { + s2 := (*[2]string)(s) + (*s2)[0] = "x" + if s[0] != "x" { + t.Errorf("s[0] should be changed") + } + + t.Skip("non-numeric slice to underlying array conversion is not supported for subslices") + s3 := (*[1]string)(s[1:]) + (*s3)[0] = "y" + if s[1] != "y" { + t.Errorf("s[1] should be changed") + } + }) + + var q []string + t.Run("NilSlice", func(t *testing.T) { + q0 := (*[0]string)(q) + if q0 != nil { + t.Error("q0 should be nil") + } + }) + + t.Run("NilSliceToLargerArray", func(t *testing.T) { + defer expectOutOfBoundsPanic(t) + q1 := (*[1]string)(q) + _ = q1 + }) + + t.Run("ZeroLenSlice", func(t *testing.T) { + u := make([]string, 0) + u0 := (*[0]string)(u) + if u0 == nil { + t.Error("u0 should not be nil") + } + }) + }) +} diff --git a/tests/syscall_legacy_test.go b/tests/syscall_legacy_test.go new file mode 100644 index 00000000..bdfc0884 --- /dev/null +++ b/tests/syscall_legacy_test.go @@ -0,0 +1,28 @@ +package tests + +import ( + "os/exec" + "runtime" + "testing" +) + +// TestLegacySyscall tests raw syscall invocation using node_syscall extension. +// +// This mode is largely deprecated (e.g. we build standard library with GOOS=js), +// but we support using the extension when "legacy_syscall" build tag is set. +// This test can be removed after we stop supporting node_syscall extension. +func TestLegacySyscall(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("This test is supported only under Linux") + } + cmd := exec.Command("gopherjs", "run", "--tags=legacy_syscall", "./testdata/legacy_syscall/main.go") + out, err := cmd.CombinedOutput() + got := string(out) + if err != nil { + t.Log(got) + t.Fatalf("Failed to run test code under gopherjs: %s", err) + } + if want := "Hello, world!\n"; got != want { + t.Errorf("Got wrong output: %q. Want: %q.", got, want) + } +} diff --git a/tests/syscall_test.go b/tests/syscall_test.go new file mode 100644 index 00000000..13631ad2 --- /dev/null +++ b/tests/syscall_test.go @@ -0,0 +1,37 @@ +//go:build js +// +build js + +package tests + +import ( + "io/ioutil" + "os" + "syscall" + "testing" +) + +func TestGetpid(t *testing.T) { + pid := syscall.Getpid() + if pid <= 0 { + t.Errorf("Got invalid pid %d. Want: > 0", pid) + } else { + t.Logf("Got pid %d", pid) + } +} + +func TestOpen(t *testing.T) { + f, err := ioutil.TempFile("", "") + if err != nil { + t.Fatalf("Failed to create a temp file: %s", err) + } + f.Close() + defer os.Remove(f.Name()) + fd, err := syscall.Open(f.Name(), syscall.O_RDONLY, 0600) + if err != nil { + t.Fatalf("syscall.Open() returned error: %s", err) + } + err = syscall.Close(fd) + if err != nil { + t.Fatalf("syscall.Close() returned error: %s", err) + } +} diff --git a/tests/syscalljs/js_test.go b/tests/syscalljs/js_test.go deleted file mode 100644 index 5a59d934..00000000 --- a/tests/syscalljs/js_test.go +++ /dev/null @@ -1,395 +0,0 @@ -// This file is basically copied from $GOROOT/src/syscall/js/js_test.go - -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build js - -// To run these tests: -// -// - Install Node -// - Add /path/to/go/misc/wasm to your $PATH (so that "go test" can find -// "go_js_wasm_exec"). -// - GOOS=js GOARCH=wasm go test -// -// See -exec in "go help test", and "go help run" for details. - -package js_test - -import ( - "fmt" - "math" - "syscall/js" - "testing" -) - -func TestMain(m *testing.M) { - // Suppress the 'deadlock' error on GopherJS by goroutine - // (https://github.com/gopherjs/gopherjs/issues/826). - go func() { - m.Run() - }() -} - -var dummys = js.Global().Call("eval", `({ - someBool: true, - someString: "abc\u1234", - someInt: 42, - someFloat: 42.123, - someArray: [41, 42, 43], - someDate: new Date(), - add: function(a, b) { - return a + b; - }, - zero: 0, - stringZero: "0", - NaN: NaN, - emptyObj: {}, - emptyArray: [], - Infinity: Infinity, - NegInfinity: -Infinity, - objNumber0: new Number(0), - objBooleanFalse: new Boolean(false), -})`) - -func TestBool(t *testing.T) { - want := true - o := dummys.Get("someBool") - if got := o.Bool(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - dummys.Set("otherBool", want) - if got := dummys.Get("otherBool").Bool(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - if dummys.Get("someBool") != dummys.Get("someBool") { - t.Errorf("same value not equal") - } -} - -func TestString(t *testing.T) { - want := "abc\u1234" - o := dummys.Get("someString") - if got := o.String(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - dummys.Set("otherString", want) - if got := dummys.Get("otherString").String(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - if dummys.Get("someString") != dummys.Get("someString") { - t.Errorf("same value not equal") - } - - wantInt := "42" - o = dummys.Get("someInt") - if got := o.String(); got != wantInt { - t.Errorf("got %#v, want %#v", got, wantInt) - } -} - -func TestInt(t *testing.T) { - want := 42 - o := dummys.Get("someInt") - if got := o.Int(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - dummys.Set("otherInt", want) - if got := dummys.Get("otherInt").Int(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - if dummys.Get("someInt") != dummys.Get("someInt") { - t.Errorf("same value not equal") - } - if got := dummys.Get("zero").Int(); got != 0 { - t.Errorf("got %#v, want %#v", got, 0) - } -} - -func TestIntConversion(t *testing.T) { - testIntConversion(t, 0) - testIntConversion(t, 1) - testIntConversion(t, -1) - testIntConversion(t, 1<<20) - testIntConversion(t, -1<<20) - - // Skip too big integers. They cannot be compiled with 32bit environment, and GopherJS is one of them. - // testIntConversion(t, 1<<40) - // testIntConversion(t, -1<<40) - // testIntConversion(t, 1<<60) - // testIntConversion(t, -1<<60) -} - -func testIntConversion(t *testing.T, want int) { - if got := js.ValueOf(want).Int(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } -} - -func TestFloat(t *testing.T) { - want := 42.123 - o := dummys.Get("someFloat") - if got := o.Float(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - dummys.Set("otherFloat", want) - if got := dummys.Get("otherFloat").Float(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - if dummys.Get("someFloat") != dummys.Get("someFloat") { - t.Errorf("same value not equal") - } -} - -func TestObject(t *testing.T) { - if dummys.Get("someArray") != dummys.Get("someArray") { - t.Errorf("same value not equal") - } - - // An object and its prototype should not be equal. - proto := js.Global().Get("Object").Get("prototype") - o := js.Global().Call("eval", "new Object()") - if proto == o { - t.Errorf("object equals to its prototype") - } -} - -func TestFrozenObject(t *testing.T) { - o := js.Global().Call("eval", "(function () { let o = new Object(); o.field = 5; Object.freeze(o); return o; })()") - want := 5 - if got := o.Get("field").Int(); want != got { - t.Errorf("got %#v, want %#v", got, want) - } -} - -func TestTypedArrayOf(t *testing.T) { - testTypedArrayOf(t, "[]int8", []int8{0, -42, 0}, -42) - testTypedArrayOf(t, "[]int16", []int16{0, -42, 0}, -42) - testTypedArrayOf(t, "[]int32", []int32{0, -42, 0}, -42) - testTypedArrayOf(t, "[]uint8", []uint8{0, 42, 0}, 42) - testTypedArrayOf(t, "[]uint16", []uint16{0, 42, 0}, 42) - testTypedArrayOf(t, "[]uint32", []uint32{0, 42, 0}, 42) - testTypedArrayOf(t, "[]float32", []float32{0, -42.5, 0}, -42.5) - testTypedArrayOf(t, "[]float64", []float64{0, -42.5, 0}, -42.5) -} - -func testTypedArrayOf(t *testing.T, name string, slice interface{}, want float64) { - t.Run(name, func(t *testing.T) { - a := js.TypedArrayOf(slice) - got := a.Index(1).Float() - a.Release() - if got != want { - t.Errorf("got %#v, want %#v", got, want) - } - }) -} - -func TestNaN(t *testing.T) { - t.Skip("NaN cannot be compared") - - want := js.ValueOf(math.NaN()) - got := dummys.Get("NaN") - if got != want { - t.Errorf("got %#v, want %#v", got, want) - } -} - -func TestUndefined(t *testing.T) { - dummys.Set("test", js.Undefined()) - if dummys == js.Undefined() || dummys.Get("test") != js.Undefined() || dummys.Get("xyz") != js.Undefined() { - t.Errorf("js.Undefined expected") - } -} - -func TestNull(t *testing.T) { - dummys.Set("test1", nil) - dummys.Set("test2", js.Null()) - if dummys == js.Null() || dummys.Get("test1") != js.Null() || dummys.Get("test2") != js.Null() { - t.Errorf("js.Null expected") - } -} - -func TestLength(t *testing.T) { - if got := dummys.Get("someArray").Length(); got != 3 { - t.Errorf("got %#v, want %#v", got, 3) - } -} - -func TestIndex(t *testing.T) { - if got := dummys.Get("someArray").Index(1).Int(); got != 42 { - t.Errorf("got %#v, want %#v", got, 42) - } -} - -func TestSetIndex(t *testing.T) { - dummys.Get("someArray").SetIndex(2, 99) - if got := dummys.Get("someArray").Index(2).Int(); got != 99 { - t.Errorf("got %#v, want %#v", got, 99) - } -} - -func TestCall(t *testing.T) { - var i int64 = 40 - if got := dummys.Call("add", i, 2).Int(); got != 42 { - t.Errorf("got %#v, want %#v", got, 42) - } - if got := dummys.Call("add", js.Global().Call("eval", "40"), 2).Int(); got != 42 { - t.Errorf("got %#v, want %#v", got, 42) - } -} - -func TestInvoke(t *testing.T) { - var i int64 = 40 - if got := dummys.Get("add").Invoke(i, 2).Int(); got != 42 { - t.Errorf("got %#v, want %#v", got, 42) - } -} - -func TestNew(t *testing.T) { - if got := js.Global().Get("Array").New(42).Length(); got != 42 { - t.Errorf("got %#v, want %#v", got, 42) - } -} - -func TestInstanceOf(t *testing.T) { - someArray := js.Global().Get("Array").New() - if got, want := someArray.InstanceOf(js.Global().Get("Array")), true; got != want { - t.Errorf("got %#v, want %#v", got, want) - } - if got, want := someArray.InstanceOf(js.Global().Get("Function")), false; got != want { - t.Errorf("got %#v, want %#v", got, want) - } -} - -func TestType(t *testing.T) { - if got, want := js.Undefined().Type(), js.TypeUndefined; got != want { - t.Errorf("got %s, want %s", got, want) - } - if got, want := js.Null().Type(), js.TypeNull; got != want { - t.Errorf("got %s, want %s", got, want) - } - if got, want := js.ValueOf(true).Type(), js.TypeBoolean; got != want { - t.Errorf("got %s, want %s", got, want) - } - if got, want := js.ValueOf(0).Type(), js.TypeNumber; got != want { - t.Errorf("got %s, want %s", got, want) - } - if got, want := js.ValueOf(42).Type(), js.TypeNumber; got != want { - t.Errorf("got %s, want %s", got, want) - } - if got, want := js.ValueOf("test").Type(), js.TypeString; got != want { - t.Errorf("got %s, want %s", got, want) - } - if got, want := js.Global().Get("Symbol").Invoke("test").Type(), js.TypeSymbol; got != want { - t.Errorf("got %s, want %s", got, want) - } - if got, want := js.Global().Get("Array").New().Type(), js.TypeObject; got != want { - t.Errorf("got %s, want %s", got, want) - } - if got, want := js.Global().Get("Array").Type(), js.TypeFunction; got != want { - t.Errorf("got %s, want %s", got, want) - } -} - -type object = map[string]interface{} -type array = []interface{} - -func TestValueOf(t *testing.T) { - a := js.ValueOf(array{0, array{0, 42, 0}, 0}) - if got := a.Index(1).Index(1).Int(); got != 42 { - t.Errorf("got %v, want %v", got, 42) - } - - o := js.ValueOf(object{"x": object{"y": 42}}) - if got := o.Get("x").Get("y").Int(); got != 42 { - t.Errorf("got %v, want %v", got, 42) - } -} - -func TestZeroValue(t *testing.T) { - var v js.Value - if v != js.Undefined() { - t.Error("zero js.Value is not js.Undefined()") - } -} - -func TestFuncOf(t *testing.T) { - c := make(chan struct{}) - cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} { - if got := args[0].Int(); got != 42 { - t.Errorf("got %#v, want %#v", got, 42) - } - c <- struct{}{} - return nil - }) - defer cb.Release() - js.Global().Call("setTimeout", cb, 0, 42) - <-c -} - -func TestInvokeFunction(t *testing.T) { - called := false - cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} { - cb2 := js.FuncOf(func(this js.Value, args []js.Value) interface{} { - called = true - return 42 - }) - defer cb2.Release() - return cb2.Invoke() - }) - defer cb.Release() - if got := cb.Invoke().Int(); got != 42 { - t.Errorf("got %#v, want %#v", got, 42) - } - if !called { - t.Error("function not called") - } -} - -func ExampleFuncOf() { - var cb js.Func - cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} { - fmt.Println("button clicked") - cb.Release() // release the function if the button will not be clicked again - return nil - }) - js.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb) -} - -// See -// - https://developer.mozilla.org/en-US/docs/Glossary/Truthy -// - https://stackoverflow.com/questions/19839952/all-falsey-values-in-javascript/19839953#19839953 -// - http://www.ecma-international.org/ecma-262/5.1/#sec-9.2 -func TestTruthy(t *testing.T) { - want := true - for _, key := range []string{ - "someBool", "someString", "someInt", "someFloat", "someArray", "someDate", - "stringZero", // "0" is truthy - "add", // functions are truthy - "emptyObj", "emptyArray", "Infinity", "NegInfinity", - // All objects are truthy, even if they're Number(0) or Boolean(false). - "objNumber0", "objBooleanFalse", - } { - if got := dummys.Get(key).Truthy(); got != want { - t.Errorf("%s: got %#v, want %#v", key, got, want) - } - } - - want = false - if got := dummys.Get("zero").Truthy(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - if got := dummys.Get("NaN").Truthy(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - if got := js.ValueOf("").Truthy(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - if got := js.Null().Truthy(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } - if got := js.Undefined().Truthy(); got != want { - t.Errorf("got %#v, want %#v", got, want) - } -} diff --git a/tests/testdata/defer_builtin.go b/tests/testdata/defer_builtin.go new file mode 100644 index 00000000..95d22f4a --- /dev/null +++ b/tests/testdata/defer_builtin.go @@ -0,0 +1,22 @@ +package main + +type set map[interface{}]struct{} +type key struct{ a int } + +var m = set{} + +func deferredDelete(k key) { + // This built-in deferral will transpile into a "delete" statement wrapped + // into a proxy lambda. This test ensures we correctly assign proxy lambda + // argument types. + defer delete(m, k) +} + +func main() { + k := key{a: 42} + m[k] = struct{}{} + deferredDelete(k) + if _, found := m[k]; found { + panic("deferred delete didn't work!") + } +} diff --git a/tests/testdata/legacy_syscall/main.go b/tests/testdata/legacy_syscall/main.go new file mode 100644 index 00000000..75ba22f6 --- /dev/null +++ b/tests/testdata/legacy_syscall/main.go @@ -0,0 +1,19 @@ +//go:build legacy_syscall && gopherjs +// +build legacy_syscall,gopherjs + +// This program tests GopherJS's ability to perform raw syscalls using the +// deprecated node_syscall extension. See TestLegacySyscall. +package main + +import ( + "syscall" + "unsafe" +) + +func main() { + msg := []byte("Hello, world!\n") + _, _, errno := syscall.Syscall(1 /* SYS_WRITE on Linux */, 1 /* stdout */, uintptr(unsafe.Pointer(&msg[0])), uintptr(len(msg))) + if errno != 0 { + println(errno.Error()) + } +} diff --git a/tests/testdata/linkname/main.go b/tests/testdata/linkname/main.go new file mode 100644 index 00000000..203f04d2 --- /dev/null +++ b/tests/testdata/linkname/main.go @@ -0,0 +1,8 @@ +// A test program to demonstrate go:linkname directive support. +package main + +import "github.com/gopherjs/gopherjs/tests/testdata/linkname/one" + +func main() { + print(one.DoAll()) +} diff --git a/tests/testdata/linkname/method/method.go b/tests/testdata/linkname/method/method.go new file mode 100644 index 00000000..958e9a10 --- /dev/null +++ b/tests/testdata/linkname/method/method.go @@ -0,0 +1,450 @@ +package method + +import ( + "sort" + "strings" + "testing" + _ "unsafe" +) + +type Point struct { + X int + Y int +} + +func (pt *Point) Set(x, y int) { + pt.X, pt.Y = x, y +} + +func (pt Point) Get() (int, int) { + return pt.X, pt.Y +} + +//go:linkname struct_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Point).Set +func struct_Set(pt *point, x int, y int) + +//go:linkname struct_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Point.Get +func struct_Get(pt point) (int, int) + +type point struct { + X int + Y int +} + +func testStruct(t *testing.T) { + var pt point + struct_Set(&pt, 1, 2) + x, y := struct_Get(pt) + if x != 1 || y != 2 { + t.Fatalf("Got: struct_Get(pt) = (%v,%v). Want: (1,2).", x, y) + } +} + +type List []string + +func (t *List) Append(s ...string) { + *t = append(*t, s...) +} + +func (t List) Get() string { + return strings.Join(t, ",") +} + +type list []string + +//go:linkname slice_Append github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*List).Append +func slice_Append(*list, ...string) + +//go:linkname slice_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.List.Get +func slice_Get(list) string + +func testSlice(t *testing.T) { + var v list + v = append(v, "one") + slice_Append(&v, "two", "three") + got := slice_Get(v) + want := "one,two,three" + if got != want { + t.Fatalf("Got: slice_Get(v) = %q. Want: %q.", got, want) + } +} + +type Array [5]string + +func (t *Array) Set(i int, s string) { + (*t)[i] = s +} + +func (t Array) Get() string { + return strings.Join(t[:], ",") +} + +type array [5]string + +//go:linkname array_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Array).Set +func array_Set(*array, int, string) + +//go:linkname array_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Array.Get +func array_Get(array) string + +func testArray(t *testing.T) { + var a array + a[0] = "one" + array_Set(&a, 1, "two") + array_Set(&a, 4, "five") + got := array_Get(a) + want := "one,two,,,five" + if got != want { + t.Fatalf("Got: array_Get(a) = %q. Want: %q.", got, want) + } +} + +type Map map[int]string + +func (m Map) Set(key int, value string) { + m[key] = value +} + +func (m *Map) SetPtr(key int, value string) { + (*m)[key] = value +} + +func (m Map) Get() string { + var list []string + for _, v := range m { + list = append(list, v) + } + sort.Strings(list) + return strings.Join(list, ",") +} + +type _map map[int]string + +//go:linkname map_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Map.Set +func map_Set(_map, int, string) + +//go:linkname map_SetPtr github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Map).SetPtr +func map_SetPtr(*_map, int, string) + +//go:linkname map_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Map.Get +func map_Get(_map) string + +func testMap(t *testing.T) { + m := make(_map) + map_Set(m, 1, "one") + map_SetPtr(&m, 2, "two") + got := map_Get(m) + want := "one,two" + if got != want { + t.Fatalf("Got: map_Get(m) = %q. Want: %q.", got, want) + } +} + +type Func func(int, int) int + +func (f Func) Call(a, b int) int { + return f(a, b) +} + +func (f *Func) CallPtr(a, b int) int { + return (*f)(a, b) +} + +type _func func(int, int) int + +//go:linkname func_Call github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Func.Call +func func_Call(_func, int, int) int + +//go:linkname func_CallPtr github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Func).CallPtr +func func_CallPtr(*_func, int, int) int + +func testFunc(t *testing.T) { + var fn _func = func(a, b int) int { + return a + b + } + r := func_Call(fn, 100, 200) + if r != 300 { + t.Fatalf("Got: func_Call(fn,100,200) = %v. Want: 300.", r) + } + r2 := func_CallPtr(&fn, 100, 200) + if r2 != 300 { + t.Fatalf("Got: func_CallPtr(fn,100,200) = %v. Want: 300.", r2) + } +} + +type Chan chan int + +func (c Chan) Send(n int) { + c <- n +} + +func (c *Chan) SendPtr(n int) { + *c <- n +} + +func (c Chan) Recv() int { + return <-c +} + +type _chan chan int + +//go:linkname chan_Send github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Chan.Send +func chan_Send(_chan, int) + +//go:linkname chan_SendPtr github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Chan).SendPtr +func chan_SendPtr(*_chan, int) + +//go:linkname chan_Recv github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Chan.Recv +func chan_Recv(_chan) int + +func testChan(t *testing.T) { + c := make(_chan) + go func() { + chan_Send(c, 100) + }() + r := chan_Recv(c) + if r != 100 { + t.Fatalf("Got: chan_Recv(c) = %v. Want: 100.", r) + } + go func() { + chan_SendPtr(&c, 200) + }() + r = chan_Recv(c) + if r != 200 { + t.Fatalf("Got: chan_Recv(c) = %v. Want: 200.", r) + } +} + +type Int int + +func (m *Int) Set(v int) { + *m = Int(v) +} + +func (m Int) Get() int { + return int(m) +} + +type _int int + +//go:linkname int_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Int).Set +func int_Set(*_int, int) int + +//go:linkname int_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Int.Get +func int_Get(_int) int + +func testInt(t *testing.T) { + var i _int + int_Set(&i, 100) + r := int_Get(i) + if r != 100 { + t.Fatalf("Got: int_Get(i) = %v. Want: 100.", r) + } +} + +type Uint uint + +func (m *Uint) Set(v uint) { + *m = Uint(v) +} + +func (m Uint) Get() uint { + return uint(m) +} + +type _uint uint + +//go:linkname uint_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Uint).Set +func uint_Set(*_uint, uint) uint + +//go:linkname uint_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Uint.Get +func uint_Get(_uint) uint + +func testUint(t *testing.T) { + var i _uint + uint_Set(&i, 100) + r := uint_Get(i) + if r != 100 { + t.Fatalf("Got: uint_Get(i) = %v. Want: 100.", r) + } +} + +type Float64 float64 + +func (m *Float64) Set(v float64) { + *m = Float64(v) +} + +func (m Float64) Get() float64 { + return float64(m) +} + +type _float64 float64 + +//go:linkname float64_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Float64).Set +func float64_Set(*_float64, float64) float64 + +//go:linkname float64_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Float64.Get +func float64_Get(_float64) float64 + +func testFloat64(t *testing.T) { + var i _float64 + float64_Set(&i, 3.14) + r := float64_Get(i) + if r != 3.14 { + t.Fatalf("Got: float64_Get(i) = %v. Want: 3.14.", r) + } +} + +type Complex128 complex128 + +func (m *Complex128) Set(v complex128) { + *m = Complex128(v) +} + +func (m Complex128) Get() complex128 { + return complex128(m) +} + +type _complex128 complex128 + +//go:linkname complex128_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Complex128).Set +func complex128_Set(*_complex128, complex128) complex128 + +//go:linkname complex128_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Complex128.Get +func complex128_Get(_complex128) complex128 + +func testComplex128(t *testing.T) { + var i _complex128 + want := 1 + 2i + complex128_Set(&i, want) + got := complex128_Get(i) + if got != want { + t.Fatalf("Got: complex128_Get(i) = %v. Want: %v.", got, want) + } +} + +type Uintptr uintptr + +func (m *Uintptr) Set(v uintptr) { + *m = Uintptr(v) +} + +func (m Uintptr) Get() uintptr { + return uintptr(m) +} + +type _uintptr uintptr + +//go:linkname uintptr_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Uintptr).Set +func uintptr_Set(*_uintptr, uintptr) uintptr + +//go:linkname uintptr_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Uintptr.Get +func uintptr_Get(_uintptr) uintptr + +func testUintptr(t *testing.T) { + var i _uintptr + uintptr_Set(&i, 0x1234) + r := uintptr_Get(i) + if r != 0x1234 { + t.Fatalf("Got: uintptr_Get(i) = %v. Want: 0x1234.", r) + } +} + +type Bool bool + +func (m *Bool) Set(v bool) { + *m = Bool(v) +} + +func (m Bool) Get() bool { + return bool(m) +} + +type _bool bool + +//go:linkname bool_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Bool).Set +func bool_Set(*_bool, bool) bool + +//go:linkname bool_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Bool.Get +func bool_Get(_bool) bool + +func testBool(t *testing.T) { + var i _bool + bool_Set(&i, true) + r := bool_Get(i) + if r != true { + t.Fatalf("Got: bool_Get(i) = %v. Want: true.", r) + } +} + +type Byte byte + +func (m *Byte) Set(v byte) { + *m = Byte(v) +} + +func (m Byte) Get() byte { + return byte(m) +} + +type _byte byte + +//go:linkname byte_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*Byte).Set +func byte_Set(*_byte, byte) byte + +//go:linkname byte_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.Byte.Get +func byte_Get(_byte) byte + +func testByte(t *testing.T) { + var i _byte + byte_Set(&i, 0x7f) + r := byte_Get(i) + if r != 0x7f { + t.Fatalf("Got: byte_Get(i) = %v. Want: 0x7f.", r) + } +} + +type String string + +func (m *String) Set(v string) { + *m = String(v) +} + +func (m String) Get() string { + return string(m) +} + +type _string string + +//go:linkname string_Set github.com/gopherjs/gopherjs/tests/testdata/linkname/method.(*String).Set +func string_Set(*_string, string) string + +//go:linkname string_Get github.com/gopherjs/gopherjs/tests/testdata/linkname/method.String.Get +func string_Get(_string) string + +func testString(t *testing.T) { + var i _string + want := "hello world" + string_Set(&i, want) + got := string_Get(i) + if got != want { + t.Fatalf("Got: string_Get(i) = %q. Want: %q.", got, want) + } +} + +func TestLinkname(t *testing.T) { + testStruct(t) + testSlice(t) + testArray(t) + testMap(t) + testFunc(t) + testChan(t) + testBool(t) + testByte(t) + testInt(t) + testUint(t) + testFloat64(t) + testComplex128(t) + testString(t) +} diff --git a/tests/testdata/linkname/one/one.go b/tests/testdata/linkname/one/one.go new file mode 100644 index 00000000..0ec75352 --- /dev/null +++ b/tests/testdata/linkname/one/one.go @@ -0,0 +1,52 @@ +// Package one is a root of test dependency tree, importing packages two and +// three. It ensures a deterministic import and initialization order of the +// test packages. +package one + +import ( + _ "unsafe" // for go:linkname + + "github.com/gopherjs/gopherjs/tests/testdata/linkname/three" + "github.com/gopherjs/gopherjs/tests/testdata/linkname/two" +) + +// DoOne is a regular function from the package one to demonstrate a call +// without any special linking trickery. +func DoOne() string { + return "doing one" +} + +// doInternalOne is a function implemented in package one, but actually called +// by package two using a go:linkname directive to gain access to it. Note: +// dead-code elimination must be able to preserve this function. +// +// This is a demonstration that an imported package can linkname a function +// from an importer package. +func doInternalOne() string { + return "doing internal one: " + oneSecret +} + +// oneSecret is an unexported variable in the package one, which doInternalOne() +// must be able to access even when called from another package using a linkname +// mechanism. +var oneSecret = "one secret" + +// doInternalThree is implemented in the package three, but not exported (for +// example, to not make it a public API), which package one gains access to +// via a go:linkname directive. +// +// This is a demonstration that an importer package can linkname a non-exported +// function from an imported package. +// +//go:linkname doInternalThree github.com/gopherjs/gopherjs/tests/testdata/linkname/three.doInternalThree +func doInternalThree() string + +func DoAll() string { + result := "" + + DoOne() + "\n" + // Normal function call in the same package. + two.DoTwo() + "\n" + // Normal cross-package function call. + two.DoImportedOne() + "\n" + // Call a function that package two linknamed. + three.DoThree() + "\n" + // Normal cross-package function call. + "doing imported three: " + doInternalThree() + "\n" // Call a function from another package this package linknamed. + return result +} diff --git a/tests/testdata/linkname/three/three.go b/tests/testdata/linkname/three/three.go new file mode 100644 index 00000000..4943d3c1 --- /dev/null +++ b/tests/testdata/linkname/three/three.go @@ -0,0 +1,19 @@ +package three + +func DoThree() string { + return "doing three" +} + +func init() { + // Avoid dead-code elimination. + // TODO(nevkontakte): This should not be necessary. + var _ = doInternalThree +} + +var threeSecret = "three secret" + +// This function is unexported and can't be accessed by other packages via a +// conventional import. +func doInternalThree() string { + return "doing internal three: " + threeSecret +} diff --git a/tests/testdata/linkname/two/two.go b/tests/testdata/linkname/two/two.go new file mode 100644 index 00000000..10f8f9a3 --- /dev/null +++ b/tests/testdata/linkname/two/two.go @@ -0,0 +1,22 @@ +package two + +import _ "unsafe" // for go:linkname + +func init() { + // Avoid dead-code elimination. + // TODO(nevkontakte): This should not be necessary. + var _ = doInternalOne +} + +func DoTwo() string { + return "doing two" +} + +// The function below can't be imported from the package one the normal way because +// that would create an import cycle. +//go:linkname doInternalOne github.com/gopherjs/gopherjs/tests/testdata/linkname/one.doInternalOne +func doInternalOne() string + +func DoImportedOne() string { + return "doing imported one: " + doInternalOne() +} diff --git a/tests/testdata/time_inexternalization.go b/tests/testdata/time_inexternalization.go index fe7c064f..99131b9b 100644 --- a/tests/testdata/time_inexternalization.go +++ b/tests/testdata/time_inexternalization.go @@ -9,10 +9,10 @@ import ( var _ = time.Sleep // Force "time" package to be imported but let time.Time and time.Unix be DCEed since they're not used. func main() { - // Excercise externalization of Go struct (with its special handling of time.Time). + // Exercise externalization of Go struct (with its special handling of time.Time). js.Global.Get("console").Call("log", struct{ S string }{"externalization ok"}) - // Excercise internalization of JavaScript Date object (with its special handling of time.Time). + // Exercise internalization of JavaScript Date object (with its special handling of time.Time). date := js.Global.Get("Date").New("2015-08-29T20:56:00.869Z").Interface() js.Global.Set("myDate", date) js.Global.Get("console").Call("log", js.Global.Get("myDate").Call("toUTCString")) diff --git a/tests/vendor/vendored/vendored.go b/tests/vendor/vendored/vendored.go deleted file mode 100644 index f1e32d22..00000000 --- a/tests/vendor/vendored/vendored.go +++ /dev/null @@ -1,3 +0,0 @@ -package vendored - -var Answer = 42 diff --git a/tests/gorepo_test.go b/tests/vendored_test.go similarity index 55% rename from tests/gorepo_test.go rename to tests/vendored_test.go index 957db3e9..f94d955d 100644 --- a/tests/gorepo_test.go +++ b/tests/vendored_test.go @@ -7,31 +7,15 @@ import ( "testing" ) -// Go repository basic compiler tests, and regression tests for fixed compiler bugs. -func TestGoRepositoryCompilerTests(t *testing.T) { - if runtime.GOARCH == "js" { - t.Skip("test meant to be run using normal Go compiler (needs os/exec)") - } - - args := []string{"go", "run", "run.go", "-summary"} - if testing.Verbose() { - args = append(args, "-v") - } - cmd := exec.Command(args[0], args[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stdout - err := cmd.Run() - if err != nil { - t.Fatal(err) - } -} - // Test that GopherJS can be vendored into a project, and then used to build Go programs. // See issue https://github.com/gopherjs/gopherjs/issues/415. func TestGopherJSCanBeVendored(t *testing.T) { - if runtime.GOARCH == "js" { + if runtime.GOOS == "js" { t.Skip("test meant to be run using normal Go compiler (needs os/exec)") } + if runtime.GOOS == "windows" { + t.Skip("test requires POSIX environment to run") + } cmd := exec.Command("sh", "gopherjsvendored_test.sh") cmd.Stderr = os.Stdout diff --git a/tool.go b/tool.go index 4c580a13..9eb05b55 100644 --- a/tool.go +++ b/tool.go @@ -6,8 +6,6 @@ import ( "fmt" "go/ast" "go/build" - "go/doc" - "go/parser" "go/scanner" "go/token" "go/types" @@ -20,24 +18,25 @@ import ( "path" "path/filepath" "runtime" - "sort" + "runtime/pprof" "strconv" "strings" + "sync" "syscall" "text/template" "time" - "unicode" - "unicode/utf8" gbuild "github.com/gopherjs/gopherjs/build" + "github.com/gopherjs/gopherjs/build/cache" "github.com/gopherjs/gopherjs/compiler" "github.com/gopherjs/gopherjs/internal/sysutil" - "github.com/kisielk/gotool" + "github.com/gopherjs/gopherjs/internal/testmain" "github.com/neelance/sourcemap" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" "golang.org/x/crypto/ssh/terminal" - "golang.org/x/tools/go/buildutil" + "golang.org/x/sync/errgroup" ) var currentDirectory string @@ -59,6 +58,11 @@ func init() { fmt.Fprintf(os.Stderr, "$GOPATH not set. For more details see: go help gopath\n") os.Exit(1) } + + e := gbuild.DefaultEnv() + if e.GOOS != "js" || e.GOARCH != "ecmascript" { + fmt.Fprintf(os.Stderr, "Using GOOS=%s and GOARCH=%s in GopherJS is deprecated and will be removed in future. Use GOOS=js GOARCH=ecmascript instead.\n", e.GOOS, e.GOARCH) + } } func main() { @@ -78,6 +82,7 @@ func main() { compilerFlags.BoolVar(&options.Color, "color", terminal.IsTerminal(int(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb", "colored output") compilerFlags.StringVar(&tags, "tags", "", "a list of build tags to consider satisfied during the build") compilerFlags.BoolVar(&options.MapToLocalDisk, "localmap", false, "use local paths for sourcemap") + compilerFlags.BoolVarP(&options.NoCache, "no_cache", "a", false, "rebuild all packages from scratch") flagWatch := pflag.NewFlagSet("", 0) flagWatch.BoolVarP(&options.Watch, "watch", "w", false, "watch for changes to the source files") @@ -91,12 +96,16 @@ func main() { cmdBuild.Flags().AddFlagSet(flagQuiet) cmdBuild.Flags().AddFlagSet(compilerFlags) cmdBuild.Flags().AddFlagSet(flagWatch) - cmdBuild.Run = func(cmd *cobra.Command, args []string) { + cmdBuild.RunE = func(cmd *cobra.Command, args []string) error { options.BuildTags = strings.Fields(tags) for { - s := gbuild.NewSession(options) + s, err := gbuild.NewSession(options) + if err != nil { + options.PrintError("%s\n", err) + return err + } - err := func() error { + err = func() error { // Handle "gopherjs build [files]" ad-hoc package mode. if len(args) > 0 && (strings.HasSuffix(args[0], ".go") || strings.HasSuffix(args[0], ".inc.js")) { for _, arg := range args { @@ -120,19 +129,21 @@ func main() { return err } + xctx := gbuild.NewBuildContext(s.InstallSuffix(), options.BuildTags) // Expand import path patterns. - patternContext := gbuild.NewBuildContext("", options.BuildTags) - pkgs := (&gotool.Context{BuildContext: *patternContext}).ImportPaths(args) - + pkgs, err := xctx.Match(args) + if err != nil { + return fmt.Errorf("failed to expand patterns %v: %w", args, err) + } for _, pkgPath := range pkgs { if s.Watcher != nil { - pkg, err := gbuild.NewBuildContext(s.InstallSuffix(), options.BuildTags).Import(pkgPath, "", build.FindOnly) + pkg, err := xctx.Import(pkgPath, currentDirectory, build.FindOnly) if err != nil { return err } s.Watcher.Add(pkg.Dir) } - pkg, err := gbuild.Import(pkgPath, 0, s.InstallSuffix(), options.BuildTags) + pkg, err := xctx.Import(pkgPath, currentDirectory, 0) if err != nil { return err } @@ -153,10 +164,11 @@ func main() { } return nil }() - exitCode := handleError(err, options, nil) if s.Watcher == nil { - os.Exit(exitCode) + return err + } else if err != nil { + handleError(err, options, nil) } s.WaitForChange() } @@ -170,15 +182,21 @@ func main() { cmdInstall.Flags().AddFlagSet(flagQuiet) cmdInstall.Flags().AddFlagSet(compilerFlags) cmdInstall.Flags().AddFlagSet(flagWatch) - cmdInstall.Run = func(cmd *cobra.Command, args []string) { + cmdInstall.RunE = func(cmd *cobra.Command, args []string) error { options.BuildTags = strings.Fields(tags) for { - s := gbuild.NewSession(options) + s, err := gbuild.NewSession(options) + if err != nil { + return err + } - err := func() error { + err = func() error { // Expand import path patterns. - patternContext := gbuild.NewBuildContext("", options.BuildTags) - pkgs := (&gotool.Context{BuildContext: *patternContext}).ImportPaths(args) + xctx := gbuild.NewBuildContext(s.InstallSuffix(), options.BuildTags) + pkgs, err := xctx.Match(args) + if err != nil { + return fmt.Errorf("failed to expand patterns %v: %w", args, err) + } if cmd.Name() == "get" { goGet := exec.Command("go", append([]string{"get", "-d", "-tags=js"}, pkgs...)...) @@ -189,7 +207,7 @@ func main() { } } for _, pkgPath := range pkgs { - pkg, err := gbuild.Import(pkgPath, 0, s.InstallSuffix(), options.BuildTags) + pkg, err := xctx.Import(pkgPath, currentDirectory, 0) if s.Watcher != nil && pkg != nil { // add watch even on error s.Watcher.Add(pkg.Dir) } @@ -203,17 +221,18 @@ func main() { } if pkg.IsCommand() && !pkg.UpToDate { - if err := s.WriteCommandPackage(archive, pkg.PkgObj); err != nil { + if err := s.WriteCommandPackage(archive, pkg.InstallPath()); err != nil { return err } } } return nil }() - exitCode := handleError(err, options, nil) if s.Watcher == nil { - os.Exit(exitCode) + return err + } else if err != nil { + handleError(err, options, nil) } s.WaitForChange() } @@ -223,14 +242,12 @@ func main() { Use: "doc [arguments]", Short: "display documentation for the requested, package, method or symbol", } - cmdDoc.Run = func(cmd *cobra.Command, args []string) { + cmdDoc.RunE = func(cmd *cobra.Command, args []string) error { goDoc := exec.Command("go", append([]string{"doc"}, args...)...) goDoc.Stdout = os.Stdout goDoc.Stderr = os.Stderr goDoc.Env = append(os.Environ(), "GOARCH=js") - err := goDoc.Run() - exitCode := handleError(err, options, nil) - os.Exit(exitCode) + return goDoc.Run() } cmdGet := &cobra.Command{ @@ -249,43 +266,42 @@ func main() { cmdRun.Flags().AddFlagSet(flagVerbose) cmdRun.Flags().AddFlagSet(flagQuiet) cmdRun.Flags().AddFlagSet(compilerFlags) - cmdRun.Run = func(cmd *cobra.Command, args []string) { - err := func() error { - lastSourceArg := 0 - for { - if lastSourceArg == len(args) || !(strings.HasSuffix(args[lastSourceArg], ".go") || strings.HasSuffix(args[lastSourceArg], ".inc.js")) { - break - } - lastSourceArg++ - } - if lastSourceArg == 0 { - return fmt.Errorf("gopherjs run: no go files listed") + cmdRun.RunE = func(cmd *cobra.Command, args []string) error { + options.BuildTags = strings.Fields(tags) + lastSourceArg := 0 + for { + if lastSourceArg == len(args) || !(strings.HasSuffix(args[lastSourceArg], ".go") || strings.HasSuffix(args[lastSourceArg], ".inc.js")) { + break } + lastSourceArg++ + } + if lastSourceArg == 0 { + return fmt.Errorf("gopherjs run: no go files listed") + } - tempfile, err := ioutil.TempFile(currentDirectory, filepath.Base(args[0])+".") - if err != nil && strings.HasPrefix(currentDirectory, runtime.GOROOT()) { - tempfile, err = ioutil.TempFile("", filepath.Base(args[0])+".") - } - if err != nil { - return err - } - defer func() { - tempfile.Close() - os.Remove(tempfile.Name()) - os.Remove(tempfile.Name() + ".map") - }() - s := gbuild.NewSession(options) - if err := s.BuildFiles(args[:lastSourceArg], tempfile.Name(), currentDirectory); err != nil { - return err - } - if err := runNode(tempfile.Name(), args[lastSourceArg:], "", options.Quiet); err != nil { - return err - } - return nil + tempfile, err := ioutil.TempFile(currentDirectory, filepath.Base(args[0])+".") + if err != nil && strings.HasPrefix(currentDirectory, runtime.GOROOT()) { + tempfile, err = ioutil.TempFile("", filepath.Base(args[0])+".") + } + if err != nil { + return err + } + defer func() { + tempfile.Close() + os.Remove(tempfile.Name()) + os.Remove(tempfile.Name() + ".map") }() - exitCode := handleError(err, options, nil) - - os.Exit(exitCode) + s, err := gbuild.NewSession(options) + if err != nil { + return err + } + if err := s.BuildFiles(args[:lastSourceArg], tempfile.Name(), currentDirectory); err != nil { + return err + } + if err := runNode(tempfile.Name(), args[lastSourceArg:], "", options.Quiet, nil); err != nil { + return err + } + return nil } cmdTest := &cobra.Command{ @@ -300,210 +316,211 @@ func main() { verbose := cmdTest.Flags().BoolP("verbose", "v", false, "Log all tests as they are run. Also print all text from Log and Logf calls even if the test succeeds.") compileOnly := cmdTest.Flags().BoolP("compileonly", "c", false, "Compile the test binary to pkg.test.js but do not run it (where pkg is the last element of the package's import path). The file name can be changed with the -o flag.") outputFilename := cmdTest.Flags().StringP("output", "o", "", "Compile the test binary to the named file. The test still runs (unless -c is specified).") + parallelTests := cmdTest.Flags().IntP("parallel", "p", runtime.NumCPU(), "Allow running tests in parallel for up to -p packages. Tests within the same package are still executed sequentially.") cmdTest.Flags().AddFlagSet(compilerFlags) - cmdTest.Run = func(cmd *cobra.Command, args []string) { + cmdTest.RunE = func(cmd *cobra.Command, args []string) error { options.BuildTags = strings.Fields(tags) - err := func() error { - // Expand import path patterns. - patternContext := gbuild.NewBuildContext("", options.BuildTags) - args = (&gotool.Context{BuildContext: *patternContext}).ImportPaths(args) - if *compileOnly && len(args) > 1 { - return errors.New("cannot use -c flag with multiple packages") + // Expand import path patterns. + patternContext := gbuild.NewBuildContext("", options.BuildTags) + matches, err := patternContext.Match(args) + if err != nil { + return fmt.Errorf("failed to expand patterns %v: %w", args, err) + } + + if *compileOnly && len(matches) > 1 { + return errors.New("cannot use -c flag with multiple packages") + } + if *outputFilename != "" && len(matches) > 1 { + return errors.New("cannot use -o flag with multiple packages") + } + if *parallelTests < 1 { + return errors.New("--parallel cannot be less than 1") + } + + parallelSlots := make(chan (bool), *parallelTests) // Semaphore for parallel test executions. + if len(matches) == 1 { + // Disable output buffering if testing only one package. + parallelSlots = make(chan (bool), 1) + } + executions := errgroup.Group{} + + pkgs := make([]*gbuild.PackageData, len(matches)) + for i, pkgPath := range matches { + var err error + pkgs[i], err = gbuild.Import(pkgPath, 0, "", options.BuildTags) + if err != nil { + return err + } + } + + var ( + exitErr error + exitErrMu = &sync.Mutex{} + ) + for _, pkg := range pkgs { + pkg := pkg // Capture for the goroutine. + if len(pkg.TestGoFiles) == 0 && len(pkg.XTestGoFiles) == 0 { + fmt.Printf("? \t%s\t[no test files]\n", pkg.ImportPath) + continue } - if *outputFilename != "" && len(args) > 1 { - return errors.New("cannot use -o flag with multiple packages") + localOpts := options + localOpts.TestedPackage = pkg.ImportPath + s, err := gbuild.NewSession(localOpts) + if err != nil { + return err } - pkgs := make([]*gbuild.PackageData, len(args)) - for i, pkgPath := range args { - var err error - pkgs[i], err = gbuild.Import(pkgPath, 0, "", options.BuildTags) - if err != nil { - return err - } + _, err = s.BuildPackage(pkg.TestPackage()) + if err != nil { + return err + } + _, err = s.BuildPackage(pkg.XTestPackage()) + if err != nil { + return err } - var exitErr error - for _, pkg := range pkgs { - if len(pkg.TestGoFiles) == 0 && len(pkg.XTestGoFiles) == 0 { - fmt.Printf("? \t%s\t[no test files]\n", pkg.ImportPath) - continue - } - s := gbuild.NewSession(options) + fset := token.NewFileSet() + tests := testmain.TestMain{Package: pkg} + tests.Scan(fset) + mainPkg, mainFile, err := tests.Synthesize(fset) + if err != nil { + return fmt.Errorf("failed to generate testmain package for %s: %w", pkg.ImportPath, err) + } + importContext := &compiler.ImportContext{ + Packages: s.Types, + Import: s.ImportResolverFor(mainPkg), + } + mainPkgArchive, err := compiler.Compile(mainPkg.ImportPath, []*ast.File{mainFile}, fset, importContext, options.Minify) + if err != nil { + return fmt.Errorf("failed to compile testmain package for %s: %w", pkg.ImportPath, err) + } - tests := &testFuncs{BuildContext: s.BuildContext(), Package: pkg.Package} - collectTests := func(testPkg *gbuild.PackageData, testPkgName string, needVar *bool) error { - if testPkgName == "_test" { - for _, file := range pkg.TestGoFiles { - if err := tests.load(pkg.Package.Dir, file, testPkgName, &tests.ImportTest, &tests.NeedTest); err != nil { - return err - } - } - } else { - for _, file := range pkg.XTestGoFiles { - if err := tests.load(pkg.Package.Dir, file, "_xtest", &tests.ImportXtest, &tests.NeedXtest); err != nil { - return err - } - } - } - _, err := s.BuildPackage(testPkg) - return err - } + if *compileOnly && *outputFilename == "" { + *outputFilename = pkg.Package.Name + "_test.js" + } - if err := collectTests(&gbuild.PackageData{ - Package: &build.Package{ - ImportPath: pkg.ImportPath, - Dir: pkg.Dir, - GoFiles: append(pkg.GoFiles, pkg.TestGoFiles...), - Imports: append(pkg.Imports, pkg.TestImports...), - }, - IsTest: true, - JSFiles: pkg.JSFiles, - }, "_test", &tests.NeedTest); err != nil { + var outfile *os.File + if *outputFilename != "" { + outfile, err = os.Create(*outputFilename) + if err != nil { return err } - - if err := collectTests(&gbuild.PackageData{ - Package: &build.Package{ - ImportPath: pkg.ImportPath + "_test", - Dir: pkg.Dir, - GoFiles: pkg.XTestGoFiles, - Imports: pkg.XTestImports, - }, - IsTest: true, - }, "_xtest", &tests.NeedXtest); err != nil { + } else { + outfile, err = os.CreateTemp(currentDirectory, pkg.Package.Name+"_test.*.js") + if err != nil { return err } - - buf := new(bytes.Buffer) - if err := testmainTmpl.Execute(buf, tests); err != nil { - return err + outfile.Close() // Release file handle early, we only need the name. + } + cleanupTemp := func() { + if *outputFilename == "" { + os.Remove(outfile.Name()) + os.Remove(outfile.Name() + ".map") } + } + defer cleanupTemp() // Safety net in case cleanup after execution doesn't happen. - fset := token.NewFileSet() - mainFile, err := parser.ParseFile(fset, "_testmain.go", buf, 0) - if err != nil { - return err - } + if err := s.WriteCommandPackage(mainPkgArchive, outfile.Name()); err != nil { + return err + } - importContext := &compiler.ImportContext{ - Packages: s.Types, - Import: func(path string) (*compiler.Archive, error) { - if path == pkg.ImportPath || path == pkg.ImportPath+"_test" { - return s.Archives[path], nil - } - return s.BuildImportPath(path) - }, - } - mainPkgArchive, err := compiler.Compile("main", []*ast.File{mainFile}, fset, importContext, options.Minify) - if err != nil { - return err - } + if *compileOnly { + continue + } - if *compileOnly && *outputFilename == "" { - *outputFilename = pkg.Package.Name + "_test.js" - } + var args []string + if *bench != "" { + args = append(args, "-test.bench", *bench) + } + if *benchtime != "" { + args = append(args, "-test.benchtime", *benchtime) + } + if *count != "" { + args = append(args, "-test.count", *count) + } + if *run != "" { + args = append(args, "-test.run", *run) + } + if *short { + args = append(args, "-test.short") + } + if *verbose { + args = append(args, "-test.v") + } + executions.Go(func() error { + parallelSlots <- true // Acquire slot + defer func() { <-parallelSlots }() // Release slot - var outfile *os.File - if *outputFilename != "" { - outfile, err = os.Create(*outputFilename) - if err != nil { - return err - } - } else { - outfile, err = ioutil.TempFile(currentDirectory, "test.") - if err != nil { - return err - } + status := "ok " + start := time.Now() + var testOut io.ReadWriter + if cap(parallelSlots) > 1 { + // If running in parallel, capture test output in a temporary buffer to avoid mixing + // output from different tests and print it later. + testOut = &bytes.Buffer{} } - defer func() { - outfile.Close() - if *outputFilename == "" { - os.Remove(outfile.Name()) - os.Remove(outfile.Name() + ".map") - } - }() - if err := s.WriteCommandPackage(mainPkgArchive, outfile.Name()); err != nil { - return err - } + err := runNode(outfile.Name(), args, runTestDir(pkg), options.Quiet, testOut) - if *compileOnly { - continue - } + cleanupTemp() // Eagerly cleanup temporary compiled files after execution. - var args []string - if *bench != "" { - args = append(args, "-test.bench", *bench) - } - if *benchtime != "" { - args = append(args, "-test.benchtime", *benchtime) - } - if *count != "" { - args = append(args, "-test.count", *count) - } - if *run != "" { - args = append(args, "-test.run", *run) + if testOut != nil { + io.Copy(os.Stdout, testOut) } - if *short { - args = append(args, "-test.short") - } - if *verbose { - args = append(args, "-test.v") - } - status := "ok " - start := time.Now() - if err := runNode(outfile.Name(), args, runTestDir(pkg), options.Quiet); err != nil { + + if err != nil { if _, ok := err.(*exec.ExitError); !ok { return err } + exitErrMu.Lock() exitErr = err + exitErrMu.Unlock() status = "FAIL" } fmt.Printf("%s\t%s\t%.3fs\n", status, pkg.ImportPath, time.Since(start).Seconds()) - } - return exitErr - }() - exitCode := handleError(err, options, nil) - - os.Exit(exitCode) + return nil + }) + } + if err := executions.Wait(); err != nil { + return err + } + return exitErr } cmdServe := &cobra.Command{ Use: "serve [root]", Short: "compile on-the-fly and serve", } + cmdServe.Args = cobra.MaximumNArgs(1) cmdServe.Flags().AddFlagSet(flagVerbose) cmdServe.Flags().AddFlagSet(flagQuiet) cmdServe.Flags().AddFlagSet(compilerFlags) var addr string cmdServe.Flags().StringVarP(&addr, "http", "", ":8080", "HTTP bind address to serve") - cmdServe.Run = func(cmd *cobra.Command, args []string) { + cmdServe.RunE = func(cmd *cobra.Command, args []string) error { options.BuildTags = strings.Fields(tags) - dirs := append(filepath.SplitList(build.Default.GOPATH), build.Default.GOROOT) var root string - if len(args) > 1 { - cmdServe.HelpFunc()(cmd, args) - os.Exit(1) - } - if len(args) == 1 { root = args[0] } + // Create a new session eagerly to check if it fails, and report the error right away. + // Otherwise users will see it only after trying to serve a package, which is a bad experience. + _, err := gbuild.NewSession(options) + if err != nil { + return err + } sourceFiles := http.FileServer(serveCommandFileSystem{ serveRoot: root, options: options, - dirs: dirs, sourceMaps: make(map[string][]byte), }) ln, err := net.Listen("tcp", addr) if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) + return err } if tcpAddr := ln.Addr().(*net.TCPAddr); tcpAddr.IP.Equal(net.IPv4zero) || tcpAddr.IP.Equal(net.IPv6zero) { // Any available addresses. fmt.Printf("serving at http://localhost:%d and on port %d of any available addresses\n", tcpAddr.Port, tcpAddr.Port) @@ -511,29 +528,83 @@ func main() { fmt.Printf("serving at http://%s\n", tcpAddr) } fmt.Fprintln(os.Stderr, http.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}, sourceFiles)) + return nil } cmdVersion := &cobra.Command{ Use: "version", Short: "print GopherJS compiler version", + Args: cobra.ExactArgs(0), } cmdVersion.Run = func(cmd *cobra.Command, args []string) { - if len(args) > 0 { - cmdServe.HelpFunc()(cmd, args) - os.Exit(1) - } - fmt.Printf("GopherJS %s\n", compiler.Version) } + cmdClean := &cobra.Command{ + Use: "clean", + Short: "clean GopherJS build cache", + } + cmdClean.RunE = func(cmd *cobra.Command, args []string) error { + return cache.Clear() + } + rootCmd := &cobra.Command{ - Use: "gopherjs", - Long: "GopherJS is a tool for compiling Go source code to JavaScript.", + Use: "gopherjs", + Long: "GopherJS is a tool for compiling Go source code to JavaScript.", + SilenceUsage: true, + SilenceErrors: true, + } + rootCmd.AddCommand(cmdBuild, cmdGet, cmdInstall, cmdRun, cmdTest, cmdServe, cmdVersion, cmdDoc, cmdClean) + + { + var logLevel string + var cpuProfile string + var allocProfile string + rootCmd.PersistentFlags().StringVar(&logLevel, "log_level", log.ErrorLevel.String(), "Compiler log level (debug, info, warn, error, fatal, panic).") + rootCmd.PersistentFlags().StringVar(&cpuProfile, "cpu_profile", "", "Save GopherJS compiler CPU profile at the given path. If unset, profiling is disabled.") + rootCmd.PersistentFlags().StringVar(&allocProfile, "alloc_profile", "", "Save GopherJS compiler allocation profile at the given path. If unset, profiling is disabled.") + + rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + lvl, err := log.ParseLevel(logLevel) + if err != nil { + return fmt.Errorf("invalid --log_level value %q: %w", logLevel, err) + } + log.SetLevel(lvl) + + if cpuProfile != "" { + f, err := os.Create(cpuProfile) + if err != nil { + return fmt.Errorf("failed to create CPU profile file at %q: %w", cpuProfile, err) + } + if err := pprof.StartCPUProfile(f); err != nil { + return fmt.Errorf("failed to start CPU profile: %w", err) + } + // Not closing the file here, since we'll be writing to it throughout + // the lifetime of the process. It will be closed automatically when + // the process terminates. + } + return nil + } + rootCmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error { + if cpuProfile != "" { + pprof.StopCPUProfile() + } + if allocProfile != "" { + f, err := os.Create(allocProfile) + if err != nil { + return fmt.Errorf("failed to create alloc profile file at %q: %w", allocProfile, err) + } + if err := pprof.Lookup("allocs").WriteTo(f, 0); err != nil { + return fmt.Errorf("failed to write alloc profile: %w", err) + } + f.Close() + } + return nil + } } - rootCmd.AddCommand(cmdBuild, cmdGet, cmdInstall, cmdRun, cmdTest, cmdServe, cmdVersion, cmdDoc) err := rootCmd.Execute() if err != nil { - os.Exit(2) + os.Exit(handleError(err, options, nil)) } } @@ -558,12 +629,12 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { type serveCommandFileSystem struct { serveRoot string options *gbuild.Options - dirs []string sourceMaps map[string][]byte } func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { name := path.Join(fs.serveRoot, requestName[1:]) // requestName[0] == '/' + log.Printf("Request: %s", name) dir, file := path.Split(name) base := path.Base(dir) // base is parent folder name, which becomes the output file name. @@ -572,9 +643,15 @@ func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { isMap := file == base+".js.map" isIndex := file == "index.html" + // Create a new session to pick up changes to source code on disk. + // TODO(dmitshur): might be possible to get a single session to detect changes to source code on disk + s, err := gbuild.NewSession(fs.options) + if err != nil { + return nil, err + } + if isPkg || isMap || isIndex { // If we're going to be serving our special files, make sure there's a Go command in this folder. - s := gbuild.NewSession(fs.options) pkg, err := gbuild.Import(path.Dir(name), 0, s.InstallSuffix(), fs.options.BuildTags) if err != nil || pkg.Name != "main" { isPkg = false @@ -594,13 +671,13 @@ func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { sourceMapFilter := &compiler.SourceMapFilter{Writer: buf} m := &sourcemap.Map{File: base + ".js"} - sourceMapFilter.MappingCallback = gbuild.NewMappingCallback(m, fs.options.GOROOT, fs.options.GOPATH, fs.options.MapToLocalDisk) + sourceMapFilter.MappingCallback = s.SourceMappingCallback(m) deps, err := compiler.ImportDependencies(archive, s.BuildImportPath) if err != nil { return err } - if err := compiler.WriteProgramCode(deps, sourceMapFilter); err != nil { + if err := compiler.WriteProgramCode(deps, sourceMapFilter, s.GoRelease()); err != nil { return err } @@ -624,19 +701,14 @@ func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { } } - for _, d := range fs.dirs { - dir := http.Dir(filepath.Join(d, "src")) - - f, err := dir.Open(name) - if err == nil { - return f, nil - } + // First try to serve the request with a root prefix supplied in the CLI. + if f, err := fs.serveSourceTree(s.XContext(), name); err == nil { + return f, nil + } - // source maps are served outside of serveRoot - f, err = dir.Open(requestName) - if err == nil { - return f, nil - } + // If that didn't work, try without the prefix. + if f, err := fs.serveSourceTree(s.XContext(), requestName); err == nil { + return f, nil } if isIndex { @@ -647,6 +719,24 @@ func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) { return nil, os.ErrNotExist } +func (fs serveCommandFileSystem) serveSourceTree(xctx gbuild.XContext, reqPath string) (http.File, error) { + parts := strings.Split(path.Clean(reqPath), "/") + // Under Go Modules different packages can be located in different module + // directories, which no longer align with import paths. + // + // We don't know which part of the requested path is package import path and + // which is a path under the package directory, so we try different slipt + // points until the package is found successfully. + for i := len(parts); i > 0; i-- { + pkgPath := path.Clean(path.Join(parts[:i]...)) + filePath := path.Clean(path.Join(parts[i:]...)) + if pkg, err := xctx.Import(pkgPath, ".", build.FindOnly); err == nil { + return http.Dir(pkg.Dir).Open(filePath) + } + } + return nil, os.ErrNotExist +} + type fakeFile struct { name string size int @@ -743,7 +833,9 @@ func sprintError(err error) string { // runNode runs script with args using Node.js in directory dir. // If dir is empty string, current directory is used. -func runNode(script string, args []string, dir string, quiet bool) error { +// Is out is not nil, process stderr and stdout are redirected to it, otherwise +// os.Stdout and os.Stderr are used. +func runNode(script string, args []string, dir string, quiet bool, out io.Writer) error { var allArgs []string if b, _ := strconv.ParseBool(os.Getenv("SOURCE_MAP_SUPPORT")); os.Getenv("SOURCE_MAP_SUPPORT") == "" || b { allArgs = []string{"--require", "source-map-support/register"} @@ -765,9 +857,11 @@ func runNode(script string, args []string, dir string, quiet bool) error { // - OS process limit // - Node.js (V8) limit // - // GopherJS fetches the current OS process limit, and sets the - // Node.js limit to the same value. So both limits are kept in sync - // and can be controlled by setting OS process limit. E.g.: + // GopherJS fetches the current OS process limit, and sets the Node.js limit + // to a value slightly below it (otherwise nodejs is likely to segfault). + // The backoff size has been determined experimentally on a linux machine, + // so it may not be 100% reliable. So both limits are kept in sync and can + // be controlled by setting OS process limit. E.g.: // // ulimit -s 10000 && gopherjs test // @@ -775,7 +869,12 @@ func runNode(script string, args []string, dir string, quiet bool) error { if err != nil { return fmt.Errorf("failed to get stack size limit: %v", err) } - allArgs = append(allArgs, fmt.Sprintf("--stack_size=%v", cur/1000)) // Convert from bytes to KB. + cur = cur / 1024 // Convert bytes to KiB. + defaultSize := uint64(984) // --stack-size default value. + if backoff := uint64(64); cur > defaultSize+backoff { + cur = cur - backoff + } + allArgs = append(allArgs, fmt.Sprintf("--stack_size=%v", cur)) } allArgs = append(allArgs, script) @@ -784,8 +883,13 @@ func runNode(script string, args []string, dir string, quiet bool) error { node := exec.Command("node", allArgs...) node.Dir = dir node.Stdin = os.Stdin - node.Stdout = os.Stdout - node.Stderr = os.Stderr + if out != nil { + node.Stdout = out + node.Stderr = out + } else { + node.Stdout = os.Stdout + node.Stderr = os.Stderr + } err := node.Run() if _, ok := err.(*exec.ExitError); err != nil && !ok { err = fmt.Errorf("could not run Node.js: %s", err.Error()) @@ -803,161 +907,3 @@ func runTestDir(p *gbuild.PackageData) string { // Run tests in the package directory. return p.Dir } - -type testFuncs struct { - BuildContext *build.Context - Tests []testFunc - Benchmarks []testFunc - Examples []testFunc - TestMain *testFunc - Package *build.Package - ImportTest bool - NeedTest bool - ImportXtest bool - NeedXtest bool -} - -type testFunc struct { - Package string // imported package name (_test or _xtest) - Name string // function name - Output string // output, for examples - Unordered bool // output is allowed to be unordered. -} - -var testFileSet = token.NewFileSet() - -func (t *testFuncs) load(dir, file, pkg string, doImport, seen *bool) error { - f, err := buildutil.ParseFile(testFileSet, t.BuildContext, nil, dir, file, parser.ParseComments) - if err != nil { - return err - } - for _, d := range f.Decls { - n, ok := d.(*ast.FuncDecl) - if !ok { - continue - } - if n.Recv != nil { - continue - } - name := n.Name.String() - switch { - case isTestMain(n): - if t.TestMain != nil { - return errors.New("multiple definitions of TestMain") - } - t.TestMain = &testFunc{pkg, name, "", false} - *doImport, *seen = true, true - case isTest(name, "Test"): - t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) - *doImport, *seen = true, true - case isTest(name, "Benchmark"): - t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false}) - *doImport, *seen = true, true - } - } - ex := doc.Examples(f) - sort.Sort(byOrder(ex)) - for _, e := range ex { - *doImport = true // import test file whether executed or not - if e.Output == "" && !e.EmptyOutput { - // Don't run examples with no output. - continue - } - t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered}) - *seen = true - } - - return nil -} - -type byOrder []*doc.Example - -func (x byOrder) Len() int { return len(x) } -func (x byOrder) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x byOrder) Less(i, j int) bool { return x[i].Order < x[j].Order } - -// isTestMain tells whether fn is a TestMain(m *testing.M) function. -func isTestMain(fn *ast.FuncDecl) bool { - if fn.Name.String() != "TestMain" || - fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || - fn.Type.Params == nil || - len(fn.Type.Params.List) != 1 || - len(fn.Type.Params.List[0].Names) > 1 { - return false - } - ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) - if !ok { - return false - } - // We can't easily check that the type is *testing.M - // because we don't know how testing has been imported, - // but at least check that it's *M or *something.M. - if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" { - return true - } - if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" { - return true - } - return false -} - -// isTest tells whether name looks like a test (or benchmark, according to prefix). -// It is a Test (say) if there is a character after Test that is not a lower-case letter. -// We don't want TesticularCancer. -func isTest(name, prefix string) bool { - if !strings.HasPrefix(name, prefix) { - return false - } - if len(name) == len(prefix) { // "Test" is ok - return true - } - rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) - return !unicode.IsLower(rune) -} - -var testmainTmpl = template.Must(template.New("main").Parse(` -package main - -import ( -{{if not .TestMain}} - "os" -{{end}} - "testing" - "testing/internal/testdeps" - -{{if .ImportTest}} - {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} -{{end}} -{{if .ImportXtest}} - {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} -{{end}} -) - -var tests = []testing.InternalTest{ -{{range .Tests}} - {"{{.Name}}", {{.Package}}.{{.Name}}}, -{{end}} -} - -var benchmarks = []testing.InternalBenchmark{ -{{range .Benchmarks}} - {"{{.Name}}", {{.Package}}.{{.Name}}}, -{{end}} -} - -var examples = []testing.InternalExample{ -{{range .Examples}} - {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, -{{end}} -} - -func main() { - m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples) -{{with .TestMain}} - {{.Package}}.{{.Name}}(m) -{{else}} - os.Exit(m.Run()) -{{end}} -} - -`))