remove unused code from vendor

This commit is contained in:
bergquist
2018-01-19 09:54:22 +01:00
parent e023f79c5a
commit 7ed643f302
6531 changed files with 6 additions and 2907556 deletions
-802
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
}
File diff suppressed because it is too large Load Diff
-106
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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/")
},
)
File diff suppressed because one or more lines are too long
@@ -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
}
@@ -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")
}
@@ -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")
}
@@ -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)
}
@@ -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
}
@@ -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
View File
@@ -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]
}
@@ -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
}
}
@@ -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
View File
@@ -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
View File
@@ -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")
}
@@ -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() {}
File diff suppressed because it is too large Load Diff
@@ -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)
}
}
@@ -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
}
@@ -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
}
@@ -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))
}
@@ -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
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
@@ -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
}
}
@@ -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
}
@@ -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
}
@@ -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 ""
}
@@ -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
}
@@ -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
}
@@ -1,8 +0,0 @@
// +build js
package testing
func callerName(skip int) string {
// TODO: Implement if possible.
return "<unknown>"
}
@@ -1,5 +0,0 @@
// +build js
package template
const maxExecDepth = 3000
-101
View File
@@ -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()
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,156 +0,0 @@
# Supported Packages
On each commit, Circle CI automatically compiles all supported packages with GopherJS and runs their tests:
[![Circle CI](https://circleci.com/gh/gopherjs/gopherjs.svg?style=svg)](https://circleci.com/gh/gopherjs/gopherjs)
Name | Supported | Comment
------------------ | ------------ | ----------------------------------------------------------------------------------
archive | |
-- tar | ✅ yes |
-- zip | ✅ yes |
bufio | ✅ yes |
builtin | ✅ yes |
bytes | ✅ yes |
compress | |
-- bzip2 | ✅ yes |
-- flate | ✅ yes |
-- gzip | ✅ yes |
-- lzw | ✅ yes |
-- zlib | ✅ yes |
container | |
-- heap | ✅ yes |
-- list | ✅ yes |
-- ring | ✅ yes |
crypto | |
-- aes | ✅ yes |
-- cipher | ✅ yes |
-- des | ✅ yes |
-- dsa | ✅ yes |
-- ecdsa | ✅ yes |
-- elliptic | ✅ yes |
-- hmac | ✅ yes |
-- md5 | ✅ yes |
-- rand | ✅ yes |
-- rc4 | ✅ yes |
-- rsa | ✅ yes |
-- sha1 | ✅ yes |
-- sha256 | ✅ yes |
-- sha512 | ✅ yes |
-- subtle | ✅ yes |
-- tls | ❌ no |
-- x509 | ✅ yes |
-- -- pkix | ✅ yes |
database | |
-- sql | ✅ yes |
-- -- driver | ✅ yes |
debug | |
-- dwarf | ✅ yes |
-- elf | ✅ yes |
-- gosym | ☑️ partially | on binaries generated by gc
-- macho | ✅ yes |
-- pe | ✅ yes |
encoding | |
-- ascii85 | ✅ yes |
-- asn1 | ✅ yes |
-- base32 | ✅ yes |
-- base64 | ✅ yes |
-- binary | ✅ yes |
-- csv | ✅ yes |
-- gob | ✅ yes |
-- hex | ✅ yes |
-- json | ✅ yes |
-- pem | ✅ yes |
-- xml | ✅ yes |
errors | ✅ yes |
expvar | ✅ yes |
flag | ✅ yes |
fmt | ✅ yes |
go | |
-- ast | ✅ yes |
-- build | ❌ no |
-- constant | ✅ yes |
-- doc | ✅ yes |
-- format | ✅ yes |
-- importer | ❌ no |
-- parser | ✅ yes |
-- printer | ✅ yes |
-- scanner | ✅ yes |
-- token | ✅ yes |
-- types | ❌ no |
hash | |
-- adler32 | ✅ yes |
-- crc32 | ✅ yes |
-- crc64 | ✅ yes |
-- fnv | ✅ yes |
html | ✅ yes |
-- template | ✅ yes |
image | ✅ yes |
-- color | ✅ yes |
-- -- palette | ✅ yes |
-- draw | ✅ yes |
-- gif | ✅ yes |
-- jpeg | ✅ yes |
-- png | ✅ yes |
index | |
-- suffixarray | ✅ yes |
io | ✅ yes |
-- ioutil | ✅ yes |
log | ✅ yes |
-- syslog | ❌ no |
math | ✅ yes |
-- big | ✅ yes |
-- bits | ✅ yes |
-- cmplx | ✅ yes |
-- rand | ✅ yes |
mime | ✅ yes |
-- multipart | ✅ yes |
-- quotedprintable | ✅ yes |
net | ❌ no |
-- http | ☑️ partially | client only, emulated via Fetch/XMLHttpRequest APIs;<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
View File
@@ -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
View File
@@ -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
}
@@ -1,7 +0,0 @@
package sysutil
import "errors"
func RlimitStack() (uint64, error) {
return 0, errors.New("RlimitStack is not implemented on Windows")
}
-8
View File
@@ -1,8 +0,0 @@
{
'targets': [
{
'target_name': 'syscall',
'sources': [ 'syscall.cc' ]
}
]
}
-98
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,2 +0,0 @@
// Package tests contains tests for GopherJS.
package tests
-7
View File
@@ -1,7 +0,0 @@
package main
var mainDidRun = false
func main() {
mainDidRun = true
}
-3
View File
@@ -1,3 +0,0 @@
package otherpkg
var Test float32
-1204
View File
File diff suppressed because it is too large Load Diff
@@ -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"))
}
@@ -1,2 +0,0 @@
{ S: 'externalization ok' }
Sat, 29 Aug 2015 20:56:00 GMT