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 }