github.com/goplusjs/gopherjs@v1.2.6-0.20211206034512-f187917453b8/compiler/package.go (about)

     1  package compiler
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"go/ast"
     8  	"go/constant"
     9  	"go/token"
    10  	"go/types"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/goplusjs/gopherjs/compiler/analysis"
    15  	"github.com/neelance/astrewrite"
    16  	"golang.org/x/tools/go/gcexportdata"
    17  	"golang.org/x/tools/go/types/typeutil"
    18  )
    19  
    20  /*
    21  go:linkname
    22  
    23  pkg.func
    24  LinkName{
    25  	Target: "pkg.func"
    26  	TargetName: "__linkname__func"
    27  	TargetImportPath: "pkg"
    28  }
    29  
    30  pkg.recv.method
    31  LinkName {
    32  	Target: "(pkg.recv).method"
    33  	TargetName: "__linkname__recv_method"
    34  	TargetRecv: "recv"
    35  	TargetMethod: "method"
    36  	TargetImportPath: "pkg"
    37  }
    38  
    39  pkg.(*recv).method
    40  LinkName {
    41  	Target: "(*pkg.recv).method"
    42  	TargetName: "__linkname__recv_method"
    43  	TargetRecv: "*recv"
    44  	TargetMethod: "method"
    45  	TargetImportPath: "pkg"
    46  }
    47  */
    48  
    49  type LinkName struct {
    50  	Local            string
    51  	Target           string
    52  	TargetName       string
    53  	TargetRecv       string
    54  	TargetMethod     string
    55  	TargetImportPath string
    56  }
    57  
    58  type pkgContext struct {
    59  	*analysis.Info
    60  	additionalSelections map[*ast.SelectorExpr]selection
    61  
    62  	typeNames    []*types.TypeName
    63  	pkgVars      map[string]string
    64  	objectNames  map[types.Object]string
    65  	varPtrNames  map[*types.Var]string
    66  	anonTypes    []*types.TypeName
    67  	anonTypeMap  typeutil.Map
    68  	escapingVars map[*types.Var]bool
    69  	indentation  int
    70  	dependencies map[types.Object]bool
    71  	minify       bool
    72  	fileSet      *token.FileSet
    73  	errList      ErrorList
    74  }
    75  
    76  func (p *pkgContext) SelectionOf(e *ast.SelectorExpr) (selection, bool) {
    77  	if sel, ok := p.Selections[e]; ok {
    78  		return sel, true
    79  	}
    80  	if sel, ok := p.additionalSelections[e]; ok {
    81  		return sel, true
    82  	}
    83  	return nil, false
    84  }
    85  
    86  type selection interface {
    87  	Kind() types.SelectionKind
    88  	Recv() types.Type
    89  	Index() []int
    90  	Obj() types.Object
    91  	Type() types.Type
    92  }
    93  
    94  type fakeSelection struct {
    95  	kind  types.SelectionKind
    96  	recv  types.Type
    97  	index []int
    98  	obj   types.Object
    99  	typ   types.Type
   100  }
   101  
   102  func (sel *fakeSelection) Kind() types.SelectionKind { return sel.kind }
   103  func (sel *fakeSelection) Recv() types.Type          { return sel.recv }
   104  func (sel *fakeSelection) Index() []int              { return sel.index }
   105  func (sel *fakeSelection) Obj() types.Object         { return sel.obj }
   106  func (sel *fakeSelection) Type() types.Type          { return sel.typ }
   107  
   108  type funcContext struct {
   109  	*analysis.FuncInfo
   110  	p             *pkgContext
   111  	parent        *funcContext
   112  	sig           *types.Signature
   113  	allVars       map[string]int
   114  	localVars     []string
   115  	resultNames   []ast.Expr
   116  	flowDatas     map[*types.Label]*flowData
   117  	caseCounter   int
   118  	labelCases    map[*types.Label]int
   119  	output        []byte
   120  	delayedOutput []byte
   121  	posAvailable  bool
   122  	pos           token.Pos
   123  }
   124  
   125  type flowData struct {
   126  	postStmt  func()
   127  	beginCase int
   128  	endCase   int
   129  }
   130  
   131  type ImportContext struct {
   132  	Packages map[string]*types.Package
   133  	Import   func(string) (*Archive, error)
   134  }
   135  
   136  // packageImporter implements go/types.Importer interface.
   137  type packageImporter struct {
   138  	importContext *ImportContext
   139  	importError   *error // A pointer to importError in Compile.
   140  }
   141  
   142  func (pi packageImporter) Import(path string) (*types.Package, error) {
   143  	if path == "unsafe" {
   144  		return types.Unsafe, nil
   145  	}
   146  
   147  	a, err := pi.importContext.Import(path)
   148  	if err != nil {
   149  		if *pi.importError == nil {
   150  			// If import failed, show first error of import only (https://github.com/gopherjs/gopherjs/issues/119).
   151  			*pi.importError = err
   152  		}
   153  		return nil, err
   154  	}
   155  
   156  	return pi.importContext.Packages[a.ImportPath], nil
   157  }
   158  
   159  func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, importContext *ImportContext, linknames []LinkName, minify bool) (*Archive, error) {
   160  	typesInfo := &types.Info{
   161  		Types:      make(map[ast.Expr]types.TypeAndValue),
   162  		Defs:       make(map[*ast.Ident]types.Object),
   163  		Uses:       make(map[*ast.Ident]types.Object),
   164  		Implicits:  make(map[ast.Node]types.Object),
   165  		Selections: make(map[*ast.SelectorExpr]*types.Selection),
   166  		Scopes:     make(map[ast.Node]*types.Scope),
   167  	}
   168  
   169  	var importError error
   170  	var errList ErrorList
   171  	var previousErr error
   172  	config := &types.Config{
   173  		Importer: packageImporter{
   174  			importContext: importContext,
   175  			importError:   &importError,
   176  		},
   177  		Sizes: sizes32,
   178  		Error: func(err error) {
   179  			if previousErr != nil && previousErr.Error() == err.Error() {
   180  				return
   181  			}
   182  			errList = append(errList, err)
   183  			previousErr = err
   184  		},
   185  	}
   186  	typesPkg, err := config.Check(importPath, fileSet, files, typesInfo)
   187  	if importError != nil {
   188  		return nil, importError
   189  	}
   190  	if errList != nil {
   191  		if len(errList) > 10 {
   192  			pos := token.NoPos
   193  			if last, ok := errList[9].(types.Error); ok {
   194  				pos = last.Pos
   195  			}
   196  			errList = append(errList[:10], types.Error{Fset: fileSet, Pos: pos, Msg: "too many errors"})
   197  		}
   198  		return nil, errList
   199  	}
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	importContext.Packages[importPath] = typesPkg
   204  
   205  	exportData := new(bytes.Buffer)
   206  	if err := gcexportdata.Write(exportData, nil, typesPkg); err != nil {
   207  		return nil, fmt.Errorf("failed to write export data: %v", err)
   208  	}
   209  	encodedFileSet := new(bytes.Buffer)
   210  	if err := fileSet.Write(json.NewEncoder(encodedFileSet).Encode); err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	simplifiedFiles := make([]*ast.File, len(files))
   215  	for i, file := range files {
   216  		simplifiedFiles[i] = astrewrite.Simplify(file, typesInfo, false)
   217  	}
   218  
   219  	isBlocking := func(f *types.Func) bool {
   220  		archive, err := importContext.Import(f.Pkg().Path())
   221  		if err != nil {
   222  			panic(err)
   223  		}
   224  		fullName := f.FullName()
   225  		for _, d := range archive.Declarations {
   226  			if string(d.FullName) == fullName {
   227  				return d.Blocking
   228  			}
   229  		}
   230  		panic(fullName)
   231  	}
   232  	pkgInfo := analysis.AnalyzePkg(simplifiedFiles, fileSet, typesInfo, typesPkg, isBlocking)
   233  
   234  	c := &funcContext{
   235  		FuncInfo: pkgInfo.InitFuncInfo,
   236  		p: &pkgContext{
   237  			Info:                 pkgInfo,
   238  			additionalSelections: make(map[*ast.SelectorExpr]selection),
   239  
   240  			pkgVars:      make(map[string]string),
   241  			objectNames:  make(map[types.Object]string),
   242  			varPtrNames:  make(map[*types.Var]string),
   243  			escapingVars: make(map[*types.Var]bool),
   244  			indentation:  1,
   245  			dependencies: make(map[types.Object]bool),
   246  			minify:       minify,
   247  			fileSet:      fileSet,
   248  		},
   249  		allVars:     make(map[string]int),
   250  		flowDatas:   map[*types.Label]*flowData{nil: {}},
   251  		caseCounter: 1,
   252  		labelCases:  make(map[*types.Label]int),
   253  	}
   254  	for name := range reservedKeywords {
   255  		c.allVars[name] = 1
   256  	}
   257  
   258  	// imports
   259  	var importDecls []*Decl
   260  	var importedPaths []string
   261  	for _, importedPkg := range typesPkg.Imports() {
   262  		if importedPkg == types.Unsafe {
   263  			// Prior to Go 1.9, unsafe import was excluded by Imports() method,
   264  			// but now we do it here to maintain previous behavior.
   265  			continue
   266  		}
   267  		c.p.pkgVars[importedPkg.Path()] = c.newVariableWithLevel(importedPkg.Name(), true)
   268  		importedPaths = append(importedPaths, importedPkg.Path())
   269  	}
   270  
   271  	sort.Strings(importedPaths)
   272  	for _, impPath := range importedPaths {
   273  		id := c.newIdent(fmt.Sprintf(`%s.$init`, c.p.pkgVars[impPath]), types.NewSignature(nil, nil, nil, false))
   274  		call := &ast.CallExpr{Fun: id}
   275  		c.Blocking[call] = true
   276  		c.Flattened[call] = true
   277  		importDecls = append(importDecls, &Decl{
   278  			Vars:     []string{c.p.pkgVars[impPath]},
   279  			DeclCode: []byte(fmt.Sprintf("\t%s = $packages[\"%s\"];\n", c.p.pkgVars[impPath], impPath)),
   280  			InitCode: c.CatchOutput(1, func() { c.translateStmt(&ast.ExprStmt{X: call}, nil) }),
   281  		})
   282  	}
   283  
   284  	var functions []*ast.FuncDecl
   285  	var vars []*types.Var
   286  	for _, file := range simplifiedFiles {
   287  		for _, decl := range file.Decls {
   288  			switch d := decl.(type) {
   289  			case *ast.FuncDecl:
   290  				sig := c.p.Defs[d.Name].(*types.Func).Type().(*types.Signature)
   291  				var recvType types.Type
   292  				if sig.Recv() != nil {
   293  					recvType = sig.Recv().Type()
   294  					if ptr, isPtr := recvType.(*types.Pointer); isPtr {
   295  						recvType = ptr.Elem()
   296  					}
   297  				}
   298  				if sig.Recv() == nil {
   299  					c.objectName(c.p.Defs[d.Name].(*types.Func)) // register toplevel name
   300  				}
   301  				if !isBlank(d.Name) {
   302  					functions = append(functions, d)
   303  				}
   304  			case *ast.GenDecl:
   305  				switch d.Tok {
   306  				case token.TYPE:
   307  					for _, spec := range d.Specs {
   308  						o := c.p.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName)
   309  						c.p.typeNames = append(c.p.typeNames, o)
   310  						c.objectName(o) // register toplevel name
   311  					}
   312  				case token.VAR:
   313  					for _, spec := range d.Specs {
   314  						for _, name := range spec.(*ast.ValueSpec).Names {
   315  							if !isBlank(name) {
   316  								o := c.p.Defs[name].(*types.Var)
   317  								vars = append(vars, o)
   318  								c.objectName(o) // register toplevel name
   319  							}
   320  						}
   321  					}
   322  				case token.CONST:
   323  					// skip, constants are inlined
   324  				}
   325  			}
   326  		}
   327  	}
   328  
   329  	collectDependencies := func(f func()) []string {
   330  		c.p.dependencies = make(map[types.Object]bool)
   331  		f()
   332  		var deps []string
   333  		for o := range c.p.dependencies {
   334  			qualifiedName := o.Pkg().Path() + "." + o.Name()
   335  			if f, ok := o.(*types.Func); ok && f.Type().(*types.Signature).Recv() != nil {
   336  				deps = append(deps, qualifiedName+"~")
   337  				continue
   338  			}
   339  			deps = append(deps, qualifiedName)
   340  		}
   341  		sort.Strings(deps)
   342  		return deps
   343  	}
   344  
   345  	// variables
   346  	var varDecls []*Decl
   347  	varsWithInit := make(map[*types.Var]bool)
   348  	for _, init := range c.p.InitOrder {
   349  		for _, o := range init.Lhs {
   350  			varsWithInit[o] = true
   351  		}
   352  	}
   353  	for _, o := range vars {
   354  		var d Decl
   355  		if !o.Exported() {
   356  			d.Vars = []string{c.objectName(o)}
   357  		}
   358  		if c.p.HasPointer[o] && !o.Exported() {
   359  			d.Vars = append(d.Vars, c.varPtrName(o))
   360  		}
   361  		if _, ok := varsWithInit[o]; !ok {
   362  			d.DceDeps = collectDependencies(func() {
   363  				d.InitCode = []byte(fmt.Sprintf("\t\t%s = %s;\n", c.objectName(o), c.translateExpr(c.zeroValue(o.Type())).String()))
   364  			})
   365  		}
   366  		d.DceObjectFilter = o.Name()
   367  		varDecls = append(varDecls, &d)
   368  	}
   369  	for _, init := range c.p.InitOrder {
   370  		lhs := make([]ast.Expr, len(init.Lhs))
   371  		for i, o := range init.Lhs {
   372  			ident := ast.NewIdent(o.Name())
   373  			c.p.Defs[ident] = o
   374  			lhs[i] = c.setType(ident, o.Type())
   375  			varsWithInit[o] = true
   376  		}
   377  		var d Decl
   378  		d.DceDeps = collectDependencies(func() {
   379  			c.localVars = nil
   380  			d.InitCode = c.CatchOutput(1, func() {
   381  				c.translateStmt(&ast.AssignStmt{
   382  					Lhs: lhs,
   383  					Tok: token.DEFINE,
   384  					Rhs: []ast.Expr{init.Rhs},
   385  				}, nil)
   386  			})
   387  			d.Vars = append(d.Vars, c.localVars...)
   388  		})
   389  		if len(init.Lhs) == 1 {
   390  			if !analysis.HasSideEffect(init.Rhs, c.p.Info.Info) {
   391  				d.DceObjectFilter = init.Lhs[0].Name()
   392  			}
   393  		}
   394  		varDecls = append(varDecls, &d)
   395  	}
   396  
   397  	// functions
   398  	var funcDecls []*Decl
   399  	var mainFunc *types.Func
   400  	for _, fun := range functions {
   401  		o := c.p.Defs[fun.Name].(*types.Func)
   402  		funcInfo := c.p.FuncDeclInfos[o]
   403  		d := Decl{
   404  			FullName: o.FullName(),
   405  			Blocking: len(funcInfo.Blocking) != 0,
   406  		}
   407  		if fun.Recv == nil {
   408  			d.Vars = []string{c.objectName(o)}
   409  			d.DceObjectFilter = o.Name()
   410  			switch o.Name() {
   411  			case "main":
   412  				mainFunc = o
   413  				d.DceObjectFilter = ""
   414  			case "init":
   415  				d.InitCode = c.CatchOutput(1, func() {
   416  					id := c.newIdent("", types.NewSignature(nil, nil, nil, false))
   417  					c.p.Uses[id] = o
   418  					call := &ast.CallExpr{Fun: id}
   419  					if len(c.p.FuncDeclInfos[o].Blocking) != 0 {
   420  						c.Blocking[call] = true
   421  					}
   422  					c.translateStmt(&ast.ExprStmt{X: call}, nil)
   423  				})
   424  				d.DceObjectFilter = ""
   425  			}
   426  		}
   427  		if fun.Recv != nil {
   428  			recvType := o.Type().(*types.Signature).Recv().Type()
   429  			ptr, isPointer := recvType.(*types.Pointer)
   430  			namedRecvType, _ := recvType.(*types.Named)
   431  			if isPointer {
   432  				namedRecvType = ptr.Elem().(*types.Named)
   433  			}
   434  			d.DceObjectFilter = namedRecvType.Obj().Name()
   435  			if !fun.Name.IsExported() {
   436  				d.DceMethodFilter = o.Name() + "~"
   437  			}
   438  		}
   439  		d.DceDeps = collectDependencies(func() {
   440  			d.DeclCode = c.translateToplevelFunction(fun, funcInfo)
   441  		})
   442  		if fun.Body == nil {
   443  			for _, link := range linknames {
   444  				if link.Local == fun.Name.Name {
   445  					d.DeclCode = []byte(fmt.Sprintf("\t%v=function(){ return $packages[%q].%v(...arguments); };\n",
   446  						c.p.objectNames[o],
   447  						link.TargetImportPath,
   448  						link.TargetName))
   449  					d.DceDeps = append(d.DceDeps, link.Target)
   450  					break
   451  				}
   452  			}
   453  		}
   454  		funcDecls = append(funcDecls, &d)
   455  	}
   456  	if typesPkg.Name() == "main" {
   457  		if mainFunc == nil {
   458  			return nil, fmt.Errorf("missing main function")
   459  		}
   460  		id := c.newIdent("", types.NewSignature(nil, nil, nil, false))
   461  		c.p.Uses[id] = mainFunc
   462  		call := &ast.CallExpr{Fun: id}
   463  		ifStmt := &ast.IfStmt{
   464  			Cond: c.newIdent("$pkg === $mainPkg", types.Typ[types.Bool]),
   465  			Body: &ast.BlockStmt{
   466  				List: []ast.Stmt{
   467  					&ast.ExprStmt{X: call},
   468  					&ast.AssignStmt{
   469  						Lhs: []ast.Expr{c.newIdent("$mainFinished", types.Typ[types.Bool])},
   470  						Tok: token.ASSIGN,
   471  						Rhs: []ast.Expr{c.newConst(types.Typ[types.Bool], constant.MakeBool(true))},
   472  					},
   473  				},
   474  			},
   475  		}
   476  		if len(c.p.FuncDeclInfos[mainFunc].Blocking) != 0 {
   477  			c.Blocking[call] = true
   478  			c.Flattened[ifStmt] = true
   479  		}
   480  		funcDecls = append(funcDecls, &Decl{
   481  			InitCode: c.CatchOutput(1, func() {
   482  				c.translateStmt(ifStmt, nil)
   483  			}),
   484  		})
   485  	}
   486  
   487  	// named types
   488  	var typeDecls []*Decl
   489  	for _, o := range c.p.typeNames {
   490  		if o.IsAlias() {
   491  			continue
   492  		}
   493  		typeName := c.objectName(o)
   494  		d := Decl{
   495  			Vars:            []string{typeName},
   496  			DceObjectFilter: o.Name(),
   497  		}
   498  		d.DceDeps = collectDependencies(func() {
   499  			d.DeclCode = c.CatchOutput(0, func() {
   500  				typeName := c.objectName(o)
   501  				lhs := typeName
   502  				if isPkgLevel(o) {
   503  					lhs += " = $pkg." + encodeIdent(o.Name())
   504  				}
   505  				size := int64(0)
   506  				constructor := "null"
   507  				switch t := o.Type().Underlying().(type) {
   508  				case *types.Struct:
   509  					params := make([]string, t.NumFields())
   510  					for i := 0; i < t.NumFields(); i++ {
   511  						params[i] = fieldName(t, i) + "_"
   512  					}
   513  					constructor = fmt.Sprintf("function(%s) {\n\t\tthis.$val = this;\n\t\tif (arguments.length === 0) {\n", strings.Join(params, ", "))
   514  					for i := 0; i < t.NumFields(); i++ {
   515  						constructor += fmt.Sprintf("\t\t\tthis.%s = %s;\n", fieldName(t, i), c.translateExpr(c.zeroValue(t.Field(i).Type())).String())
   516  					}
   517  					constructor += "\t\t\treturn;\n\t\t}\n"
   518  					for i := 0; i < t.NumFields(); i++ {
   519  						constructor += fmt.Sprintf("\t\tthis.%[1]s = %[1]s_;\n", fieldName(t, i))
   520  					}
   521  					constructor += "\t}"
   522  				case *types.Basic, *types.Array, *types.Slice, *types.Chan, *types.Signature, *types.Interface, *types.Pointer, *types.Map:
   523  					size = sizes32.Sizeof(t)
   524  				}
   525  				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)
   526  			})
   527  			d.MethodListCode = c.CatchOutput(0, func() {
   528  				named := o.Type().(*types.Named)
   529  				if _, ok := named.Underlying().(*types.Interface); ok {
   530  					return
   531  				}
   532  				var methods []string
   533  				var ptrMethods []string
   534  				for i := 0; i < named.NumMethods(); i++ {
   535  					method := named.Method(i)
   536  					name := method.Name()
   537  					if reservedKeywords[name] {
   538  						name += "$"
   539  					}
   540  					pkgPath := ""
   541  					if !method.Exported() {
   542  						pkgPath = method.Pkg().Path()
   543  					}
   544  					t := method.Type().(*types.Signature)
   545  					entry := fmt.Sprintf(`{prop: "%s", name: %s, pkg: "%s", typ: $funcType(%s)}`, name, encodeString(method.Name()), pkgPath, c.initArgs(t))
   546  					if _, isPtr := t.Recv().Type().(*types.Pointer); isPtr {
   547  						ptrMethods = append(ptrMethods, entry)
   548  						continue
   549  					}
   550  					methods = append(methods, entry)
   551  				}
   552  				if len(methods) > 0 {
   553  					c.Printf("%s.methods = [%s];", c.typeName(named), strings.Join(methods, ", "))
   554  				}
   555  				if len(ptrMethods) > 0 {
   556  					c.Printf("%s.methods = [%s];", c.typeName(types.NewPointer(named)), strings.Join(ptrMethods, ", "))
   557  				}
   558  			})
   559  			switch t := o.Type().Underlying().(type) {
   560  			case *types.Array, *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Slice, *types.Signature, *types.Struct:
   561  				d.TypeInitCode = c.CatchOutput(0, func() {
   562  					c.Printf("%s.init(%s);", c.objectName(o), c.initArgs(t))
   563  				})
   564  			}
   565  		})
   566  		typeDecls = append(typeDecls, &d)
   567  	}
   568  
   569  	// anonymous types
   570  	for _, t := range c.p.anonTypes {
   571  		d := Decl{
   572  			Vars:            []string{t.Name()},
   573  			DceObjectFilter: t.Name(),
   574  		}
   575  		d.DceDeps = collectDependencies(func() {
   576  			d.DeclCode = []byte(fmt.Sprintf("\t%s = $%sType(%s);\n", t.Name(), strings.ToLower(typeKind(t.Type())[5:]), c.initArgs(t.Type())))
   577  		})
   578  		typeDecls = append(typeDecls, &d)
   579  	}
   580  
   581  	var allDecls []*Decl
   582  	for _, d := range append(append(append(importDecls, typeDecls...), varDecls...), funcDecls...) {
   583  		d.DeclCode = removeWhitespace(d.DeclCode, minify)
   584  		d.MethodListCode = removeWhitespace(d.MethodListCode, minify)
   585  		d.TypeInitCode = removeWhitespace(d.TypeInitCode, minify)
   586  		d.InitCode = removeWhitespace(d.InitCode, minify)
   587  		allDecls = append(allDecls, d)
   588  	}
   589  
   590  	if len(c.p.errList) != 0 {
   591  		return nil, c.p.errList
   592  	}
   593  
   594  	return &Archive{
   595  		ImportPath:   importPath,
   596  		Name:         typesPkg.Name(),
   597  		Imports:      importedPaths,
   598  		ExportData:   exportData.Bytes(),
   599  		Declarations: allDecls,
   600  		FileSet:      encodedFileSet.Bytes(),
   601  		Minified:     minify,
   602  	}, nil
   603  }
   604  
   605  func UpdateLinkNames(ar *Archive, linknames []LinkName) {
   606  	for _, d := range ar.Declarations {
   607  		for _, link := range linknames {
   608  			if d.FullName == link.Target {
   609  				fnName := link.TargetName
   610  				if link.TargetMethod != "" {
   611  					if link.TargetRecv[0] == '*' {
   612  						fnName = fmt.Sprintf("function(s) { return s.%v(...[...arguments].slice(1))}", link.TargetMethod)
   613  					} else {
   614  						pos := bytes.Index(d.DeclCode, []byte{'.'})
   615  						recv := strings.TrimSpace(string(d.DeclCode[:pos]))
   616  						fnName = fmt.Sprintf("function(s) { return $clone(s, %v).%v(...[...arguments].slice(1))}", recv, link.TargetMethod)
   617  					}
   618  				} else {
   619  					pos := bytes.Index(d.DeclCode, []byte{'='})
   620  					if pos > 0 {
   621  						fnName = strings.TrimSpace(string(d.DeclCode[:pos]))
   622  					}
   623  				}
   624  				if ar.Minified {
   625  					d.DeclCode = append(d.DeclCode, []byte(fmt.Sprintf("$pkg.%v=%v;", link.TargetName, fnName))...)
   626  				} else {
   627  					d.DeclCode = append(d.DeclCode, []byte(fmt.Sprintf("\t$pkg.%v=%v;\n", link.TargetName, fnName))...)
   628  				}
   629  				break
   630  			}
   631  		}
   632  	}
   633  }
   634  
   635  func (c *funcContext) initArgs(ty types.Type) string {
   636  	switch t := ty.(type) {
   637  	case *types.Array:
   638  		return fmt.Sprintf("%s, %d", c.typeName(t.Elem()), t.Len())
   639  	case *types.Chan:
   640  		return fmt.Sprintf("%s, %t, %t", c.typeName(t.Elem()), t.Dir()&types.SendOnly != 0, t.Dir()&types.RecvOnly != 0)
   641  	case *types.Interface:
   642  		methods := make([]string, t.NumMethods())
   643  		for i := range methods {
   644  			method := t.Method(i)
   645  			pkgPath := ""
   646  			if !method.Exported() {
   647  				pkgPath = method.Pkg().Path()
   648  			}
   649  			methods[i] = fmt.Sprintf(`{prop: "%s", name: "%s", pkg: "%s", typ: $funcType(%s)}`, method.Name(), method.Name(), pkgPath, c.initArgs(method.Type()))
   650  		}
   651  		return fmt.Sprintf("[%s]", strings.Join(methods, ", "))
   652  	case *types.Map:
   653  		return fmt.Sprintf("%s, %s", c.typeName(t.Key()), c.typeName(t.Elem()))
   654  	case *types.Pointer:
   655  		return fmt.Sprintf("%s", c.typeName(t.Elem()))
   656  	case *types.Slice:
   657  		return fmt.Sprintf("%s", c.typeName(t.Elem()))
   658  	case *types.Signature:
   659  		params := make([]string, t.Params().Len())
   660  		for i := range params {
   661  			params[i] = c.typeName(t.Params().At(i).Type())
   662  		}
   663  		results := make([]string, t.Results().Len())
   664  		for i := range results {
   665  			results[i] = c.typeName(t.Results().At(i).Type())
   666  		}
   667  		return fmt.Sprintf("[%s], [%s], %t", strings.Join(params, ", "), strings.Join(results, ", "), t.Variadic())
   668  	case *types.Struct:
   669  		pkgPath := ""
   670  		fields := make([]string, t.NumFields())
   671  		for i := range fields {
   672  			field := t.Field(i)
   673  			if !field.Exported() {
   674  				pkgPath = field.Pkg().Path()
   675  			}
   676  			fields[i] = fmt.Sprintf(`{prop: "%s", name: %s, embedded: %t, exported: %t, typ: %s, tag: %s}`, fieldName(t, i), encodeString(field.Name()), field.Anonymous(), field.Exported(), c.typeName(field.Type()), encodeString(t.Tag(i)))
   677  		}
   678  		return fmt.Sprintf(`"%s", [%s]`, pkgPath, strings.Join(fields, ", "))
   679  	default:
   680  		panic("invalid type")
   681  	}
   682  }
   683  
   684  func (c *funcContext) translateToplevelFunction(fun *ast.FuncDecl, info *analysis.FuncInfo) []byte {
   685  	o := c.p.Defs[fun.Name].(*types.Func)
   686  	sig := o.Type().(*types.Signature)
   687  	var recv *ast.Ident
   688  	if fun.Recv != nil && fun.Recv.List[0].Names != nil {
   689  		recv = fun.Recv.List[0].Names[0]
   690  	}
   691  
   692  	var joinedParams string
   693  	primaryFunction := func(funcRef string) []byte {
   694  		if fun.Body == nil {
   695  			return []byte(fmt.Sprintf("\t%s = function() {\n\t\t$throwRuntimeError(\"native function not implemented: %s\");\n\t};\n", funcRef, o.FullName()))
   696  		}
   697  
   698  		params, fun := translateFunction(fun.Type, recv, fun.Body, c, sig, info, funcRef)
   699  		joinedParams = strings.Join(params, ", ")
   700  		return []byte(fmt.Sprintf("\t%s = %s;\n", funcRef, fun))
   701  	}
   702  
   703  	code := bytes.NewBuffer(nil)
   704  
   705  	if fun.Recv == nil {
   706  		funcRef := c.objectName(o)
   707  		code.Write(primaryFunction(funcRef))
   708  		if fun.Name.IsExported() {
   709  			fmt.Fprintf(code, "\t$pkg.%s = %s;\n", encodeIdent(fun.Name.Name), funcRef)
   710  		}
   711  		return code.Bytes()
   712  	}
   713  
   714  	recvType := sig.Recv().Type()
   715  	ptr, isPointer := recvType.(*types.Pointer)
   716  	namedRecvType, _ := recvType.(*types.Named)
   717  	if isPointer {
   718  		namedRecvType = ptr.Elem().(*types.Named)
   719  	}
   720  	typeName := c.objectName(namedRecvType.Obj())
   721  	funName := fun.Name.Name
   722  	if reservedKeywords[funName] {
   723  		funName += "$"
   724  	}
   725  
   726  	if _, isStruct := namedRecvType.Underlying().(*types.Struct); isStruct {
   727  		code.Write(primaryFunction(typeName + ".ptr.prototype." + funName))
   728  		fmt.Fprintf(code, "\t%s.prototype.%s = function(%s) { return this.$val.%s(%s); };\n", typeName, funName, joinedParams, funName, joinedParams)
   729  		return code.Bytes()
   730  	}
   731  
   732  	if isPointer {
   733  		if _, isArray := ptr.Elem().Underlying().(*types.Array); isArray {
   734  			code.Write(primaryFunction(typeName + ".prototype." + funName))
   735  			fmt.Fprintf(code, "\t$ptrType(%s).prototype.%s = function(%s) { return (new %s(this.$get())).%s(%s); };\n", typeName, funName, joinedParams, typeName, funName, joinedParams)
   736  			return code.Bytes()
   737  		}
   738  		return primaryFunction(fmt.Sprintf("$ptrType(%s).prototype.%s", typeName, funName))
   739  	}
   740  
   741  	value := "this.$get()"
   742  	if isWrapped(recvType) {
   743  		value = fmt.Sprintf("new %s(%s)", typeName, value)
   744  	}
   745  	code.Write(primaryFunction(typeName + ".prototype." + funName))
   746  	fmt.Fprintf(code, "\t$ptrType(%s).prototype.%s = function(%s) { return %s.%s(%s); };\n", typeName, funName, joinedParams, value, funName, joinedParams)
   747  	return code.Bytes()
   748  }
   749  
   750  func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, outerContext *funcContext, sig *types.Signature, info *analysis.FuncInfo, funcRef string) ([]string, string) {
   751  	if info == nil {
   752  		panic("nil info")
   753  	}
   754  
   755  	c := &funcContext{
   756  		FuncInfo:    info,
   757  		p:           outerContext.p,
   758  		parent:      outerContext,
   759  		sig:         sig,
   760  		allVars:     make(map[string]int, len(outerContext.allVars)),
   761  		localVars:   []string{},
   762  		flowDatas:   map[*types.Label]*flowData{nil: {}},
   763  		caseCounter: 1,
   764  		labelCases:  make(map[*types.Label]int),
   765  	}
   766  	for k, v := range outerContext.allVars {
   767  		c.allVars[k] = v
   768  	}
   769  	prevEV := c.p.escapingVars
   770  
   771  	var params []string
   772  	for _, param := range typ.Params.List {
   773  		if len(param.Names) == 0 {
   774  			params = append(params, c.newVariable("param"))
   775  			continue
   776  		}
   777  		for _, ident := range param.Names {
   778  			if isBlank(ident) {
   779  				params = append(params, c.newVariable("param"))
   780  				continue
   781  			}
   782  			params = append(params, c.objectName(c.p.Defs[ident]))
   783  		}
   784  	}
   785  
   786  	bodyOutput := string(c.CatchOutput(1, func() {
   787  		if len(c.Blocking) != 0 {
   788  			c.p.Scopes[body] = c.p.Scopes[typ]
   789  			c.handleEscapingVars(body)
   790  		}
   791  
   792  		if c.sig != nil && c.sig.Results().Len() != 0 && c.sig.Results().At(0).Name() != "" {
   793  			c.resultNames = make([]ast.Expr, c.sig.Results().Len())
   794  			for i := 0; i < c.sig.Results().Len(); i++ {
   795  				result := c.sig.Results().At(i)
   796  				c.Printf("%s = %s;", c.objectName(result), c.translateExpr(c.zeroValue(result.Type())).String())
   797  				id := ast.NewIdent("")
   798  				c.p.Uses[id] = result
   799  				c.resultNames[i] = c.setType(id, result.Type())
   800  			}
   801  		}
   802  
   803  		if recv != nil && !isBlank(recv) {
   804  			this := "this"
   805  			if isWrapped(c.p.TypeOf(recv)) {
   806  				this = "this.$val"
   807  			}
   808  			c.Printf("%s = %s;", c.translateExpr(recv), this)
   809  		}
   810  
   811  		c.translateStmtList(body.List)
   812  		if len(c.Flattened) != 0 && !endsWithReturn(body.List) {
   813  			c.translateStmt(&ast.ReturnStmt{}, nil)
   814  		}
   815  	}))
   816  
   817  	sort.Strings(c.localVars)
   818  
   819  	var prefix, suffix, functionName string
   820  
   821  	if len(c.Flattened) != 0 {
   822  		c.localVars = append(c.localVars, "$s")
   823  		prefix = prefix + " $s = 0;"
   824  	}
   825  
   826  	if c.HasDefer {
   827  		c.localVars = append(c.localVars, "$deferred")
   828  		suffix = " }" + suffix
   829  		if len(c.Blocking) != 0 {
   830  			suffix = " }" + suffix
   831  		}
   832  	}
   833  
   834  	if len(c.Blocking) != 0 {
   835  		c.localVars = append(c.localVars, "$r")
   836  		if funcRef == "" {
   837  			funcRef = "$b"
   838  			functionName = " $b"
   839  		}
   840  		var stores, loads string
   841  		for _, v := range c.localVars {
   842  			loads += fmt.Sprintf("%s = $f.%s; ", v, v)
   843  			stores += fmt.Sprintf("$f.%s = %s; ", v, v)
   844  		}
   845  		prefix = prefix + " var $f, $c = false; if (this !== undefined && this.$blk !== undefined) { $f = this; $c = true; " + loads + "}"
   846  		suffix = " if ($f === undefined) { $f = { $blk: " + funcRef + " }; } " + stores + "return $f;" + suffix
   847  	}
   848  
   849  	if c.HasDefer {
   850  		prefix = prefix + " var $err = null; try {"
   851  		deferSuffix := " } catch(err) { $err = err;"
   852  		if len(c.Blocking) != 0 {
   853  			deferSuffix += " $s = -1;"
   854  		}
   855  		if c.resultNames == nil && c.sig.Results().Len() > 0 {
   856  			deferSuffix += fmt.Sprintf(" return%s;", c.translateResults(nil))
   857  		}
   858  		deferSuffix += " } finally { $callDeferred($deferred, $err);"
   859  		if c.resultNames != nil {
   860  			deferSuffix += fmt.Sprintf(" if (!$curGoroutine.asleep) { return %s; }", c.translateResults(c.resultNames))
   861  		}
   862  		if len(c.Blocking) != 0 {
   863  			deferSuffix += " if($curGoroutine.asleep) {"
   864  		}
   865  		suffix = deferSuffix + suffix
   866  	}
   867  
   868  	if len(c.Flattened) != 0 {
   869  		prefix = prefix + " s: while (true) { switch ($s) { case 0:"
   870  		suffix = " } return; }" + suffix
   871  	}
   872  
   873  	if c.HasDefer {
   874  		prefix = prefix + " $deferred = []; $deferred.index = $curGoroutine.deferStack.length; $curGoroutine.deferStack.push($deferred);"
   875  	}
   876  
   877  	if prefix != "" {
   878  		bodyOutput = strings.Repeat("\t", c.p.indentation+1) + "/* */" + prefix + "\n" + bodyOutput
   879  	}
   880  	if suffix != "" {
   881  		bodyOutput = bodyOutput + strings.Repeat("\t", c.p.indentation+1) + "/* */" + suffix + "\n"
   882  	}
   883  	if len(c.localVars) != 0 {
   884  		bodyOutput = fmt.Sprintf("%svar %s;\n", strings.Repeat("\t", c.p.indentation+1), strings.Join(c.localVars, ", ")) + bodyOutput
   885  	}
   886  
   887  	c.p.escapingVars = prevEV
   888  
   889  	return params, fmt.Sprintf("function%s(%s) {\n%s%s}", functionName, strings.Join(params, ", "), bodyOutput, strings.Repeat("\t", c.p.indentation))
   890  }