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