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 }