github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/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  	"time"
    14  
    15  	"github.com/gopherjs/gopherjs/compiler/analysis"
    16  	"github.com/gopherjs/gopherjs/compiler/astutil"
    17  	"github.com/gopherjs/gopherjs/compiler/internal/symbol"
    18  	"github.com/gopherjs/gopherjs/compiler/internal/typeparams"
    19  	"github.com/gopherjs/gopherjs/compiler/typesutil"
    20  	"github.com/gopherjs/gopherjs/internal/experiments"
    21  	"golang.org/x/tools/go/gcexportdata"
    22  	"golang.org/x/tools/go/types/typeutil"
    23  )
    24  
    25  // pkgContext maintains compiler context for a specific package.
    26  type pkgContext struct {
    27  	*analysis.Info
    28  	additionalSelections map[*ast.SelectorExpr]typesutil.Selection
    29  
    30  	typesCtx     *types.Context
    31  	typeNames    typesutil.TypeNames
    32  	pkgVars      map[string]string
    33  	varPtrNames  map[*types.Var]string
    34  	anonTypes    []*types.TypeName
    35  	anonTypeMap  typeutil.Map
    36  	escapingVars map[*types.Var]bool
    37  	indentation  int
    38  	dependencies map[types.Object]bool
    39  	minify       bool
    40  	fileSet      *token.FileSet
    41  	errList      ErrorList
    42  	instanceSet  *typeparams.PackageInstanceSets
    43  }
    44  
    45  // funcContext maintains compiler context for a specific function.
    46  //
    47  // An instance of this type roughly corresponds to a lexical scope for generated
    48  // JavaScript code (as defined for `var` declarations).
    49  type funcContext struct {
    50  	*analysis.FuncInfo
    51  	// Surrounding package context.
    52  	pkgCtx *pkgContext
    53  	// Function context, surrounding this function definition. For package-level
    54  	// functions or methods it is the package-level function context (even though
    55  	// it technically doesn't correspond to a function). nil for the package-level
    56  	// function context.
    57  	parent *funcContext
    58  	// Signature of the function this context corresponds to or nil for the
    59  	// package-level function context. For generic functions it is the original
    60  	// generic signature to make sure result variable identity in the signature
    61  	// matches the variable objects referenced in the function body.
    62  	sig *typesutil.Signature
    63  	// All variable names available in the current function scope. The key is a Go
    64  	// variable name and the value is the number of synonymous variable names
    65  	// visible from this scope (e.g. due to shadowing). This number is used to
    66  	// avoid conflicts when assigning JS variable names for Go variables.
    67  	allVars map[string]int
    68  	// Local JS variable names defined within this function context. This list
    69  	// contains JS variable names assigned to Go variables, as well as other
    70  	// auxiliary variables the compiler needs. It is used to generate `var`
    71  	// declaration at the top of the function, as well as context save/restore.
    72  	localVars []string
    73  	// AST expressions representing function's named return values. nil if the
    74  	// function has no return values or they are not named.
    75  	resultNames []ast.Expr
    76  	// Function's internal control flow graph used for generation of a "flattened"
    77  	// version of the function when the function is blocking or uses goto.
    78  	// TODO(nevkontakte): Describe the exact semantics of this map.
    79  	flowDatas map[*types.Label]*flowData
    80  	// Number of control flow blocks in a "flattened" function.
    81  	caseCounter int
    82  	// A mapping from Go labels statements (e.g. labelled loop) to the flow block
    83  	// id corresponding to it.
    84  	labelCases map[*types.Label]int
    85  	// Generated code buffer for the current function.
    86  	output []byte
    87  	// Generated code that should be emitted at the end of the JS statement.
    88  	delayedOutput []byte
    89  	// Set to true if source position is available and should be emitted for the
    90  	// source map.
    91  	posAvailable bool
    92  	// Current position in the Go source code.
    93  	pos token.Pos
    94  	// For each instantiation of a generic function or method, contains the
    95  	// current mapping between type parameters and corresponding type arguments.
    96  	// The mapping is used to determine concrete types for expressions within the
    97  	// instance's context. Can be nil outside of the generic context, in which
    98  	// case calling its methods is safe and simply does no substitution.
    99  	typeResolver *typeparams.Resolver
   100  	// Mapping from function-level objects to JS variable names they have been assigned.
   101  	objectNames map[types.Object]string
   102  }
   103  
   104  type flowData struct {
   105  	postStmt  func()
   106  	beginCase int
   107  	endCase   int
   108  }
   109  
   110  // ImportContext provides access to information about imported packages.
   111  type ImportContext struct {
   112  	// Mapping for an absolute import path to the package type information.
   113  	Packages map[string]*types.Package
   114  	// Import returns a previously compiled Archive for a dependency package. If
   115  	// the Import() call was successful, the corresponding entry must be added to
   116  	// the Packages map.
   117  	Import func(importPath string) (*Archive, error)
   118  }
   119  
   120  // Compile the provided Go sources as a single package.
   121  //
   122  // Import path must be the absolute import path for a package. Provided sources
   123  // are always sorted by name to ensure reproducible JavaScript output.
   124  func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, importContext *ImportContext, minify bool) (_ *Archive, err error) {
   125  	defer func() {
   126  		e := recover()
   127  		if e == nil {
   128  			return
   129  		}
   130  		if fe, ok := bailingOut(e); ok {
   131  			// Orderly bailout, return whatever clues we already have.
   132  			fmt.Fprintf(fe, `building package %q`, importPath)
   133  			err = fe
   134  			return
   135  		}
   136  		// Some other unexpected panic, catch the stack trace and return as an error.
   137  		err = bailout(fmt.Errorf("unexpected compiler panic while building package %q: %v", importPath, e))
   138  	}()
   139  
   140  	srcs := sources{
   141  		ImportPath: importPath,
   142  		Files:      files,
   143  		FileSet:    fileSet,
   144  	}.Sort()
   145  
   146  	tContext := types.NewContext()
   147  	typesInfo, typesPkg, err := srcs.TypeCheck(importContext, tContext)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	if genErr := typeparams.RequiresGenericsSupport(typesInfo); genErr != nil && !experiments.Env.Generics {
   152  		return nil, fmt.Errorf("package %s requires generics support (https://github.com/gopherjs/gopherjs/issues/1013): %w", importPath, genErr)
   153  	}
   154  	importContext.Packages[srcs.ImportPath] = typesPkg
   155  
   156  	// Extract all go:linkname compiler directives from the package source.
   157  	goLinknames, err := srcs.ParseGoLinknames()
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	srcs = srcs.Simplified(typesInfo)
   163  
   164  	isBlocking := func(f *types.Func) bool {
   165  		archive, err := importContext.Import(f.Pkg().Path())
   166  		if err != nil {
   167  			panic(err)
   168  		}
   169  		fullName := f.FullName()
   170  		for _, d := range archive.Declarations {
   171  			if string(d.FullName) == fullName {
   172  				return d.Blocking
   173  			}
   174  		}
   175  		panic(fullName)
   176  	}
   177  
   178  	tc := typeparams.Collector{
   179  		TContext:  tContext,
   180  		Info:      typesInfo,
   181  		Instances: &typeparams.PackageInstanceSets{},
   182  	}
   183  	tc.Scan(typesPkg, srcs.Files...)
   184  	instancesByObj := map[types.Object][]typeparams.Instance{}
   185  	for _, inst := range tc.Instances.Pkg(typesPkg).Values() {
   186  		instancesByObj[inst.Object] = append(instancesByObj[inst.Object], inst)
   187  	}
   188  
   189  	pkgInfo := analysis.AnalyzePkg(srcs.Files, fileSet, typesInfo, typesPkg, isBlocking)
   190  	funcCtx := &funcContext{
   191  		FuncInfo: pkgInfo.InitFuncInfo,
   192  		pkgCtx: &pkgContext{
   193  			Info:                 pkgInfo,
   194  			additionalSelections: make(map[*ast.SelectorExpr]typesutil.Selection),
   195  
   196  			typesCtx:     tContext,
   197  			pkgVars:      make(map[string]string),
   198  			varPtrNames:  make(map[*types.Var]string),
   199  			escapingVars: make(map[*types.Var]bool),
   200  			indentation:  1,
   201  			dependencies: make(map[types.Object]bool),
   202  			minify:       minify,
   203  			fileSet:      srcs.FileSet,
   204  			instanceSet:  tc.Instances,
   205  		},
   206  		allVars:     make(map[string]int),
   207  		flowDatas:   map[*types.Label]*flowData{nil: {}},
   208  		caseCounter: 1,
   209  		labelCases:  make(map[*types.Label]int),
   210  		objectNames: map[types.Object]string{},
   211  	}
   212  	for name := range reservedKeywords {
   213  		funcCtx.allVars[name] = 1
   214  	}
   215  
   216  	// imports
   217  	var importDecls []*Decl
   218  	var importedPaths []string
   219  	for _, importedPkg := range typesPkg.Imports() {
   220  		if importedPkg == types.Unsafe {
   221  			// Prior to Go 1.9, unsafe import was excluded by Imports() method,
   222  			// but now we do it here to maintain previous behavior.
   223  			continue
   224  		}
   225  		funcCtx.pkgCtx.pkgVars[importedPkg.Path()] = funcCtx.newVariable(importedPkg.Name(), true)
   226  		importedPaths = append(importedPaths, importedPkg.Path())
   227  	}
   228  	sort.Strings(importedPaths)
   229  	for _, impPath := range importedPaths {
   230  		id := funcCtx.newIdent(fmt.Sprintf(`%s.$init`, funcCtx.pkgCtx.pkgVars[impPath]), types.NewSignatureType(nil, nil, nil, nil, nil, false))
   231  		call := &ast.CallExpr{Fun: id}
   232  		funcCtx.Blocking[call] = true
   233  		funcCtx.Flattened[call] = true
   234  		importDecls = append(importDecls, &Decl{
   235  			Vars:     []string{funcCtx.pkgCtx.pkgVars[impPath]},
   236  			DeclCode: []byte(fmt.Sprintf("\t%s = $packages[\"%s\"];\n", funcCtx.pkgCtx.pkgVars[impPath], impPath)),
   237  			InitCode: funcCtx.CatchOutput(1, func() { funcCtx.translateStmt(&ast.ExprStmt{X: call}, nil) }),
   238  		})
   239  	}
   240  
   241  	var functions []*ast.FuncDecl
   242  	var vars []*types.Var
   243  	for _, file := range srcs.Files {
   244  		for _, decl := range file.Decls {
   245  			switch d := decl.(type) {
   246  			case *ast.FuncDecl:
   247  				sig := funcCtx.pkgCtx.Defs[d.Name].(*types.Func).Type().(*types.Signature)
   248  				if sig.Recv() == nil {
   249  					funcCtx.objectName(funcCtx.pkgCtx.Defs[d.Name].(*types.Func)) // register toplevel name
   250  				}
   251  				if !isBlank(d.Name) {
   252  					functions = append(functions, d)
   253  				}
   254  			case *ast.GenDecl:
   255  				switch d.Tok {
   256  				case token.TYPE:
   257  					for _, spec := range d.Specs {
   258  						o := funcCtx.pkgCtx.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName)
   259  						funcCtx.pkgCtx.typeNames.Add(o)
   260  						funcCtx.objectName(o) // register toplevel name
   261  					}
   262  				case token.VAR:
   263  					for _, spec := range d.Specs {
   264  						for _, name := range spec.(*ast.ValueSpec).Names {
   265  							if !isBlank(name) {
   266  								o := funcCtx.pkgCtx.Defs[name].(*types.Var)
   267  								vars = append(vars, o)
   268  								funcCtx.objectName(o) // register toplevel name
   269  							}
   270  						}
   271  					}
   272  				case token.CONST:
   273  					// skip, constants are inlined
   274  				}
   275  			}
   276  		}
   277  	}
   278  
   279  	collectDependencies := func(f func()) []string {
   280  		funcCtx.pkgCtx.dependencies = make(map[types.Object]bool)
   281  		f()
   282  		var deps []string
   283  		for o := range funcCtx.pkgCtx.dependencies {
   284  			qualifiedName := o.Pkg().Path() + "." + o.Name()
   285  			if f, ok := o.(*types.Func); ok && f.Type().(*types.Signature).Recv() != nil {
   286  				deps = append(deps, qualifiedName+"~")
   287  				continue
   288  			}
   289  			deps = append(deps, qualifiedName)
   290  		}
   291  		sort.Strings(deps)
   292  		return deps
   293  	}
   294  
   295  	// variables
   296  	var varDecls []*Decl
   297  	varsWithInit := make(map[*types.Var]bool)
   298  	for _, init := range funcCtx.pkgCtx.InitOrder {
   299  		for _, o := range init.Lhs {
   300  			varsWithInit[o] = true
   301  		}
   302  	}
   303  	for _, o := range vars {
   304  		var d Decl
   305  		if !o.Exported() {
   306  			d.Vars = []string{funcCtx.objectName(o)}
   307  		}
   308  		if funcCtx.pkgCtx.HasPointer[o] && !o.Exported() {
   309  			d.Vars = append(d.Vars, funcCtx.varPtrName(o))
   310  		}
   311  		if _, ok := varsWithInit[o]; !ok {
   312  			d.DceDeps = collectDependencies(func() {
   313  				d.InitCode = []byte(fmt.Sprintf("\t\t%s = %s;\n", funcCtx.objectName(o), funcCtx.translateExpr(funcCtx.zeroValue(o.Type())).String()))
   314  			})
   315  		}
   316  		d.DceObjectFilter = o.Name()
   317  		varDecls = append(varDecls, &d)
   318  	}
   319  	for _, init := range funcCtx.pkgCtx.InitOrder {
   320  		lhs := make([]ast.Expr, len(init.Lhs))
   321  		for i, o := range init.Lhs {
   322  			ident := ast.NewIdent(o.Name())
   323  			ident.NamePos = o.Pos()
   324  			funcCtx.pkgCtx.Defs[ident] = o
   325  			lhs[i] = funcCtx.setType(ident, o.Type())
   326  			varsWithInit[o] = true
   327  		}
   328  		var d Decl
   329  		d.DceDeps = collectDependencies(func() {
   330  			funcCtx.localVars = nil
   331  			d.InitCode = funcCtx.CatchOutput(1, func() {
   332  				funcCtx.translateStmt(&ast.AssignStmt{
   333  					Lhs: lhs,
   334  					Tok: token.DEFINE,
   335  					Rhs: []ast.Expr{init.Rhs},
   336  				}, nil)
   337  			})
   338  			d.Vars = append(d.Vars, funcCtx.localVars...)
   339  		})
   340  		if len(init.Lhs) == 1 {
   341  			if !analysis.HasSideEffect(init.Rhs, funcCtx.pkgCtx.Info.Info) {
   342  				d.DceObjectFilter = init.Lhs[0].Name()
   343  			}
   344  		}
   345  		varDecls = append(varDecls, &d)
   346  	}
   347  
   348  	// functions
   349  	var funcDecls []*Decl
   350  	var mainFunc *types.Func
   351  	for _, fun := range functions {
   352  		o := funcCtx.pkgCtx.Defs[fun.Name].(*types.Func)
   353  		sig := o.Type().(*types.Signature)
   354  
   355  		var instances []typeparams.Instance
   356  		if typeparams.SignatureTypeParams(sig) != nil {
   357  			instances = instancesByObj[o]
   358  		} else {
   359  			instances = []typeparams.Instance{{Object: o}}
   360  		}
   361  
   362  		if fun.Recv == nil {
   363  			// Auxiliary decl shared by all instances of the function that defines
   364  			// package-level variable by which they all are referenced.
   365  			// TODO(nevkontakte): Set DCE attributes such that it is eliminated if all
   366  			// instances are dead.
   367  			varDecl := Decl{}
   368  			varDecl.Vars = []string{funcCtx.objectName(o)}
   369  			if o.Type().(*types.Signature).TypeParams().Len() != 0 {
   370  				varDecl.DeclCode = funcCtx.CatchOutput(0, func() {
   371  					funcCtx.Printf("%s = {};", funcCtx.objectName(o))
   372  				})
   373  			}
   374  			funcDecls = append(funcDecls, &varDecl)
   375  		}
   376  
   377  		for _, inst := range instances {
   378  			funcInfo := funcCtx.pkgCtx.FuncDeclInfos[o]
   379  			d := Decl{
   380  				FullName: o.FullName(),
   381  				Blocking: len(funcInfo.Blocking) != 0,
   382  			}
   383  			d.LinkingName = symbol.New(o)
   384  			if fun.Recv == nil {
   385  				d.RefExpr = funcCtx.instName(inst)
   386  				d.DceObjectFilter = o.Name()
   387  				switch o.Name() {
   388  				case "main":
   389  					mainFunc = o
   390  					d.DceObjectFilter = ""
   391  				case "init":
   392  					d.InitCode = funcCtx.CatchOutput(1, func() {
   393  						id := funcCtx.newIdent("", types.NewSignatureType( /*recv=*/ nil /*rectTypeParams=*/, nil /*typeParams=*/, nil /*params=*/, nil /*results=*/, nil /*variadic=*/, false))
   394  						funcCtx.pkgCtx.Uses[id] = o
   395  						call := &ast.CallExpr{Fun: id}
   396  						if len(funcCtx.pkgCtx.FuncDeclInfos[o].Blocking) != 0 {
   397  							funcCtx.Blocking[call] = true
   398  						}
   399  						funcCtx.translateStmt(&ast.ExprStmt{X: call}, nil)
   400  					})
   401  					d.DceObjectFilter = ""
   402  				}
   403  			} else {
   404  				recvType := o.Type().(*types.Signature).Recv().Type()
   405  				ptr, isPointer := recvType.(*types.Pointer)
   406  				namedRecvType, _ := recvType.(*types.Named)
   407  				if isPointer {
   408  					namedRecvType = ptr.Elem().(*types.Named)
   409  				}
   410  				d.NamedRecvType = funcCtx.objectName(namedRecvType.Obj())
   411  				d.DceObjectFilter = namedRecvType.Obj().Name()
   412  				if !fun.Name.IsExported() {
   413  					d.DceMethodFilter = o.Name() + "~"
   414  				}
   415  			}
   416  
   417  			d.DceDeps = collectDependencies(func() {
   418  				d.DeclCode = funcCtx.translateToplevelFunction(fun, funcInfo, inst)
   419  			})
   420  			funcDecls = append(funcDecls, &d)
   421  		}
   422  	}
   423  	if typesPkg.Name() == "main" {
   424  		if mainFunc == nil {
   425  			return nil, fmt.Errorf("missing main function")
   426  		}
   427  		id := funcCtx.newIdent("", types.NewSignatureType( /*recv=*/ nil /*rectTypeParams=*/, nil /*typeParams=*/, nil /*params=*/, nil /*results=*/, nil /*variadic=*/, false))
   428  		funcCtx.pkgCtx.Uses[id] = mainFunc
   429  		call := &ast.CallExpr{Fun: id}
   430  		ifStmt := &ast.IfStmt{
   431  			Cond: funcCtx.newIdent("$pkg === $mainPkg", types.Typ[types.Bool]),
   432  			Body: &ast.BlockStmt{
   433  				List: []ast.Stmt{
   434  					&ast.ExprStmt{X: call},
   435  					&ast.AssignStmt{
   436  						Lhs: []ast.Expr{funcCtx.newIdent("$mainFinished", types.Typ[types.Bool])},
   437  						Tok: token.ASSIGN,
   438  						Rhs: []ast.Expr{funcCtx.newConst(types.Typ[types.Bool], constant.MakeBool(true))},
   439  					},
   440  				},
   441  			},
   442  		}
   443  		if len(funcCtx.pkgCtx.FuncDeclInfos[mainFunc].Blocking) != 0 {
   444  			funcCtx.Blocking[call] = true
   445  			funcCtx.Flattened[ifStmt] = true
   446  		}
   447  		funcDecls = append(funcDecls, &Decl{
   448  			InitCode: funcCtx.CatchOutput(1, func() {
   449  				funcCtx.translateStmt(ifStmt, nil)
   450  			}),
   451  		})
   452  	}
   453  
   454  	// named types
   455  	var typeDecls []*Decl
   456  	for _, o := range funcCtx.pkgCtx.typeNames.Slice() {
   457  		if o.IsAlias() {
   458  			continue
   459  		}
   460  		typ := o.Type().(*types.Named)
   461  		var instances []typeparams.Instance
   462  		if typ.TypeParams() != nil {
   463  			instances = instancesByObj[o]
   464  		} else {
   465  			instances = []typeparams.Instance{{Object: o}}
   466  		}
   467  
   468  		typeName := funcCtx.objectName(o)
   469  
   470  		varDecl := Decl{Vars: []string{typeName}}
   471  		if typ.TypeParams() != nil {
   472  			varDecl.DeclCode = funcCtx.CatchOutput(0, func() {
   473  				funcCtx.Printf("%s = {};", funcCtx.objectName(o))
   474  			})
   475  		}
   476  		if isPkgLevel(o) {
   477  			varDecl.TypeInitCode = funcCtx.CatchOutput(0, func() {
   478  				funcCtx.Printf("$pkg.%s = %s;", encodeIdent(o.Name()), funcCtx.objectName(o))
   479  			})
   480  		}
   481  		typeDecls = append(typeDecls, &varDecl)
   482  
   483  		for _, inst := range instances {
   484  			funcCtx.typeResolver = typeparams.NewResolver(funcCtx.pkgCtx.typesCtx, typeparams.ToSlice(typ.TypeParams()), inst.TArgs)
   485  
   486  			named := typ
   487  			if !inst.IsTrivial() {
   488  				instantiated, err := types.Instantiate(funcCtx.pkgCtx.typesCtx, typ, inst.TArgs, true)
   489  				if err != nil {
   490  					return nil, fmt.Errorf("failed to instantiate type %v with args %v: %w", typ, inst.TArgs, err)
   491  				}
   492  				named = instantiated.(*types.Named)
   493  			}
   494  			underlying := named.Underlying()
   495  			d := Decl{
   496  				DceObjectFilter: o.Name(),
   497  			}
   498  			d.DceDeps = collectDependencies(func() {
   499  				d.DeclCode = funcCtx.CatchOutput(0, func() {
   500  					size := int64(0)
   501  					constructor := "null"
   502  					switch t := underlying.(type) {
   503  					case *types.Struct:
   504  						params := make([]string, t.NumFields())
   505  						for i := 0; i < t.NumFields(); i++ {
   506  							params[i] = fieldName(t, i) + "_"
   507  						}
   508  						constructor = fmt.Sprintf("function(%s) {\n\t\tthis.$val = this;\n\t\tif (arguments.length === 0) {\n", strings.Join(params, ", "))
   509  						for i := 0; i < t.NumFields(); i++ {
   510  							constructor += fmt.Sprintf("\t\t\tthis.%s = %s;\n", fieldName(t, i), funcCtx.translateExpr(funcCtx.zeroValue(t.Field(i).Type())).String())
   511  						}
   512  						constructor += "\t\t\treturn;\n\t\t}\n"
   513  						for i := 0; i < t.NumFields(); i++ {
   514  							constructor += fmt.Sprintf("\t\tthis.%[1]s = %[1]s_;\n", fieldName(t, i))
   515  						}
   516  						constructor += "\t}"
   517  					case *types.Basic, *types.Array, *types.Slice, *types.Chan, *types.Signature, *types.Interface, *types.Pointer, *types.Map:
   518  						size = sizes32.Sizeof(t)
   519  					}
   520  					if tPointer, ok := underlying.(*types.Pointer); ok {
   521  						if _, ok := tPointer.Elem().Underlying().(*types.Array); ok {
   522  							// Array pointers have non-default constructors to support wrapping
   523  							// of the native objects.
   524  							constructor = "$arrayPtrCtor()"
   525  						}
   526  					}
   527  					funcCtx.Printf(`%s = $newType(%d, %s, %q, %t, "%s", %t, %s);`, funcCtx.instName(inst), size, typeKind(typ), inst.TypeString(), o.Name() != "", o.Pkg().Path(), o.Exported(), constructor)
   528  				})
   529  				d.MethodListCode = funcCtx.CatchOutput(0, func() {
   530  					if _, ok := underlying.(*types.Interface); ok {
   531  						return
   532  					}
   533  					var methods []string
   534  					var ptrMethods []string
   535  					for i := 0; i < named.NumMethods(); i++ {
   536  						method := named.Method(i)
   537  						name := method.Name()
   538  						if reservedKeywords[name] {
   539  							name += "$"
   540  						}
   541  						pkgPath := ""
   542  						if !method.Exported() {
   543  							pkgPath = method.Pkg().Path()
   544  						}
   545  						t := method.Type().(*types.Signature)
   546  						entry := fmt.Sprintf(`{prop: "%s", name: %s, pkg: "%s", typ: $funcType(%s)}`, name, encodeString(method.Name()), pkgPath, funcCtx.initArgs(t))
   547  						if _, isPtr := t.Recv().Type().(*types.Pointer); isPtr {
   548  							ptrMethods = append(ptrMethods, entry)
   549  							continue
   550  						}
   551  						methods = append(methods, entry)
   552  					}
   553  					if len(methods) > 0 {
   554  						funcCtx.Printf("%s.methods = [%s];", funcCtx.instName(inst), strings.Join(methods, ", "))
   555  					}
   556  					if len(ptrMethods) > 0 {
   557  						funcCtx.Printf("%s.methods = [%s];", funcCtx.typeName(types.NewPointer(named)), strings.Join(ptrMethods, ", "))
   558  					}
   559  				})
   560  				switch t := underlying.(type) {
   561  				case *types.Array, *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Slice, *types.Signature, *types.Struct:
   562  					d.TypeInitCode = funcCtx.CatchOutput(0, func() {
   563  						funcCtx.Printf("%s.init(%s);", funcCtx.instName(inst), funcCtx.initArgs(t))
   564  					})
   565  				}
   566  			})
   567  			typeDecls = append(typeDecls, &d)
   568  		}
   569  		funcCtx.typeResolver = nil
   570  	}
   571  
   572  	// anonymous types
   573  	for _, t := range funcCtx.pkgCtx.anonTypes {
   574  		d := Decl{
   575  			Vars:            []string{t.Name()},
   576  			DceObjectFilter: t.Name(),
   577  		}
   578  		d.DceDeps = collectDependencies(func() {
   579  			d.DeclCode = []byte(fmt.Sprintf("\t%s = $%sType(%s);\n", t.Name(), strings.ToLower(typeKind(t.Type())[5:]), funcCtx.initArgs(t.Type())))
   580  		})
   581  		typeDecls = append(typeDecls, &d)
   582  	}
   583  
   584  	var allDecls []*Decl
   585  	for _, d := range append(append(append(importDecls, typeDecls...), varDecls...), funcDecls...) {
   586  		d.DeclCode = removeWhitespace(d.DeclCode, minify)
   587  		d.MethodListCode = removeWhitespace(d.MethodListCode, minify)
   588  		d.TypeInitCode = removeWhitespace(d.TypeInitCode, minify)
   589  		d.InitCode = removeWhitespace(d.InitCode, minify)
   590  		allDecls = append(allDecls, d)
   591  	}
   592  
   593  	if len(funcCtx.pkgCtx.errList) != 0 {
   594  		return nil, funcCtx.pkgCtx.errList
   595  	}
   596  
   597  	exportData := new(bytes.Buffer)
   598  	if err := gcexportdata.Write(exportData, nil, typesPkg); err != nil {
   599  		return nil, fmt.Errorf("failed to write export data: %w", err)
   600  	}
   601  	encodedFileSet := new(bytes.Buffer)
   602  	if err := srcs.FileSet.Write(json.NewEncoder(encodedFileSet).Encode); err != nil {
   603  		return nil, err
   604  	}
   605  
   606  	return &Archive{
   607  		ImportPath:   srcs.ImportPath,
   608  		Name:         typesPkg.Name(),
   609  		Imports:      importedPaths,
   610  		ExportData:   exportData.Bytes(),
   611  		Declarations: allDecls,
   612  		FileSet:      encodedFileSet.Bytes(),
   613  		Minified:     minify,
   614  		GoLinknames:  goLinknames,
   615  		BuildTime:    time.Now(),
   616  	}, nil
   617  }
   618  
   619  func (fc *funcContext) initArgs(ty types.Type) string {
   620  	switch t := ty.(type) {
   621  	case *types.Array:
   622  		return fmt.Sprintf("%s, %d", fc.typeName(t.Elem()), t.Len())
   623  	case *types.Chan:
   624  		return fmt.Sprintf("%s, %t, %t", fc.typeName(t.Elem()), t.Dir()&types.SendOnly != 0, t.Dir()&types.RecvOnly != 0)
   625  	case *types.Interface:
   626  		methods := make([]string, t.NumMethods())
   627  		for i := range methods {
   628  			method := t.Method(i)
   629  			pkgPath := ""
   630  			if !method.Exported() {
   631  				pkgPath = method.Pkg().Path()
   632  			}
   633  			methods[i] = fmt.Sprintf(`{prop: "%s", name: "%s", pkg: "%s", typ: $funcType(%s)}`, method.Name(), method.Name(), pkgPath, fc.initArgs(method.Type()))
   634  		}
   635  		return fmt.Sprintf("[%s]", strings.Join(methods, ", "))
   636  	case *types.Map:
   637  		return fmt.Sprintf("%s, %s", fc.typeName(t.Key()), fc.typeName(t.Elem()))
   638  	case *types.Pointer:
   639  		return fc.typeName(t.Elem())
   640  	case *types.Slice:
   641  		return fc.typeName(t.Elem())
   642  	case *types.Signature:
   643  		params := make([]string, t.Params().Len())
   644  		for i := range params {
   645  			params[i] = fc.typeName(t.Params().At(i).Type())
   646  		}
   647  		results := make([]string, t.Results().Len())
   648  		for i := range results {
   649  			results[i] = fc.typeName(t.Results().At(i).Type())
   650  		}
   651  		return fmt.Sprintf("[%s], [%s], %t", strings.Join(params, ", "), strings.Join(results, ", "), t.Variadic())
   652  	case *types.Struct:
   653  		pkgPath := ""
   654  		fields := make([]string, t.NumFields())
   655  		for i := range fields {
   656  			field := t.Field(i)
   657  			if !field.Exported() {
   658  				pkgPath = field.Pkg().Path()
   659  			}
   660  			fields[i] = fmt.Sprintf(`{prop: "%s", name: %s, embedded: %t, exported: %t, typ: %s, tag: %s}`, fieldName(t, i), encodeString(field.Name()), field.Anonymous(), field.Exported(), fc.typeName(field.Type()), encodeString(t.Tag(i)))
   661  		}
   662  		return fmt.Sprintf(`"%s", [%s]`, pkgPath, strings.Join(fields, ", "))
   663  	case *types.TypeParam:
   664  		err := bailout(fmt.Errorf(`%v has unexpected generic type parameter %T`, ty, ty))
   665  		panic(err)
   666  	default:
   667  		err := bailout(fmt.Errorf("%v has unexpected type %T", ty, ty))
   668  		panic(err)
   669  	}
   670  }
   671  
   672  func (fc *funcContext) translateToplevelFunction(fun *ast.FuncDecl, info *analysis.FuncInfo, inst typeparams.Instance) []byte {
   673  	o := inst.Object.(*types.Func)
   674  	sig := o.Type().(*types.Signature)
   675  	var recv *ast.Ident
   676  	if fun.Recv != nil && fun.Recv.List[0].Names != nil {
   677  		recv = fun.Recv.List[0].Names[0]
   678  	}
   679  
   680  	var joinedParams string
   681  	primaryFunction := func(funcRef string) []byte {
   682  		if fun.Body == nil {
   683  			return []byte(fmt.Sprintf("\t%s = function() {\n\t\t$throwRuntimeError(\"native function not implemented: %s\");\n\t};\n", funcRef, o.FullName()))
   684  		}
   685  
   686  		params, fun := translateFunction(fun.Type, recv, fun.Body, fc, sig, info, funcRef, inst)
   687  		joinedParams = strings.Join(params, ", ")
   688  		return []byte(fmt.Sprintf("\t%s = %s;\n", funcRef, fun))
   689  	}
   690  
   691  	code := bytes.NewBuffer(nil)
   692  
   693  	if fun.Recv == nil {
   694  		funcRef := fc.instName(inst)
   695  		code.Write(primaryFunction(funcRef))
   696  		if fun.Name.IsExported() {
   697  			fmt.Fprintf(code, "\t$pkg.%s = %s;\n", encodeIdent(fun.Name.Name), funcRef)
   698  		}
   699  		return code.Bytes()
   700  	}
   701  
   702  	recvInst := inst.Recv()
   703  	recvInstName := fc.instName(recvInst)
   704  	recvType := recvInst.Object.Type().(*types.Named)
   705  	funName := fun.Name.Name
   706  	if reservedKeywords[funName] {
   707  		funName += "$"
   708  	}
   709  
   710  	if _, isStruct := recvType.Underlying().(*types.Struct); isStruct {
   711  		code.Write(primaryFunction(recvInstName + ".ptr.prototype." + funName))
   712  		fmt.Fprintf(code, "\t%s.prototype.%s = function(%s) { return this.$val.%s(%s); };\n", recvInstName, funName, joinedParams, funName, joinedParams)
   713  		return code.Bytes()
   714  	}
   715  
   716  	if ptr, isPointer := sig.Recv().Type().(*types.Pointer); isPointer {
   717  		if _, isArray := ptr.Elem().Underlying().(*types.Array); isArray {
   718  			code.Write(primaryFunction(recvInstName + ".prototype." + funName))
   719  			fmt.Fprintf(code, "\t$ptrType(%s).prototype.%s = function(%s) { return (new %s(this.$get())).%s(%s); };\n", recvInstName, funName, joinedParams, recvInstName, funName, joinedParams)
   720  			return code.Bytes()
   721  		}
   722  		return primaryFunction(fmt.Sprintf("$ptrType(%s).prototype.%s", recvInstName, funName))
   723  	}
   724  
   725  	value := "this.$get()"
   726  	if isWrapped(recvType) {
   727  		value = fmt.Sprintf("new %s(%s)", recvInstName, value)
   728  	}
   729  	code.Write(primaryFunction(recvInstName + ".prototype." + funName))
   730  	fmt.Fprintf(code, "\t$ptrType(%s).prototype.%s = function(%s) { return %s.%s(%s); };\n", recvInstName, funName, joinedParams, value, funName, joinedParams)
   731  	return code.Bytes()
   732  }
   733  
   734  func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt, outerContext *funcContext, sig *types.Signature, info *analysis.FuncInfo, funcRef string, inst typeparams.Instance) ([]string, string) {
   735  	if info == nil {
   736  		panic("nil info")
   737  	}
   738  
   739  	c := &funcContext{
   740  		FuncInfo:     info,
   741  		pkgCtx:       outerContext.pkgCtx,
   742  		parent:       outerContext,
   743  		allVars:      make(map[string]int, len(outerContext.allVars)),
   744  		localVars:    []string{},
   745  		flowDatas:    map[*types.Label]*flowData{nil: {}},
   746  		caseCounter:  1,
   747  		labelCases:   make(map[*types.Label]int),
   748  		typeResolver: outerContext.typeResolver,
   749  		objectNames:  map[types.Object]string{},
   750  		sig:          &typesutil.Signature{Sig: sig},
   751  	}
   752  	for k, v := range outerContext.allVars {
   753  		c.allVars[k] = v
   754  	}
   755  	prevEV := c.pkgCtx.escapingVars
   756  
   757  	if sig.TypeParams().Len() > 0 {
   758  		c.typeResolver = typeparams.NewResolver(c.pkgCtx.typesCtx, typeparams.ToSlice(sig.TypeParams()), inst.TArgs)
   759  	} else if sig.RecvTypeParams().Len() > 0 {
   760  		c.typeResolver = typeparams.NewResolver(c.pkgCtx.typesCtx, typeparams.ToSlice(sig.RecvTypeParams()), inst.TArgs)
   761  	}
   762  	if c.objectNames == nil {
   763  		c.objectNames = map[types.Object]string{}
   764  	}
   765  
   766  	var params []string
   767  	for _, param := range typ.Params.List {
   768  		if len(param.Names) == 0 {
   769  			params = append(params, c.newLocalVariable("param"))
   770  			continue
   771  		}
   772  		for _, ident := range param.Names {
   773  			if isBlank(ident) {
   774  				params = append(params, c.newLocalVariable("param"))
   775  				continue
   776  			}
   777  			params = append(params, c.objectName(c.pkgCtx.Defs[ident]))
   778  		}
   779  	}
   780  
   781  	bodyOutput := string(c.CatchOutput(1, func() {
   782  		if len(c.Blocking) != 0 {
   783  			c.pkgCtx.Scopes[body] = c.pkgCtx.Scopes[typ]
   784  			c.handleEscapingVars(body)
   785  		}
   786  
   787  		if c.sig != nil && c.sig.HasNamedResults() {
   788  			c.resultNames = make([]ast.Expr, c.sig.Sig.Results().Len())
   789  			for i := 0; i < c.sig.Sig.Results().Len(); i++ {
   790  				result := c.sig.Sig.Results().At(i)
   791  				typ := c.typeResolver.Substitute(result.Type())
   792  				c.Printf("%s = %s;", c.objectName(result), c.translateExpr(c.zeroValue(typ)).String())
   793  				id := ast.NewIdent("")
   794  				c.pkgCtx.Uses[id] = result
   795  				c.resultNames[i] = c.setType(id, typ)
   796  			}
   797  		}
   798  
   799  		if recv != nil && !isBlank(recv) {
   800  			this := "this"
   801  			if isWrapped(c.typeOf(recv)) {
   802  				this = "this.$val" // Unwrap receiver value.
   803  			}
   804  			c.Printf("%s = %s;", c.translateExpr(recv), this)
   805  		}
   806  
   807  		c.translateStmtList(body.List)
   808  		if len(c.Flattened) != 0 && !astutil.EndsWithReturn(body.List) {
   809  			c.translateStmt(&ast.ReturnStmt{}, nil)
   810  		}
   811  	}))
   812  
   813  	sort.Strings(c.localVars)
   814  
   815  	var prefix, suffix, functionName string
   816  
   817  	if len(c.Flattened) != 0 {
   818  		c.localVars = append(c.localVars, "$s")
   819  		prefix = prefix + " $s = $s || 0;"
   820  	}
   821  
   822  	if c.HasDefer {
   823  		c.localVars = append(c.localVars, "$deferred")
   824  		suffix = " }" + suffix
   825  		if len(c.Blocking) != 0 {
   826  			suffix = " }" + suffix
   827  		}
   828  	}
   829  
   830  	localVarDefs := "" // Function-local var declaration at the top.
   831  
   832  	if len(c.Blocking) != 0 {
   833  		if funcRef == "" {
   834  			funcRef = "$b"
   835  			functionName = " $b"
   836  		}
   837  
   838  		localVars := append([]string{}, c.localVars...)
   839  		// There are several special variables involved in handling blocking functions:
   840  		// $r is sometimes used as a temporary variable to store blocking call result.
   841  		// $c indicates that a function is being resumed after a blocking call when set to true.
   842  		// $f is an object used to save and restore function context for blocking calls.
   843  		localVars = append(localVars, "$r")
   844  		// If a blocking function is being resumed, initialize local variables from the saved context.
   845  		localVarDefs = fmt.Sprintf("var {%s, $c} = $restore(this, {%s});\n", strings.Join(localVars, ", "), strings.Join(params, ", "))
   846  		// If the function gets blocked, save local variables for future.
   847  		saveContext := fmt.Sprintf("var $f = {$blk: "+funcRef+", $c: true, $r, %s};", strings.Join(c.localVars, ", "))
   848  
   849  		suffix = " " + saveContext + "return $f;" + suffix
   850  	} else if len(c.localVars) > 0 {
   851  		// Non-blocking functions simply declare local variables with no need for restore support.
   852  		localVarDefs = fmt.Sprintf("var %s;\n", strings.Join(c.localVars, ", "))
   853  	}
   854  
   855  	if c.HasDefer {
   856  		prefix = prefix + " var $err = null; try {"
   857  		deferSuffix := " } catch(err) { $err = err;"
   858  		if len(c.Blocking) != 0 {
   859  			deferSuffix += " $s = -1;"
   860  		}
   861  		if c.resultNames == nil && c.sig.HasResults() {
   862  			deferSuffix += fmt.Sprintf(" return%s;", c.translateResults(nil))
   863  		}
   864  		deferSuffix += " } finally { $callDeferred($deferred, $err);"
   865  		if c.resultNames != nil {
   866  			deferSuffix += fmt.Sprintf(" if (!$curGoroutine.asleep) { return %s; }", c.translateResults(c.resultNames))
   867  		}
   868  		if len(c.Blocking) != 0 {
   869  			deferSuffix += " if($curGoroutine.asleep) {"
   870  		}
   871  		suffix = deferSuffix + suffix
   872  	}
   873  
   874  	if len(c.Flattened) != 0 {
   875  		prefix = prefix + " s: while (true) { switch ($s) { case 0:"
   876  		suffix = " } return; }" + suffix
   877  	}
   878  
   879  	if c.HasDefer {
   880  		prefix = prefix + " $deferred = []; $curGoroutine.deferStack.push($deferred);"
   881  	}
   882  
   883  	if prefix != "" {
   884  		bodyOutput = c.Indentation(1) + "/* */" + prefix + "\n" + bodyOutput
   885  	}
   886  	if suffix != "" {
   887  		bodyOutput = bodyOutput + c.Indentation(1) + "/* */" + suffix + "\n"
   888  	}
   889  	if localVarDefs != "" {
   890  		bodyOutput = c.Indentation(1) + localVarDefs + bodyOutput
   891  	}
   892  
   893  	c.pkgCtx.escapingVars = prevEV
   894  
   895  	return params, fmt.Sprintf("function%s(%s) {\n%s%s}", functionName, strings.Join(params, ", "), bodyOutput, c.Indentation(0))
   896  }