remove unused code from vendor
This commit is contained in:
-802
@@ -1,802 +0,0 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gopherjs/gopherjs/compiler"
|
||||
"github.com/gopherjs/gopherjs/compiler/natives"
|
||||
"github.com/neelance/sourcemap"
|
||||
)
|
||||
|
||||
type ImportCError struct {
|
||||
pkgPath string
|
||||
}
|
||||
|
||||
func (e *ImportCError) Error() string {
|
||||
return e.pkgPath + `: importing "C" is not supported by GopherJS`
|
||||
}
|
||||
|
||||
func NewBuildContext(installSuffix string, buildTags []string) *build.Context {
|
||||
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"),
|
||||
ReleaseTags: build.Default.ReleaseTags,
|
||||
CgoEnabled: true, // detect `import "C"` to throw proper error
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// that path.
|
||||
//
|
||||
// In the directory containing the package, .go and .inc.js files are
|
||||
// considered part of the package except for:
|
||||
//
|
||||
// - .go files in package documentation
|
||||
// - files starting with _ or . (likely editor temporary files)
|
||||
// - files with build constraints not satisfied by the context
|
||||
//
|
||||
// If an error occurs, Import returns a non-nil error and a nil
|
||||
// *PackageData.
|
||||
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
|
||||
// it by falling back to empty working directory. It just means
|
||||
// Import will not be able to resolve relative import paths.
|
||||
wd = ""
|
||||
}
|
||||
return importWithSrcDir(path, wd, mode, installSuffix, buildTags)
|
||||
}
|
||||
|
||||
func importWithSrcDir(path string, srcDir string, mode build.ImportMode, installSuffix string, buildTags []string) (*PackageData, error) {
|
||||
bctx := NewBuildContext(installSuffix, buildTags)
|
||||
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 "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
|
||||
}
|
||||
pkg, err := bctx.Import(path, srcDir, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Resolve issue #415 and remove this temporary workaround.
|
||||
if strings.HasSuffix(pkg.ImportPath, "/vendor/github.com/gopherjs/gopherjs/js") {
|
||||
return nil, fmt.Errorf("vendoring github.com/gopherjs/gopherjs/js package is not supported, see https://github.com/gopherjs/gopherjs/issues/415")
|
||||
}
|
||||
|
||||
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"}
|
||||
}
|
||||
|
||||
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(pkg.Dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PackageData{Package: pkg, JSFiles: jsFiles}, nil
|
||||
}
|
||||
|
||||
// excludeExecutable excludes all executable implementation .go files.
|
||||
// They have "executable_" prefix.
|
||||
func excludeExecutable(goFiles []string) []string {
|
||||
var s []string
|
||||
for _, f := range goFiles {
|
||||
if strings.HasPrefix(f, "executable_") {
|
||||
continue
|
||||
}
|
||||
s = append(s, f)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// exclude returns files, excluding specified files.
|
||||
func exclude(files []string, exclude ...string) []string {
|
||||
var s []string
|
||||
Outer:
|
||||
for _, f := range files {
|
||||
for _, e := range exclude {
|
||||
if f == e {
|
||||
continue Outer
|
||||
}
|
||||
}
|
||||
s = append(s, f)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 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) {
|
||||
pkg, err := NewBuildContext(installSuffix, buildTags).ImportDir(dir, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsFiles, err := jsFilesFromDir(pkg.Dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PackageData{Package: pkg, JSFiles: jsFiles}, nil
|
||||
}
|
||||
|
||||
// parseAndAugment parses and returns all .go files of given pkg.
|
||||
// Standard Go library packages are augmented with files in compiler/natives folder.
|
||||
// If isTest is true and pkg.ImportPath has no _test suffix, package is built for running internal tests.
|
||||
// If isTest is true and pkg.ImportPath has _test suffix, package is built for running external tests.
|
||||
//
|
||||
// The native packages are augmented by the contents of natives.FS in the following way.
|
||||
// The file names do not matter except the usual `_test` suffix. The files for
|
||||
// native overrides get added to the package (even if they have the same name
|
||||
// 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(pkg *build.Package, isTest bool, fileSet *token.FileSet) ([]*ast.File, 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
|
||||
}
|
||||
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)
|
||||
},
|
||||
}
|
||||
if nativesPkg, err := nativesContext.Import(importPath, "", 0); err == nil {
|
||||
names := nativesPkg.GoFiles
|
||||
if isTest {
|
||||
names = append(names, nativesPkg.TestGoFiles...)
|
||||
}
|
||||
if isXTest {
|
||||
names = nativesPkg.XTestGoFiles
|
||||
}
|
||||
for _, name := range names {
|
||||
fullPath := path.Join(nativesPkg.Dir, name)
|
||||
r, err := nativesContext.OpenFile(fullPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
file, err := parser.ParseFile(fileSet, fullPath, r, parser.ParseComments)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r.Close()
|
||||
for _, decl := range file.Decls {
|
||||
switch d := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
replacedDeclNames[funcName(d)] = true
|
||||
case *ast.GenDecl:
|
||||
switch d.Tok {
|
||||
case token.TYPE:
|
||||
for _, spec := range d.Specs {
|
||||
replacedDeclNames[spec.(*ast.TypeSpec).Name.Name] = true
|
||||
}
|
||||
case token.VAR, token.CONST:
|
||||
for _, spec := range d.Specs {
|
||||
for _, name := range spec.(*ast.ValueSpec).Names {
|
||||
replacedDeclNames[name.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
delete(replacedDeclNames, "init")
|
||||
|
||||
var errList compiler.ErrorList
|
||||
for _, name := range pkg.GoFiles {
|
||||
if !filepath.IsAbs(name) {
|
||||
name = filepath.Join(pkg.Dir, name)
|
||||
}
|
||||
r, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file, err := parser.ParseFile(fileSet, name, r, parser.ParseComments)
|
||||
r.Close()
|
||||
if err != nil {
|
||||
if list, isList := err.(scanner.ErrorList); isList {
|
||||
if len(list) > 10 {
|
||||
list = append(list[:10], &scanner.Error{Pos: list[9].Pos, Msg: "too many errors"})
|
||||
}
|
||||
for _, entry := range list {
|
||||
errList = append(errList, entry)
|
||||
}
|
||||
continue
|
||||
}
|
||||
errList = append(errList, err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch pkg.ImportPath {
|
||||
case "crypto/rand", "encoding/gob", "encoding/json", "expvar", "go/token", "log", "math/big", "math/rand", "regexp", "testing", "time":
|
||||
for _, spec := range file.Imports {
|
||||
path, _ := strconv.Unquote(spec.Path.Value)
|
||||
if path == "sync" {
|
||||
if spec.Name == nil {
|
||||
spec.Name = ast.NewIdent("sync")
|
||||
}
|
||||
spec.Path.Value = `"github.com/gopherjs/gopherjs/nosync"`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, decl := range file.Decls {
|
||||
switch d := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
if replacedDeclNames[funcName(d)] {
|
||||
d.Name = ast.NewIdent("_")
|
||||
}
|
||||
case *ast.GenDecl:
|
||||
switch d.Tok {
|
||||
case token.TYPE:
|
||||
for _, spec := range d.Specs {
|
||||
s := spec.(*ast.TypeSpec)
|
||||
if replacedDeclNames[s.Name.Name] {
|
||||
s.Name = ast.NewIdent("_")
|
||||
}
|
||||
}
|
||||
case token.VAR, token.CONST:
|
||||
for _, spec := range d.Specs {
|
||||
s := spec.(*ast.ValueSpec)
|
||||
for i, name := range s.Names {
|
||||
if replacedDeclNames[name.Name] {
|
||||
s.Names[i] = ast.NewIdent("_")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
files = append(files, file)
|
||||
}
|
||||
if errList != nil {
|
||||
return nil, errList
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
GOROOT string
|
||||
GOPATH string
|
||||
Verbose bool
|
||||
Quiet bool
|
||||
Watch bool
|
||||
CreateMapFile bool
|
||||
MapToLocalDisk bool
|
||||
Minify bool
|
||||
Color bool
|
||||
BuildTags []string
|
||||
}
|
||||
|
||||
func (o *Options) PrintError(format string, a ...interface{}) {
|
||||
if o.Color {
|
||||
format = "\x1B[31m" + format + "\x1B[39m"
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, format, a...)
|
||||
}
|
||||
|
||||
func (o *Options) PrintSuccess(format string, a ...interface{}) {
|
||||
if o.Color {
|
||||
format = "\x1B[32m" + format + "\x1B[39m"
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, format, a...)
|
||||
}
|
||||
|
||||
type PackageData struct {
|
||||
*build.Package
|
||||
JSFiles []string
|
||||
IsTest bool // IsTest is true if the package is being built for running tests.
|
||||
SrcModTime time.Time
|
||||
UpToDate bool
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
options *Options
|
||||
Archives map[string]*compiler.Archive
|
||||
Types map[string]*types.Package
|
||||
Watcher *fsnotify.Watcher
|
||||
}
|
||||
|
||||
func NewSession(options *Options) *Session {
|
||||
if options.GOROOT == "" {
|
||||
options.GOROOT = build.Default.GOROOT
|
||||
}
|
||||
if options.GOPATH == "" {
|
||||
options.GOPATH = build.Default.GOPATH
|
||||
}
|
||||
options.Verbose = options.Verbose || options.Watch
|
||||
|
||||
s := &Session{
|
||||
options: options,
|
||||
Archives: make(map[string]*compiler.Archive),
|
||||
}
|
||||
s.Types = make(map[string]*types.Package)
|
||||
if options.Watch {
|
||||
if out, err := exec.Command("ulimit", "-n").Output(); err == nil {
|
||||
if n, err := strconv.Atoi(strings.TrimSpace(string(out))); err == nil && n < 1024 {
|
||||
fmt.Printf("Warning: The maximum number of open file descriptors is very low (%d). Change it with 'ulimit -n 8192'.\n", n)
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
s.Watcher, err = fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Session) InstallSuffix() string {
|
||||
if s.options.Minify {
|
||||
return "min"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Session) BuildDir(packagePath string, importPath string, pkgObj string) error {
|
||||
if s.Watcher != nil {
|
||||
s.Watcher.Add(packagePath)
|
||||
}
|
||||
buildPkg, err := NewBuildContext(s.InstallSuffix(), s.options.BuildTags).ImportDir(packagePath, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg := &PackageData{Package: buildPkg}
|
||||
jsFiles, err := jsFilesFromDir(pkg.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg.JSFiles = jsFiles
|
||||
archive, err := s.BuildPackage(pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pkgObj == "" {
|
||||
pkgObj = filepath.Base(packagePath) + ".js"
|
||||
}
|
||||
if pkg.IsCommand() && !pkg.UpToDate {
|
||||
if err := s.WriteCommandPackage(archive, pkgObj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) BuildFiles(filenames []string, pkgObj string, packagePath string) error {
|
||||
pkg := &PackageData{
|
||||
Package: &build.Package{
|
||||
Name: "main",
|
||||
ImportPath: "main",
|
||||
Dir: packagePath,
|
||||
},
|
||||
}
|
||||
|
||||
for _, file := range filenames {
|
||||
if strings.HasSuffix(file, ".inc.js") {
|
||||
pkg.JSFiles = append(pkg.JSFiles, file)
|
||||
continue
|
||||
}
|
||||
pkg.GoFiles = append(pkg.GoFiles, file)
|
||||
}
|
||||
|
||||
archive, err := s.BuildPackage(pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.Types["main"].Name() != "main" {
|
||||
return fmt.Errorf("cannot build/run non-main package")
|
||||
}
|
||||
return s.WriteCommandPackage(archive, pkgObj)
|
||||
}
|
||||
|
||||
func (s *Session) BuildImportPath(path string) (*compiler.Archive, error) {
|
||||
_, archive, err := s.buildImportPathWithSrcDir(path, "")
|
||||
return archive, err
|
||||
}
|
||||
|
||||
func (s *Session) buildImportPathWithSrcDir(path string, srcDir string) (*PackageData, *compiler.Archive, error) {
|
||||
pkg, err := importWithSrcDir(path, srcDir, 0, s.InstallSuffix(), s.options.BuildTags)
|
||||
if s.Watcher != nil && pkg != nil { // add watch even on error
|
||||
s.Watcher.Add(pkg.Dir)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
archive, err := s.BuildPackage(pkg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pkg, archive, nil
|
||||
}
|
||||
|
||||
func (s *Session) BuildPackage(pkg *PackageData) (*compiler.Archive, error) {
|
||||
if archive, ok := s.Archives[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()
|
||||
}
|
||||
}
|
||||
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 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 _, name := range append(pkg.GoFiles, pkg.JSFiles...) {
|
||||
fileInfo, err := os.Stat(filepath.Join(pkg.Dir, name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fileInfo.ModTime().After(pkg.SrcModTime) {
|
||||
pkg.SrcModTime = fileInfo.ModTime()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
objFile, err := os.Open(pkg.PkgObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer objFile.Close()
|
||||
|
||||
archive, err := compiler.ReadArchive(pkg.PkgObj, pkg.ImportPath, objFile, s.Types)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.Archives[pkg.ImportPath] = archive
|
||||
return archive, err
|
||||
}
|
||||
}
|
||||
|
||||
fileSet := token.NewFileSet()
|
||||
files, err := parseAndAugment(pkg.Package, pkg.IsTest, fileSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localImportPathCache := make(map[string]*compiler.Archive)
|
||||
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
|
||||
},
|
||||
}
|
||||
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
|
||||
}
|
||||
archive.IncJSCode = append(archive.IncJSCode, []byte("\t(function() {\n")...)
|
||||
archive.IncJSCode = append(archive.IncJSCode, code...)
|
||||
archive.IncJSCode = append(archive.IncJSCode, []byte("\n\t}).call($global);\n")...)
|
||||
}
|
||||
|
||||
if s.options.Verbose {
|
||||
fmt.Println(pkg.ImportPath)
|
||||
}
|
||||
|
||||
s.Archives[pkg.ImportPath] = archive
|
||||
|
||||
if pkg.PkgObj == "" || pkg.IsCommand() {
|
||||
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
|
||||
}
|
||||
return archive, nil
|
||||
}
|
||||
return nil, 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)
|
||||
}
|
||||
|
||||
func (s *Session) WriteCommandPackage(archive *compiler.Archive, pkgObj string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(pkgObj), 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
codeFile, err := os.Create(pkgObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer codeFile.Close()
|
||||
|
||||
sourceMapFilter := &compiler.SourceMapFilter{Writer: codeFile}
|
||||
if s.options.CreateMapFile {
|
||||
m := &sourcemap.Map{File: filepath.Base(pkgObj)}
|
||||
mapFile, err := os.Create(pkgObj + ".map")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
m.WriteTo(mapFile)
|
||||
mapFile.Close()
|
||||
fmt.Fprintf(codeFile, "//# sourceMappingURL=%s.map\n", filepath.Base(pkgObj))
|
||||
}()
|
||||
|
||||
sourceMapFilter.MappingCallback = NewMappingCallback(m, s.options.GOROOT, s.options.GOPATH, s.options.MapToLocalDisk)
|
||||
}
|
||||
|
||||
deps, err := compiler.ImportDependencies(archive, func(path string) (*compiler.Archive, error) {
|
||||
if archive, ok := s.Archives[path]; ok {
|
||||
return archive, nil
|
||||
}
|
||||
_, archive, err := s.buildImportPathWithSrcDir(path, "")
|
||||
return archive, err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return compiler.WriteProgramCode(deps, sourceMapFilter)
|
||||
}
|
||||
|
||||
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() {
|
||||
m.AddMapping(&sourcemap.Mapping{GeneratedLine: generatedLine, GeneratedColumn: generatedColumn})
|
||||
return
|
||||
}
|
||||
|
||||
file := originalPos.Filename
|
||||
|
||||
switch hasGopathPrefix, prefixLen := hasGopathPrefix(file, gopath); {
|
||||
case localMap:
|
||||
// no-op: keep file as-is
|
||||
case hasGopathPrefix:
|
||||
file = filepath.ToSlash(file[prefixLen+4:])
|
||||
case strings.HasPrefix(file, goroot):
|
||||
file = filepath.ToSlash(file[len(goroot)+4:])
|
||||
default:
|
||||
file = filepath.Base(file)
|
||||
}
|
||||
|
||||
m.AddMapping(&sourcemap.Mapping{GeneratedLine: generatedLine, GeneratedColumn: generatedColumn, OriginalFile: file, OriginalLine: originalPos.Line, OriginalColumn: originalPos.Column})
|
||||
}
|
||||
}
|
||||
|
||||
func jsFilesFromDir(dir string) ([]string, error) {
|
||||
files, err := ioutil.ReadDir(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) {
|
||||
gopathWorkspaces := filepath.SplitList(gopath)
|
||||
for _, gopathWorkspace := range gopathWorkspaces {
|
||||
gopathWorkspace = filepath.Clean(gopathWorkspace)
|
||||
if strings.HasPrefix(file, gopathWorkspace) {
|
||||
return true, len(gopathWorkspace)
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (s *Session) WaitForChange() {
|
||||
s.options.PrintSuccess("watching for changes...\n")
|
||||
for {
|
||||
select {
|
||||
case ev := <-s.Watcher.Events:
|
||||
if ev.Op&(fsnotify.Create|fsnotify.Write|fsnotify.Remove|fsnotify.Rename) == 0 || filepath.Base(ev.Name)[0] == '.' {
|
||||
continue
|
||||
}
|
||||
if !strings.HasSuffix(ev.Name, ".go") && !strings.HasSuffix(ev.Name, ".inc.js") {
|
||||
continue
|
||||
}
|
||||
s.options.PrintSuccess("change detected: %s\n", ev.Name)
|
||||
case err := <-s.Watcher.Errors:
|
||||
s.options.PrintError("watcher error: %s\n", err.Error())
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
go func() {
|
||||
for range s.Watcher.Events {
|
||||
// consume, else Close() may deadlock
|
||||
}
|
||||
}()
|
||||
s.Watcher.Close()
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func BoolValue(expr ast.Expr, info *types.Info) (bool, bool) {
|
||||
v := info.Types[expr].Value
|
||||
if v != nil && v.Kind() == constant.Bool {
|
||||
return constant.BoolVal(v), true
|
||||
}
|
||||
switch e := expr.(type) {
|
||||
case *ast.BinaryExpr:
|
||||
switch e.Op {
|
||||
case token.LAND:
|
||||
if b, ok := BoolValue(e.X, info); ok {
|
||||
if !b {
|
||||
return false, true
|
||||
}
|
||||
return BoolValue(e.Y, info)
|
||||
}
|
||||
case token.LOR:
|
||||
if b, ok := BoolValue(e.X, info); ok {
|
||||
if b {
|
||||
return true, true
|
||||
}
|
||||
return BoolValue(e.Y, info)
|
||||
}
|
||||
}
|
||||
case *ast.UnaryExpr:
|
||||
if e.Op == token.NOT {
|
||||
if b, ok := BoolValue(e.X, info); ok {
|
||||
return !b, true
|
||||
}
|
||||
}
|
||||
case *ast.ParenExpr:
|
||||
return BoolValue(e.X, info)
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func HasBreak(n ast.Node) bool {
|
||||
v := hasBreakVisitor{}
|
||||
ast.Walk(&v, n)
|
||||
return v.hasBreak
|
||||
}
|
||||
|
||||
type hasBreakVisitor struct {
|
||||
hasBreak bool
|
||||
}
|
||||
|
||||
func (v *hasBreakVisitor) Visit(node ast.Node) (w ast.Visitor) {
|
||||
if v.hasBreak {
|
||||
return nil
|
||||
}
|
||||
switch n := node.(type) {
|
||||
case *ast.BranchStmt:
|
||||
if n.Tok == token.BREAK && n.Label == nil {
|
||||
v.hasBreak = true
|
||||
return nil
|
||||
}
|
||||
case *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, ast.Expr:
|
||||
return nil
|
||||
}
|
||||
return v
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func EscapingObjects(n ast.Node, info *types.Info) []*types.Var {
|
||||
v := escapeAnalysis{
|
||||
info: info,
|
||||
escaping: make(map[*types.Var]bool),
|
||||
topScope: info.Scopes[n],
|
||||
bottomScopes: make(map[*types.Scope]bool),
|
||||
}
|
||||
ast.Walk(&v, n)
|
||||
var list []*types.Var
|
||||
for obj := range v.escaping {
|
||||
list = append(list, obj)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
type escapeAnalysis struct {
|
||||
info *types.Info
|
||||
escaping map[*types.Var]bool
|
||||
topScope *types.Scope
|
||||
bottomScopes map[*types.Scope]bool
|
||||
}
|
||||
|
||||
func (v *escapeAnalysis) Visit(node ast.Node) (w ast.Visitor) {
|
||||
// huge overapproximation
|
||||
switch n := node.(type) {
|
||||
case *ast.UnaryExpr:
|
||||
if n.Op == token.AND {
|
||||
if _, ok := n.X.(*ast.Ident); ok {
|
||||
return &escapingObjectCollector{v}
|
||||
}
|
||||
}
|
||||
case *ast.FuncLit:
|
||||
v.bottomScopes[v.info.Scopes[n.Type]] = true
|
||||
return &escapingObjectCollector{v}
|
||||
case *ast.ForStmt:
|
||||
v.bottomScopes[v.info.Scopes[n.Body]] = true
|
||||
case *ast.RangeStmt:
|
||||
v.bottomScopes[v.info.Scopes[n.Body]] = true
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
type escapingObjectCollector struct {
|
||||
analysis *escapeAnalysis
|
||||
}
|
||||
|
||||
func (v *escapingObjectCollector) Visit(node ast.Node) (w ast.Visitor) {
|
||||
if id, ok := node.(*ast.Ident); ok {
|
||||
if obj, ok := v.analysis.info.Uses[id].(*types.Var); ok {
|
||||
for s := obj.Parent(); s != nil; s = s.Parent() {
|
||||
if s == v.analysis.topScope {
|
||||
v.analysis.escaping[obj] = true
|
||||
break
|
||||
}
|
||||
if v.analysis.bottomScopes[s] {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
-254
@@ -1,254 +0,0 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"github.com/gopherjs/gopherjs/compiler/astutil"
|
||||
"github.com/gopherjs/gopherjs/compiler/typesutil"
|
||||
)
|
||||
|
||||
type continueStmt struct {
|
||||
forStmt *ast.ForStmt
|
||||
analyzeStack []ast.Node
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (info *Info) newFuncInfo() *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),
|
||||
}
|
||||
info.allInfos = append(info.allInfos, funcInfo)
|
||||
return funcInfo
|
||||
}
|
||||
|
||||
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.InitFuncInfo = info.newFuncInfo()
|
||||
|
||||
for _, file := range files {
|
||||
for k, v := range ast.NewCommentMap(fileSet, file, file.Comments) {
|
||||
info.comments[k] = v
|
||||
}
|
||||
ast.Walk(info.InitFuncInfo, file)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
delete(funcInfo.LocalCalls, obj)
|
||||
done = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, funcInfo := range info.allInfos {
|
||||
for _, continueStmt := range funcInfo.ContinueStmts {
|
||||
if funcInfo.Blocking[continueStmt.forStmt.Post] {
|
||||
funcInfo.markBlocking(continueStmt.analyzeStack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func (c *FuncInfo) Visit(node ast.Node) ast.Visitor {
|
||||
if node == nil {
|
||||
if len(c.analyzeStack) != 0 {
|
||||
c.analyzeStack = c.analyzeStack[:len(c.analyzeStack)-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
c.analyzeStack = append(c.analyzeStack, 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.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
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
case *ast.SendStmt:
|
||||
c.markBlocking(c.analyzeStack)
|
||||
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
|
||||
}
|
||||
case token.ARROW:
|
||||
c.markBlocking(c.analyzeStack)
|
||||
}
|
||||
case *ast.RangeStmt:
|
||||
if _, ok := c.p.TypeOf(n.X).Underlying().(*types.Chan); ok {
|
||||
c.markBlocking(c.analyzeStack)
|
||||
}
|
||||
case *ast.SelectStmt:
|
||||
for _, s := range n.Body.List {
|
||||
if s.(*ast.CommClause).Comm == nil { // default clause
|
||||
return c
|
||||
}
|
||||
}
|
||||
c.markBlocking(c.analyzeStack)
|
||||
case *ast.CommClause:
|
||||
switch comm := n.Comm.(type) {
|
||||
case *ast.SendStmt:
|
||||
ast.Walk(c, comm.Chan)
|
||||
ast.Walk(c, comm.Value)
|
||||
case *ast.ExprStmt:
|
||||
ast.Walk(c, comm.X.(*ast.UnaryExpr).X)
|
||||
case *ast.AssignStmt:
|
||||
ast.Walk(c, comm.Rhs[0].(*ast.UnaryExpr).X)
|
||||
}
|
||||
for _, s := range n.Body {
|
||||
ast.Walk(c, s)
|
||||
}
|
||||
return nil
|
||||
case *ast.GoStmt:
|
||||
ast.Walk(c, n.Call.Fun)
|
||||
for _, arg := range n.Call.Args {
|
||||
ast.Walk(c, arg)
|
||||
}
|
||||
return nil
|
||||
case *ast.DeferStmt:
|
||||
c.HasDefer = true
|
||||
if funcLit, ok := n.Call.Fun.(*ast.FuncLit); ok {
|
||||
ast.Walk(c, funcLit.Body)
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *FuncInfo) markBlocking(stack []ast.Node) {
|
||||
for _, n := range stack {
|
||||
c.Blocking[n] = true
|
||||
c.Flattened[n] = true
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func HasSideEffect(n ast.Node, info *types.Info) bool {
|
||||
v := hasSideEffectVisitor{info: info}
|
||||
ast.Walk(&v, n)
|
||||
return v.hasSideEffect
|
||||
}
|
||||
|
||||
type hasSideEffectVisitor struct {
|
||||
info *types.Info
|
||||
hasSideEffect bool
|
||||
}
|
||||
|
||||
func (v *hasSideEffectVisitor) Visit(node ast.Node) (w ast.Visitor) {
|
||||
if v.hasSideEffect {
|
||||
return nil
|
||||
}
|
||||
switch n := node.(type) {
|
||||
case *ast.CallExpr:
|
||||
if _, isSig := v.info.TypeOf(n.Fun).(*types.Signature); isSig { // skip conversions
|
||||
v.hasSideEffect = true
|
||||
return nil
|
||||
}
|
||||
case *ast.UnaryExpr:
|
||||
if n.Op == token.ARROW {
|
||||
v.hasSideEffect = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package astutil
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func RemoveParens(e ast.Expr) ast.Expr {
|
||||
for {
|
||||
p, isParen := e.(*ast.ParenExpr)
|
||||
if !isParen {
|
||||
return e
|
||||
}
|
||||
e = p.X
|
||||
}
|
||||
}
|
||||
|
||||
func SetType(info *types.Info, t types.Type, e ast.Expr) ast.Expr {
|
||||
info.Types[e] = types.TypeAndValue{Type: t}
|
||||
return e
|
||||
}
|
||||
|
||||
func NewIdent(name string, t types.Type, info *types.Info, pkg *types.Package) *ast.Ident {
|
||||
ident := ast.NewIdent(name)
|
||||
info.Types[ident] = types.TypeAndValue{Type: t}
|
||||
obj := types.NewVar(0, pkg, name, t)
|
||||
info.Uses[ident] = obj
|
||||
return ident
|
||||
}
|
||||
|
||||
func IsTypeExpr(expr ast.Expr, info *types.Info) bool {
|
||||
switch e := expr.(type) {
|
||||
case *ast.ArrayType, *ast.ChanType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.StructType:
|
||||
return true
|
||||
case *ast.StarExpr:
|
||||
return IsTypeExpr(e.X, info)
|
||||
case *ast.Ident:
|
||||
_, ok := info.Uses[e].(*types.TypeName)
|
||||
return ok
|
||||
case *ast.SelectorExpr:
|
||||
_, ok := info.Uses[e.Sel].(*types.TypeName)
|
||||
return ok
|
||||
case *ast.ParenExpr:
|
||||
return IsTypeExpr(e.X, info)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
-293
@@ -1,293 +0,0 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/gopherjs/gopherjs/compiler/prelude"
|
||||
"golang.org/x/tools/go/gcimporter15"
|
||||
)
|
||||
|
||||
var sizes32 = &types.StdSizes{WordSize: 4, MaxAlign: 8}
|
||||
var reservedKeywords = make(map[string]bool)
|
||||
var _ = ___GOPHERJS_REQUIRES_GO_VERSION_1_9___ // 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"} {
|
||||
reservedKeywords[keyword] = true
|
||||
}
|
||||
}
|
||||
|
||||
type ErrorList []error
|
||||
|
||||
func (err ErrorList) Error() string {
|
||||
return err[0].Error()
|
||||
}
|
||||
|
||||
type Archive struct {
|
||||
ImportPath string
|
||||
Name string
|
||||
Imports []string
|
||||
ExportData []byte
|
||||
Declarations []*Decl
|
||||
IncJSCode []byte
|
||||
FileSet []byte
|
||||
Minified bool
|
||||
}
|
||||
|
||||
type Decl struct {
|
||||
FullName string
|
||||
Vars []string
|
||||
DeclCode []byte
|
||||
MethodListCode []byte
|
||||
TypeInitCode []byte
|
||||
InitCode []byte
|
||||
DceObjectFilter string
|
||||
DceMethodFilter string
|
||||
DceDeps []string
|
||||
Blocking bool
|
||||
}
|
||||
|
||||
type Dependency struct {
|
||||
Pkg string
|
||||
Type string
|
||||
Method string
|
||||
}
|
||||
|
||||
func ImportDependencies(archive *Archive, importPkg func(string) (*Archive, error)) ([]*Archive, error) {
|
||||
var deps []*Archive
|
||||
paths := make(map[string]bool)
|
||||
var collectDependencies func(path string) error
|
||||
collectDependencies = func(path string) error {
|
||||
if paths[path] {
|
||||
return nil
|
||||
}
|
||||
dep, err := importPkg(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, imp := range dep.Imports {
|
||||
if err := collectDependencies(imp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
deps = append(deps, dep)
|
||||
paths[dep.ImportPath] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := collectDependencies("runtime"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, imp := range archive.Imports {
|
||||
if err := collectDependencies(imp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
deps = append(deps, archive)
|
||||
return deps, nil
|
||||
}
|
||||
|
||||
type dceInfo struct {
|
||||
decl *Decl
|
||||
objectFilter string
|
||||
methodFilter string
|
||||
}
|
||||
|
||||
func WriteProgramCode(pkgs []*Archive, w *SourceMapFilter) error {
|
||||
mainPkg := pkgs[len(pkgs)-1]
|
||||
minify := mainPkg.Minified
|
||||
|
||||
byFilter := make(map[string][]*dceInfo)
|
||||
var pendingDecls []*Decl
|
||||
for _, pkg := range pkgs {
|
||||
for _, d := range pkg.Declarations {
|
||||
if d.DceObjectFilter == "" && d.DceMethodFilter == "" {
|
||||
pendingDecls = append(pendingDecls, d)
|
||||
continue
|
||||
}
|
||||
info := &dceInfo{decl: d}
|
||||
if d.DceObjectFilter != "" {
|
||||
info.objectFilter = pkg.ImportPath + "." + d.DceObjectFilter
|
||||
byFilter[info.objectFilter] = append(byFilter[info.objectFilter], info)
|
||||
}
|
||||
if d.DceMethodFilter != "" {
|
||||
info.methodFilter = pkg.ImportPath + "." + d.DceMethodFilter
|
||||
byFilter[info.methodFilter] = append(byFilter[info.methodFilter], info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dceSelection := make(map[*Decl]struct{})
|
||||
for len(pendingDecls) != 0 {
|
||||
d := pendingDecls[len(pendingDecls)-1]
|
||||
pendingDecls = pendingDecls[:len(pendingDecls)-1]
|
||||
|
||||
dceSelection[d] = struct{}{}
|
||||
|
||||
for _, dep := range d.DceDeps {
|
||||
if infos, ok := byFilter[dep]; ok {
|
||||
delete(byFilter, dep)
|
||||
for _, info := range infos {
|
||||
if info.objectFilter == dep {
|
||||
info.objectFilter = ""
|
||||
}
|
||||
if info.methodFilter == dep {
|
||||
info.methodFilter = ""
|
||||
}
|
||||
if info.objectFilter == "" && info.methodFilter == "" {
|
||||
pendingDecls = append(pendingDecls, info.decl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte("\"use strict\";\n(function() {\n\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(removeWhitespace([]byte(prelude.Prelude), minify)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write packages
|
||||
for _, pkg := range pkgs {
|
||||
if err := WritePkgCode(pkg, dceSelection, 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, 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 {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if _, err := w.Write(pkg.IncJSCode); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(removeWhitespace([]byte(fmt.Sprintf("$packages[\"%s\"] = (function() {\n", pkg.ImportPath)), minify)); err != nil {
|
||||
return err
|
||||
}
|
||||
vars := []string{"$pkg = {}", "$init"}
|
||||
var filteredDecls []*Decl
|
||||
for _, d := range pkg.Declarations {
|
||||
if _, ok := dceSelection[d]; ok {
|
||||
vars = append(vars, d.Vars...)
|
||||
filteredDecls = append(filteredDecls, d)
|
||||
}
|
||||
}
|
||||
if _, err := w.Write(removeWhitespace([]byte(fmt.Sprintf("\tvar %s;\n", strings.Join(vars, ", "))), minify)); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, d := range filteredDecls {
|
||||
if _, err := w.Write(d.DeclCode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, d := range filteredDecls {
|
||||
if _, err := w.Write(d.MethodListCode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, d := range filteredDecls {
|
||||
if _, err := w.Write(d.TypeInitCode); 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
|
||||
}
|
||||
for _, d := range filteredDecls {
|
||||
if _, err := w.Write(d.InitCode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := w.Write(removeWhitespace([]byte("\t\t/* */ } return; } if ($f === undefined) { $f = { $blk: $init }; } $f.$s = $s; $f.$r = $r; return $f;\n\t};\n\t$pkg.$init = $init;\n\treturn $pkg;\n})();"), minify)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("\n")); err != nil { // keep this \n even when minified
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadArchive(filename, path string, r io.Reader, packages map[string]*types.Package) (*Archive, error) {
|
||||
var a Archive
|
||||
if err := gob.NewDecoder(r).Decode(&a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var err error
|
||||
_, packages[path], err = gcimporter.BImportData(token.NewFileSet(), packages, a.ExportData, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func WriteArchive(a *Archive, w io.Writer) error {
|
||||
return gob.NewEncoder(w).Encode(a)
|
||||
}
|
||||
|
||||
type SourceMapFilter struct {
|
||||
Writer io.Writer
|
||||
MappingCallback func(generatedLine, generatedColumn int, originalPos token.Position)
|
||||
line int
|
||||
column int
|
||||
fileSet *token.FileSet
|
||||
}
|
||||
|
||||
func (f *SourceMapFilter) Write(p []byte) (n int, err error) {
|
||||
var n2 int
|
||||
for {
|
||||
i := bytes.IndexByte(p, '\b')
|
||||
w := p
|
||||
if i != -1 {
|
||||
w = p[:i]
|
||||
}
|
||||
|
||||
n2, err = f.Writer.Write(w)
|
||||
n += n2
|
||||
for {
|
||||
i := bytes.IndexByte(w, '\n')
|
||||
if i == -1 {
|
||||
f.column += len(w)
|
||||
break
|
||||
}
|
||||
f.line++
|
||||
f.column = 0
|
||||
w = w[i+1:]
|
||||
}
|
||||
|
||||
if err != nil || i == -1 {
|
||||
return
|
||||
}
|
||||
if f.MappingCallback != nil {
|
||||
f.MappingCallback(f.line+1, f.column, f.fileSet.Position(token.Pos(binary.BigEndian.Uint32(p[i+1:i+5]))))
|
||||
}
|
||||
p = p[i+5:]
|
||||
n += 5
|
||||
}
|
||||
}
|
||||
-1373
File diff suppressed because it is too large
Load Diff
-106
@@ -1,106 +0,0 @@
|
||||
package filter
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"github.com/gopherjs/gopherjs/compiler/astutil"
|
||||
)
|
||||
|
||||
func Assign(stmt ast.Stmt, info *types.Info, pkg *types.Package) ast.Stmt {
|
||||
if s, ok := stmt.(*ast.AssignStmt); ok && s.Tok != token.ASSIGN && s.Tok != token.DEFINE {
|
||||
var op token.Token
|
||||
switch s.Tok {
|
||||
case token.ADD_ASSIGN:
|
||||
op = token.ADD
|
||||
case token.SUB_ASSIGN:
|
||||
op = token.SUB
|
||||
case token.MUL_ASSIGN:
|
||||
op = token.MUL
|
||||
case token.QUO_ASSIGN:
|
||||
op = token.QUO
|
||||
case token.REM_ASSIGN:
|
||||
op = token.REM
|
||||
case token.AND_ASSIGN:
|
||||
op = token.AND
|
||||
case token.OR_ASSIGN:
|
||||
op = token.OR
|
||||
case token.XOR_ASSIGN:
|
||||
op = token.XOR
|
||||
case token.SHL_ASSIGN:
|
||||
op = token.SHL
|
||||
case token.SHR_ASSIGN:
|
||||
op = token.SHR
|
||||
case token.AND_NOT_ASSIGN:
|
||||
op = token.AND_NOT
|
||||
default:
|
||||
panic(s.Tok)
|
||||
}
|
||||
|
||||
var list []ast.Stmt
|
||||
|
||||
var viaTmpVars func(expr ast.Expr, name string) ast.Expr
|
||||
viaTmpVars = func(expr ast.Expr, name string) ast.Expr {
|
||||
switch e := astutil.RemoveParens(expr).(type) {
|
||||
case *ast.IndexExpr:
|
||||
return astutil.SetType(info, info.TypeOf(e), &ast.IndexExpr{
|
||||
X: viaTmpVars(e.X, "_slice"),
|
||||
Index: viaTmpVars(e.Index, "_index"),
|
||||
})
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
sel, ok := info.Selections[e]
|
||||
if !ok {
|
||||
// qualified identifier
|
||||
return e
|
||||
}
|
||||
newSel := &ast.SelectorExpr{
|
||||
X: viaTmpVars(e.X, "_struct"),
|
||||
Sel: e.Sel,
|
||||
}
|
||||
info.Selections[newSel] = sel
|
||||
return astutil.SetType(info, info.TypeOf(e), newSel)
|
||||
|
||||
case *ast.StarExpr:
|
||||
return astutil.SetType(info, info.TypeOf(e), &ast.StarExpr{
|
||||
X: viaTmpVars(e.X, "_ptr"),
|
||||
})
|
||||
|
||||
case *ast.Ident, *ast.BasicLit:
|
||||
return e
|
||||
|
||||
default:
|
||||
tmpVar := astutil.NewIdent(name, info.TypeOf(e), info, pkg)
|
||||
list = append(list, &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{tmpVar},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{e},
|
||||
})
|
||||
return tmpVar
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
lhs := viaTmpVars(s.Lhs[0], "_val")
|
||||
|
||||
list = append(list, &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{lhs},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{
|
||||
astutil.SetType(info, info.TypeOf(s.Lhs[0]), &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: op,
|
||||
Y: astutil.SetType(info, info.TypeOf(s.Rhs[0]), &ast.ParenExpr{
|
||||
X: s.Rhs[0],
|
||||
}),
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
return &ast.BlockStmt{
|
||||
List: list,
|
||||
}
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package filter
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func IncDecStmt(stmt ast.Stmt, info *types.Info) ast.Stmt {
|
||||
if s, ok := stmt.(*ast.IncDecStmt); ok {
|
||||
t := info.TypeOf(s.X)
|
||||
if iExpr, isIExpr := s.X.(*ast.IndexExpr); isIExpr {
|
||||
switch u := info.TypeOf(iExpr.X).Underlying().(type) {
|
||||
case *types.Array:
|
||||
t = u.Elem()
|
||||
case *types.Slice:
|
||||
t = u.Elem()
|
||||
case *types.Map:
|
||||
t = u.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
tok := token.ADD_ASSIGN
|
||||
if s.Tok == token.DEC {
|
||||
tok = token.SUB_ASSIGN
|
||||
}
|
||||
|
||||
one := &ast.BasicLit{Kind: token.INT}
|
||||
info.Types[one] = types.TypeAndValue{Type: t, Value: constant.MakeInt64(1)}
|
||||
|
||||
return &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{s.X},
|
||||
Tok: tok,
|
||||
Rhs: []ast.Expr{one},
|
||||
}
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// Package natives provides native packages via a virtual filesystem.
|
||||
//
|
||||
// See documentation of parseAndAugment in github.com/gopherjs/gopherjs/build
|
||||
// for explanation of behavior used to augment the native packages using the files
|
||||
// in src subfolder.
|
||||
package natives
|
||||
|
||||
//go:generate vfsgendev -source="github.com/gopherjs/gopherjs/compiler/natives".FS -tag=gopherjsdev
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
// +build gopherjsdev
|
||||
|
||||
package natives
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/shurcooL/httpfs/filter"
|
||||
)
|
||||
|
||||
func importPathToDir(importPath string) string {
|
||||
p, err := build.Import(importPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
return p.Dir
|
||||
}
|
||||
|
||||
// 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/")
|
||||
},
|
||||
)
|
||||
-907
File diff suppressed because one or more lines are too long
-43
@@ -1,43 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package bytes
|
||||
|
||||
func IndexByte(s []byte, c byte) int {
|
||||
for i, b := range s {
|
||||
if b == c {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func Equal(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, c := range a {
|
||||
if c != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Compare(a, b []byte) int {
|
||||
for i, ca := range a {
|
||||
if i >= len(b) {
|
||||
return 1
|
||||
}
|
||||
cb := b[i]
|
||||
if ca < cb {
|
||||
return -1
|
||||
}
|
||||
if ca > cb {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package rand
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Reader = &rngReader{}
|
||||
}
|
||||
|
||||
type rngReader struct{}
|
||||
|
||||
func (r *rngReader) Read(b []byte) (n int, err error) {
|
||||
array := js.InternalObject(b).Get("$array")
|
||||
offset := js.InternalObject(b).Get("$offset").Int()
|
||||
|
||||
// browser
|
||||
crypto := js.Global.Get("crypto")
|
||||
if crypto == js.Undefined {
|
||||
crypto = js.Global.Get("msCrypto")
|
||||
}
|
||||
if crypto != js.Undefined {
|
||||
if crypto.Get("getRandomValues") != js.Undefined {
|
||||
n = len(b)
|
||||
if n > 65536 {
|
||||
// Avoid QuotaExceededError thrown by getRandomValues
|
||||
// when length is more than 65536, as specified in
|
||||
// http://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
|
||||
n = 65536
|
||||
}
|
||||
crypto.Call("getRandomValues", array.Call("subarray", offset, offset+n))
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Node.js
|
||||
if require := js.Global.Get("require"); require != js.Undefined {
|
||||
if randomBytes := require.Invoke("crypto").Get("randomBytes"); randomBytes != js.Undefined {
|
||||
array.Call("set", randomBytes.Invoke(len(b)), offset)
|
||||
return len(b), nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.New("crypto/rand not available in this environment")
|
||||
}
|
||||
-9
@@ -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")
|
||||
}
|
||||
Generated
Vendored
-57
@@ -1,57 +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)
|
||||
}
|
||||
Generated
Vendored
-26
@@ -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
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package big
|
||||
|
||||
// TODO: This is a workaround for https://github.com/gopherjs/gopherjs/issues/652.
|
||||
// Remove after that issue is resolved.
|
||||
type Word uintptr
|
||||
-262
@@ -1,262 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package math
|
||||
|
||||
import (
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
var math = js.Global.Get("Math")
|
||||
var zero float64 = 0
|
||||
var posInf = 1 / zero
|
||||
var negInf = -1 / zero
|
||||
var nan = 0 / zero
|
||||
|
||||
func Acos(x float64) float64 {
|
||||
return math.Call("acos", x).Float()
|
||||
}
|
||||
|
||||
func Acosh(x float64) float64 {
|
||||
return math.Call("acosh", x).Float()
|
||||
}
|
||||
|
||||
func Asin(x float64) float64 {
|
||||
return math.Call("asin", x).Float()
|
||||
}
|
||||
|
||||
func Asinh(x float64) float64 {
|
||||
return math.Call("asinh", x).Float()
|
||||
}
|
||||
|
||||
func Atan(x float64) float64 {
|
||||
return math.Call("atan", x).Float()
|
||||
}
|
||||
|
||||
func Atanh(x float64) float64 {
|
||||
return math.Call("atanh", x).Float()
|
||||
}
|
||||
|
||||
func Atan2(y, x float64) float64 {
|
||||
return math.Call("atan2", y, x).Float()
|
||||
}
|
||||
|
||||
func Cbrt(x float64) float64 {
|
||||
return math.Call("cbrt", x).Float()
|
||||
}
|
||||
|
||||
func Ceil(x float64) float64 {
|
||||
return math.Call("ceil", x).Float()
|
||||
}
|
||||
|
||||
func Copysign(x, y float64) float64 {
|
||||
if (x < 0 || 1/x == negInf) != (y < 0 || 1/y == negInf) {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func Cos(x float64) float64 {
|
||||
return math.Call("cos", x).Float()
|
||||
}
|
||||
|
||||
func Cosh(x float64) float64 {
|
||||
return math.Call("cosh", x).Float()
|
||||
}
|
||||
|
||||
func Dim(x, y float64) float64 {
|
||||
return dim(x, y)
|
||||
}
|
||||
|
||||
func Erf(x float64) float64 {
|
||||
return erf(x)
|
||||
}
|
||||
|
||||
func Erfc(x float64) float64 {
|
||||
return erfc(x)
|
||||
}
|
||||
|
||||
func Exp(x float64) float64 {
|
||||
return math.Call("exp", x).Float()
|
||||
}
|
||||
|
||||
func Exp2(x float64) float64 {
|
||||
return math.Call("pow", 2, x).Float()
|
||||
}
|
||||
|
||||
func Expm1(x float64) float64 {
|
||||
return expm1(x)
|
||||
}
|
||||
|
||||
func Floor(x float64) float64 {
|
||||
return math.Call("floor", x).Float()
|
||||
}
|
||||
|
||||
func Frexp(f float64) (frac float64, exp int) {
|
||||
return frexp(f)
|
||||
}
|
||||
|
||||
func Hypot(p, q float64) float64 {
|
||||
return hypot(p, q)
|
||||
}
|
||||
|
||||
func Inf(sign int) float64 {
|
||||
switch {
|
||||
case sign >= 0:
|
||||
return posInf
|
||||
default:
|
||||
return negInf
|
||||
}
|
||||
}
|
||||
|
||||
func IsInf(f float64, sign int) bool {
|
||||
if f == posInf {
|
||||
return sign >= 0
|
||||
}
|
||||
if f == negInf {
|
||||
return sign <= 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsNaN(f float64) (is bool) {
|
||||
return f != f
|
||||
}
|
||||
|
||||
func Ldexp(frac float64, exp int) float64 {
|
||||
if frac == 0 {
|
||||
return frac
|
||||
}
|
||||
if exp >= 1024 {
|
||||
return frac * math.Call("pow", 2, 1023).Float() * math.Call("pow", 2, exp-1023).Float()
|
||||
}
|
||||
if exp <= -1024 {
|
||||
return frac * math.Call("pow", 2, -1023).Float() * math.Call("pow", 2, exp+1023).Float()
|
||||
}
|
||||
return frac * math.Call("pow", 2, exp).Float()
|
||||
}
|
||||
|
||||
func Log(x float64) float64 {
|
||||
if x != x { // workaround for optimizer bug in V8, remove at some point
|
||||
return nan
|
||||
}
|
||||
return math.Call("log", x).Float()
|
||||
}
|
||||
|
||||
func Log10(x float64) float64 {
|
||||
return log10(x)
|
||||
}
|
||||
|
||||
func Log1p(x float64) float64 {
|
||||
return log1p(x)
|
||||
}
|
||||
|
||||
func Log2(x float64) float64 {
|
||||
return log2(x)
|
||||
}
|
||||
|
||||
func Max(x, y float64) float64 {
|
||||
return max(x, y)
|
||||
}
|
||||
|
||||
func Min(x, y float64) float64 {
|
||||
return min(x, y)
|
||||
}
|
||||
|
||||
func Mod(x, y float64) float64 {
|
||||
return js.Global.Call("$mod", x, y).Float()
|
||||
}
|
||||
|
||||
func Modf(f float64) (float64, float64) {
|
||||
if f == posInf || f == negInf {
|
||||
return f, nan
|
||||
}
|
||||
if 1/f == negInf {
|
||||
return f, f
|
||||
}
|
||||
frac := Mod(f, 1)
|
||||
return f - frac, frac
|
||||
}
|
||||
|
||||
func NaN() float64 {
|
||||
return nan
|
||||
}
|
||||
|
||||
func Pow(x, y float64) float64 {
|
||||
if x == 1 || (x == -1 && (y == posInf || y == negInf)) {
|
||||
return 1
|
||||
}
|
||||
return math.Call("pow", x, y).Float()
|
||||
}
|
||||
|
||||
func Remainder(x, y float64) float64 {
|
||||
return remainder(x, y)
|
||||
}
|
||||
|
||||
func Signbit(x float64) bool {
|
||||
return x < 0 || 1/x == negInf
|
||||
}
|
||||
|
||||
func Sin(x float64) float64 {
|
||||
return math.Call("sin", x).Float()
|
||||
}
|
||||
|
||||
func Sinh(x float64) float64 {
|
||||
return math.Call("sinh", x).Float()
|
||||
}
|
||||
|
||||
func Sincos(x float64) (sin, cos float64) {
|
||||
return Sin(x), Cos(x)
|
||||
}
|
||||
|
||||
func Sqrt(x float64) float64 {
|
||||
return math.Call("sqrt", x).Float()
|
||||
}
|
||||
|
||||
func Tan(x float64) float64 {
|
||||
return math.Call("tan", x).Float()
|
||||
}
|
||||
|
||||
func Tanh(x float64) float64 {
|
||||
return math.Call("tanh", x).Float()
|
||||
}
|
||||
|
||||
func Trunc(x float64) float64 {
|
||||
if x == posInf || x == negInf || x != x || 1/x == negInf {
|
||||
return x
|
||||
}
|
||||
return float64(int(x))
|
||||
}
|
||||
|
||||
var buf struct {
|
||||
uint32array [2]uint32
|
||||
float32array [2]float32
|
||||
float64array [1]float64
|
||||
}
|
||||
|
||||
func init() {
|
||||
ab := js.Global.Get("ArrayBuffer").New(8)
|
||||
js.InternalObject(buf).Set("uint32array", js.Global.Get("Uint32Array").New(ab))
|
||||
js.InternalObject(buf).Set("float32array", js.Global.Get("Float32Array").New(ab))
|
||||
js.InternalObject(buf).Set("float64array", js.Global.Get("Float64Array").New(ab))
|
||||
}
|
||||
|
||||
func Float32bits(f float32) uint32 {
|
||||
buf.float32array[0] = f
|
||||
return buf.uint32array[0]
|
||||
}
|
||||
|
||||
func Float32frombits(b uint32) float32 {
|
||||
buf.uint32array[0] = b
|
||||
return buf.float32array[0]
|
||||
}
|
||||
|
||||
func Float64bits(f float64) uint64 {
|
||||
buf.float64array[0] = f
|
||||
return uint64(buf.uint32array[1])<<32 + uint64(buf.uint32array[0])
|
||||
}
|
||||
|
||||
func Float64frombits(b uint64) float64 {
|
||||
buf.uint32array[0] = uint32(b)
|
||||
buf.uint32array[1] = uint32(b >> 32)
|
||||
return buf.float64array[0]
|
||||
}
|
||||
-134
@@ -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
|
||||
}
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/textproto"
|
||||
"strconv"
|
||||
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
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("XMLHttpRequest") != js.Undefined:
|
||||
return &XHRTransport{}
|
||||
default:
|
||||
return noTransport{}
|
||||
}
|
||||
}()
|
||||
|
||||
// noTransport is used when neither Fetch API nor XMLHttpRequest API are available. It always fails.
|
||||
type noTransport struct{}
|
||||
|
||||
func (noTransport) RoundTrip(req *Request) (*Response, error) {
|
||||
return nil, errors.New("net/http: neither of Fetch nor XMLHttpRequest APIs is available")
|
||||
}
|
||||
|
||||
type XHRTransport struct {
|
||||
inflight map[*Request]*js.Object
|
||||
}
|
||||
|
||||
func (t *XHRTransport) RoundTrip(req *Request) (*Response, error) {
|
||||
xhr := js.Global.Get("XMLHttpRequest").New()
|
||||
|
||||
if t.inflight == nil {
|
||||
t.inflight = map[*Request]*js.Object{}
|
||||
}
|
||||
t.inflight[req] = xhr
|
||||
defer delete(t.inflight, req)
|
||||
|
||||
respCh := make(chan *Response)
|
||||
errCh := make(chan error)
|
||||
|
||||
xhr.Set("onload", func() {
|
||||
header, _ := textproto.NewReader(bufio.NewReader(bytes.NewReader([]byte(xhr.Call("getAllResponseHeaders").String() + "\n")))).ReadMIMEHeader()
|
||||
body := js.Global.Get("Uint8Array").New(xhr.Get("response")).Interface().([]byte)
|
||||
|
||||
contentLength := int64(-1)
|
||||
switch req.Method {
|
||||
case "HEAD":
|
||||
if l, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil {
|
||||
contentLength = l
|
||||
}
|
||||
default:
|
||||
contentLength = int64(len(body))
|
||||
}
|
||||
|
||||
respCh <- &Response{
|
||||
Status: xhr.Get("status").String() + " " + xhr.Get("statusText").String(),
|
||||
StatusCode: xhr.Get("status").Int(),
|
||||
Header: Header(header),
|
||||
ContentLength: contentLength,
|
||||
Body: ioutil.NopCloser(bytes.NewReader(body)),
|
||||
Request: req,
|
||||
}
|
||||
})
|
||||
|
||||
xhr.Set("onerror", func(e *js.Object) {
|
||||
errCh <- errors.New("net/http: XMLHttpRequest failed")
|
||||
})
|
||||
|
||||
xhr.Set("onabort", func(e *js.Object) {
|
||||
errCh <- errors.New("net/http: request canceled")
|
||||
})
|
||||
|
||||
xhr.Call("open", req.Method, req.URL.String())
|
||||
xhr.Set("responseType", "arraybuffer") // has to be after "open" until https://bugzilla.mozilla.org/show_bug.cgi?id=1110761 is resolved
|
||||
for key, values := range req.Header {
|
||||
for _, value := range values {
|
||||
xhr.Call("setRequestHeader", key, value)
|
||||
}
|
||||
}
|
||||
if req.Body == nil {
|
||||
xhr.Call("send")
|
||||
} else {
|
||||
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()
|
||||
xhr.Call("send", body)
|
||||
}
|
||||
|
||||
select {
|
||||
case resp := <-respCh:
|
||||
return resp, nil
|
||||
case err := <-errCh:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (t *XHRTransport) CancelRequest(req *Request) {
|
||||
if xhr, ok := t.inflight[req]; ok {
|
||||
xhr.Call("abort")
|
||||
}
|
||||
}
|
||||
-65
@@ -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
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
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 len(Args) == 0 {
|
||||
Args = []string{"?"}
|
||||
}
|
||||
}
|
||||
|
||||
func runtime_beforeExit() {}
|
||||
|
||||
func executable() (string, error) {
|
||||
return "", errors.New("Executable not implemented for GOARCH=js")
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package signal
|
||||
|
||||
// Package signal is not implemented for GOARCH=js.
|
||||
|
||||
func signal_disable(uint32) {}
|
||||
func signal_enable(uint32) {}
|
||||
func signal_ignore(uint32) {}
|
||||
func signal_recv() uint32 { return 0 }
|
||||
|
||||
func loop() {}
|
||||
-1456
File diff suppressed because it is too large
Load Diff
-29
@@ -1,29 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package reflect
|
||||
|
||||
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.
|
||||
switch v.Len() {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
tmp := New(v.Type().Elem()).Elem()
|
||||
return func(i, j int) {
|
||||
v1 := v.Index(i)
|
||||
v2 := v.Index(j)
|
||||
tmp.Set(v1)
|
||||
v1.Set(v2)
|
||||
v2.Set(tmp)
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package debug
|
||||
|
||||
func setGCPercent(int32) int32 {
|
||||
// Not implemented. Return initial setting.
|
||||
return 100
|
||||
}
|
||||
|
||||
func setMaxStack(bytes int) int {
|
||||
// Not implemented. Return initial setting.
|
||||
// The initial setting is 1 GB on 64-bit systems, 250 MB on 32-bit systems.
|
||||
return 250000000
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package pprof
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Profile struct {
|
||||
name string
|
||||
mu sync.Mutex
|
||||
m map[interface{}][]uintptr
|
||||
count func() int
|
||||
write func(io.Writer, int) error
|
||||
}
|
||||
|
||||
func (p *Profile) WriteTo(w io.Writer, debug int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Profile) Count() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *Profile) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Profile) Add(value interface{}, skip int) {
|
||||
}
|
||||
|
||||
func (p *Profile) Remove(value interface{}) {
|
||||
}
|
||||
|
||||
func StartCPUProfile(w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopCPUProfile() {
|
||||
}
|
||||
|
||||
func WriteHeapProfile(w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Lookup(name string) *Profile {
|
||||
return nil
|
||||
}
|
||||
-220
@@ -1,220 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"runtime/internal/sys"
|
||||
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
const GOOS = sys.GOOS
|
||||
const GOARCH = "js"
|
||||
const Compiler = "gopherjs"
|
||||
|
||||
// fake for error.go
|
||||
type eface struct {
|
||||
_type *_type
|
||||
}
|
||||
type _type struct {
|
||||
}
|
||||
|
||||
func (t *_type) string() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
jsPkg := js.Global.Get("$packages").Get("github.com/gopherjs/gopherjs/js")
|
||||
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))
|
||||
// avoid dead code elimination
|
||||
var e error
|
||||
e = &TypeAssertionError{}
|
||||
_ = e
|
||||
}
|
||||
|
||||
func GOROOT() string {
|
||||
process := js.Global.Get("process")
|
||||
if process == js.Undefined {
|
||||
return "/"
|
||||
}
|
||||
goroot := process.Get("env").Get("GOROOT")
|
||||
if goroot != js.Undefined {
|
||||
return goroot.String()
|
||||
}
|
||||
return sys.DefaultGoroot
|
||||
}
|
||||
|
||||
func Breakpoint() {
|
||||
js.Debugger()
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
func Callers(skip int, pc []uintptr) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// CallersFrames is not implemented for GOARCH=js.
|
||||
// TODO: Implement if possible.
|
||||
func CallersFrames(callers []uintptr) *Frames { return &Frames{} }
|
||||
|
||||
type Frames struct{}
|
||||
|
||||
func (ci *Frames) Next() (frame Frame, more bool) { return }
|
||||
|
||||
type Frame struct {
|
||||
PC uintptr
|
||||
Func *Func
|
||||
Function string
|
||||
File string
|
||||
Line int
|
||||
Entry uintptr
|
||||
}
|
||||
|
||||
func GC() {
|
||||
}
|
||||
|
||||
func Goexit() {
|
||||
js.Global.Get("$curGoroutine").Set("exit", true)
|
||||
js.Global.Call("$throw", nil)
|
||||
}
|
||||
|
||||
func GOMAXPROCS(n int) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func Gosched() {
|
||||
c := make(chan struct{})
|
||||
js.Global.Call("$setTimeout", js.InternalObject(func() { close(c) }), 0)
|
||||
<-c
|
||||
}
|
||||
|
||||
func NumCPU() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func NumGoroutine() int {
|
||||
return js.Global.Get("$totalGoroutines").Int()
|
||||
}
|
||||
|
||||
type MemStats struct {
|
||||
// General statistics.
|
||||
Alloc uint64 // bytes allocated and still in use
|
||||
TotalAlloc uint64 // bytes allocated (even if freed)
|
||||
Sys uint64 // bytes obtained from system (sum of XxxSys below)
|
||||
Lookups uint64 // number of pointer lookups
|
||||
Mallocs uint64 // number of mallocs
|
||||
Frees uint64 // number of frees
|
||||
|
||||
// Main allocation heap statistics.
|
||||
HeapAlloc uint64 // bytes allocated and still in use
|
||||
HeapSys uint64 // bytes obtained from system
|
||||
HeapIdle uint64 // bytes in idle spans
|
||||
HeapInuse uint64 // bytes in non-idle span
|
||||
HeapReleased uint64 // bytes released to the OS
|
||||
HeapObjects uint64 // total number of allocated objects
|
||||
|
||||
// Low-level fixed-size structure allocator statistics.
|
||||
// Inuse is bytes used now.
|
||||
// Sys is bytes obtained from system.
|
||||
StackInuse uint64 // bytes used by stack allocator
|
||||
StackSys uint64
|
||||
MSpanInuse uint64 // mspan structures
|
||||
MSpanSys uint64
|
||||
MCacheInuse uint64 // mcache structures
|
||||
MCacheSys uint64
|
||||
BuckHashSys uint64 // profiling bucket hash table
|
||||
GCSys uint64 // GC metadata
|
||||
OtherSys uint64 // other system allocations
|
||||
|
||||
// Garbage collector statistics.
|
||||
NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount
|
||||
LastGC uint64 // end time of last collection (nanoseconds since 1970)
|
||||
PauseTotalNs uint64
|
||||
PauseNs [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
|
||||
PauseEnd [256]uint64 // circular buffer of recent GC pause end times
|
||||
NumGC uint32
|
||||
GCCPUFraction float64 // fraction of CPU time used by GC
|
||||
EnableGC bool
|
||||
DebugGC bool
|
||||
|
||||
// Per-size allocation statistics.
|
||||
// 61 is NumSizeClasses in the C code.
|
||||
BySize [61]struct {
|
||||
Size uint32
|
||||
Mallocs uint64
|
||||
Frees uint64
|
||||
}
|
||||
}
|
||||
|
||||
func ReadMemStats(m *MemStats) {
|
||||
}
|
||||
|
||||
func SetFinalizer(x, f interface{}) {
|
||||
}
|
||||
|
||||
type Func struct {
|
||||
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 FuncForPC(pc uintptr) *Func {
|
||||
return nil
|
||||
}
|
||||
|
||||
var MemProfileRate int = 512 * 1024
|
||||
|
||||
func SetBlockProfileRate(rate int) {
|
||||
}
|
||||
|
||||
func SetMutexProfileFraction(rate int) int {
|
||||
// TODO: Investigate this. If it's possible to implement, consider doing so, otherwise remove this comment.
|
||||
return 0
|
||||
}
|
||||
|
||||
func Stack(buf []byte, all bool) int {
|
||||
s := js.Global.Get("Error").New().Get("stack")
|
||||
if s == js.Undefined {
|
||||
return 0
|
||||
}
|
||||
return copy(buf, s.Call("substr", s.Call("indexOf", "\n").Int()+1).String())
|
||||
}
|
||||
|
||||
func LockOSThread() {}
|
||||
|
||||
func UnlockOSThread() {}
|
||||
|
||||
func Version() string {
|
||||
return sys.TheVersion
|
||||
}
|
||||
|
||||
func StartTrace() error { return nil }
|
||||
func StopTrace() {}
|
||||
func ReadTrace() []byte
|
||||
|
||||
// We fake a cgo environment to catch errors. Therefor we have to implement this and always return 0
|
||||
func NumCgoCall() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func efaceOf(ep *interface{}) *eface {
|
||||
panic("efaceOf: not supported")
|
||||
}
|
||||
|
||||
func KeepAlive(interface{}) {}
|
||||
|
||||
func throw(s string) {
|
||||
panic(errorString(s))
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package strings
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
func IndexByte(s string, c byte) int {
|
||||
return js.InternalObject(s).Call("indexOf", js.Global.Get("String").Call("fromCharCode", c)).Int()
|
||||
}
|
||||
|
||||
func Index(s, sep string) int {
|
||||
return js.InternalObject(s).Call("indexOf", js.InternalObject(sep)).Int()
|
||||
}
|
||||
|
||||
func LastIndex(s, sep string) int {
|
||||
return js.InternalObject(s).Call("lastIndexOf", js.InternalObject(sep)).Int()
|
||||
}
|
||||
|
||||
func Count(s, sep string) int {
|
||||
n := 0
|
||||
// special cases
|
||||
switch {
|
||||
case len(sep) == 0:
|
||||
return utf8.RuneCountInString(s) + 1
|
||||
case len(sep) > len(s):
|
||||
return 0
|
||||
case len(sep) == len(s):
|
||||
if sep == s {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
for {
|
||||
pos := Index(s, sep)
|
||||
if pos == -1 {
|
||||
break
|
||||
}
|
||||
n++
|
||||
s = s[pos+len(sep):]
|
||||
}
|
||||
return n
|
||||
}
|
||||
-185
@@ -1,185 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
func SwapInt32(addr *int32, new int32) int32 {
|
||||
old := *addr
|
||||
*addr = new
|
||||
return old
|
||||
}
|
||||
|
||||
func SwapInt64(addr *int64, new int64) int64 {
|
||||
old := *addr
|
||||
*addr = new
|
||||
return old
|
||||
}
|
||||
|
||||
func SwapUint32(addr *uint32, new uint32) uint32 {
|
||||
old := *addr
|
||||
*addr = new
|
||||
return old
|
||||
}
|
||||
|
||||
func SwapUint64(addr *uint64, new uint64) uint64 {
|
||||
old := *addr
|
||||
*addr = new
|
||||
return old
|
||||
}
|
||||
|
||||
func SwapUintptr(addr *uintptr, new uintptr) uintptr {
|
||||
old := *addr
|
||||
*addr = new
|
||||
return old
|
||||
}
|
||||
|
||||
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer {
|
||||
old := *addr
|
||||
*addr = new
|
||||
return old
|
||||
}
|
||||
|
||||
func CompareAndSwapInt32(addr *int32, old, new int32) bool {
|
||||
if *addr == old {
|
||||
*addr = new
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CompareAndSwapInt64(addr *int64, old, new int64) bool {
|
||||
if *addr == old {
|
||||
*addr = new
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CompareAndSwapUint32(addr *uint32, old, new uint32) bool {
|
||||
if *addr == old {
|
||||
*addr = new
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CompareAndSwapUint64(addr *uint64, old, new uint64) bool {
|
||||
if *addr == old {
|
||||
*addr = new
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) bool {
|
||||
if *addr == old {
|
||||
*addr = new
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) bool {
|
||||
if *addr == old {
|
||||
*addr = new
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AddInt32(addr *int32, delta int32) int32 {
|
||||
new := *addr + delta
|
||||
*addr = new
|
||||
return new
|
||||
}
|
||||
|
||||
func AddUint32(addr *uint32, delta uint32) uint32 {
|
||||
new := *addr + delta
|
||||
*addr = new
|
||||
return new
|
||||
}
|
||||
|
||||
func AddInt64(addr *int64, delta int64) int64 {
|
||||
new := *addr + delta
|
||||
*addr = new
|
||||
return new
|
||||
}
|
||||
|
||||
func AddUint64(addr *uint64, delta uint64) uint64 {
|
||||
new := *addr + delta
|
||||
*addr = new
|
||||
return new
|
||||
}
|
||||
|
||||
func AddUintptr(addr *uintptr, delta uintptr) uintptr {
|
||||
new := *addr + delta
|
||||
*addr = new
|
||||
return new
|
||||
}
|
||||
|
||||
func LoadInt32(addr *int32) int32 {
|
||||
return *addr
|
||||
}
|
||||
|
||||
func LoadInt64(addr *int64) int64 {
|
||||
return *addr
|
||||
}
|
||||
|
||||
func LoadUint32(addr *uint32) uint32 {
|
||||
return *addr
|
||||
}
|
||||
|
||||
func LoadUint64(addr *uint64) uint64 {
|
||||
return *addr
|
||||
}
|
||||
|
||||
func LoadUintptr(addr *uintptr) uintptr {
|
||||
return *addr
|
||||
}
|
||||
|
||||
func LoadPointer(addr *unsafe.Pointer) unsafe.Pointer {
|
||||
return *addr
|
||||
}
|
||||
|
||||
func StoreInt32(addr *int32, val int32) {
|
||||
*addr = val
|
||||
}
|
||||
|
||||
func StoreInt64(addr *int64, val int64) {
|
||||
*addr = val
|
||||
}
|
||||
|
||||
func StoreUint32(addr *uint32, val uint32) {
|
||||
*addr = val
|
||||
}
|
||||
|
||||
func StoreUint64(addr *uint64, val uint64) {
|
||||
*addr = val
|
||||
}
|
||||
|
||||
func StoreUintptr(addr *uintptr, val uintptr) {
|
||||
*addr = val
|
||||
}
|
||||
|
||||
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) {
|
||||
*addr = val
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
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 = x
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package sync
|
||||
|
||||
type Cond struct {
|
||||
// fields used by vanilla implementation
|
||||
noCopy noCopy
|
||||
L Locker
|
||||
notify notifyList
|
||||
checker copyChecker
|
||||
|
||||
// fields used by new implementation
|
||||
n int
|
||||
ch chan bool
|
||||
}
|
||||
|
||||
func (c *Cond) Wait() {
|
||||
c.n++
|
||||
if c.ch == nil {
|
||||
c.ch = make(chan bool)
|
||||
}
|
||||
c.L.Unlock()
|
||||
<-c.ch
|
||||
c.L.Lock()
|
||||
}
|
||||
|
||||
func (c *Cond) Signal() {
|
||||
if c.n == 0 {
|
||||
return
|
||||
}
|
||||
c.n--
|
||||
c.ch <- true
|
||||
}
|
||||
|
||||
func (c *Cond) Broadcast() {
|
||||
n := c.n
|
||||
c.n = 0
|
||||
for i := 0; i < n; i++ {
|
||||
c.ch <- true
|
||||
}
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package sync
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type Pool struct {
|
||||
local unsafe.Pointer
|
||||
localSize uintptr
|
||||
|
||||
store []interface{}
|
||||
New func() interface{}
|
||||
}
|
||||
|
||||
func (p *Pool) Get() interface{} {
|
||||
if len(p.store) == 0 {
|
||||
if p.New != nil {
|
||||
return p.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
x := p.store[len(p.store)-1]
|
||||
p.store = p.store[:len(p.store)-1]
|
||||
return x
|
||||
}
|
||||
|
||||
func (p *Pool) Put(x interface{}) {
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
p.store = append(p.store, x)
|
||||
}
|
||||
|
||||
func runtime_registerPoolCleanup(cleanup func()) {
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package sync
|
||||
|
||||
import "github.com/gopherjs/gopherjs/js"
|
||||
|
||||
var semWaiters = make(map[*uint32][]chan bool)
|
||||
|
||||
// semAwoken tracks the number of waiters awoken by runtime_Semrelease (`ch <- true`)
|
||||
// that have not yet acquired the semaphore (`<-ch` in runtime_SemacquireMutex).
|
||||
//
|
||||
// This prevents a new call to runtime_SemacquireMutex to wrongly acquire the semaphore
|
||||
// in between (because runtime_Semrelease has already incremented the semaphore while
|
||||
// all the pending calls to runtime_SemacquireMutex have not yet received from the channel
|
||||
// and thus decremented the semaphore).
|
||||
//
|
||||
// See https://github.com/gopherjs/gopherjs/issues/736.
|
||||
var semAwoken = make(map[*uint32]uint32)
|
||||
|
||||
func runtime_Semacquire(s *uint32) {
|
||||
runtime_SemacquireMutex(s, false)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (*s - semAwoken[s]) == 0 {
|
||||
ch := make(chan bool)
|
||||
if lifo {
|
||||
semWaiters[s] = append([]chan bool{ch}, semWaiters[s]...)
|
||||
} else {
|
||||
semWaiters[s] = append(semWaiters[s], ch)
|
||||
}
|
||||
<-ch
|
||||
semAwoken[s] -= 1
|
||||
if semAwoken[s] == 0 {
|
||||
delete(semAwoken, s)
|
||||
}
|
||||
}
|
||||
*s--
|
||||
}
|
||||
|
||||
func runtime_Semrelease(s *uint32, handoff bool) {
|
||||
// TODO: Use handoff if needed/possible.
|
||||
*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)
|
||||
}
|
||||
|
||||
semAwoken[s] += 1
|
||||
|
||||
ch <- true
|
||||
}
|
||||
|
||||
func runtime_notifyListCheck(size uintptr) {}
|
||||
|
||||
func runtime_canSpin(i int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Copy of time.runtimeNano.
|
||||
func runtime_nanotime() int64 {
|
||||
const millisecond = 1000000
|
||||
return js.Global.Get("Date").New().Call("getTime").Int64() * millisecond
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package sync
|
||||
|
||||
type WaitGroup struct {
|
||||
counter int
|
||||
ch chan struct{}
|
||||
|
||||
state1 [12]byte
|
||||
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
|
||||
}
|
||||
}
|
||||
-60
@@ -1,60 +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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
// +build js,!windows
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func setenv_c(k, v string) {
|
||||
process := js.Global.Get("process")
|
||||
if process != js.Undefined {
|
||||
process.Get("env").Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
var syscallModule *js.Object
|
||||
var alreadyTriedToLoad = false
|
||||
var minusOne = -1
|
||||
|
||||
func syscall(name string) *js.Object {
|
||||
defer func() {
|
||||
recover()
|
||||
// return nil if recovered
|
||||
}()
|
||||
if syscallModule == nil {
|
||||
if alreadyTriedToLoad {
|
||||
return nil
|
||||
}
|
||||
alreadyTriedToLoad = true
|
||||
require := js.Global.Get("require")
|
||||
if require == js.Undefined {
|
||||
panic("")
|
||||
}
|
||||
syscallModule = require.Invoke("syscall")
|
||||
}
|
||||
return syscallModule.Get(name)
|
||||
}
|
||||
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
|
||||
if f := syscall("Syscall"); f != nil {
|
||||
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 == SYS_EXIT {
|
||||
runtime.Goexit()
|
||||
}
|
||||
printWarning()
|
||||
return uintptr(minusOne), 0, EACCES
|
||||
}
|
||||
|
||||
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
|
||||
if f := syscall("Syscall6"); f != nil {
|
||||
r := f.Invoke(trap, a1, a2, a3, a4, a5, a6)
|
||||
return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
|
||||
}
|
||||
if trap != 202 { // kern.osrelease on OS X, happens in init of "os" package
|
||||
printWarning()
|
||||
}
|
||||
return uintptr(minusOne), 0, EACCES
|
||||
}
|
||||
|
||||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
|
||||
if f := syscall("Syscall"); f != nil {
|
||||
r := f.Invoke(trap, a1, a2, a3)
|
||||
return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
|
||||
}
|
||||
printWarning()
|
||||
return uintptr(minusOne), 0, EACCES
|
||||
}
|
||||
|
||||
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
|
||||
if f := syscall("Syscall6"); f != nil {
|
||||
r := f.Invoke(trap, a1, a2, a3, a4, a5, a6)
|
||||
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
|
||||
}
|
||||
Generated
Vendored
-100
@@ -1,100 +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 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 ""
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func runExample(eg InternalExample) (ok bool) {
|
||||
if *chatty {
|
||||
fmt.Printf("=== RUN %s\n", eg.Name)
|
||||
}
|
||||
|
||||
// Capture stdout.
|
||||
stdout := os.Stdout
|
||||
w, err := tempFile("." + eg.Name + ".stdout.")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Stdout = w
|
||||
|
||||
start := time.Now()
|
||||
ok = true
|
||||
|
||||
// Clean up in a deferred call so we can recover if the example panics.
|
||||
defer func() {
|
||||
dstr := fmtDuration(time.Now().Sub(start))
|
||||
|
||||
// Close file, restore stdout, get output.
|
||||
w.Close()
|
||||
os.Stdout = stdout
|
||||
out, readFileErr := readFile(w.Name())
|
||||
_ = os.Remove(w.Name())
|
||||
if readFileErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "testing: reading stdout file: %v\n", readFileErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var fail string
|
||||
err := recover()
|
||||
got := strings.TrimSpace(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)
|
||||
}
|
||||
} else {
|
||||
if got != want && err == nil {
|
||||
fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", got, want)
|
||||
}
|
||||
}
|
||||
if fail != "" || err != nil {
|
||||
fmt.Printf("--- FAIL: %s (%s)\n%s", eg.Name, dstr, fail)
|
||||
ok = false
|
||||
} else if *chatty {
|
||||
fmt.Printf("--- PASS: %s (%s)\n", eg.Name, dstr)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Run example.
|
||||
eg.F()
|
||||
return
|
||||
}
|
||||
-66
@@ -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
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package testing
|
||||
|
||||
func callerName(skip int) string {
|
||||
// TODO: Implement if possible.
|
||||
return "<unknown>"
|
||||
}
|
||||
Generated
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package template
|
||||
|
||||
const maxExecDepth = 3000
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package time
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
// Make sure time.Unix func and time.Time struct it returns are always included with this package (despite DCE),
|
||||
// because they're needed for internalization/externalization of time.Time/Date. See issue https://github.com/gopherjs/gopherjs/issues/279.
|
||||
func init() {
|
||||
// avoid dead code elimination
|
||||
var _ Time = Unix(0, 0)
|
||||
}
|
||||
|
||||
type runtimeTimer struct {
|
||||
i int32
|
||||
when int64
|
||||
period int64
|
||||
f func(interface{}, uintptr)
|
||||
arg interface{}
|
||||
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
|
||||
}
|
||||
|
||||
func Sleep(d Duration) {
|
||||
c := make(chan struct{})
|
||||
js.Global.Call("$setTimeout", js.InternalObject(func() { close(c) }), int(d/Millisecond))
|
||||
<-c
|
||||
}
|
||||
|
||||
func startTimer(t *runtimeTimer) {
|
||||
t.active = true
|
||||
diff := (t.when - runtimeNano()) / int64(Millisecond)
|
||||
if diff > 1<<31-1 { // math.MaxInt32
|
||||
return
|
||||
}
|
||||
if diff < 0 {
|
||||
diff = 0
|
||||
}
|
||||
t.timeout = js.Global.Call("$setTimeout", js.InternalObject(func() {
|
||||
t.active = false
|
||||
if t.period != 0 {
|
||||
t.when += t.period
|
||||
startTimer(t)
|
||||
}
|
||||
go t.f(t.arg, 0)
|
||||
}), diff+1)
|
||||
}
|
||||
|
||||
func stopTimer(t *runtimeTimer) bool {
|
||||
js.Global.Call("clearTimeout", t.timeout)
|
||||
wasActive := t.active
|
||||
t.active = false
|
||||
return wasActive
|
||||
}
|
||||
|
||||
func loadLocation(name string) (*Location, error) {
|
||||
return loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name)
|
||||
}
|
||||
|
||||
func forceZipFileForTesting(zipOnly bool) {
|
||||
}
|
||||
|
||||
func initTestingZone() {
|
||||
z, err := loadLocation("America/Los_Angeles")
|
||||
if err != nil {
|
||||
panic("cannot load America/Los_Angeles for testing: " + err.Error())
|
||||
}
|
||||
z.name = "Local"
|
||||
localLoc = *z
|
||||
}
|
||||
|
||||
// indexByte is copied from strings package to avoid importing it (since the real time package doesn't).
|
||||
func indexByte(s string, c byte) int {
|
||||
return js.InternalObject(s).Call("indexOf", js.Global.Get("String").Call("fromCharCode", c)).Int()
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package unicode
|
||||
|
||||
func to(_case int, r rune, caseRange []CaseRange) rune {
|
||||
if _case < 0 || MaxCase <= _case {
|
||||
return ReplacementChar
|
||||
}
|
||||
lo := 0
|
||||
hi := len(caseRange)
|
||||
for lo < hi {
|
||||
m := lo + (hi-lo)/2
|
||||
cr := &caseRange[m] // performance critical for GopherJS: get address here instead of copying the CaseRange
|
||||
if rune(cr.Lo) <= r && r <= rune(cr.Hi) {
|
||||
delta := rune(cr.Delta[_case])
|
||||
if delta > MaxRune {
|
||||
return rune(cr.Lo) + ((r-rune(cr.Lo))&^1 | rune(_case&1))
|
||||
}
|
||||
return r + delta
|
||||
}
|
||||
if r < rune(cr.Lo) {
|
||||
hi = m
|
||||
} else {
|
||||
lo = m + 1
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
-806
@@ -1,806 +0,0 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gopherjs/gopherjs/compiler/analysis"
|
||||
"github.com/neelance/astrewrite"
|
||||
"golang.org/x/tools/go/gcimporter15"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
type pkgContext struct {
|
||||
*analysis.Info
|
||||
additionalSelections map[*ast.SelectorExpr]selection
|
||||
|
||||
typeNames []*types.TypeName
|
||||
pkgVars map[string]string
|
||||
objectNames map[types.Object]string
|
||||
varPtrNames map[*types.Var]string
|
||||
anonTypes []*types.TypeName
|
||||
anonTypeMap typeutil.Map
|
||||
escapingVars map[*types.Var]bool
|
||||
indentation int
|
||||
dependencies map[types.Object]bool
|
||||
minify bool
|
||||
fileSet *token.FileSet
|
||||
errList ErrorList
|
||||
}
|
||||
|
||||
func (p *pkgContext) SelectionOf(e *ast.SelectorExpr) (selection, bool) {
|
||||
if sel, ok := p.Selections[e]; ok {
|
||||
return sel, true
|
||||
}
|
||||
if sel, ok := p.additionalSelections[e]; ok {
|
||||
return sel, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
type selection interface {
|
||||
Kind() types.SelectionKind
|
||||
Recv() types.Type
|
||||
Index() []int
|
||||
Obj() types.Object
|
||||
Type() types.Type
|
||||
}
|
||||
|
||||
type fakeSelection struct {
|
||||
kind types.SelectionKind
|
||||
recv types.Type
|
||||
index []int
|
||||
obj types.Object
|
||||
typ types.Type
|
||||
}
|
||||
|
||||
func (sel *fakeSelection) Kind() types.SelectionKind { return sel.kind }
|
||||
func (sel *fakeSelection) Recv() types.Type { return sel.recv }
|
||||
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 }
|
||||
|
||||
type funcContext struct {
|
||||
*analysis.FuncInfo
|
||||
p *pkgContext
|
||||
parent *funcContext
|
||||
sig *types.Signature
|
||||
allVars map[string]int
|
||||
localVars []string
|
||||
resultNames []ast.Expr
|
||||
flowDatas map[*types.Label]*flowData
|
||||
caseCounter int
|
||||
labelCases map[*types.Label]int
|
||||
output []byte
|
||||
delayedOutput []byte
|
||||
posAvailable bool
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
type flowData struct {
|
||||
postStmt func()
|
||||
beginCase int
|
||||
endCase int
|
||||
}
|
||||
|
||||
type ImportContext struct {
|
||||
Packages map[string]*types.Package
|
||||
Import func(string) (*Archive, error)
|
||||
}
|
||||
|
||||
// packageImporter implements go/types.Importer interface.
|
||||
type packageImporter struct {
|
||||
importContext *ImportContext
|
||||
importError *error // A pointer to importError in Compile.
|
||||
}
|
||||
|
||||
func (pi packageImporter) Import(path string) (*types.Package, error) {
|
||||
if path == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
|
||||
a, err := pi.importContext.Import(path)
|
||||
if err != nil {
|
||||
if *pi.importError == nil {
|
||||
// If import failed, show first error of import only (https://github.com/gopherjs/gopherjs/issues/119).
|
||||
*pi.importError = err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pi.importContext.Packages[a.ImportPath], nil
|
||||
}
|
||||
|
||||
func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, importContext *ImportContext, minify bool) (*Archive, error) {
|
||||
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),
|
||||
}
|
||||
|
||||
var importError error
|
||||
var errList ErrorList
|
||||
var previousErr error
|
||||
config := &types.Config{
|
||||
Importer: packageImporter{
|
||||
importContext: importContext,
|
||||
importError: &importError,
|
||||
},
|
||||
Sizes: sizes32,
|
||||
Error: func(err error) {
|
||||
if previousErr != nil && previousErr.Error() == err.Error() {
|
||||
return
|
||||
}
|
||||
errList = append(errList, err)
|
||||
previousErr = err
|
||||
},
|
||||
}
|
||||
typesPkg, err := config.Check(importPath, fileSet, files, typesInfo)
|
||||
if importError != nil {
|
||||
return nil, importError
|
||||
}
|
||||
if errList != nil {
|
||||
if len(errList) > 10 {
|
||||
pos := token.NoPos
|
||||
if last, ok := errList[9].(types.Error); ok {
|
||||
pos = last.Pos
|
||||
}
|
||||
errList = append(errList[:10], types.Error{Fset: fileSet, Pos: pos, Msg: "too many errors"})
|
||||
}
|
||||
return nil, errList
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
importContext.Packages[importPath] = typesPkg
|
||||
|
||||
exportData := gcimporter.BExportData(nil, typesPkg)
|
||||
encodedFileSet := bytes.NewBuffer(nil)
|
||||
if err := fileSet.Write(json.NewEncoder(encodedFileSet).Encode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
simplifiedFiles := make([]*ast.File, len(files))
|
||||
for i, file := range files {
|
||||
simplifiedFiles[i] = astrewrite.Simplify(file, typesInfo, false)
|
||||
}
|
||||
|
||||
isBlocking := func(f *types.Func) bool {
|
||||
archive, err := importContext.Import(f.Pkg().Path())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fullName := f.FullName()
|
||||
for _, d := range archive.Declarations {
|
||||
if string(d.FullName) == fullName {
|
||||
return d.Blocking
|
||||
}
|
||||
}
|
||||
panic(fullName)
|
||||
}
|
||||
pkgInfo := analysis.AnalyzePkg(simplifiedFiles, fileSet, typesInfo, typesPkg, isBlocking)
|
||||
c := &funcContext{
|
||||
FuncInfo: pkgInfo.InitFuncInfo,
|
||||
p: &pkgContext{
|
||||
Info: pkgInfo,
|
||||
additionalSelections: make(map[*ast.SelectorExpr]selection),
|
||||
|
||||
pkgVars: make(map[string]string),
|
||||
objectNames: make(map[types.Object]string),
|
||||
varPtrNames: make(map[*types.Var]string),
|
||||
escapingVars: make(map[*types.Var]bool),
|
||||
indentation: 1,
|
||||
dependencies: make(map[types.Object]bool),
|
||||
minify: minify,
|
||||
fileSet: fileSet,
|
||||
},
|
||||
allVars: make(map[string]int),
|
||||
flowDatas: map[*types.Label]*flowData{nil: {}},
|
||||
caseCounter: 1,
|
||||
labelCases: make(map[*types.Label]int),
|
||||
}
|
||||
for name := range reservedKeywords {
|
||||
c.allVars[name] = 1
|
||||
}
|
||||
|
||||
// imports
|
||||
var importDecls []*Decl
|
||||
var importedPaths []string
|
||||
for _, importedPkg := range typesPkg.Imports() {
|
||||
if importedPkg == types.Unsafe {
|
||||
// Prior to Go 1.9, unsafe import was excluded by Imports() method,
|
||||
// but now we do it here to maintain previous behavior.
|
||||
continue
|
||||
}
|
||||
c.p.pkgVars[importedPkg.Path()] = c.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))
|
||||
call := &ast.CallExpr{Fun: id}
|
||||
c.Blocking[call] = true
|
||||
c.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) }),
|
||||
})
|
||||
}
|
||||
|
||||
var functions []*ast.FuncDecl
|
||||
var vars []*types.Var
|
||||
for _, file := range simplifiedFiles {
|
||||
for _, decl := range file.Decls {
|
||||
switch d := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
sig := c.p.Defs[d.Name].(*types.Func).Type().(*types.Signature)
|
||||
var recvType types.Type
|
||||
if sig.Recv() != nil {
|
||||
recvType = sig.Recv().Type()
|
||||
if ptr, isPtr := recvType.(*types.Pointer); isPtr {
|
||||
recvType = ptr.Elem()
|
||||
}
|
||||
}
|
||||
if sig.Recv() == nil {
|
||||
c.objectName(c.p.Defs[d.Name].(*types.Func)) // register toplevel name
|
||||
}
|
||||
if !isBlank(d.Name) {
|
||||
functions = append(functions, d)
|
||||
}
|
||||
case *ast.GenDecl:
|
||||
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
|
||||
}
|
||||
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)
|
||||
vars = append(vars, o)
|
||||
c.objectName(o) // register toplevel name
|
||||
}
|
||||
}
|
||||
}
|
||||
case token.CONST:
|
||||
// skip, constants are inlined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectDependencies := func(f func()) []string {
|
||||
c.p.dependencies = make(map[types.Object]bool)
|
||||
f()
|
||||
var deps []string
|
||||
for o := range c.p.dependencies {
|
||||
qualifiedName := o.Pkg().Path() + "." + o.Name()
|
||||
if f, ok := o.(*types.Func); ok && f.Type().(*types.Signature).Recv() != nil {
|
||||
deps = append(deps, qualifiedName+"~")
|
||||
continue
|
||||
}
|
||||
deps = append(deps, qualifiedName)
|
||||
}
|
||||
sort.Strings(deps)
|
||||
return deps
|
||||
}
|
||||
|
||||
// variables
|
||||
var varDecls []*Decl
|
||||
varsWithInit := make(map[*types.Var]bool)
|
||||
for _, init := range c.p.InitOrder {
|
||||
for _, o := range init.Lhs {
|
||||
varsWithInit[o] = true
|
||||
}
|
||||
}
|
||||
for _, o := range vars {
|
||||
var d Decl
|
||||
if !o.Exported() {
|
||||
d.Vars = []string{c.objectName(o)}
|
||||
}
|
||||
if c.p.HasPointer[o] && !o.Exported() {
|
||||
d.Vars = append(d.Vars, c.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.DceObjectFilter = o.Name()
|
||||
varDecls = append(varDecls, &d)
|
||||
}
|
||||
for _, init := range c.p.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())
|
||||
varsWithInit[o] = true
|
||||
}
|
||||
var d Decl
|
||||
d.DceDeps = collectDependencies(func() {
|
||||
c.localVars = nil
|
||||
d.InitCode = c.CatchOutput(1, func() {
|
||||
c.translateStmt(&ast.AssignStmt{
|
||||
Lhs: lhs,
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{init.Rhs},
|
||||
}, nil)
|
||||
})
|
||||
d.Vars = append(d.Vars, c.localVars...)
|
||||
})
|
||||
if len(init.Lhs) == 1 {
|
||||
if !analysis.HasSideEffect(init.Rhs, c.p.Info.Info) {
|
||||
d.DceObjectFilter = init.Lhs[0].Name()
|
||||
}
|
||||
}
|
||||
varDecls = append(varDecls, &d)
|
||||
}
|
||||
|
||||
// functions
|
||||
var funcDecls []*Decl
|
||||
var mainFunc *types.Func
|
||||
for _, fun := range functions {
|
||||
o := c.p.Defs[fun.Name].(*types.Func)
|
||||
funcInfo := c.p.FuncDeclInfos[o]
|
||||
d := Decl{
|
||||
FullName: o.FullName(),
|
||||
Blocking: len(funcInfo.Blocking) != 0,
|
||||
}
|
||||
if fun.Recv == nil {
|
||||
d.Vars = []string{c.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
|
||||
call := &ast.CallExpr{Fun: id}
|
||||
if len(c.p.FuncDeclInfos[o].Blocking) != 0 {
|
||||
c.Blocking[call] = true
|
||||
}
|
||||
c.translateStmt(&ast.ExprStmt{X: call}, nil)
|
||||
})
|
||||
d.DceObjectFilter = ""
|
||||
}
|
||||
}
|
||||
if fun.Recv != nil {
|
||||
recvType := o.Type().(*types.Signature).Recv().Type()
|
||||
ptr, isPointer := recvType.(*types.Pointer)
|
||||
namedRecvType, _ := recvType.(*types.Named)
|
||||
if isPointer {
|
||||
namedRecvType = ptr.Elem().(*types.Named)
|
||||
}
|
||||
d.DceObjectFilter = namedRecvType.Obj().Name()
|
||||
if !fun.Name.IsExported() {
|
||||
d.DceMethodFilter = o.Name() + "~"
|
||||
}
|
||||
}
|
||||
|
||||
d.DceDeps = collectDependencies(func() {
|
||||
d.DeclCode = c.translateToplevelFunction(fun, funcInfo)
|
||||
})
|
||||
funcDecls = append(funcDecls, &d)
|
||||
}
|
||||
if typesPkg.Name() == "main" {
|
||||
if mainFunc == nil {
|
||||
return nil, fmt.Errorf("missing main function")
|
||||
}
|
||||
id := c.newIdent("", types.NewSignature(nil, nil, nil, false))
|
||||
c.p.Uses[id] = mainFunc
|
||||
call := &ast.CallExpr{Fun: id}
|
||||
ifStmt := &ast.IfStmt{
|
||||
Cond: c.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])},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{c.newConst(types.Typ[types.Bool], constant.MakeBool(true))},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if len(c.p.FuncDeclInfos[mainFunc].Blocking) != 0 {
|
||||
c.Blocking[call] = true
|
||||
c.Flattened[ifStmt] = true
|
||||
}
|
||||
funcDecls = append(funcDecls, &Decl{
|
||||
InitCode: c.CatchOutput(1, func() {
|
||||
c.translateStmt(ifStmt, nil)
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
// named types
|
||||
var typeDecls []*Decl
|
||||
for _, o := range c.p.typeNames {
|
||||
if o.IsAlias() {
|
||||
continue
|
||||
}
|
||||
typeName := c.objectName(o)
|
||||
d := Decl{
|
||||
Vars: []string{typeName},
|
||||
DceObjectFilter: o.Name(),
|
||||
}
|
||||
d.DceDeps = collectDependencies(func() {
|
||||
d.DeclCode = c.CatchOutput(0, func() {
|
||||
typeName := c.objectName(o)
|
||||
lhs := typeName
|
||||
if isPkgLevel(o) {
|
||||
lhs += " = $pkg." + encodeIdent(o.Name())
|
||||
}
|
||||
size := int64(0)
|
||||
constructor := "null"
|
||||
switch t := o.Type().Underlying().(type) {
|
||||
case *types.Struct:
|
||||
params := make([]string, t.NumFields())
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
params[i] = fieldName(t, i) + "_"
|
||||
}
|
||||
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 += "\t\t\treturn;\n\t\t}\n"
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
constructor += fmt.Sprintf("\t\tthis.%[1]s = %[1]s_;\n", fieldName(t, i))
|
||||
}
|
||||
constructor += "\t}"
|
||||
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)
|
||||
})
|
||||
d.MethodListCode = c.CatchOutput(0, func() {
|
||||
named := o.Type().(*types.Named)
|
||||
if _, ok := named.Underlying().(*types.Interface); ok {
|
||||
return
|
||||
}
|
||||
var methods []string
|
||||
var ptrMethods []string
|
||||
for i := 0; i < named.NumMethods(); i++ {
|
||||
method := named.Method(i)
|
||||
name := method.Name()
|
||||
if reservedKeywords[name] {
|
||||
name += "$"
|
||||
}
|
||||
pkgPath := ""
|
||||
if !method.Exported() {
|
||||
pkgPath = method.Pkg().Path()
|
||||
}
|
||||
t := method.Type().(*types.Signature)
|
||||
entry := fmt.Sprintf(`{prop: "%s", name: "%s", pkg: "%s", typ: $funcType(%s)}`, name, method.Name(), pkgPath, c.initArgs(t))
|
||||
if _, isPtr := t.Recv().Type().(*types.Pointer); isPtr {
|
||||
ptrMethods = append(ptrMethods, entry)
|
||||
continue
|
||||
}
|
||||
methods = append(methods, entry)
|
||||
}
|
||||
if len(methods) > 0 {
|
||||
c.Printf("%s.methods = [%s];", c.typeName(named), strings.Join(methods, ", "))
|
||||
}
|
||||
if len(ptrMethods) > 0 {
|
||||
c.Printf("%s.methods = [%s];", c.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))
|
||||
})
|
||||
}
|
||||
})
|
||||
typeDecls = append(typeDecls, &d)
|
||||
}
|
||||
|
||||
// anonymous types
|
||||
for _, t := range c.p.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())))
|
||||
})
|
||||
typeDecls = append(typeDecls, &d)
|
||||
}
|
||||
|
||||
var allDecls []*Decl
|
||||
for _, d := range append(append(append(importDecls, typeDecls...), varDecls...), funcDecls...) {
|
||||
d.DeclCode = removeWhitespace(d.DeclCode, minify)
|
||||
d.MethodListCode = removeWhitespace(d.MethodListCode, minify)
|
||||
d.TypeInitCode = removeWhitespace(d.TypeInitCode, minify)
|
||||
d.InitCode = removeWhitespace(d.InitCode, minify)
|
||||
allDecls = append(allDecls, d)
|
||||
}
|
||||
|
||||
if len(c.p.errList) != 0 {
|
||||
return nil, c.p.errList
|
||||
}
|
||||
|
||||
return &Archive{
|
||||
ImportPath: importPath,
|
||||
Name: typesPkg.Name(),
|
||||
Imports: importedPaths,
|
||||
ExportData: exportData,
|
||||
Declarations: allDecls,
|
||||
FileSet: encodedFileSet.Bytes(),
|
||||
Minified: minify,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *funcContext) initArgs(ty types.Type) string {
|
||||
switch t := ty.(type) {
|
||||
case *types.Array:
|
||||
return fmt.Sprintf("%s, %d", c.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)
|
||||
case *types.Interface:
|
||||
methods := make([]string, t.NumMethods())
|
||||
for i := range methods {
|
||||
method := t.Method(i)
|
||||
pkgPath := ""
|
||||
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()))
|
||||
}
|
||||
return fmt.Sprintf("[%s]", strings.Join(methods, ", "))
|
||||
case *types.Map:
|
||||
return fmt.Sprintf("%s, %s", c.typeName(t.Key()), c.typeName(t.Elem()))
|
||||
case *types.Pointer:
|
||||
return fmt.Sprintf("%s", c.typeName(t.Elem()))
|
||||
case *types.Slice:
|
||||
return fmt.Sprintf("%s", c.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())
|
||||
}
|
||||
results := make([]string, t.Results().Len())
|
||||
for i := range results {
|
||||
results[i] = c.typeName(t.Results().At(i).Type())
|
||||
}
|
||||
return fmt.Sprintf("[%s], [%s], %t", strings.Join(params, ", "), strings.Join(results, ", "), t.Variadic())
|
||||
case *types.Struct:
|
||||
pkgPath := ""
|
||||
fields := make([]string, t.NumFields())
|
||||
for i := range fields {
|
||||
field := t.Field(i)
|
||||
if !field.Exported() {
|
||||
pkgPath = field.Pkg().Path()
|
||||
}
|
||||
fields[i] = fmt.Sprintf(`{prop: "%s", name: "%s", anonymous: %t, exported: %t, typ: %s, tag: %s}`, fieldName(t, i), field.Name(), field.Anonymous(), field.Exported(), c.typeName(field.Type()), encodeString(t.Tag(i)))
|
||||
}
|
||||
return fmt.Sprintf(`"%s", [%s]`, pkgPath, strings.Join(fields, ", "))
|
||||
default:
|
||||
panic("invalid type")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *funcContext) translateToplevelFunction(fun *ast.FuncDecl, info *analysis.FuncInfo) []byte {
|
||||
o := c.p.Defs[fun.Name].(*types.Func)
|
||||
sig := o.Type().(*types.Signature)
|
||||
var recv *ast.Ident
|
||||
if fun.Recv != nil && fun.Recv.List[0].Names != nil {
|
||||
recv = fun.Recv.List[0].Names[0]
|
||||
}
|
||||
|
||||
var joinedParams string
|
||||
primaryFunction := func(funcRef string) []byte {
|
||||
if fun.Body == nil {
|
||||
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)
|
||||
joinedParams = strings.Join(params, ", ")
|
||||
return []byte(fmt.Sprintf("\t%s = %s;\n", funcRef, fun))
|
||||
}
|
||||
|
||||
code := bytes.NewBuffer(nil)
|
||||
|
||||
if fun.Recv == nil {
|
||||
funcRef := c.objectName(o)
|
||||
code.Write(primaryFunction(funcRef))
|
||||
if fun.Name.IsExported() {
|
||||
fmt.Fprintf(code, "\t$pkg.%s = %s;\n", encodeIdent(fun.Name.Name), funcRef)
|
||||
}
|
||||
return code.Bytes()
|
||||
}
|
||||
|
||||
recvType := sig.Recv().Type()
|
||||
ptr, isPointer := recvType.(*types.Pointer)
|
||||
namedRecvType, _ := recvType.(*types.Named)
|
||||
if isPointer {
|
||||
namedRecvType = ptr.Elem().(*types.Named)
|
||||
}
|
||||
typeName := c.objectName(namedRecvType.Obj())
|
||||
funName := fun.Name.Name
|
||||
if reservedKeywords[funName] {
|
||||
funName += "$"
|
||||
}
|
||||
|
||||
if _, isStruct := namedRecvType.Underlying().(*types.Struct); isStruct {
|
||||
code.Write(primaryFunction(typeName + ".ptr.prototype." + funName))
|
||||
fmt.Fprintf(code, "\t%s.prototype.%s = function(%s) { return this.$val.%s(%s); };\n", typeName, funName, joinedParams, funName, joinedParams)
|
||||
return code.Bytes()
|
||||
}
|
||||
|
||||
if isPointer {
|
||||
if _, isArray := ptr.Elem().Underlying().(*types.Array); isArray {
|
||||
code.Write(primaryFunction(typeName + ".prototype." + funName))
|
||||
fmt.Fprintf(code, "\t$ptrType(%s).prototype.%s = function(%s) { return (new %s(this.$get())).%s(%s); };\n", typeName, funName, joinedParams, typeName, funName, joinedParams)
|
||||
return code.Bytes()
|
||||
}
|
||||
return primaryFunction(fmt.Sprintf("$ptrType(%s).prototype.%s", typeName, funName))
|
||||
}
|
||||
|
||||
value := "this.$get()"
|
||||
if isWrapped(recvType) {
|
||||
value = fmt.Sprintf("new %s(%s)", typeName, value)
|
||||
}
|
||||
code.Write(primaryFunction(typeName + ".prototype." + funName))
|
||||
fmt.Fprintf(code, "\t$ptrType(%s).prototype.%s = function(%s) { return %s.%s(%s); };\n", typeName, funName, joinedParams, value, funName, joinedParams)
|
||||
return code.Bytes()
|
||||
}
|
||||
|
||||
func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, outerContext *funcContext, sig *types.Signature, info *analysis.FuncInfo, funcRef string) ([]string, string) {
|
||||
if info == nil {
|
||||
panic("nil info")
|
||||
}
|
||||
|
||||
c := &funcContext{
|
||||
FuncInfo: info,
|
||||
p: outerContext.p,
|
||||
parent: outerContext,
|
||||
sig: sig,
|
||||
allVars: make(map[string]int, len(outerContext.allVars)),
|
||||
localVars: []string{},
|
||||
flowDatas: map[*types.Label]*flowData{nil: {}},
|
||||
caseCounter: 1,
|
||||
labelCases: make(map[*types.Label]int),
|
||||
}
|
||||
for k, v := range outerContext.allVars {
|
||||
c.allVars[k] = v
|
||||
}
|
||||
prevEV := c.p.escapingVars
|
||||
|
||||
var params []string
|
||||
for _, param := range typ.Params.List {
|
||||
if len(param.Names) == 0 {
|
||||
params = append(params, c.newVariable("param"))
|
||||
continue
|
||||
}
|
||||
for _, ident := range param.Names {
|
||||
if isBlank(ident) {
|
||||
params = append(params, c.newVariable("param"))
|
||||
continue
|
||||
}
|
||||
params = append(params, c.objectName(c.p.Defs[ident]))
|
||||
}
|
||||
}
|
||||
|
||||
bodyOutput := string(c.CatchOutput(1, func() {
|
||||
if len(c.Blocking) != 0 {
|
||||
c.p.Scopes[body] = c.p.Scopes[typ]
|
||||
c.handleEscapingVars(body)
|
||||
}
|
||||
|
||||
if c.sig != nil && c.sig.Results().Len() != 0 && c.sig.Results().At(0).Name() != "" {
|
||||
c.resultNames = make([]ast.Expr, c.sig.Results().Len())
|
||||
for i := 0; i < c.sig.Results().Len(); i++ {
|
||||
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.resultNames[i] = c.setType(id, result.Type())
|
||||
}
|
||||
}
|
||||
|
||||
if recv != nil && !isBlank(recv) {
|
||||
this := "this"
|
||||
if isWrapped(c.p.TypeOf(recv)) {
|
||||
this = "this.$val"
|
||||
}
|
||||
c.Printf("%s = %s;", c.translateExpr(recv), this)
|
||||
}
|
||||
|
||||
c.translateStmtList(body.List)
|
||||
if len(c.Flattened) != 0 && !endsWithReturn(body.List) {
|
||||
c.translateStmt(&ast.ReturnStmt{}, nil)
|
||||
}
|
||||
}))
|
||||
|
||||
sort.Strings(c.localVars)
|
||||
|
||||
var prefix, suffix, functionName string
|
||||
|
||||
if len(c.Flattened) != 0 {
|
||||
c.localVars = append(c.localVars, "$s")
|
||||
prefix = prefix + " $s = 0;"
|
||||
}
|
||||
|
||||
if c.HasDefer {
|
||||
c.localVars = append(c.localVars, "$deferred")
|
||||
suffix = " }" + suffix
|
||||
if len(c.Blocking) != 0 {
|
||||
suffix = " }" + suffix
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if c.HasDefer {
|
||||
prefix = prefix + " var $err = null; try {"
|
||||
deferSuffix := " } catch(err) { $err = err;"
|
||||
if len(c.Blocking) != 0 {
|
||||
deferSuffix += " $s = -1;"
|
||||
}
|
||||
if c.resultNames == nil && c.sig.Results().Len() > 0 {
|
||||
deferSuffix += fmt.Sprintf(" return%s;", c.translateResults(nil))
|
||||
}
|
||||
deferSuffix += " } finally { $callDeferred($deferred, $err);"
|
||||
if c.resultNames != nil {
|
||||
deferSuffix += fmt.Sprintf(" if (!$curGoroutine.asleep) { return %s; }", c.translateResults(c.resultNames))
|
||||
}
|
||||
if len(c.Blocking) != 0 {
|
||||
deferSuffix += " if($curGoroutine.asleep) {"
|
||||
}
|
||||
suffix = deferSuffix + suffix
|
||||
}
|
||||
|
||||
if len(c.Flattened) != 0 {
|
||||
prefix = prefix + " s: while (true) { switch ($s) { case 0:"
|
||||
suffix = " } return; }" + suffix
|
||||
}
|
||||
|
||||
if c.HasDefer {
|
||||
prefix = prefix + " $deferred = []; $deferred.index = $curGoroutine.deferStack.length; $curGoroutine.deferStack.push($deferred);"
|
||||
}
|
||||
|
||||
if prefix != "" {
|
||||
bodyOutput = strings.Repeat("\t", c.p.indentation+1) + "/* */" + prefix + "\n" + bodyOutput
|
||||
}
|
||||
if suffix != "" {
|
||||
bodyOutput = bodyOutput + strings.Repeat("\t", c.p.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
|
||||
}
|
||||
|
||||
c.p.escapingVars = prevEV
|
||||
|
||||
return params, fmt.Sprintf("function%s(%s) {\n%s%s}", functionName, strings.Join(params, ", "), bodyOutput, strings.Repeat("\t", c.p.indentation))
|
||||
}
|
||||
-358
@@ -1,358 +0,0 @@
|
||||
package prelude
|
||||
|
||||
const goroutines = `
|
||||
var $stackDepthOffset = 0;
|
||||
var $getStackDepth = function() {
|
||||
var err = new Error();
|
||||
if (err.stack === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return $stackDepthOffset + err.stack.split("\n").length;
|
||||
};
|
||||
|
||||
var $panicStackDepth = null, $panicValue;
|
||||
var $callDeferred = function(deferred, jsErr, fromPanic) {
|
||||
if (!fromPanic && deferred !== null && deferred.index >= $curGoroutine.deferStack.length) {
|
||||
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;
|
||||
}
|
||||
if ($curGoroutine.asleep) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stackDepthOffset--;
|
||||
var outerPanicStackDepth = $panicStackDepth;
|
||||
var outerPanicValue = $panicValue;
|
||||
|
||||
var localPanicValue = $curGoroutine.panicStack.pop();
|
||||
if (localPanicValue !== undefined) {
|
||||
$panicStackDepth = $getStackDepth();
|
||||
$panicValue = localPanicValue;
|
||||
}
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
if (deferred === null) {
|
||||
deferred = $curGoroutine.deferStack[$curGoroutine.deferStack.length - 1];
|
||||
if (deferred === undefined) {
|
||||
/* The panic reached the top of the stack. Clear it and throw it as a JavaScript error. */
|
||||
$panicStackDepth = null;
|
||||
if (localPanicValue.Object instanceof Error) {
|
||||
throw localPanicValue.Object;
|
||||
}
|
||||
var msg;
|
||||
if (localPanicValue.constructor === $String) {
|
||||
msg = localPanicValue.$val;
|
||||
} else if (localPanicValue.Error !== undefined) {
|
||||
msg = localPanicValue.Error();
|
||||
} else if (localPanicValue.String !== undefined) {
|
||||
msg = localPanicValue.String();
|
||||
} else {
|
||||
msg = localPanicValue;
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
var call = deferred.pop();
|
||||
if (call === undefined) {
|
||||
$curGoroutine.deferStack.pop();
|
||||
if (localPanicValue !== undefined) {
|
||||
deferred = null;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
var r = call[0].apply(call[2], call[1]);
|
||||
if (r && r.$blk !== undefined) {
|
||||
deferred.push([r.$blk, [], r]);
|
||||
if (fromPanic) {
|
||||
throw null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (localPanicValue !== undefined && $panicStackDepth === null) {
|
||||
throw null; /* error was recovered */
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (localPanicValue !== undefined) {
|
||||
if ($panicStackDepth !== null) {
|
||||
$curGoroutine.panicStack.push(localPanicValue);
|
||||
}
|
||||
$panicStackDepth = outerPanicStackDepth;
|
||||
$panicValue = outerPanicValue;
|
||||
}
|
||||
$stackDepthOffset++;
|
||||
}
|
||||
};
|
||||
|
||||
var $panic = function(value) {
|
||||
$curGoroutine.panicStack.push(value);
|
||||
$callDeferred(null, null, true);
|
||||
};
|
||||
var $recover = function() {
|
||||
if ($panicStackDepth === null || ($panicStackDepth !== undefined && $panicStackDepth !== $getStackDepth() - 2)) {
|
||||
return $ifaceNil;
|
||||
}
|
||||
$panicStackDepth = null;
|
||||
return $panicValue;
|
||||
};
|
||||
var $throw = function(err) { throw err; };
|
||||
|
||||
var $noGoroutine = { asleep: false, exit: false, deferStack: [], panicStack: [] };
|
||||
var $curGoroutine = $noGoroutine, $totalGoroutines = 0, $awakeGoroutines = 0, $checkForDeadlock = true;
|
||||
var $mainFinished = false;
|
||||
var $go = function(fun, args) {
|
||||
$totalGoroutines++;
|
||||
$awakeGoroutines++;
|
||||
var $goroutine = function() {
|
||||
try {
|
||||
$curGoroutine = $goroutine;
|
||||
var r = fun.apply(undefined, args);
|
||||
if (r && r.$blk !== undefined) {
|
||||
fun = function() { return r.$blk(); };
|
||||
args = [];
|
||||
return;
|
||||
}
|
||||
$goroutine.exit = true;
|
||||
} catch (err) {
|
||||
if (!$goroutine.exit) {
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
$curGoroutine = $noGoroutine;
|
||||
if ($goroutine.exit) { /* also set by runtime.Goexit() */
|
||||
$totalGoroutines--;
|
||||
$goroutine.asleep = true;
|
||||
}
|
||||
if ($goroutine.asleep) {
|
||||
$awakeGoroutines--;
|
||||
if (!$mainFinished && $awakeGoroutines === 0 && $checkForDeadlock) {
|
||||
console.error("fatal error: all goroutines are asleep - deadlock!");
|
||||
if ($global.process !== undefined) {
|
||||
$global.process.exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
$goroutine.asleep = false;
|
||||
$goroutine.exit = false;
|
||||
$goroutine.deferStack = [];
|
||||
$goroutine.panicStack = [];
|
||||
$schedule($goroutine);
|
||||
};
|
||||
|
||||
var $scheduled = [];
|
||||
var $runScheduled = function() {
|
||||
try {
|
||||
var r;
|
||||
while ((r = $scheduled.shift()) !== undefined) {
|
||||
r();
|
||||
}
|
||||
} finally {
|
||||
if ($scheduled.length > 0) {
|
||||
setTimeout($runScheduled, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var $schedule = function(goroutine) {
|
||||
if (goroutine.asleep) {
|
||||
goroutine.asleep = false;
|
||||
$awakeGoroutines++;
|
||||
}
|
||||
$scheduled.push(goroutine);
|
||||
if ($curGoroutine === $noGoroutine) {
|
||||
$runScheduled();
|
||||
}
|
||||
};
|
||||
|
||||
var $setTimeout = function(f, t) {
|
||||
$awakeGoroutines++;
|
||||
return setTimeout(function() {
|
||||
$awakeGoroutines--;
|
||||
f();
|
||||
}, t);
|
||||
};
|
||||
|
||||
var $block = function() {
|
||||
if ($curGoroutine === $noGoroutine) {
|
||||
$throwRuntimeError("cannot block in JavaScript callback, fix by wrapping code in goroutine");
|
||||
}
|
||||
$curGoroutine.asleep = true;
|
||||
};
|
||||
|
||||
var $send = function(chan, value) {
|
||||
if (chan.$closed) {
|
||||
$throwRuntimeError("send on closed channel");
|
||||
}
|
||||
var queuedRecv = chan.$recvQueue.shift();
|
||||
if (queuedRecv !== undefined) {
|
||||
queuedRecv([value, true]);
|
||||
return;
|
||||
}
|
||||
if (chan.$buffer.length < chan.$capacity) {
|
||||
chan.$buffer.push(value);
|
||||
return;
|
||||
}
|
||||
|
||||
var thisGoroutine = $curGoroutine;
|
||||
var closedDuringSend;
|
||||
chan.$sendQueue.push(function(closed) {
|
||||
closedDuringSend = closed;
|
||||
$schedule(thisGoroutine);
|
||||
return value;
|
||||
});
|
||||
$block();
|
||||
return {
|
||||
$blk: function() {
|
||||
if (closedDuringSend) {
|
||||
$throwRuntimeError("send on closed channel");
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
var $recv = function(chan) {
|
||||
var queuedSend = chan.$sendQueue.shift();
|
||||
if (queuedSend !== undefined) {
|
||||
chan.$buffer.push(queuedSend(false));
|
||||
}
|
||||
var bufferedValue = chan.$buffer.shift();
|
||||
if (bufferedValue !== undefined) {
|
||||
return [bufferedValue, true];
|
||||
}
|
||||
if (chan.$closed) {
|
||||
return [chan.$elem.zero(), false];
|
||||
}
|
||||
|
||||
var thisGoroutine = $curGoroutine;
|
||||
var f = { $blk: function() { return this.value; } };
|
||||
var queueEntry = function(v) {
|
||||
f.value = v;
|
||||
$schedule(thisGoroutine);
|
||||
};
|
||||
chan.$recvQueue.push(queueEntry);
|
||||
$block();
|
||||
return f;
|
||||
};
|
||||
var $close = function(chan) {
|
||||
if (chan.$closed) {
|
||||
$throwRuntimeError("close of closed channel");
|
||||
}
|
||||
chan.$closed = true;
|
||||
while (true) {
|
||||
var queuedSend = chan.$sendQueue.shift();
|
||||
if (queuedSend === undefined) {
|
||||
break;
|
||||
}
|
||||
queuedSend(true); /* will panic */
|
||||
}
|
||||
while (true) {
|
||||
var queuedRecv = chan.$recvQueue.shift();
|
||||
if (queuedRecv === undefined) {
|
||||
break;
|
||||
}
|
||||
queuedRecv([chan.$elem.zero(), false]);
|
||||
}
|
||||
};
|
||||
var $select = function(comms) {
|
||||
var ready = [];
|
||||
var selection = -1;
|
||||
for (var i = 0; i < comms.length; i++) {
|
||||
var comm = comms[i];
|
||||
var chan = comm[0];
|
||||
switch (comm.length) {
|
||||
case 0: /* default */
|
||||
selection = i;
|
||||
break;
|
||||
case 1: /* recv */
|
||||
if (chan.$sendQueue.length !== 0 || chan.$buffer.length !== 0 || chan.$closed) {
|
||||
ready.push(i);
|
||||
}
|
||||
break;
|
||||
case 2: /* send */
|
||||
if (chan.$closed) {
|
||||
$throwRuntimeError("send on closed channel");
|
||||
}
|
||||
if (chan.$recvQueue.length !== 0 || chan.$buffer.length < chan.$capacity) {
|
||||
ready.push(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ready.length !== 0) {
|
||||
selection = ready[Math.floor(Math.random() * ready.length)];
|
||||
}
|
||||
if (selection !== -1) {
|
||||
var comm = comms[selection];
|
||||
switch (comm.length) {
|
||||
case 0: /* default */
|
||||
return [selection];
|
||||
case 1: /* recv */
|
||||
return [selection, $recv(comm[0])];
|
||||
case 2: /* send */
|
||||
$send(comm[0], comm[1]);
|
||||
return [selection];
|
||||
}
|
||||
}
|
||||
|
||||
var entries = [];
|
||||
var thisGoroutine = $curGoroutine;
|
||||
var f = { $blk: function() { return this.selection; } };
|
||||
var removeFromQueues = function() {
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
var entry = entries[i];
|
||||
var queue = entry[0];
|
||||
var index = queue.indexOf(entry[1]);
|
||||
if (index !== -1) {
|
||||
queue.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (var i = 0; i < comms.length; i++) {
|
||||
(function(i) {
|
||||
var comm = comms[i];
|
||||
switch (comm.length) {
|
||||
case 1: /* recv */
|
||||
var queueEntry = function(value) {
|
||||
f.selection = [i, value];
|
||||
removeFromQueues();
|
||||
$schedule(thisGoroutine);
|
||||
};
|
||||
entries.push([comm[0].$recvQueue, queueEntry]);
|
||||
comm[0].$recvQueue.push(queueEntry);
|
||||
break;
|
||||
case 2: /* send */
|
||||
var queueEntry = function() {
|
||||
if (comm[0].$closed) {
|
||||
$throwRuntimeError("send on closed channel");
|
||||
}
|
||||
f.selection = [i];
|
||||
removeFromQueues();
|
||||
$schedule(thisGoroutine);
|
||||
return comm[1];
|
||||
};
|
||||
entries.push([comm[0].$sendQueue, queueEntry]);
|
||||
comm[0].$sendQueue.push(queueEntry);
|
||||
break;
|
||||
}
|
||||
})(i);
|
||||
}
|
||||
$block();
|
||||
return f;
|
||||
};
|
||||
`
|
||||
-379
@@ -1,379 +0,0 @@
|
||||
package prelude
|
||||
|
||||
const jsmapping = `
|
||||
var $jsObjectPtr, $jsErrorPtr;
|
||||
|
||||
var $needsExternalization = function(t) {
|
||||
switch (t.kind) {
|
||||
case $kindBool:
|
||||
case $kindInt:
|
||||
case $kindInt8:
|
||||
case $kindInt16:
|
||||
case $kindInt32:
|
||||
case $kindUint:
|
||||
case $kindUint8:
|
||||
case $kindUint16:
|
||||
case $kindUint32:
|
||||
case $kindUintptr:
|
||||
case $kindFloat32:
|
||||
case $kindFloat64:
|
||||
return false;
|
||||
default:
|
||||
return t !== $jsObjectPtr;
|
||||
}
|
||||
};
|
||||
|
||||
var $externalize = function(v, t) {
|
||||
if (t === $jsObjectPtr) {
|
||||
return v;
|
||||
}
|
||||
switch (t.kind) {
|
||||
case $kindBool:
|
||||
case $kindInt:
|
||||
case $kindInt8:
|
||||
case $kindInt16:
|
||||
case $kindInt32:
|
||||
case $kindUint:
|
||||
case $kindUint8:
|
||||
case $kindUint16:
|
||||
case $kindUint32:
|
||||
case $kindUintptr:
|
||||
case $kindFloat32:
|
||||
case $kindFloat64:
|
||||
return v;
|
||||
case $kindInt64:
|
||||
case $kindUint64:
|
||||
return $flatten64(v);
|
||||
case $kindArray:
|
||||
if ($needsExternalization(t.elem)) {
|
||||
return $mapArray(v, function(e) { return $externalize(e, t.elem); });
|
||||
}
|
||||
return v;
|
||||
case $kindFunc:
|
||||
return $externalizeFunction(v, t, false);
|
||||
case $kindInterface:
|
||||
if (v === $ifaceNil) {
|
||||
return null;
|
||||
}
|
||||
if (v.constructor === $jsObjectPtr) {
|
||||
return v.$val.object;
|
||||
}
|
||||
return $externalize(v.$val, v.constructor);
|
||||
case $kindMap:
|
||||
var m = {};
|
||||
var keys = $keys(v);
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var entry = v[keys[i]];
|
||||
m[$externalize(entry.k, t.key)] = $externalize(entry.v, t.elem);
|
||||
}
|
||||
return m;
|
||||
case $kindPtr:
|
||||
if (v === t.nil) {
|
||||
return null;
|
||||
}
|
||||
return $externalize(v.$get(), t.elem);
|
||||
case $kindSlice:
|
||||
if ($needsExternalization(t.elem)) {
|
||||
return $mapArray($sliceToArray(v), function(e) { return $externalize(e, t.elem); });
|
||||
}
|
||||
return $sliceToArray(v);
|
||||
case $kindString:
|
||||
if ($isASCII(v)) {
|
||||
return v;
|
||||
}
|
||||
var s = "", r;
|
||||
for (var i = 0; i < v.length; i += r[1]) {
|
||||
r = $decodeRune(v, i);
|
||||
var c = r[0];
|
||||
if (c > 0xFFFF) {
|
||||
var h = Math.floor((c - 0x10000) / 0x400) + 0xD800;
|
||||
var l = (c - 0x10000) % 0x400 + 0xDC00;
|
||||
s += String.fromCharCode(h, l);
|
||||
continue;
|
||||
}
|
||||
s += String.fromCharCode(c);
|
||||
}
|
||||
return s;
|
||||
case $kindStruct:
|
||||
var timePkg = $packages["time"];
|
||||
if (timePkg !== undefined && v.constructor === timePkg.Time.ptr) {
|
||||
var milli = $div64(v.UnixNano(), new $Int64(0, 1000000));
|
||||
return new Date($flatten64(milli));
|
||||
}
|
||||
|
||||
var noJsObject = {};
|
||||
var searchJsObject = function(v, t) {
|
||||
if (t === $jsObjectPtr) {
|
||||
return v;
|
||||
}
|
||||
switch (t.kind) {
|
||||
case $kindPtr:
|
||||
if (v === t.nil) {
|
||||
return noJsObject;
|
||||
}
|
||||
return searchJsObject(v.$get(), t.elem);
|
||||
case $kindStruct:
|
||||
var f = t.fields[0];
|
||||
return searchJsObject(v[f.prop], f.typ);
|
||||
case $kindInterface:
|
||||
return searchJsObject(v.$val, v.constructor);
|
||||
default:
|
||||
return noJsObject;
|
||||
}
|
||||
};
|
||||
var o = searchJsObject(v, t);
|
||||
if (o !== noJsObject) {
|
||||
return o;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
$throwRuntimeError("cannot externalize " + t.string);
|
||||
};
|
||||
|
||||
var $externalizeFunction = function(v, t, passThis) {
|
||||
if (v === $throwNilPointerError) {
|
||||
return null;
|
||||
}
|
||||
if (v.$externalizeWrapper === undefined) {
|
||||
$checkForDeadlock = false;
|
||||
v.$externalizeWrapper = function() {
|
||||
var args = [];
|
||||
for (var i = 0; i < t.params.length; i++) {
|
||||
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));
|
||||
}
|
||||
args.push(new (t.params[i])(varargs));
|
||||
break;
|
||||
}
|
||||
args.push($internalize(arguments[i], t.params[i]));
|
||||
}
|
||||
var result = v.apply(passThis ? this : undefined, args);
|
||||
switch (t.results.length) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
return $externalize(result, t.results[0]);
|
||||
default:
|
||||
for (var i = 0; i < t.results.length; i++) {
|
||||
result[i] = $externalize(result[i], t.results[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
return v.$externalizeWrapper;
|
||||
};
|
||||
|
||||
var $internalize = function(v, t, recv) {
|
||||
if (t === $jsObjectPtr) {
|
||||
return v;
|
||||
}
|
||||
if (t === $jsObjectPtr.elem) {
|
||||
$throwRuntimeError("cannot internalize js.Object, use *js.Object instead");
|
||||
}
|
||||
if (v && v.__internal_object__ !== undefined) {
|
||||
return $assertType(v.__internal_object__, t, false);
|
||||
}
|
||||
var timePkg = $packages["time"];
|
||||
if (timePkg !== undefined && t === timePkg.Time) {
|
||||
if (!(v !== null && v !== undefined && v.constructor === Date)) {
|
||||
$throwRuntimeError("cannot internalize time.Time from " + typeof v + ", must be Date");
|
||||
}
|
||||
return timePkg.Unix(new $Int64(0, 0), new $Int64(0, v.getTime() * 1000000));
|
||||
}
|
||||
switch (t.kind) {
|
||||
case $kindBool:
|
||||
return !!v;
|
||||
case $kindInt:
|
||||
return parseInt(v);
|
||||
case $kindInt8:
|
||||
return parseInt(v) << 24 >> 24;
|
||||
case $kindInt16:
|
||||
return parseInt(v) << 16 >> 16;
|
||||
case $kindInt32:
|
||||
return parseInt(v) >> 0;
|
||||
case $kindUint:
|
||||
return parseInt(v);
|
||||
case $kindUint8:
|
||||
return parseInt(v) << 24 >>> 24;
|
||||
case $kindUint16:
|
||||
return parseInt(v) << 16 >>> 16;
|
||||
case $kindUint32:
|
||||
case $kindUintptr:
|
||||
return parseInt(v) >>> 0;
|
||||
case $kindInt64:
|
||||
case $kindUint64:
|
||||
return new t(0, v);
|
||||
case $kindFloat32:
|
||||
case $kindFloat64:
|
||||
return parseFloat(v);
|
||||
case $kindArray:
|
||||
if (v.length !== t.len) {
|
||||
$throwRuntimeError("got array with wrong size from JavaScript native");
|
||||
}
|
||||
return $mapArray(v, function(e) { return $internalize(e, t.elem); });
|
||||
case $kindFunc:
|
||||
return function() {
|
||||
var args = [];
|
||||
for (var i = 0; i < t.params.length; i++) {
|
||||
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));
|
||||
}
|
||||
break;
|
||||
}
|
||||
args.push($externalize(arguments[i], t.params[i]));
|
||||
}
|
||||
var result = v.apply(recv, args);
|
||||
switch (t.results.length) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
return $internalize(result, t.results[0]);
|
||||
default:
|
||||
for (var i = 0; i < t.results.length; i++) {
|
||||
result[i] = $internalize(result[i], t.results[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
case $kindInterface:
|
||||
if (t.methods.length !== 0) {
|
||||
$throwRuntimeError("cannot internalize " + t.string);
|
||||
}
|
||||
if (v === null) {
|
||||
return $ifaceNil;
|
||||
}
|
||||
if (v === undefined) {
|
||||
return new $jsObjectPtr(undefined);
|
||||
}
|
||||
switch (v.constructor) {
|
||||
case Int8Array:
|
||||
return new ($sliceType($Int8))(v);
|
||||
case Int16Array:
|
||||
return new ($sliceType($Int16))(v);
|
||||
case Int32Array:
|
||||
return new ($sliceType($Int))(v);
|
||||
case Uint8Array:
|
||||
return new ($sliceType($Uint8))(v);
|
||||
case Uint16Array:
|
||||
return new ($sliceType($Uint16))(v);
|
||||
case Uint32Array:
|
||||
return new ($sliceType($Uint))(v);
|
||||
case Float32Array:
|
||||
return new ($sliceType($Float32))(v);
|
||||
case Float64Array:
|
||||
return new ($sliceType($Float64))(v);
|
||||
case Array:
|
||||
return $internalize(v, $sliceType($emptyInterface));
|
||||
case Boolean:
|
||||
return new $Bool(!!v);
|
||||
case Date:
|
||||
if (timePkg === undefined) {
|
||||
/* 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:
|
||||
var funcType = $funcType([$sliceType($emptyInterface)], [$jsObjectPtr], true);
|
||||
return new funcType($internalize(v, funcType));
|
||||
case Number:
|
||||
return new $Float64(parseFloat(v));
|
||||
case String:
|
||||
return new $String($internalize(v, $String));
|
||||
default:
|
||||
if ($global.Node && v instanceof $global.Node) {
|
||||
return new $jsObjectPtr(v);
|
||||
}
|
||||
var mapType = $mapType($String, $emptyInterface);
|
||||
return new mapType($internalize(v, mapType));
|
||||
}
|
||||
case $kindMap:
|
||||
var 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) };
|
||||
}
|
||||
return m;
|
||||
case $kindPtr:
|
||||
if (t.elem.kind === $kindStruct) {
|
||||
return $internalize(v, t.elem);
|
||||
}
|
||||
case $kindSlice:
|
||||
return new t($mapArray(v, function(e) { return $internalize(e, t.elem); }));
|
||||
case $kindString:
|
||||
v = String(v);
|
||||
if ($isASCII(v)) {
|
||||
return v;
|
||||
}
|
||||
var s = "";
|
||||
var i = 0;
|
||||
while (i < v.length) {
|
||||
var h = v.charCodeAt(i);
|
||||
if (0xD800 <= h && h <= 0xDBFF) {
|
||||
var l = v.charCodeAt(i + 1);
|
||||
var c = (h - 0xD800) * 0x400 + l - 0xDC00 + 0x10000;
|
||||
s += $encodeRune(c);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
s += $encodeRune(h);
|
||||
i++;
|
||||
}
|
||||
return s;
|
||||
case $kindStruct:
|
||||
var noJsObject = {};
|
||||
var searchJsObject = function(t) {
|
||||
if (t === $jsObjectPtr) {
|
||||
return v;
|
||||
}
|
||||
if (t === $jsObjectPtr.elem) {
|
||||
$throwRuntimeError("cannot internalize js.Object, use *js.Object instead");
|
||||
}
|
||||
switch (t.kind) {
|
||||
case $kindPtr:
|
||||
return searchJsObject(t.elem);
|
||||
case $kindStruct:
|
||||
var f = t.fields[0];
|
||||
var o = searchJsObject(f.typ);
|
||||
if (o !== noJsObject) {
|
||||
var n = new t.ptr();
|
||||
n[f.prop] = o;
|
||||
return n;
|
||||
}
|
||||
return noJsObject;
|
||||
default:
|
||||
return noJsObject;
|
||||
}
|
||||
};
|
||||
var o = searchJsObject(t);
|
||||
if (o !== noJsObject) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
$throwRuntimeError("cannot internalize " + t.string);
|
||||
};
|
||||
|
||||
/* $isASCII reports whether string s contains only ASCII characters. */
|
||||
var $isASCII = function(s) {
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
if (s.charCodeAt(i) >= 128) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
`
|
||||
-196
@@ -1,196 +0,0 @@
|
||||
package prelude
|
||||
|
||||
const numeric = `
|
||||
var $min = Math.min;
|
||||
var $mod = function(x, y) { return x % y; };
|
||||
var $parseInt = parseInt;
|
||||
var $parseFloat = function(f) {
|
||||
if (f !== undefined && f !== null && f.constructor === Number) {
|
||||
return f;
|
||||
}
|
||||
return parseFloat(f);
|
||||
};
|
||||
|
||||
var $froundBuf = new Float32Array(1);
|
||||
var $fround = Math.fround || function(f) {
|
||||
$froundBuf[0] = f;
|
||||
return $froundBuf[0];
|
||||
};
|
||||
|
||||
var $imul = Math.imul || function(a, b) {
|
||||
var ah = (a >>> 16) & 0xffff;
|
||||
var al = a & 0xffff;
|
||||
var bh = (b >>> 16) & 0xffff;
|
||||
var bl = b & 0xffff;
|
||||
return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) >> 0);
|
||||
};
|
||||
|
||||
var $floatKey = function(f) {
|
||||
if (f !== f) {
|
||||
$idCounter++;
|
||||
return "NaN$" + $idCounter;
|
||||
}
|
||||
return String(f);
|
||||
};
|
||||
|
||||
var $flatten64 = function(x) {
|
||||
return x.$high * 4294967296 + x.$low;
|
||||
};
|
||||
|
||||
var $shiftLeft64 = function(x, y) {
|
||||
if (y === 0) {
|
||||
return x;
|
||||
}
|
||||
if (y < 32) {
|
||||
return new x.constructor(x.$high << y | x.$low >>> (32 - y), (x.$low << y) >>> 0);
|
||||
}
|
||||
if (y < 64) {
|
||||
return new x.constructor(x.$low << (y - 32), 0);
|
||||
}
|
||||
return new x.constructor(0, 0);
|
||||
};
|
||||
|
||||
var $shiftRightInt64 = function(x, y) {
|
||||
if (y === 0) {
|
||||
return x;
|
||||
}
|
||||
if (y < 32) {
|
||||
return new x.constructor(x.$high >> y, (x.$low >>> y | x.$high << (32 - y)) >>> 0);
|
||||
}
|
||||
if (y < 64) {
|
||||
return new x.constructor(x.$high >> 31, (x.$high >> (y - 32)) >>> 0);
|
||||
}
|
||||
if (x.$high < 0) {
|
||||
return new x.constructor(-1, 4294967295);
|
||||
}
|
||||
return new x.constructor(0, 0);
|
||||
};
|
||||
|
||||
var $shiftRightUint64 = function(x, y) {
|
||||
if (y === 0) {
|
||||
return x;
|
||||
}
|
||||
if (y < 32) {
|
||||
return new x.constructor(x.$high >>> y, (x.$low >>> y | x.$high << (32 - y)) >>> 0);
|
||||
}
|
||||
if (y < 64) {
|
||||
return new x.constructor(0, x.$high >>> (y - 32));
|
||||
}
|
||||
return new x.constructor(0, 0);
|
||||
};
|
||||
|
||||
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<<i) !== 0) {
|
||||
high += x.$high << i | x.$low >>> (32 - i);
|
||||
low += (x.$low << i) >>> 0;
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < 32; i++) {
|
||||
if ((y.$high & 1<<i) !== 0) {
|
||||
high += x.$low << i;
|
||||
}
|
||||
}
|
||||
return new x.constructor(high, low);
|
||||
};
|
||||
|
||||
var $div64 = function(x, y, returnRemainder) {
|
||||
if (y.$high === 0 && y.$low === 0) {
|
||||
$throwRuntimeError("integer divide by zero");
|
||||
}
|
||||
|
||||
var s = 1;
|
||||
var rs = 1;
|
||||
|
||||
var xHigh = x.$high;
|
||||
var xLow = x.$low;
|
||||
if (xHigh < 0) {
|
||||
s = -1;
|
||||
rs = -1;
|
||||
xHigh = -xHigh;
|
||||
if (xLow !== 0) {
|
||||
xHigh--;
|
||||
xLow = 4294967296 - xLow;
|
||||
}
|
||||
}
|
||||
|
||||
var yHigh = y.$high;
|
||||
var yLow = y.$low;
|
||||
if (y.$high < 0) {
|
||||
s *= -1;
|
||||
yHigh = -yHigh;
|
||||
if (yLow !== 0) {
|
||||
yHigh--;
|
||||
yLow = 4294967296 - yLow;
|
||||
}
|
||||
}
|
||||
|
||||
var high = 0, low = 0, n = 0;
|
||||
while (yHigh < 2147483648 && ((xHigh > yHigh) || (xHigh === yHigh && xLow > yLow))) {
|
||||
yHigh = (yHigh << 1 | yLow >>> 31) >>> 0;
|
||||
yLow = (yLow << 1) >>> 0;
|
||||
n++;
|
||||
}
|
||||
for (var i = 0; i <= n; i++) {
|
||||
high = high << 1 | low >>> 31;
|
||||
low = (low << 1) >>> 0;
|
||||
if ((xHigh > yHigh) || (xHigh === yHigh && xLow >= yLow)) {
|
||||
xHigh = xHigh - yHigh;
|
||||
xLow = xLow - yLow;
|
||||
if (xLow < 0) {
|
||||
xHigh--;
|
||||
xLow += 4294967296;
|
||||
}
|
||||
low++;
|
||||
if (low === 4294967296) {
|
||||
high++;
|
||||
low = 0;
|
||||
}
|
||||
}
|
||||
yLow = (yLow >>> 1 | yHigh << (32 - 1)) >>> 0;
|
||||
yHigh = yHigh >>> 1;
|
||||
}
|
||||
|
||||
if (returnRemainder) {
|
||||
return new x.constructor(xHigh * rs, xLow * rs);
|
||||
}
|
||||
return new x.constructor(high * s, low * s);
|
||||
};
|
||||
|
||||
var $divComplex = function(n, d) {
|
||||
var ninf = n.$real === Infinity || n.$real === -Infinity || n.$imag === Infinity || n.$imag === -Infinity;
|
||||
var dinf = d.$real === Infinity || d.$real === -Infinity || d.$imag === Infinity || d.$imag === -Infinity;
|
||||
var nnan = !ninf && (n.$real !== n.$real || n.$imag !== n.$imag);
|
||||
var dnan = !dinf && (d.$real !== d.$real || d.$imag !== d.$imag);
|
||||
if(nnan || dnan) {
|
||||
return new n.constructor(NaN, NaN);
|
||||
}
|
||||
if (ninf && !dinf) {
|
||||
return new n.constructor(Infinity, Infinity);
|
||||
}
|
||||
if (!ninf && dinf) {
|
||||
return new n.constructor(0, 0);
|
||||
}
|
||||
if (d.$real === 0 && d.$imag === 0) {
|
||||
if (n.$real === 0 && n.$imag === 0) {
|
||||
return new n.constructor(NaN, NaN);
|
||||
}
|
||||
return new n.constructor(Infinity, Infinity);
|
||||
}
|
||||
var a = Math.abs(d.$real);
|
||||
var b = Math.abs(d.$imag);
|
||||
if (a <= b) {
|
||||
var ratio = d.$real / d.$imag;
|
||||
var denom = d.$real * ratio + d.$imag;
|
||||
return new n.constructor((n.$real * ratio + n.$imag) / denom, (n.$imag * ratio - n.$real) / denom);
|
||||
}
|
||||
var ratio = d.$imag / d.$real;
|
||||
var denom = d.$imag * ratio + d.$real;
|
||||
return new n.constructor((n.$imag * ratio + n.$real) / denom, (n.$imag - n.$real * ratio) / denom);
|
||||
};
|
||||
`
|
||||
-419
@@ -1,419 +0,0 @@
|
||||
package prelude
|
||||
|
||||
const Prelude = prelude + numeric + types + goroutines + jsmapping
|
||||
|
||||
const prelude = `Error.stackTraceLimit = Infinity;
|
||||
|
||||
var $global, $module;
|
||||
if (typeof window !== "undefined") { /* web page */
|
||||
$global = window;
|
||||
} else if (typeof self !== "undefined") { /* web worker */
|
||||
$global = self;
|
||||
} else if (typeof global !== "undefined") { /* Node.js */
|
||||
$global = global;
|
||||
$global.require = require;
|
||||
} else { /* others (e.g. Nashorn) */
|
||||
$global = this;
|
||||
}
|
||||
|
||||
if ($global === undefined || $global.Array === undefined) {
|
||||
throw new Error("no global object found");
|
||||
}
|
||||
if (typeof module !== "undefined") {
|
||||
$module = module;
|
||||
}
|
||||
|
||||
var $packages = {}, $idCounter = 0;
|
||||
var $keys = function(m) { return m ? Object.keys(m) : []; };
|
||||
var $flushConsole = function() {};
|
||||
var $throwRuntimeError; /* set by package "runtime" */
|
||||
var $throwNilPointerError = function() { $throwRuntimeError("invalid memory address or nil pointer dereference"); };
|
||||
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 $mapArray = function(array, f) {
|
||||
var newArray = new array.constructor(array.length);
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
newArray[i] = f(array[i]);
|
||||
}
|
||||
return newArray;
|
||||
};
|
||||
|
||||
var $methodVal = function(recv, name) {
|
||||
var vals = recv.$methodVals || {};
|
||||
recv.$methodVals = vals; /* noop for primitives */
|
||||
var f = vals[name];
|
||||
if (f !== undefined) {
|
||||
return f;
|
||||
}
|
||||
var method = recv[name];
|
||||
f = function() {
|
||||
$stackDepthOffset--;
|
||||
try {
|
||||
return method.apply(recv, arguments);
|
||||
} finally {
|
||||
$stackDepthOffset++;
|
||||
}
|
||||
};
|
||||
vals[name] = f;
|
||||
return f;
|
||||
};
|
||||
|
||||
var $methodExpr = function(typ, name) {
|
||||
var method = typ.prototype[name];
|
||||
if (method.$expr === undefined) {
|
||||
method.$expr = function() {
|
||||
$stackDepthOffset--;
|
||||
try {
|
||||
if (typ.wrapped) {
|
||||
arguments[0] = new typ(arguments[0]);
|
||||
}
|
||||
return Function.call.apply(method, arguments);
|
||||
} finally {
|
||||
$stackDepthOffset++;
|
||||
}
|
||||
};
|
||||
}
|
||||
return method.$expr;
|
||||
};
|
||||
|
||||
var $ifaceMethodExprs = {};
|
||||
var $ifaceMethodExpr = function(name) {
|
||||
var expr = $ifaceMethodExprs["$" + name];
|
||||
if (expr === undefined) {
|
||||
expr = $ifaceMethodExprs["$" + name] = function() {
|
||||
$stackDepthOffset--;
|
||||
try {
|
||||
return Function.call.apply(arguments[0][name], arguments);
|
||||
} finally {
|
||||
$stackDepthOffset++;
|
||||
}
|
||||
};
|
||||
}
|
||||
return expr;
|
||||
};
|
||||
|
||||
var $subslice = function(slice, low, high, max) {
|
||||
if (low < 0 || high < low || max < high || high > slice.$capacity || max > slice.$capacity) {
|
||||
$throwRuntimeError("slice bounds out of range");
|
||||
}
|
||||
var s = new slice.constructor(slice.$array);
|
||||
s.$offset = slice.$offset + low;
|
||||
s.$length = slice.$length - low;
|
||||
s.$capacity = slice.$capacity - low;
|
||||
if (high !== undefined) {
|
||||
s.$length = high - low;
|
||||
}
|
||||
if (max !== undefined) {
|
||||
s.$capacity = max - low;
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
var $substring = function(str, low, high) {
|
||||
if (low < 0 || high < low || high > str.length) {
|
||||
$throwRuntimeError("slice bounds out of range");
|
||||
}
|
||||
return str.substring(low, high);
|
||||
};
|
||||
|
||||
var $sliceToArray = 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);
|
||||
};
|
||||
|
||||
var $decodeRune = function(str, pos) {
|
||||
var c0 = str.charCodeAt(pos);
|
||||
|
||||
if (c0 < 0x80) {
|
||||
return [c0, 1];
|
||||
}
|
||||
|
||||
if (c0 !== c0 || c0 < 0xC0) {
|
||||
return [0xFFFD, 1];
|
||||
}
|
||||
|
||||
var c1 = str.charCodeAt(pos + 1);
|
||||
if (c1 !== c1 || c1 < 0x80 || 0xC0 <= c1) {
|
||||
return [0xFFFD, 1];
|
||||
}
|
||||
|
||||
if (c0 < 0xE0) {
|
||||
var r = (c0 & 0x1F) << 6 | (c1 & 0x3F);
|
||||
if (r <= 0x7F) {
|
||||
return [0xFFFD, 1];
|
||||
}
|
||||
return [r, 2];
|
||||
}
|
||||
|
||||
var c2 = str.charCodeAt(pos + 2);
|
||||
if (c2 !== c2 || c2 < 0x80 || 0xC0 <= c2) {
|
||||
return [0xFFFD, 1];
|
||||
}
|
||||
|
||||
if (c0 < 0xF0) {
|
||||
var r = (c0 & 0x0F) << 12 | (c1 & 0x3F) << 6 | (c2 & 0x3F);
|
||||
if (r <= 0x7FF) {
|
||||
return [0xFFFD, 1];
|
||||
}
|
||||
if (0xD800 <= r && r <= 0xDFFF) {
|
||||
return [0xFFFD, 1];
|
||||
}
|
||||
return [r, 3];
|
||||
}
|
||||
|
||||
var c3 = str.charCodeAt(pos + 3);
|
||||
if (c3 !== c3 || c3 < 0x80 || 0xC0 <= c3) {
|
||||
return [0xFFFD, 1];
|
||||
}
|
||||
|
||||
if (c0 < 0xF8) {
|
||||
var r = (c0 & 0x07) << 18 | (c1 & 0x3F) << 12 | (c2 & 0x3F) << 6 | (c3 & 0x3F);
|
||||
if (r <= 0xFFFF || 0x10FFFF < r) {
|
||||
return [0xFFFD, 1];
|
||||
}
|
||||
return [r, 4];
|
||||
}
|
||||
|
||||
return [0xFFFD, 1];
|
||||
};
|
||||
|
||||
var $encodeRune = function(r) {
|
||||
if (r < 0 || r > 0x10FFFF || (0xD800 <= r && r <= 0xDFFF)) {
|
||||
r = 0xFFFD;
|
||||
}
|
||||
if (r <= 0x7F) {
|
||||
return String.fromCharCode(r);
|
||||
}
|
||||
if (r <= 0x7FF) {
|
||||
return String.fromCharCode(0xC0 | r >> 6, 0x80 | (r & 0x3F));
|
||||
}
|
||||
if (r <= 0xFFFF) {
|
||||
return String.fromCharCode(0xE0 | r >> 12, 0x80 | (r >> 6 & 0x3F), 0x80 | (r & 0x3F));
|
||||
}
|
||||
return String.fromCharCode(0xF0 | r >> 18, 0x80 | (r >> 12 & 0x3F), 0x80 | (r >> 6 & 0x3F), 0x80 | (r & 0x3F));
|
||||
};
|
||||
|
||||
var $stringToBytes = function(str) {
|
||||
var array = new Uint8Array(str.length);
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
array[i] = str.charCodeAt(i);
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
var $bytesToString = function(slice) {
|
||||
if (slice.$length === 0) {
|
||||
return "";
|
||||
}
|
||||
var str = "";
|
||||
for (var i = 0; i < slice.$length; i += 10000) {
|
||||
str += String.fromCharCode.apply(undefined, slice.$array.subarray(slice.$offset + i, slice.$offset + Math.min(slice.$length, i + 10000)));
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
var $stringToRunes = function(str) {
|
||||
var array = new Int32Array(str.length);
|
||||
var rune, j = 0;
|
||||
for (var i = 0; i < str.length; i += rune[1], j++) {
|
||||
rune = $decodeRune(str, i);
|
||||
array[j] = rune[0];
|
||||
}
|
||||
return array.subarray(0, j);
|
||||
};
|
||||
|
||||
var $runesToString = function(slice) {
|
||||
if (slice.$length === 0) {
|
||||
return "";
|
||||
}
|
||||
var str = "";
|
||||
for (var i = 0; i < slice.$length; i++) {
|
||||
str += $encodeRune(slice.$array[slice.$offset + i]);
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
var $copyString = function(dst, src) {
|
||||
var n = Math.min(src.length, dst.$length);
|
||||
for (var i = 0; i < n; i++) {
|
||||
dst.$array[dst.$offset + i] = src.charCodeAt(i);
|
||||
}
|
||||
return n;
|
||||
};
|
||||
|
||||
var $copySlice = function(dst, src) {
|
||||
var n = Math.min(src.$length, dst.$length);
|
||||
$copyArray(dst.$array, src.$array, dst.$offset, src.$offset, n, dst.constructor.elem);
|
||||
return n;
|
||||
};
|
||||
|
||||
var $copyArray = function(dst, src, dstOffset, srcOffset, n, elem) {
|
||||
if (n === 0 || (dst === src && dstOffset === srcOffset)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (src.subarray) {
|
||||
dst.set(src.subarray(srcOffset, srcOffset + n), dstOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (elem.kind) {
|
||||
case $kindArray:
|
||||
case $kindStruct:
|
||||
if (dst === src && dstOffset > srcOffset) {
|
||||
for (var i = n - 1; i >= 0; i--) {
|
||||
elem.copy(dst[dstOffset + i], src[srcOffset + i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < n; i++) {
|
||||
elem.copy(dst[dstOffset + i], src[srcOffset + i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dst === src && dstOffset > srcOffset) {
|
||||
for (var i = n - 1; i >= 0; i--) {
|
||||
dst[dstOffset + i] = src[srcOffset + i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < n; i++) {
|
||||
dst[dstOffset + i] = src[srcOffset + i];
|
||||
}
|
||||
};
|
||||
|
||||
var $clone = function(src, type) {
|
||||
var clone = type.zero();
|
||||
type.copy(clone, src);
|
||||
return clone;
|
||||
};
|
||||
|
||||
var $pointerOfStructConversion = function(obj, type) {
|
||||
if(obj.$proxies === undefined) {
|
||||
obj.$proxies = {};
|
||||
obj.$proxies[obj.constructor.string] = obj;
|
||||
}
|
||||
var proxy = obj.$proxies[type.string];
|
||||
if (proxy === undefined) {
|
||||
var properties = {};
|
||||
for (var i = 0; i < type.elem.fields.length; i++) {
|
||||
(function(fieldProp) {
|
||||
properties[fieldProp] = {
|
||||
get: function() { return obj[fieldProp]; },
|
||||
set: function(value) { obj[fieldProp] = value; }
|
||||
};
|
||||
})(type.elem.fields[i].prop);
|
||||
}
|
||||
proxy = Object.create(type.prototype, properties);
|
||||
proxy.$val = proxy;
|
||||
obj.$proxies[type.string] = proxy;
|
||||
proxy.$proxies = obj.$proxies;
|
||||
}
|
||||
return proxy;
|
||||
};
|
||||
|
||||
var $append = function(slice) {
|
||||
return $internalAppend(slice, arguments, 1, arguments.length - 1);
|
||||
};
|
||||
|
||||
var $appendSlice = function(slice, toAppend) {
|
||||
if (toAppend.constructor === String) {
|
||||
var bytes = $stringToBytes(toAppend);
|
||||
return $internalAppend(slice, bytes, 0, bytes.length);
|
||||
}
|
||||
return $internalAppend(slice, toAppend.$array, toAppend.$offset, toAppend.$length);
|
||||
};
|
||||
|
||||
var $internalAppend = function(slice, array, offset, length) {
|
||||
if (length === 0) {
|
||||
return slice;
|
||||
}
|
||||
|
||||
var newArray = slice.$array;
|
||||
var newOffset = slice.$offset;
|
||||
var newLength = slice.$length + length;
|
||||
var newCapacity = slice.$capacity;
|
||||
|
||||
if (newLength > newCapacity) {
|
||||
newOffset = 0;
|
||||
newCapacity = Math.max(newLength, slice.$capacity < 1024 ? slice.$capacity * 2 : Math.floor(slice.$capacity * 5 / 4));
|
||||
|
||||
if (slice.$array.constructor === Array) {
|
||||
newArray = slice.$array.slice(slice.$offset, slice.$offset + slice.$length);
|
||||
newArray.length = newCapacity;
|
||||
var zero = slice.constructor.elem.zero;
|
||||
for (var i = slice.$length; i < newCapacity; i++) {
|
||||
newArray[i] = zero();
|
||||
}
|
||||
} else {
|
||||
newArray = new slice.$array.constructor(newCapacity);
|
||||
newArray.set(slice.$array.subarray(slice.$offset, slice.$offset + slice.$length));
|
||||
}
|
||||
}
|
||||
|
||||
$copyArray(newArray, array, newOffset + slice.$length, offset, length, slice.constructor.elem);
|
||||
|
||||
var newSlice = new slice.constructor(newArray);
|
||||
newSlice.$offset = newOffset;
|
||||
newSlice.$length = newLength;
|
||||
newSlice.$capacity = newCapacity;
|
||||
return newSlice;
|
||||
};
|
||||
|
||||
var $equal = function(a, b, type) {
|
||||
if (type === $jsObjectPtr) {
|
||||
return a === b;
|
||||
}
|
||||
switch (type.kind) {
|
||||
case $kindComplex64:
|
||||
case $kindComplex128:
|
||||
return a.$real === b.$real && a.$imag === b.$imag;
|
||||
case $kindInt64:
|
||||
case $kindUint64:
|
||||
return a.$high === b.$high && a.$low === b.$low;
|
||||
case $kindArray:
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (!$equal(a[i], b[i], type.elem)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case $kindStruct:
|
||||
for (var i = 0; i < type.fields.length; i++) {
|
||||
var f = type.fields[i];
|
||||
if (!$equal(a[f.prop], b[f.prop], f.typ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case $kindInterface:
|
||||
return $interfaceIsEqual(a, b);
|
||||
default:
|
||||
return a === b;
|
||||
}
|
||||
};
|
||||
|
||||
var $interfaceIsEqual = function(a, b) {
|
||||
if (a === $ifaceNil || b === $ifaceNil) {
|
||||
return a === b;
|
||||
}
|
||||
if (a.constructor !== b.constructor) {
|
||||
return false;
|
||||
}
|
||||
if (a.constructor === $jsObjectPtr) {
|
||||
return a.object === b.object;
|
||||
}
|
||||
if (!a.constructor.comparable) {
|
||||
$throwRuntimeError("comparing uncomparable type " + a.constructor.string);
|
||||
}
|
||||
return $equal(a.$val, b.$val, a.constructor);
|
||||
};
|
||||
`
|
||||
-743
@@ -1,743 +0,0 @@
|
||||
package prelude
|
||||
|
||||
const types = `
|
||||
var $kindBool = 1;
|
||||
var $kindInt = 2;
|
||||
var $kindInt8 = 3;
|
||||
var $kindInt16 = 4;
|
||||
var $kindInt32 = 5;
|
||||
var $kindInt64 = 6;
|
||||
var $kindUint = 7;
|
||||
var $kindUint8 = 8;
|
||||
var $kindUint16 = 9;
|
||||
var $kindUint32 = 10;
|
||||
var $kindUint64 = 11;
|
||||
var $kindUintptr = 12;
|
||||
var $kindFloat32 = 13;
|
||||
var $kindFloat64 = 14;
|
||||
var $kindComplex64 = 15;
|
||||
var $kindComplex128 = 16;
|
||||
var $kindArray = 17;
|
||||
var $kindChan = 18;
|
||||
var $kindFunc = 19;
|
||||
var $kindInterface = 20;
|
||||
var $kindMap = 21;
|
||||
var $kindPtr = 22;
|
||||
var $kindSlice = 23;
|
||||
var $kindString = 24;
|
||||
var $kindStruct = 25;
|
||||
var $kindUnsafePointer = 26;
|
||||
|
||||
var $methodSynthesizers = [];
|
||||
var $addMethodSynthesizer = function(f) {
|
||||
if ($methodSynthesizers === null) {
|
||||
f();
|
||||
return;
|
||||
}
|
||||
$methodSynthesizers.push(f);
|
||||
};
|
||||
var $synthesizeMethods = function() {
|
||||
$methodSynthesizers.forEach(function(f) { f(); });
|
||||
$methodSynthesizers = null;
|
||||
};
|
||||
|
||||
var $ifaceKeyFor = function(x) {
|
||||
if (x === $ifaceNil) {
|
||||
return 'nil';
|
||||
}
|
||||
var c = x.constructor;
|
||||
return c.string + '$' + c.keyFor(x.$val);
|
||||
};
|
||||
|
||||
var $identity = function(x) { return x; };
|
||||
|
||||
var $typeIDCounter = 0;
|
||||
|
||||
var $idKey = function(x) {
|
||||
if (x.$id === undefined) {
|
||||
$idCounter++;
|
||||
x.$id = $idCounter;
|
||||
}
|
||||
return String(x.$id);
|
||||
};
|
||||
|
||||
var $newType = function(size, kind, string, named, pkg, exported, constructor) {
|
||||
var typ;
|
||||
switch(kind) {
|
||||
case $kindBool:
|
||||
case $kindInt:
|
||||
case $kindInt8:
|
||||
case $kindInt16:
|
||||
case $kindInt32:
|
||||
case $kindUint:
|
||||
case $kindUint8:
|
||||
case $kindUint16:
|
||||
case $kindUint32:
|
||||
case $kindUintptr:
|
||||
case $kindUnsafePointer:
|
||||
typ = function(v) { this.$val = v; };
|
||||
typ.wrapped = true;
|
||||
typ.keyFor = $identity;
|
||||
break;
|
||||
|
||||
case $kindString:
|
||||
typ = function(v) { this.$val = v; };
|
||||
typ.wrapped = true;
|
||||
typ.keyFor = function(x) { return "$" + x; };
|
||||
break;
|
||||
|
||||
case $kindFloat32:
|
||||
case $kindFloat64:
|
||||
typ = function(v) { this.$val = v; };
|
||||
typ.wrapped = true;
|
||||
typ.keyFor = function(x) { return $floatKey(x); };
|
||||
break;
|
||||
|
||||
case $kindInt64:
|
||||
typ = function(high, low) {
|
||||
this.$high = (high + Math.floor(Math.ceil(low) / 4294967296)) >> 0;
|
||||
this.$low = low >>> 0;
|
||||
this.$val = this;
|
||||
};
|
||||
typ.keyFor = function(x) { return x.$high + "$" + x.$low; };
|
||||
break;
|
||||
|
||||
case $kindUint64:
|
||||
typ = function(high, low) {
|
||||
this.$high = (high + Math.floor(Math.ceil(low) / 4294967296)) >>> 0;
|
||||
this.$low = low >>> 0;
|
||||
this.$val = this;
|
||||
};
|
||||
typ.keyFor = function(x) { return x.$high + "$" + x.$low; };
|
||||
break;
|
||||
|
||||
case $kindComplex64:
|
||||
typ = function(real, imag) {
|
||||
this.$real = $fround(real);
|
||||
this.$imag = $fround(imag);
|
||||
this.$val = this;
|
||||
};
|
||||
typ.keyFor = function(x) { return x.$real + "$" + x.$imag; };
|
||||
break;
|
||||
|
||||
case $kindComplex128:
|
||||
typ = function(real, imag) {
|
||||
this.$real = real;
|
||||
this.$imag = imag;
|
||||
this.$val = this;
|
||||
};
|
||||
typ.keyFor = function(x) { return x.$real + "$" + x.$imag; };
|
||||
break;
|
||||
|
||||
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.init = function(elem, len) {
|
||||
typ.elem = elem;
|
||||
typ.len = len;
|
||||
typ.comparable = elem.comparable;
|
||||
typ.keyFor = function(x) {
|
||||
return Array.prototype.join.call($mapArray(x, function(e) {
|
||||
return String(elem.keyFor(e)).replace(/\\/g, "\\\\").replace(/\$/g, "\\$");
|
||||
}), "$");
|
||||
};
|
||||
typ.copy = function(dst, src) {
|
||||
$copyArray(dst, src, 0, 0, src.length, elem);
|
||||
};
|
||||
typ.ptr.init(typ);
|
||||
Object.defineProperty(typ.ptr.nil, "nilCheck", { get: $throwNilPointerError });
|
||||
};
|
||||
break;
|
||||
|
||||
case $kindChan:
|
||||
typ = function(v) { this.$val = v; };
|
||||
typ.wrapped = true;
|
||||
typ.keyFor = $idKey;
|
||||
typ.init = function(elem, sendOnly, recvOnly) {
|
||||
typ.elem = elem;
|
||||
typ.sendOnly = sendOnly;
|
||||
typ.recvOnly = recvOnly;
|
||||
};
|
||||
break;
|
||||
|
||||
case $kindFunc:
|
||||
typ = function(v) { this.$val = v; };
|
||||
typ.wrapped = true;
|
||||
typ.init = function(params, results, variadic) {
|
||||
typ.params = params;
|
||||
typ.results = results;
|
||||
typ.variadic = variadic;
|
||||
typ.comparable = false;
|
||||
};
|
||||
break;
|
||||
|
||||
case $kindInterface:
|
||||
typ = { implementedBy: {}, missingMethodFor: {} };
|
||||
typ.keyFor = $ifaceKeyFor;
|
||||
typ.init = function(methods) {
|
||||
typ.methods = methods;
|
||||
methods.forEach(function(m) {
|
||||
$ifaceNil[m.prop] = $throwNilPointerError;
|
||||
});
|
||||
};
|
||||
break;
|
||||
|
||||
case $kindMap:
|
||||
typ = function(v) { this.$val = v; };
|
||||
typ.wrapped = true;
|
||||
typ.init = function(key, elem) {
|
||||
typ.key = key;
|
||||
typ.elem = elem;
|
||||
typ.comparable = false;
|
||||
};
|
||||
break;
|
||||
|
||||
case $kindPtr:
|
||||
typ = constructor || function(getter, setter, target) {
|
||||
this.$get = getter;
|
||||
this.$set = setter;
|
||||
this.$target = target;
|
||||
this.$val = this;
|
||||
};
|
||||
typ.keyFor = $idKey;
|
||||
typ.init = function(elem) {
|
||||
typ.elem = elem;
|
||||
typ.wrapped = (elem.kind === $kindArray);
|
||||
typ.nil = new typ($throwNilPointerError, $throwNilPointerError);
|
||||
};
|
||||
break;
|
||||
|
||||
case $kindSlice:
|
||||
typ = function(array) {
|
||||
if (array.constructor !== typ.nativeArray) {
|
||||
array = new typ.nativeArray(array);
|
||||
}
|
||||
this.$array = array;
|
||||
this.$offset = 0;
|
||||
this.$length = array.length;
|
||||
this.$capacity = array.length;
|
||||
this.$val = this;
|
||||
};
|
||||
typ.init = function(elem) {
|
||||
typ.elem = elem;
|
||||
typ.comparable = false;
|
||||
typ.nativeArray = $nativeArray(elem.kind);
|
||||
typ.nil = new typ([]);
|
||||
};
|
||||
break;
|
||||
|
||||
case $kindStruct:
|
||||
typ = function(v) { this.$val = v; };
|
||||
typ.wrapped = true;
|
||||
typ.ptr = $newType(4, $kindPtr, "*" + string, false, pkg, exported, constructor);
|
||||
typ.ptr.elem = typ;
|
||||
typ.ptr.prototype.$get = function() { return this; };
|
||||
typ.ptr.prototype.$set = function(v) { typ.copy(this, v); };
|
||||
typ.init = function(pkgPath, fields) {
|
||||
typ.pkgPath = pkgPath;
|
||||
typ.fields = fields;
|
||||
fields.forEach(function(f) {
|
||||
if (!f.typ.comparable) {
|
||||
typ.comparable = false;
|
||||
}
|
||||
});
|
||||
typ.keyFor = function(x) {
|
||||
var val = x.$val;
|
||||
return $mapArray(fields, function(f) {
|
||||
return String(f.typ.keyFor(val[f.prop])).replace(/\\/g, "\\\\").replace(/\$/g, "\\$");
|
||||
}).join("$");
|
||||
};
|
||||
typ.copy = function(dst, src) {
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
var f = fields[i];
|
||||
switch (f.typ.kind) {
|
||||
case $kindArray:
|
||||
case $kindStruct:
|
||||
f.typ.copy(dst[f.prop], src[f.prop]);
|
||||
continue;
|
||||
default:
|
||||
dst[f.prop] = src[f.prop];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
/* nil value */
|
||||
var properties = {};
|
||||
fields.forEach(function(f) {
|
||||
properties[f.prop] = { get: $throwNilPointerError, set: $throwNilPointerError };
|
||||
});
|
||||
typ.ptr.nil = Object.create(constructor.prototype, properties);
|
||||
typ.ptr.nil.$val = typ.ptr.nil;
|
||||
/* methods for embedded fields */
|
||||
$addMethodSynthesizer(function() {
|
||||
var synthesizeMethod = function(target, m, f) {
|
||||
if (target.prototype[m.prop] !== undefined) { return; }
|
||||
target.prototype[m.prop] = function() {
|
||||
var v = this.$val[f.prop];
|
||||
if (f.typ === $jsObjectPtr) {
|
||||
v = new $jsObjectPtr(v);
|
||||
}
|
||||
if (v.$val === undefined) {
|
||||
v = new f.typ(v);
|
||||
}
|
||||
return v[m.prop].apply(v, arguments);
|
||||
};
|
||||
};
|
||||
fields.forEach(function(f) {
|
||||
if (f.anonymous) {
|
||||
$methodSet(f.typ).forEach(function(m) {
|
||||
synthesizeMethod(typ, m, f);
|
||||
synthesizeMethod(typ.ptr, m, f);
|
||||
});
|
||||
$methodSet($ptrType(f.typ)).forEach(function(m) {
|
||||
synthesizeMethod(typ.ptr, m, f);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
$panic(new $String("invalid kind: " + kind));
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case $kindBool:
|
||||
case $kindMap:
|
||||
typ.zero = function() { return false; };
|
||||
break;
|
||||
|
||||
case $kindInt:
|
||||
case $kindInt8:
|
||||
case $kindInt16:
|
||||
case $kindInt32:
|
||||
case $kindUint:
|
||||
case $kindUint8 :
|
||||
case $kindUint16:
|
||||
case $kindUint32:
|
||||
case $kindUintptr:
|
||||
case $kindUnsafePointer:
|
||||
case $kindFloat32:
|
||||
case $kindFloat64:
|
||||
typ.zero = function() { return 0; };
|
||||
break;
|
||||
|
||||
case $kindString:
|
||||
typ.zero = function() { return ""; };
|
||||
break;
|
||||
|
||||
case $kindInt64:
|
||||
case $kindUint64:
|
||||
case $kindComplex64:
|
||||
case $kindComplex128:
|
||||
var zero = new typ(0, 0);
|
||||
typ.zero = function() { return zero; };
|
||||
break;
|
||||
|
||||
case $kindPtr:
|
||||
case $kindSlice:
|
||||
typ.zero = function() { return typ.nil; };
|
||||
break;
|
||||
|
||||
case $kindChan:
|
||||
typ.zero = function() { return $chanNil; };
|
||||
break;
|
||||
|
||||
case $kindFunc:
|
||||
typ.zero = function() { return $throwNilPointerError; };
|
||||
break;
|
||||
|
||||
case $kindInterface:
|
||||
typ.zero = function() { return $ifaceNil; };
|
||||
break;
|
||||
|
||||
case $kindArray:
|
||||
typ.zero = function() {
|
||||
var arrayClass = $nativeArray(typ.elem.kind);
|
||||
if (arrayClass !== Array) {
|
||||
return new arrayClass(typ.len);
|
||||
}
|
||||
var array = new Array(typ.len);
|
||||
for (var i = 0; i < typ.len; i++) {
|
||||
array[i] = typ.elem.zero();
|
||||
}
|
||||
return array;
|
||||
};
|
||||
break;
|
||||
|
||||
case $kindStruct:
|
||||
typ.zero = function() { return new typ.ptr(); };
|
||||
break;
|
||||
|
||||
default:
|
||||
$panic(new $String("invalid kind: " + kind));
|
||||
}
|
||||
|
||||
typ.id = $typeIDCounter;
|
||||
$typeIDCounter++;
|
||||
typ.size = size;
|
||||
typ.kind = kind;
|
||||
typ.string = string;
|
||||
typ.named = named;
|
||||
typ.pkg = pkg;
|
||||
typ.exported = exported;
|
||||
typ.methods = [];
|
||||
typ.methodSetCache = null;
|
||||
typ.comparable = true;
|
||||
return typ;
|
||||
};
|
||||
|
||||
var $methodSet = function(typ) {
|
||||
if (typ.methodSetCache !== null) {
|
||||
return typ.methodSetCache;
|
||||
}
|
||||
var base = {};
|
||||
|
||||
var isPtr = (typ.kind === $kindPtr);
|
||||
if (isPtr && typ.elem.kind === $kindInterface) {
|
||||
typ.methodSetCache = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
var current = [{typ: isPtr ? typ.elem : typ, indirect: isPtr}];
|
||||
|
||||
var seen = {};
|
||||
|
||||
while (current.length > 0) {
|
||||
var next = [];
|
||||
var mset = [];
|
||||
|
||||
current.forEach(function(e) {
|
||||
if (seen[e.typ.string]) {
|
||||
return;
|
||||
}
|
||||
seen[e.typ.string] = true;
|
||||
|
||||
if (e.typ.named) {
|
||||
mset = mset.concat(e.typ.methods);
|
||||
if (e.indirect) {
|
||||
mset = mset.concat($ptrType(e.typ).methods);
|
||||
}
|
||||
}
|
||||
|
||||
switch (e.typ.kind) {
|
||||
case $kindStruct:
|
||||
e.typ.fields.forEach(function(f) {
|
||||
if (f.anonymous) {
|
||||
var fTyp = f.typ;
|
||||
var fIsPtr = (fTyp.kind === $kindPtr);
|
||||
next.push({typ: fIsPtr ? fTyp.elem : fTyp, indirect: e.indirect || fIsPtr});
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case $kindInterface:
|
||||
mset = mset.concat(e.typ.methods);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
mset.forEach(function(m) {
|
||||
if (base[m.name] === undefined) {
|
||||
base[m.name] = m;
|
||||
}
|
||||
});
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
typ.methodSetCache = [];
|
||||
Object.keys(base).sort().forEach(function(name) {
|
||||
typ.methodSetCache.push(base[name]);
|
||||
});
|
||||
return typ.methodSetCache;
|
||||
};
|
||||
|
||||
var $Bool = $newType( 1, $kindBool, "bool", true, "", false, null);
|
||||
var $Int = $newType( 4, $kindInt, "int", true, "", false, null);
|
||||
var $Int8 = $newType( 1, $kindInt8, "int8", true, "", false, null);
|
||||
var $Int16 = $newType( 2, $kindInt16, "int16", true, "", false, null);
|
||||
var $Int32 = $newType( 4, $kindInt32, "int32", true, "", false, null);
|
||||
var $Int64 = $newType( 8, $kindInt64, "int64", true, "", false, null);
|
||||
var $Uint = $newType( 4, $kindUint, "uint", true, "", false, null);
|
||||
var $Uint8 = $newType( 1, $kindUint8, "uint8", true, "", false, null);
|
||||
var $Uint16 = $newType( 2, $kindUint16, "uint16", true, "", false, null);
|
||||
var $Uint32 = $newType( 4, $kindUint32, "uint32", true, "", false, null);
|
||||
var $Uint64 = $newType( 8, $kindUint64, "uint64", true, "", false, null);
|
||||
var $Uintptr = $newType( 4, $kindUintptr, "uintptr", true, "", false, null);
|
||||
var $Float32 = $newType( 4, $kindFloat32, "float32", true, "", false, null);
|
||||
var $Float64 = $newType( 8, $kindFloat64, "float64", true, "", false, null);
|
||||
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 $nativeArray = function(elemKind) {
|
||||
switch (elemKind) {
|
||||
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:
|
||||
return Uint32Array;
|
||||
case $kindUintptr:
|
||||
return Uint32Array;
|
||||
case $kindFloat32:
|
||||
return Float32Array;
|
||||
case $kindFloat64:
|
||||
return Float64Array;
|
||||
default:
|
||||
return Array;
|
||||
}
|
||||
};
|
||||
var $toNativeArray = function(elemKind, array) {
|
||||
var nativeArray = $nativeArray(elemKind);
|
||||
if (nativeArray === Array) {
|
||||
return array;
|
||||
}
|
||||
return new nativeArray(array);
|
||||
};
|
||||
var $arrayTypes = {};
|
||||
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);
|
||||
$arrayTypes[typeKey] = typ;
|
||||
typ.init(elem, len);
|
||||
}
|
||||
return typ;
|
||||
};
|
||||
|
||||
var $chanType = function(elem, sendOnly, recvOnly) {
|
||||
var string = (recvOnly ? "<-" : "") + "chan" + (sendOnly ? "<- " : " ") + elem.string;
|
||||
var field = sendOnly ? "SendChan" : (recvOnly ? "RecvChan" : "Chan");
|
||||
var typ = elem[field];
|
||||
if (typ === undefined) {
|
||||
typ = $newType(4, $kindChan, string, false, "", false, null);
|
||||
elem[field] = typ;
|
||||
typ.init(elem, sendOnly, recvOnly);
|
||||
}
|
||||
return typ;
|
||||
};
|
||||
var $Chan = function(elem, capacity) {
|
||||
if (capacity < 0 || capacity > 2147483647) {
|
||||
$throwRuntimeError("makechan: size out of range");
|
||||
}
|
||||
this.$elem = elem;
|
||||
this.$capacity = capacity;
|
||||
this.$buffer = [];
|
||||
this.$sendQueue = [];
|
||||
this.$recvQueue = [];
|
||||
this.$closed = false;
|
||||
};
|
||||
var $chanNil = new $Chan(null, 0);
|
||||
$chanNil.$sendQueue = $chanNil.$recvQueue = { length: 0, push: function() {}, shift: function() { return undefined; }, indexOf: function() { return -1; } };
|
||||
|
||||
var $funcTypes = {};
|
||||
var $funcType = function(params, results, variadic) {
|
||||
var typeKey = $mapArray(params, function(p) { return p.id; }).join(",") + "$" + $mapArray(results, function(r) { return r.id; }).join(",") + "$" + variadic;
|
||||
var typ = $funcTypes[typeKey];
|
||||
if (typ === undefined) {
|
||||
var paramTypes = $mapArray(params, function(p) { return p.string; });
|
||||
if (variadic) {
|
||||
paramTypes[paramTypes.length - 1] = "..." + paramTypes[paramTypes.length - 1].substr(2);
|
||||
}
|
||||
var string = "func(" + paramTypes.join(", ") + ")";
|
||||
if (results.length === 1) {
|
||||
string += " " + results[0].string;
|
||||
} else if (results.length > 1) {
|
||||
string += " (" + $mapArray(results, function(r) { return r.string; }).join(", ") + ")";
|
||||
}
|
||||
typ = $newType(4, $kindFunc, string, false, "", false, null);
|
||||
$funcTypes[typeKey] = typ;
|
||||
typ.init(params, results, variadic);
|
||||
}
|
||||
return typ;
|
||||
};
|
||||
|
||||
var $interfaceTypes = {};
|
||||
var $interfaceType = function(methods) {
|
||||
var typeKey = $mapArray(methods, function(m) { return m.pkg + "," + m.name + "," + m.typ.id; }).join("$");
|
||||
var typ = $interfaceTypes[typeKey];
|
||||
if (typ === undefined) {
|
||||
var string = "interface {}";
|
||||
if (methods.length !== 0) {
|
||||
string = "interface { " + $mapArray(methods, function(m) {
|
||||
return (m.pkg !== "" ? m.pkg + "." : "") + m.name + m.typ.string.substr(4);
|
||||
}).join("; ") + " }";
|
||||
}
|
||||
typ = $newType(8, $kindInterface, string, false, "", false, null);
|
||||
$interfaceTypes[typeKey] = typ;
|
||||
typ.init(methods);
|
||||
}
|
||||
return typ;
|
||||
};
|
||||
var $emptyInterface = $interfaceType([]);
|
||||
var $ifaceNil = {};
|
||||
var $error = $newType(8, $kindInterface, "error", true, "", false, null);
|
||||
$error.init([{prop: "Error", name: "Error", pkg: "", typ: $funcType([], [$String], false)}]);
|
||||
|
||||
var $mapTypes = {};
|
||||
var $mapType = function(key, elem) {
|
||||
var typeKey = key.id + "$" + elem.id;
|
||||
var typ = $mapTypes[typeKey];
|
||||
if (typ === undefined) {
|
||||
typ = $newType(4, $kindMap, "map[" + key.string + "]" + elem.string, false, "", false, null);
|
||||
$mapTypes[typeKey] = typ;
|
||||
typ.init(key, elem);
|
||||
}
|
||||
return typ;
|
||||
};
|
||||
var $makeMap = function(keyForFunc, entries) {
|
||||
var m = {};
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
var e = entries[i];
|
||||
m[keyForFunc(e.k)] = e;
|
||||
}
|
||||
return m;
|
||||
};
|
||||
|
||||
var $ptrType = function(elem) {
|
||||
var typ = elem.ptr;
|
||||
if (typ === undefined) {
|
||||
typ = $newType(4, $kindPtr, "*" + elem.string, false, "", elem.exported, null);
|
||||
elem.ptr = typ;
|
||||
typ.init(elem);
|
||||
}
|
||||
return typ;
|
||||
};
|
||||
|
||||
var $newDataPointer = function(data, constructor) {
|
||||
if (constructor.elem.kind === $kindStruct) {
|
||||
return data;
|
||||
}
|
||||
return new constructor(function() { return data; }, function(v) { data = v; });
|
||||
};
|
||||
|
||||
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; }));
|
||||
};
|
||||
|
||||
var $sliceType = function(elem) {
|
||||
var typ = elem.slice;
|
||||
if (typ === undefined) {
|
||||
typ = $newType(12, $kindSlice, "[]" + elem.string, false, "", false, null);
|
||||
elem.slice = typ;
|
||||
typ.init(elem);
|
||||
}
|
||||
return typ;
|
||||
};
|
||||
var $makeSlice = function(typ, length, capacity) {
|
||||
capacity = capacity || length;
|
||||
if (length < 0 || length > 2147483647) {
|
||||
$throwRuntimeError("makeslice: len out of range");
|
||||
}
|
||||
if (capacity < 0 || capacity < length || capacity > 2147483647) {
|
||||
$throwRuntimeError("makeslice: cap out of range");
|
||||
}
|
||||
var array = new typ.nativeArray(capacity);
|
||||
if (typ.nativeArray === Array) {
|
||||
for (var i = 0; i < capacity; i++) {
|
||||
array[i] = typ.elem.zero();
|
||||
}
|
||||
}
|
||||
var slice = new typ(array);
|
||||
slice.$length = length;
|
||||
return slice;
|
||||
};
|
||||
|
||||
var $structTypes = {};
|
||||
var $structType = function(pkgPath, fields) {
|
||||
var typeKey = $mapArray(fields, function(f) { return f.name + "," + f.typ.id + "," + f.tag; }).join("$");
|
||||
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, "\\\"") + "\"") : "");
|
||||
}).join("; ") + " }";
|
||||
if (fields.length === 0) {
|
||||
string = "struct {}";
|
||||
}
|
||||
typ = $newType(0, $kindStruct, string, false, "", false, function() {
|
||||
this.$val = this;
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
var f = fields[i];
|
||||
var arg = arguments[i];
|
||||
this[f.prop] = arg !== undefined ? arg : f.typ.zero();
|
||||
}
|
||||
});
|
||||
$structTypes[typeKey] = typ;
|
||||
typ.init(pkgPath, fields);
|
||||
}
|
||||
return typ;
|
||||
};
|
||||
|
||||
var $assertType = function(value, type, returnTuple) {
|
||||
var isInterface = (type.kind === $kindInterface), ok, missingMethod = "";
|
||||
if (value === $ifaceNil) {
|
||||
ok = false;
|
||||
} else if (!isInterface) {
|
||||
ok = value.constructor === type;
|
||||
} else {
|
||||
var valueTypeString = value.constructor.string;
|
||||
ok = type.implementedBy[valueTypeString];
|
||||
if (ok === undefined) {
|
||||
ok = true;
|
||||
var valueMethodSet = $methodSet(value.constructor);
|
||||
var interfaceMethods = type.methods;
|
||||
for (var i = 0; i < interfaceMethods.length; i++) {
|
||||
var tm = interfaceMethods[i];
|
||||
var found = false;
|
||||
for (var j = 0; j < valueMethodSet.length; j++) {
|
||||
var vm = valueMethodSet[j];
|
||||
if (vm.name === tm.name && vm.pkg === tm.pkg && vm.typ === tm.typ) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ok = false;
|
||||
type.missingMethodFor[valueTypeString] = tm.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
type.implementedBy[valueTypeString] = ok;
|
||||
}
|
||||
if (!ok) {
|
||||
missingMethod = type.missingMethodFor[valueTypeString];
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
if (returnTuple) {
|
||||
return [type.zero(), false];
|
||||
}
|
||||
$panic(new $packages["runtime"].TypeAssertionError.ptr("", (value === $ifaceNil ? "" : value.constructor.string), type.string, missingMethod));
|
||||
}
|
||||
|
||||
if (!isInterface) {
|
||||
value = value.$val;
|
||||
}
|
||||
if (type === $jsObjectPtr) {
|
||||
value = value.object;
|
||||
}
|
||||
return returnTuple ? [value, true] : value;
|
||||
};
|
||||
`
|
||||
-786
@@ -1,786 +0,0 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"github.com/gopherjs/gopherjs/compiler/analysis"
|
||||
"github.com/gopherjs/gopherjs/compiler/astutil"
|
||||
"github.com/gopherjs/gopherjs/compiler/filter"
|
||||
"github.com/gopherjs/gopherjs/compiler/typesutil"
|
||||
)
|
||||
|
||||
func (c *funcContext) translateStmtList(stmts []ast.Stmt) {
|
||||
for _, stmt := range stmts {
|
||||
c.translateStmt(stmt, nil)
|
||||
}
|
||||
c.SetPos(token.NoPos)
|
||||
}
|
||||
|
||||
func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) {
|
||||
c.SetPos(stmt.Pos())
|
||||
|
||||
stmt = filter.IncDecStmt(stmt, c.p.Info.Info)
|
||||
stmt = filter.Assign(stmt, c.p.Info.Info, c.p.Info.Pkg)
|
||||
|
||||
switch s := stmt.(type) {
|
||||
case *ast.BlockStmt:
|
||||
c.translateStmtList(s.List)
|
||||
|
||||
case *ast.IfStmt:
|
||||
var caseClauses []*ast.CaseClause
|
||||
ifStmt := s
|
||||
for {
|
||||
if ifStmt.Init != nil {
|
||||
panic("simplification error")
|
||||
}
|
||||
caseClauses = append(caseClauses, &ast.CaseClause{List: []ast.Expr{ifStmt.Cond}, Body: ifStmt.Body.List})
|
||||
elseStmt, ok := ifStmt.Else.(*ast.IfStmt)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
ifStmt = elseStmt
|
||||
}
|
||||
var defaultClause *ast.CaseClause
|
||||
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])
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
if s.Init != nil || s.Tag != nil || len(s.Body.List) != 1 {
|
||||
panic("simplification error")
|
||||
}
|
||||
clause := s.Body.List[0].(*ast.CaseClause)
|
||||
if len(clause.List) != 0 {
|
||||
panic("simplification error")
|
||||
}
|
||||
|
||||
prevFlowData := c.flowDatas[nil]
|
||||
data := &flowData{
|
||||
postStmt: prevFlowData.postStmt, // for "continue" of outer loop
|
||||
beginCase: prevFlowData.beginCase, // same
|
||||
}
|
||||
c.flowDatas[nil] = data
|
||||
c.flowDatas[label] = data
|
||||
defer func() {
|
||||
delete(c.flowDatas, label)
|
||||
c.flowDatas[nil] = prevFlowData
|
||||
}()
|
||||
|
||||
if c.Flattened[s] {
|
||||
data.endCase = c.caseCounter
|
||||
c.caseCounter++
|
||||
|
||||
c.Indent(func() {
|
||||
c.translateStmtList(clause.Body)
|
||||
})
|
||||
c.Printf("case %d:", data.endCase)
|
||||
return
|
||||
}
|
||||
|
||||
if label != nil || analysis.HasBreak(clause) {
|
||||
if label != nil {
|
||||
c.Printf("%s:", label.Name())
|
||||
}
|
||||
c.Printf("switch (0) { default:")
|
||||
c.Indent(func() {
|
||||
c.translateStmtList(clause.Body)
|
||||
})
|
||||
c.Printf("}")
|
||||
return
|
||||
}
|
||||
|
||||
c.translateStmtList(clause.Body)
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
if s.Init != nil {
|
||||
c.translateStmt(s.Init, nil)
|
||||
}
|
||||
refVar := c.newVariable("_ref")
|
||||
var expr ast.Expr
|
||||
switch a := s.Assign.(type) {
|
||||
case *ast.AssignStmt:
|
||||
expr = a.Rhs[0].(*ast.TypeAssertExpr).X
|
||||
case *ast.ExprStmt:
|
||||
expr = a.X.(*ast.TypeAssertExpr).X
|
||||
}
|
||||
c.Printf("%s = %s;", refVar, c.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)
|
||||
}
|
||||
return c.formatExpr("$assertType(%s, %s, true)[1]", refVar, c.typeName(c.p.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 {
|
||||
value := refVar
|
||||
if typesutil.IsJsObject(implicit.Type().Underlying()) {
|
||||
value += ".$val.object"
|
||||
} else if _, ok := implicit.Type().Underlying().(*types.Interface); !ok {
|
||||
value += ".$val"
|
||||
}
|
||||
bodyPrefix = []ast.Stmt{&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{c.newIdent(c.objectName(implicit), implicit.Type())},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{c.newIdent(value, implicit.Type())},
|
||||
}}
|
||||
}
|
||||
c := &ast.CaseClause{
|
||||
List: clause.List,
|
||||
Body: append(bodyPrefix, clause.Body...),
|
||||
}
|
||||
if len(c.List) == 0 {
|
||||
defaultClause = c
|
||||
continue
|
||||
}
|
||||
caseClauses = append(caseClauses, c)
|
||||
}
|
||||
c.translateBranchingStmt(caseClauses, defaultClause, true, translateCond, label, c.Flattened[s])
|
||||
|
||||
case *ast.ForStmt:
|
||||
if s.Init != nil {
|
||||
c.translateStmt(s.Init, nil)
|
||||
}
|
||||
cond := func() string {
|
||||
if s.Cond == nil {
|
||||
return "true"
|
||||
}
|
||||
return c.translateExpr(s.Cond).String()
|
||||
}
|
||||
c.translateLoopingStmt(cond, s.Body, nil, func() {
|
||||
if s.Post != nil {
|
||||
c.translateStmt(s.Post, nil)
|
||||
}
|
||||
}, label, c.Flattened[s])
|
||||
|
||||
case *ast.RangeStmt:
|
||||
refVar := c.newVariable("_ref")
|
||||
c.Printf("%s = %s;", refVar, c.translateExpr(s.X))
|
||||
|
||||
switch t := c.p.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)
|
||||
if !isBlank(s.Key) {
|
||||
c.Printf("%s", c.translateAssign(s.Key, c.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))
|
||||
}
|
||||
}, func() {
|
||||
c.Printf("%s += %s[1];", iVar, runeVar)
|
||||
}, label, c.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]),
|
||||
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))
|
||||
}
|
||||
if !isBlank(s.Value) {
|
||||
c.Printf("%s", c.translateAssign(s.Value, c.newIdent(entryVar+".v", t.Elem()), s.Tok == token.DEFINE))
|
||||
}
|
||||
}, func() {
|
||||
c.Printf("%s++;", iVar)
|
||||
}, label, c.Flattened[s])
|
||||
|
||||
case *types.Array, *types.Pointer, *types.Slice:
|
||||
var length string
|
||||
var elemType types.Type
|
||||
switch t2 := t.(type) {
|
||||
case *types.Array:
|
||||
length = fmt.Sprintf("%d", t2.Len())
|
||||
elemType = t2.Elem()
|
||||
case *types.Pointer:
|
||||
length = fmt.Sprintf("%d", t2.Elem().Underlying().(*types.Array).Len())
|
||||
elemType = t2.Elem().Underlying().(*types.Array).Elem()
|
||||
case *types.Slice:
|
||||
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() {
|
||||
if !isBlank(s.Key) {
|
||||
c.Printf("%s", c.translateAssign(s.Key, c.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]),
|
||||
}, elemType), s.Tok == token.DEFINE))
|
||||
}
|
||||
}, func() {
|
||||
c.Printf("%s++;", iVar)
|
||||
}, label, c.Flattened[s])
|
||||
|
||||
case *types.Chan:
|
||||
okVar := c.newIdent(c.newVariable("_ok"), types.Typ[types.Bool])
|
||||
key := s.Key
|
||||
tok := s.Tok
|
||||
if key == nil {
|
||||
key = ast.NewIdent("_")
|
||||
tok = token.ASSIGN
|
||||
}
|
||||
forStmt := &ast.ForStmt{
|
||||
Body: &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
key,
|
||||
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]))),
|
||||
},
|
||||
Tok: tok,
|
||||
},
|
||||
&ast.IfStmt{
|
||||
Cond: &ast.UnaryExpr{X: okVar, Op: token.NOT},
|
||||
Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.BREAK}}},
|
||||
},
|
||||
s.Body,
|
||||
},
|
||||
},
|
||||
}
|
||||
c.Flattened[forStmt] = true
|
||||
c.translateStmt(forStmt, label)
|
||||
|
||||
default:
|
||||
panic("")
|
||||
}
|
||||
|
||||
case *ast.BranchStmt:
|
||||
normalLabel := ""
|
||||
blockingLabel := ""
|
||||
data := c.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)]
|
||||
}
|
||||
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))
|
||||
case token.CONTINUE:
|
||||
data.postStmt()
|
||||
c.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))))
|
||||
case token.FALLTHROUGH:
|
||||
// handled in CaseClause
|
||||
default:
|
||||
panic("Unhandled branch statment: " + s.Tok.String())
|
||||
}
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
results := s.Results
|
||||
if c.resultNames != nil {
|
||||
if len(s.Results) != 0 {
|
||||
c.translateStmt(&ast.AssignStmt{
|
||||
Lhs: c.resultNames,
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: s.Results,
|
||||
}, nil)
|
||||
}
|
||||
results = c.resultNames
|
||||
}
|
||||
rVal := c.translateResults(results)
|
||||
if len(c.Flattened) != 0 {
|
||||
c.Printf("$s = -1; 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, ", "))
|
||||
return
|
||||
}
|
||||
c.Printf("$deferred.push([%s, [%s]]);", c.translateExpr(s.Call.Fun), strings.Join(args, ", "))
|
||||
|
||||
case *ast.AssignStmt:
|
||||
if s.Tok != token.ASSIGN && s.Tok != token.DEFINE {
|
||||
panic(s.Tok)
|
||||
}
|
||||
|
||||
switch {
|
||||
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]))
|
||||
return
|
||||
}
|
||||
c.Printf("%s", c.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)
|
||||
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))
|
||||
}
|
||||
}
|
||||
case len(s.Lhs) == len(s.Rhs):
|
||||
tmpVars := make([]string, len(s.Rhs))
|
||||
for i, rhs := range s.Rhs {
|
||||
tmpVars[i] = c.newVariable("_tmp")
|
||||
if isBlank(astutil.RemoveParens(s.Lhs[i])) {
|
||||
c.Printf("$unused(%s);", c.translateExpr(rhs))
|
||||
continue
|
||||
}
|
||||
c.Printf("%s", c.translateAssign(c.newIdent(tmpVars[i], c.p.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))
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
panic("Invalid arity of AssignStmt.")
|
||||
|
||||
}
|
||||
|
||||
case *ast.DeclStmt:
|
||||
decl := s.Decl.(*ast.GenDecl)
|
||||
switch decl.Tok {
|
||||
case token.VAR:
|
||||
for _, spec := range s.Decl.(*ast.GenDecl).Specs {
|
||||
valueSpec := spec.(*ast.ValueSpec)
|
||||
lhs := make([]ast.Expr, len(valueSpec.Names))
|
||||
for i, name := range valueSpec.Names {
|
||||
lhs[i] = name
|
||||
}
|
||||
rhs := valueSpec.Values
|
||||
if len(rhs) == 0 {
|
||||
rhs = make([]ast.Expr, len(lhs))
|
||||
for i, e := range lhs {
|
||||
rhs[i] = c.zeroValue(c.p.TypeOf(e))
|
||||
}
|
||||
}
|
||||
c.translateStmt(&ast.AssignStmt{
|
||||
Lhs: lhs,
|
||||
Tok: token.DEFINE,
|
||||
Rhs: rhs,
|
||||
}, nil)
|
||||
}
|
||||
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
|
||||
}
|
||||
case token.CONST:
|
||||
// skip, constants are inlined
|
||||
}
|
||||
|
||||
case *ast.ExprStmt:
|
||||
expr := c.translateExpr(s.X)
|
||||
if expr != nil && expr.String() != "" {
|
||||
c.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)))
|
||||
}
|
||||
c.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()), ", "))
|
||||
|
||||
case *ast.SendStmt:
|
||||
chanType := c.p.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())},
|
||||
}
|
||||
c.Blocking[call] = true
|
||||
c.translateStmt(&ast.ExprStmt{X: call}, label)
|
||||
|
||||
case *ast.SelectStmt:
|
||||
selectionVar := c.newVariable("_selection")
|
||||
var channels []string
|
||||
var caseClauses []*ast.CaseClause
|
||||
flattened := false
|
||||
hasDefault := false
|
||||
for i, cc := range s.Body.List {
|
||||
clause := cc.(*ast.CommClause)
|
||||
switch comm := clause.Comm.(type) {
|
||||
case nil:
|
||||
channels = append(channels, "[]")
|
||||
hasDefault = true
|
||||
case *ast.ExprStmt:
|
||||
channels = append(channels, c.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())
|
||||
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())
|
||||
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))}
|
||||
|
||||
var bodyPrefix []ast.Stmt
|
||||
if assign, ok := clause.Comm.(*ast.AssignStmt); ok {
|
||||
switch rhsType := c.p.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}}
|
||||
default:
|
||||
bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}}
|
||||
}
|
||||
}
|
||||
|
||||
caseClauses = append(caseClauses, &ast.CaseClause{
|
||||
List: []ast.Expr{indexLit},
|
||||
Body: append(bodyPrefix, clause.Body...),
|
||||
})
|
||||
|
||||
flattened = flattened || c.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))},
|
||||
}, types.Typ[types.Int])
|
||||
c.Blocking[selectCall] = !hasDefault
|
||||
c.Printf("%s = %s;", selectionVar, c.translateExpr(selectCall))
|
||||
|
||||
if len(caseClauses) != 0 {
|
||||
translateCond := func(cond ast.Expr) *expression {
|
||||
return c.formatExpr("%s[0] === %e", selectionVar, cond)
|
||||
}
|
||||
c.translateBranchingStmt(caseClauses, nil, true, translateCond, label, flattened)
|
||||
}
|
||||
|
||||
case *ast.EmptyStmt:
|
||||
// skip
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Unhandled statement: %T\n", s))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (c *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
|
||||
defaultCase = caseOffset + len(caseClauses)
|
||||
endCase = defaultCase
|
||||
if defaultClause != nil {
|
||||
endCase++
|
||||
}
|
||||
c.caseCounter = endCase + 1
|
||||
}
|
||||
|
||||
hasBreak := false
|
||||
if canBreak {
|
||||
prevFlowData := c.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
|
||||
defer func() {
|
||||
delete(c.flowDatas, label)
|
||||
c.flowDatas[nil] = prevFlowData
|
||||
}()
|
||||
|
||||
for _, child := range caseClauses {
|
||||
if analysis.HasBreak(child) {
|
||||
hasBreak = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if defaultClause != nil && analysis.HasBreak(defaultClause) {
|
||||
hasBreak = true
|
||||
}
|
||||
}
|
||||
|
||||
if label != nil && !flatten {
|
||||
c.Printf("%s:", label.Name())
|
||||
}
|
||||
|
||||
condStrs := make([]string, len(caseClauses))
|
||||
for i, clause := range caseClauses {
|
||||
conds := make([]string, len(clause.List))
|
||||
for j, cond := range clause.List {
|
||||
conds[j] = translateCond(cond).String()
|
||||
}
|
||||
condStrs[i] = strings.Join(conds, " || ")
|
||||
if flatten {
|
||||
c.Printf("/* */ if (%s) { $s = %d; continue; }", condStrs[i], caseOffset+i)
|
||||
}
|
||||
}
|
||||
|
||||
if flatten {
|
||||
c.Printf("/* */ $s = %d; continue;", defaultCase)
|
||||
}
|
||||
|
||||
prefix := ""
|
||||
suffix := ""
|
||||
if label != nil || hasBreak {
|
||||
prefix = "switch (0) { default: "
|
||||
suffix = " }"
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
prefix = "} else "
|
||||
}
|
||||
|
||||
if defaultClause != nil {
|
||||
c.PrintCond(!flatten, prefix+"{", fmt.Sprintf("case %d:", caseOffset+len(caseClauses)))
|
||||
c.Indent(func() {
|
||||
c.translateStmtList(defaultClause.Body)
|
||||
})
|
||||
}
|
||||
|
||||
c.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]
|
||||
data := &flowData{
|
||||
postStmt: post,
|
||||
}
|
||||
if flatten {
|
||||
data.beginCase = c.caseCounter
|
||||
data.endCase = c.caseCounter + 1
|
||||
c.caseCounter += 2
|
||||
}
|
||||
c.flowDatas[nil] = data
|
||||
c.flowDatas[label] = data
|
||||
defer func() {
|
||||
delete(c.flowDatas, label)
|
||||
c.flowDatas[nil] = prevFlowData
|
||||
}()
|
||||
|
||||
if !flatten && label != nil {
|
||||
c.Printf("%s:", label.Name())
|
||||
}
|
||||
c.PrintCond(!flatten, "while (true) {", fmt.Sprintf("case %d:", data.beginCase))
|
||||
c.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))
|
||||
}
|
||||
|
||||
prevEV := c.p.escapingVars
|
||||
c.handleEscapingVars(body)
|
||||
|
||||
if bodyPrefix != nil {
|
||||
bodyPrefix()
|
||||
}
|
||||
c.translateStmtList(body.List)
|
||||
isTerminated := false
|
||||
if len(body.List) != 0 {
|
||||
switch body.List[len(body.List)-1].(type) {
|
||||
case *ast.ReturnStmt, *ast.BranchStmt:
|
||||
isTerminated = true
|
||||
}
|
||||
}
|
||||
if !isTerminated {
|
||||
post()
|
||||
}
|
||||
|
||||
c.p.escapingVars = prevEV
|
||||
})
|
||||
c.PrintCond(!flatten, "}", fmt.Sprintf("$s = %d; continue; case %d:", data.beginCase, data.endCase))
|
||||
}
|
||||
|
||||
func (c *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()))
|
||||
}
|
||||
}
|
||||
|
||||
lhsType := c.p.TypeOf(lhs)
|
||||
rhsExpr := c.translateImplicitConversion(rhs, lhsType)
|
||||
if _, ok := rhs.(*ast.CompositeLit); ok && define {
|
||||
return fmt.Sprintf("%s = %s;", c.translateExpr(lhs), rhsExpr) // skip $copy
|
||||
}
|
||||
|
||||
isReflectValue := false
|
||||
if named, ok := lhsType.(*types.Named); ok && named.Obj().Pkg() != nil && named.Obj().Pkg().Path() == "reflect" && named.Obj().Name() == "Value" {
|
||||
isReflectValue = true
|
||||
}
|
||||
if !isReflectValue { // this is a performance hack, but it is safe since reflect.Value has no exported fields and the reflect package does not violate this assumption
|
||||
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.copy(%s, %s);", c.typeName(lhsType), c.translateExpr(lhs), rhsExpr)
|
||||
}
|
||||
}
|
||||
|
||||
switch l := lhs.(type) {
|
||||
case *ast.Ident:
|
||||
return fmt.Sprintf("%s = %s;", c.objectName(c.p.ObjectOf(l)), rhsExpr)
|
||||
case *ast.SelectorExpr:
|
||||
sel, ok := c.p.SelectionOf(l)
|
||||
if !ok {
|
||||
// qualified identifier
|
||||
return fmt.Sprintf("%s = %s;", c.objectName(c.p.Uses[l.Sel]), rhsExpr)
|
||||
}
|
||||
fields, jsTag := c.translateSelection(sel, l.Pos())
|
||||
if jsTag != "" {
|
||||
return fmt.Sprintf("%s.%s.%s = %s;", c.translateExpr(l.X), strings.Join(fields, "."), jsTag, c.externalize(rhsExpr.String(), sel.Type()))
|
||||
}
|
||||
return fmt.Sprintf("%s.%s = %s;", c.translateExpr(l.X), strings.Join(fields, "."), rhsExpr)
|
||||
case *ast.StarExpr:
|
||||
return fmt.Sprintf("%s.$set(%s);", c.translateExpr(l.X), rhsExpr)
|
||||
case *ast.IndexExpr:
|
||||
switch t := c.p.TypeOf(l.X).Underlying().(type) {
|
||||
case *types.Array, *types.Pointer:
|
||||
pattern := rangeCheck("%1e[%2f] = %3s", c.p.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() + ";"
|
||||
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() + ";"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unhandled lhs type: %T\n", t))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unhandled lhs type: %T\n", l))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *funcContext) translateResults(results []ast.Expr) string {
|
||||
tuple := c.sig.Results()
|
||||
switch tuple.Len() {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
result := c.zeroValue(tuple.At(0).Type())
|
||||
if results != nil {
|
||||
result = results[0]
|
||||
}
|
||||
v := c.translateImplicitConversion(result, tuple.At(0).Type())
|
||||
c.delayedOutput = nil
|
||||
return " " + v.String()
|
||||
default:
|
||||
if len(results) == 1 {
|
||||
resultTuple := c.p.TypeOf(results[0]).(*types.Tuple)
|
||||
|
||||
if resultTuple.Len() != tuple.Len() {
|
||||
panic("invalid tuple return assignment")
|
||||
}
|
||||
|
||||
resultExpr := c.translateExpr(results[0]).String()
|
||||
|
||||
if types.Identical(resultTuple, tuple) {
|
||||
return " " + resultExpr
|
||||
}
|
||||
|
||||
tmpVar := c.newVariable("_returncast")
|
||||
c.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())
|
||||
}
|
||||
}
|
||||
values := make([]string, tuple.Len())
|
||||
for i := range values {
|
||||
result := c.zeroValue(tuple.At(i).Type())
|
||||
if results != nil {
|
||||
result = results[i]
|
||||
}
|
||||
values[i] = c.translateImplicitConversion(result, tuple.At(i).Type()).String()
|
||||
}
|
||||
c.delayedOutput = nil
|
||||
return " [" + strings.Join(values, ", ") + "]"
|
||||
}
|
||||
}
|
||||
|
||||
func (c *funcContext) labelCase(label *types.Label) int {
|
||||
labelCase, ok := c.labelCases[label]
|
||||
if !ok {
|
||||
labelCase = c.caseCounter
|
||||
c.caseCounter++
|
||||
c.labelCases[label] = labelCase
|
||||
}
|
||||
return labelCase
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package typesutil
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func IsJsPackage(pkg *types.Package) bool {
|
||||
return pkg != nil && (pkg.Path() == "github.com/gopherjs/gopherjs/js" || strings.HasSuffix(pkg.Path(), "/vendor/github.com/gopherjs/gopherjs/js"))
|
||||
}
|
||||
|
||||
func IsJsObject(t types.Type) bool {
|
||||
ptr, isPtr := t.(*types.Pointer)
|
||||
if !isPtr {
|
||||
return false
|
||||
}
|
||||
named, isNamed := ptr.Elem().(*types.Named)
|
||||
return isNamed && IsJsPackage(named.Obj().Pkg()) && named.Obj().Name() == "Object"
|
||||
}
|
||||
-645
@@ -1,645 +0,0 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gopherjs/gopherjs/compiler/analysis"
|
||||
"github.com/gopherjs/gopherjs/compiler/typesutil"
|
||||
)
|
||||
|
||||
func (c *funcContext) Write(b []byte) (int, error) {
|
||||
c.writePos()
|
||||
c.output = append(c.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 (c *funcContext) PrintCond(cond bool, onTrue, onFalse string) {
|
||||
if !cond {
|
||||
c.Printf("/* %s */ %s", strings.Replace(onTrue, "*/", "<star>/", -1), onFalse)
|
||||
return
|
||||
}
|
||||
c.Printf("%s", onTrue)
|
||||
}
|
||||
|
||||
func (c *funcContext) SetPos(pos token.Pos) {
|
||||
c.posAvailable = true
|
||||
c.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 (c *funcContext) Indent(f func()) {
|
||||
c.p.indentation++
|
||||
f()
|
||||
c.p.indentation--
|
||||
}
|
||||
|
||||
func (c *funcContext) CatchOutput(indent int, f func()) []byte {
|
||||
origoutput := c.output
|
||||
c.output = nil
|
||||
c.p.indentation += indent
|
||||
f()
|
||||
c.writePos()
|
||||
catched := c.output
|
||||
c.output = origoutput
|
||||
c.p.indentation -= indent
|
||||
return catched
|
||||
}
|
||||
|
||||
func (c *funcContext) Delayed(f func()) {
|
||||
c.delayedOutput = c.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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paramsLen := sig.Params().Len()
|
||||
|
||||
var varargType *types.Slice
|
||||
if sig.Variadic() && !ellipsis {
|
||||
varargType = sig.Params().At(paramsLen - 1).Type().(*types.Slice)
|
||||
}
|
||||
|
||||
preserveOrder := false
|
||||
for i := 1; i < len(argExprs); i++ {
|
||||
preserveOrder = preserveOrder || c.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()
|
||||
|
||||
if preserveOrder && c.p.Types[argExpr].Value == nil {
|
||||
argVar := c.newVariable("_arg")
|
||||
c.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:], ", ")))
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (c *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 {
|
||||
t = ptr.Elem()
|
||||
}
|
||||
s := t.Underlying().(*types.Struct)
|
||||
if jsTag := getJsTag(s.Tag(index)); jsTag != "" {
|
||||
jsFieldName := s.Field(index).Name()
|
||||
for {
|
||||
fields = append(fields, fieldName(s, 0))
|
||||
ft := s.Field(0).Type()
|
||||
if typesutil.IsJsObject(ft) {
|
||||
return fields, jsTag
|
||||
}
|
||||
ft = ft.Underlying()
|
||||
if ptr, ok := ft.(*types.Pointer); ok {
|
||||
ft = ptr.Elem().Underlying()
|
||||
}
|
||||
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})
|
||||
return nil, ""
|
||||
}
|
||||
}
|
||||
}
|
||||
fields = append(fields, fieldName(s, index))
|
||||
t = s.Field(index).Type()
|
||||
}
|
||||
return fields, ""
|
||||
}
|
||||
|
||||
var nilObj = types.Universe.Lookup("nil")
|
||||
|
||||
func (c *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))
|
||||
case isNumeric(t):
|
||||
return c.newConst(ty, constant.MakeInt64(0))
|
||||
case isString(t):
|
||||
return c.newConst(ty, constant.MakeString(""))
|
||||
case t.Kind() == types.UnsafePointer:
|
||||
// fall through to "nil"
|
||||
case t.Kind() == types.UntypedNil:
|
||||
panic("Zero value for untyped nil.")
|
||||
default:
|
||||
panic(fmt.Sprintf("Unhandled basic type: %v\n", t))
|
||||
}
|
||||
case *types.Array, *types.Struct:
|
||||
return c.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
|
||||
return id
|
||||
}
|
||||
|
||||
func (c *funcContext) newConst(t types.Type, value constant.Value) ast.Expr {
|
||||
id := &ast.Ident{}
|
||||
c.p.Types[id] = types.TypeAndValue{Type: t, Value: value}
|
||||
return id
|
||||
}
|
||||
|
||||
func (c *funcContext) newVariable(name string) string {
|
||||
return c.newVariableWithLevel(name, false)
|
||||
}
|
||||
|
||||
func (c *funcContext) newVariableWithLevel(name string, pkgLevel bool) string {
|
||||
if name == "" {
|
||||
panic("newVariable: empty name")
|
||||
}
|
||||
name = encodeIdent(name)
|
||||
if c.p.minify {
|
||||
i := 0
|
||||
for {
|
||||
offset := int('a')
|
||||
if pkgLevel {
|
||||
offset = int('A')
|
||||
}
|
||||
j := i
|
||||
name = ""
|
||||
for {
|
||||
name = string(offset+(j%26)) + name
|
||||
j = j/26 - 1
|
||||
if j == -1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if c.allVars[name] == 0 {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
n := c.allVars[name]
|
||||
c.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 {
|
||||
c2.allVars[name] = n + 1
|
||||
}
|
||||
return varName
|
||||
}
|
||||
|
||||
c.localVars = append(c.localVars, varName)
|
||||
return varName
|
||||
}
|
||||
|
||||
func (c *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
|
||||
return ident
|
||||
}
|
||||
|
||||
func (c *funcContext) setType(e ast.Expr, t types.Type) ast.Expr {
|
||||
c.p.Types[e] = types.TypeAndValue{Type: t}
|
||||
return e
|
||||
}
|
||||
|
||||
func (c *funcContext) pkgVar(pkg *types.Package) string {
|
||||
if pkg == c.p.Pkg {
|
||||
return "$pkg"
|
||||
}
|
||||
|
||||
pkgVar, found := c.p.pkgVars[pkg.Path()]
|
||||
if !found {
|
||||
pkgVar = fmt.Sprintf(`$packages["%s"]`, pkg.Path())
|
||||
}
|
||||
return pkgVar
|
||||
}
|
||||
|
||||
func isVarOrConst(o types.Object) bool {
|
||||
switch o.(type) {
|
||||
case *types.Var, *types.Const:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isPkgLevel(o types.Object) bool {
|
||||
return o.Parent() != nil && o.Parent().Parent() == types.Universe
|
||||
}
|
||||
|
||||
func (c *funcContext) objectName(o types.Object) string {
|
||||
if isPkgLevel(o) {
|
||||
c.p.dependencies[o] = true
|
||||
|
||||
if o.Pkg() != c.p.Pkg || (isVarOrConst(o) && o.Exported()) {
|
||||
return c.pkgVar(o.Pkg()) + "." + o.Name()
|
||||
}
|
||||
}
|
||||
|
||||
name, ok := c.p.objectNames[o]
|
||||
if !ok {
|
||||
name = c.newVariableWithLevel(o.Name(), isPkgLevel(o))
|
||||
c.p.objectNames[o] = name
|
||||
}
|
||||
|
||||
if v, ok := o.(*types.Var); ok && c.p.escapingVars[v] {
|
||||
return name + "[0]"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (c *funcContext) varPtrName(o *types.Var) string {
|
||||
if isPkgLevel(o) && o.Exported() {
|
||||
return c.pkgVar(o.Pkg()) + "." + o.Name() + "$ptr"
|
||||
}
|
||||
|
||||
name, ok := c.p.varPtrNames[o]
|
||||
if !ok {
|
||||
name = c.newVariableWithLevel(o.Name()+"$ptr", isPkgLevel(o))
|
||||
c.p.varPtrNames[o] = name
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (c *funcContext) typeName(ty types.Type) string {
|
||||
switch t := ty.(type) {
|
||||
case *types.Basic:
|
||||
return "$" + toJavaScriptType(t)
|
||||
case *types.Named:
|
||||
if t.Obj().Name() == "error" {
|
||||
return "$error"
|
||||
}
|
||||
return c.objectName(t.Obj())
|
||||
case *types.Interface:
|
||||
if t.Empty() {
|
||||
return "$emptyInterface"
|
||||
}
|
||||
}
|
||||
|
||||
anonType, ok := c.p.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)
|
||||
}
|
||||
c.p.dependencies[anonType] = true
|
||||
return anonType.Name()
|
||||
}
|
||||
|
||||
func (c *funcContext) externalize(s string, t types.Type) string {
|
||||
if typesutil.IsJsObject(t) {
|
||||
return s
|
||||
}
|
||||
switch u := t.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if isNumeric(u) && !is64Bit(u) && !isComplex(u) {
|
||||
return s
|
||||
}
|
||||
if u.Kind() == types.UntypedNil {
|
||||
return "null"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("$externalize(%s, %s)", s, c.typeName(t))
|
||||
}
|
||||
|
||||
func (c *funcContext) handleEscapingVars(n ast.Node) {
|
||||
newEscapingVars := make(map[*types.Var]bool)
|
||||
for escaping := range c.p.escapingVars {
|
||||
newEscapingVars[escaping] = true
|
||||
}
|
||||
c.p.escapingVars = newEscapingVars
|
||||
|
||||
var names []string
|
||||
objs := analysis.EscapingObjects(n, c.p.Info.Info)
|
||||
sort.Slice(objs, func(i, j int) bool {
|
||||
if objs[i].Name() == objs[j].Name() {
|
||||
return objs[i].Pos() < objs[j].Pos()
|
||||
}
|
||||
return objs[i].Name() < objs[j].Name()
|
||||
})
|
||||
for _, obj := range objs {
|
||||
names = append(names, c.objectName(obj))
|
||||
c.p.escapingVars[obj] = true
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
c.Printf("%s = [%s];", name, name)
|
||||
}
|
||||
}
|
||||
|
||||
func fieldName(t *types.Struct, i int) string {
|
||||
name := t.Field(i).Name()
|
||||
if name == "_" || reservedKeywords[name] {
|
||||
return fmt.Sprintf("%s$%d", name, i)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func typeKind(ty types.Type) string {
|
||||
switch t := ty.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return "$kind" + toJavaScriptType(t)
|
||||
case *types.Array:
|
||||
return "$kindArray"
|
||||
case *types.Chan:
|
||||
return "$kindChan"
|
||||
case *types.Interface:
|
||||
return "$kindInterface"
|
||||
case *types.Map:
|
||||
return "$kindMap"
|
||||
case *types.Signature:
|
||||
return "$kindFunc"
|
||||
case *types.Slice:
|
||||
return "$kindSlice"
|
||||
case *types.Struct:
|
||||
return "$kindStruct"
|
||||
case *types.Pointer:
|
||||
return "$kindPtr"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unhandled type: %T\n", t))
|
||||
}
|
||||
}
|
||||
|
||||
func toJavaScriptType(t *types.Basic) string {
|
||||
switch t.Kind() {
|
||||
case types.UntypedInt:
|
||||
return "Int"
|
||||
case types.Byte:
|
||||
return "Uint8"
|
||||
case types.Rune:
|
||||
return "Int32"
|
||||
case types.UnsafePointer:
|
||||
return "UnsafePointer"
|
||||
default:
|
||||
name := t.String()
|
||||
return strings.ToUpper(name[:1]) + name[1:]
|
||||
}
|
||||
}
|
||||
|
||||
func is64Bit(t *types.Basic) bool {
|
||||
return t.Kind() == types.Int64 || t.Kind() == types.Uint64
|
||||
}
|
||||
|
||||
func isBoolean(t *types.Basic) bool {
|
||||
return t.Info()&types.IsBoolean != 0
|
||||
}
|
||||
|
||||
func isComplex(t *types.Basic) bool {
|
||||
return t.Info()&types.IsComplex != 0
|
||||
}
|
||||
|
||||
func isFloat(t *types.Basic) bool {
|
||||
return t.Info()&types.IsFloat != 0
|
||||
}
|
||||
|
||||
func isInteger(t *types.Basic) bool {
|
||||
return t.Info()&types.IsInteger != 0
|
||||
}
|
||||
|
||||
func isNumeric(t *types.Basic) bool {
|
||||
return t.Info()&types.IsNumeric != 0
|
||||
}
|
||||
|
||||
func isString(t *types.Basic) bool {
|
||||
return t.Info()&types.IsString != 0
|
||||
}
|
||||
|
||||
func isUnsigned(t *types.Basic) bool {
|
||||
return t.Info()&types.IsUnsigned != 0
|
||||
}
|
||||
|
||||
func isBlank(expr ast.Expr) bool {
|
||||
if expr == nil {
|
||||
return true
|
||||
}
|
||||
if id, isIdent := expr.(*ast.Ident); isIdent {
|
||||
return id.Name == "_"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isWrapped(ty types.Type) bool {
|
||||
switch t := ty.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return !is64Bit(t) && !isComplex(t) && t.Kind() != types.UntypedNil
|
||||
case *types.Array, *types.Chan, *types.Map, *types.Signature:
|
||||
return true
|
||||
case *types.Pointer:
|
||||
_, isArray := t.Elem().Underlying().(*types.Array)
|
||||
return isArray
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func encodeString(s string) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for _, r := range []byte(s) {
|
||||
switch r {
|
||||
case '\b':
|
||||
buffer.WriteString(`\b`)
|
||||
case '\f':
|
||||
buffer.WriteString(`\f`)
|
||||
case '\n':
|
||||
buffer.WriteString(`\n`)
|
||||
case '\r':
|
||||
buffer.WriteString(`\r`)
|
||||
case '\t':
|
||||
buffer.WriteString(`\t`)
|
||||
case '\v':
|
||||
buffer.WriteString(`\v`)
|
||||
case '"':
|
||||
buffer.WriteString(`\"`)
|
||||
case '\\':
|
||||
buffer.WriteString(`\\`)
|
||||
default:
|
||||
if r < 0x20 || r > 0x7E {
|
||||
fmt.Fprintf(buffer, `\x%02X`, r)
|
||||
continue
|
||||
}
|
||||
buffer.WriteByte(r)
|
||||
}
|
||||
}
|
||||
return `"` + buffer.String() + `"`
|
||||
}
|
||||
|
||||
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 needsSpace(c byte) bool {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '$'
|
||||
}
|
||||
|
||||
func removeWhitespace(b []byte, minify bool) []byte {
|
||||
if !minify {
|
||||
return b
|
||||
}
|
||||
|
||||
var out []byte
|
||||
var previous byte
|
||||
for len(b) > 0 {
|
||||
switch b[0] {
|
||||
case '\b':
|
||||
out = append(out, b[:5]...)
|
||||
b = b[5:]
|
||||
continue
|
||||
case ' ', '\t', '\n':
|
||||
if (!needsSpace(previous) || !needsSpace(b[1])) && !(previous == '-' && b[1] == '-') {
|
||||
b = b[1:]
|
||||
continue
|
||||
}
|
||||
case '"':
|
||||
out = append(out, '"')
|
||||
b = b[1:]
|
||||
for {
|
||||
i := bytes.IndexAny(b, "\"\\")
|
||||
out = append(out, b[:i]...)
|
||||
b = b[i:]
|
||||
if b[0] == '"' {
|
||||
break
|
||||
}
|
||||
// backslash
|
||||
out = append(out, b[:2]...)
|
||||
b = b[2:]
|
||||
}
|
||||
case '/':
|
||||
if b[1] == '*' {
|
||||
i := bytes.Index(b[2:], []byte("*/"))
|
||||
b = b[i+4:]
|
||||
continue
|
||||
}
|
||||
}
|
||||
out = append(out, b[0])
|
||||
previous = b[0]
|
||||
b = b[1:]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func rangeCheck(pattern string, constantIndex, array bool) string {
|
||||
if constantIndex && array {
|
||||
return pattern
|
||||
}
|
||||
lengthProp := "$length"
|
||||
if array {
|
||||
lengthProp = "length"
|
||||
}
|
||||
check := "%2f >= %1e." + lengthProp
|
||||
if !constantIndex {
|
||||
check = "(%2f < 0 || " + check + ")"
|
||||
}
|
||||
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)
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// +build !go1.10
|
||||
// +build go1.9
|
||||
|
||||
package compiler
|
||||
|
||||
const ___GOPHERJS_REQUIRES_GO_VERSION_1_9___ = true
|
||||
|
||||
// Version is the GopherJS compiler version string.
|
||||
const Version = "1.9-1"
|
||||
-156
@@ -1,156 +0,0 @@
|
||||
# Supported Packages
|
||||
|
||||
On each commit, Circle CI automatically compiles all supported packages with GopherJS and runs their tests:
|
||||
|
||||
[](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;<br>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 |
|
||||
reflect | ✅ yes | except StructOf (pending)
|
||||
regexp | ✅ yes |
|
||||
-- syntax | ✅ yes |
|
||||
runtime | ☑️ partially | SetMutexProfileFraction, SetFinalizer 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 | ✅ yes |
|
||||
-- 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 |
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
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.
|
||||
|
||||
### 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.
|
||||
|
||||
### In Browser
|
||||
|
||||
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 Windows
|
||||
|
||||
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 OS X and Linux, 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.
|
||||
|
||||
### Node.js on OS X and Linux
|
||||
|
||||
GopherJS has support for system calls on OS X and Linux. Before running your code with Node.js, you need to install the system calls module. The module is compatible with Node.js version 0.12 and above. 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
|
||||
```
|
||||
|
||||
### Caveats
|
||||
|
||||
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.
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
// Package sysutil contains system-specific utilities.
|
||||
package sysutil
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// RlimitStack reports the current stack size limit in bytes.
|
||||
func RlimitStack() (cur uint64, err error) {
|
||||
var r unix.Rlimit
|
||||
err = unix.Getrlimit(unix.RLIMIT_STACK, &r)
|
||||
return r.Cur, err
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package sysutil
|
||||
|
||||
import "errors"
|
||||
|
||||
func RlimitStack() (uint64, error) {
|
||||
return 0, errors.New("RlimitStack is not implemented on Windows")
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'syscall',
|
||||
'sources': [ 'syscall.cc' ]
|
||||
}
|
||||
]
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
#include <cstdlib>
|
||||
#include <node.h>
|
||||
#include <v8.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace v8;
|
||||
|
||||
#if NODE_MAJOR_VERSION == 0
|
||||
#define ARRAY_BUFFER_DATA_OFFSET 23
|
||||
#else
|
||||
#define ARRAY_BUFFER_DATA_OFFSET 31
|
||||
#endif
|
||||
|
||||
intptr_t toNative(Local<Value> value) {
|
||||
if (value.IsEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
if (value->IsArrayBufferView()) {
|
||||
Local<ArrayBufferView> view = Local<ArrayBufferView>::Cast(value);
|
||||
return *reinterpret_cast<intptr_t*>(*reinterpret_cast<char**>(*view->Buffer()) + ARRAY_BUFFER_DATA_OFFSET) + view->ByteOffset(); // ugly hack, because of https://codereview.chromium.org/25221002
|
||||
}
|
||||
if (value->IsArray()) {
|
||||
Local<Array> array = Local<Array>::Cast(value);
|
||||
intptr_t* native = reinterpret_cast<intptr_t*>(malloc(array->Length() * sizeof(intptr_t))); // TODO memory leak
|
||||
for (uint32_t i = 0; i < array->Length(); i++) {
|
||||
native[i] = toNative(array->CloneElementAt(i));
|
||||
}
|
||||
return reinterpret_cast<intptr_t>(native);
|
||||
}
|
||||
return static_cast<intptr_t>(static_cast<int32_t>(value->ToInteger()->Value()));
|
||||
}
|
||||
|
||||
void Syscall(const FunctionCallbackInfo<Value>& 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];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
r1 = syscall(
|
||||
trap,
|
||||
toNative(info[1]),
|
||||
toNative(info[2]),
|
||||
toNative(info[3])
|
||||
);
|
||||
break;
|
||||
}
|
||||
int err = 0;
|
||||
if (r1 < 0) {
|
||||
err = errno;
|
||||
}
|
||||
Isolate* isolate = info.GetIsolate();
|
||||
Local<Array> 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<Value>& 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;
|
||||
}
|
||||
Isolate* isolate = info.GetIsolate();
|
||||
Local<Array> 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);
|
||||
}
|
||||
|
||||
void init(Handle<Object> target) {
|
||||
NODE_SET_METHOD(target, "Syscall", Syscall);
|
||||
NODE_SET_METHOD(target, "Syscall6", Syscall6);
|
||||
}
|
||||
|
||||
NODE_MODULE(syscall, init);
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
package nosync
|
||||
|
||||
// Map is a concurrent map with amortized-constant-time loads, stores, and deletes.
|
||||
// It is safe for multiple goroutines to call a Map's methods concurrently.
|
||||
//
|
||||
// The zero Map is valid and empty.
|
||||
//
|
||||
// A Map must not be copied after first use.
|
||||
type Map struct {
|
||||
m map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// Load returns the value stored in the map for a key, or nil if no
|
||||
// value is present.
|
||||
// The ok result indicates whether value was found in the map.
|
||||
func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
|
||||
value, ok = m.m[key]
|
||||
return value, ok
|
||||
}
|
||||
|
||||
// Store sets the value for a key.
|
||||
func (m *Map) Store(key, value interface{}) {
|
||||
if m.m == nil {
|
||||
m.m = make(map[interface{}]interface{})
|
||||
}
|
||||
m.m[key] = value
|
||||
}
|
||||
|
||||
// LoadOrStore returns the existing value for the key if present.
|
||||
// Otherwise, it stores and returns the given value.
|
||||
// The loaded result is true if the value was loaded, false if stored.
|
||||
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) {
|
||||
if value, ok := m.m[key]; ok {
|
||||
return value, true
|
||||
}
|
||||
if m.m == nil {
|
||||
m.m = make(map[interface{}]interface{})
|
||||
}
|
||||
m.m[key] = value
|
||||
return value, false
|
||||
}
|
||||
|
||||
// Delete deletes the value for a key.
|
||||
func (m *Map) Delete(key interface{}) {
|
||||
if m.m == nil {
|
||||
return
|
||||
}
|
||||
delete(m.m, key)
|
||||
}
|
||||
|
||||
// Range calls f sequentially for each key and value present in the map.
|
||||
// If f returns false, range stops the iteration.
|
||||
//
|
||||
// Range does not necessarily correspond to any consistent snapshot of the Map's
|
||||
// contents: no key will be visited more than once, but if the value for any key
|
||||
// is stored or deleted concurrently, Range may reflect any mapping for that key
|
||||
// from any point during the Range call.
|
||||
//
|
||||
// Range may be O(N) with the number of elements in the map even if f returns
|
||||
// false after a constant number of calls.
|
||||
func (m *Map) Range(f func(key, value interface{}) bool) {
|
||||
for k, v := range m.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
package nosync
|
||||
|
||||
// Mutex is a dummy which is non-blocking.
|
||||
type Mutex struct {
|
||||
locked bool
|
||||
}
|
||||
|
||||
// Lock locks m. It is a run-time error if m is already locked.
|
||||
func (m *Mutex) Lock() {
|
||||
if m.locked {
|
||||
panic("nosync: mutex is already locked")
|
||||
}
|
||||
m.locked = true
|
||||
}
|
||||
|
||||
// Unlock unlocks m. It is a run-time error if m is not locked.
|
||||
func (m *Mutex) Unlock() {
|
||||
if !m.locked {
|
||||
panic("nosync: unlock of unlocked mutex")
|
||||
}
|
||||
m.locked = false
|
||||
}
|
||||
|
||||
// RWMutex is a dummy which is non-blocking.
|
||||
type RWMutex struct {
|
||||
writeLocked bool
|
||||
readLockCounter int
|
||||
}
|
||||
|
||||
// Lock locks m for writing. It is a run-time error if rw is already locked for reading or writing.
|
||||
func (rw *RWMutex) Lock() {
|
||||
if rw.readLockCounter != 0 || rw.writeLocked {
|
||||
panic("nosync: mutex is already locked")
|
||||
}
|
||||
rw.writeLocked = true
|
||||
}
|
||||
|
||||
// Unlock unlocks rw for writing. It is a run-time error if rw is not locked for writing.
|
||||
func (rw *RWMutex) Unlock() {
|
||||
if !rw.writeLocked {
|
||||
panic("nosync: unlock of unlocked mutex")
|
||||
}
|
||||
rw.writeLocked = false
|
||||
}
|
||||
|
||||
// RLock locks m for reading. It is a run-time error if rw is already locked for reading or writing.
|
||||
func (rw *RWMutex) RLock() {
|
||||
if rw.writeLocked {
|
||||
panic("nosync: mutex is already locked")
|
||||
}
|
||||
rw.readLockCounter++
|
||||
}
|
||||
|
||||
// RUnlock undoes a single RLock call; it does not affect other simultaneous readers. It is a run-time error if rw is not locked for reading.
|
||||
func (rw *RWMutex) RUnlock() {
|
||||
if rw.readLockCounter == 0 {
|
||||
panic("nosync: unlock of unlocked mutex")
|
||||
}
|
||||
rw.readLockCounter--
|
||||
}
|
||||
|
||||
// WaitGroup is a dummy which is non-blocking.
|
||||
type WaitGroup struct {
|
||||
counter int
|
||||
}
|
||||
|
||||
// Add adds delta, which may be negative, to the WaitGroup If the counter goes negative, Add panics.
|
||||
func (wg *WaitGroup) Add(delta int) {
|
||||
wg.counter += delta
|
||||
if wg.counter < 0 {
|
||||
panic("sync: negative WaitGroup counter")
|
||||
}
|
||||
}
|
||||
|
||||
// Done decrements the WaitGroup counter.
|
||||
func (wg *WaitGroup) Done() {
|
||||
wg.Add(-1)
|
||||
}
|
||||
|
||||
// Wait panics if the WaitGroup counter is not zero.
|
||||
func (wg *WaitGroup) Wait() {
|
||||
if wg.counter != 0 {
|
||||
panic("sync: WaitGroup counter not zero")
|
||||
}
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package nosync
|
||||
|
||||
// Once is an object that will perform exactly one action.
|
||||
type Once struct {
|
||||
doing bool
|
||||
done bool
|
||||
}
|
||||
|
||||
// Do calls the function f if and only if Do is being called for the
|
||||
// first time for this instance of Once. In other words, given
|
||||
// var once Once
|
||||
// if once.Do(f) is called multiple times, only the first call will invoke f,
|
||||
// even if f has a different value in each invocation. A new instance of
|
||||
// Once is required for each function to execute.
|
||||
//
|
||||
// Do is intended for initialization that must be run exactly once. Since f
|
||||
// is niladic, it may be necessary to use a function literal to capture the
|
||||
// arguments to a function to be invoked by Do:
|
||||
// config.once.Do(func() { config.init(filename) })
|
||||
//
|
||||
// If f causes Do to be called, it will panic.
|
||||
//
|
||||
// If f panics, Do considers it to have returned; future calls of Do return
|
||||
// without calling f.
|
||||
//
|
||||
func (o *Once) Do(f func()) {
|
||||
if o.done {
|
||||
return
|
||||
}
|
||||
if o.doing {
|
||||
panic("nosync: Do called within f")
|
||||
}
|
||||
o.doing = true
|
||||
defer func() {
|
||||
o.doing = false
|
||||
o.done = true
|
||||
}()
|
||||
f()
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
package nosync
|
||||
|
||||
// A Pool is a set of temporary objects that may be individually saved and
|
||||
// retrieved.
|
||||
//
|
||||
// Any item stored in the Pool may be removed automatically at any time without
|
||||
// notification. If the Pool holds the only reference when this happens, the
|
||||
// item might be deallocated.
|
||||
//
|
||||
// A Pool is safe for use by multiple goroutines simultaneously.
|
||||
//
|
||||
// Pool's purpose is to cache allocated but unused items for later reuse,
|
||||
// relieving pressure on the garbage collector. That is, it makes it easy to
|
||||
// build efficient, thread-safe free lists. However, it is not suitable for all
|
||||
// free lists.
|
||||
//
|
||||
// An appropriate use of a Pool is to manage a group of temporary items
|
||||
// silently shared among and potentially reused by concurrent independent
|
||||
// clients of a package. Pool provides a way to amortize allocation overhead
|
||||
// across many clients.
|
||||
//
|
||||
// An example of good use of a Pool is in the fmt package, which maintains a
|
||||
// dynamically-sized store of temporary output buffers. The store scales under
|
||||
// load (when many goroutines are actively printing) and shrinks when
|
||||
// quiescent.
|
||||
//
|
||||
// On the other hand, a free list maintained as part of a short-lived object is
|
||||
// not a suitable use for a Pool, since the overhead does not amortize well in
|
||||
// that scenario. It is more efficient to have such objects implement their own
|
||||
// free list.
|
||||
//
|
||||
type Pool struct {
|
||||
store []interface{}
|
||||
New func() interface{}
|
||||
}
|
||||
|
||||
// Get selects an arbitrary item from the Pool, removes it from the
|
||||
// Pool, and returns it to the caller.
|
||||
// Get may choose to ignore the pool and treat it as empty.
|
||||
// Callers should not assume any relation between values passed to Put and
|
||||
// the values returned by Get.
|
||||
//
|
||||
// If Get would otherwise return nil and p.New is non-nil, Get returns
|
||||
// the result of calling p.New.
|
||||
func (p *Pool) Get() interface{} {
|
||||
if len(p.store) == 0 {
|
||||
if p.New != nil {
|
||||
return p.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
x := p.store[len(p.store)-1]
|
||||
p.store = p.store[:len(p.store)-1]
|
||||
return x
|
||||
}
|
||||
|
||||
// Put adds x to the pool.
|
||||
func (p *Pool) Put(x interface{}) {
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
p.store = append(p.store, x)
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
// Package tests contains tests for GopherJS.
|
||||
package tests
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
var mainDidRun = false
|
||||
|
||||
func main() {
|
||||
mainDidRun = true
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
package otherpkg
|
||||
|
||||
var Test float32
|
||||
-1204
File diff suppressed because it is too large
Load Diff
-19
@@ -1,19 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
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).
|
||||
js.Global.Get("console").Call("log", struct{ S string }{"externalization ok"})
|
||||
|
||||
// Excercise 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"))
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
{ S: 'externalization ok' }
|
||||
Sat, 29 Aug 2015 20:56:00 GMT
|
||||
Reference in New Issue
Block a user