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