func (t *Translator) TranslatePackage(importPath string, files []*ast.File, fileSet *token.FileSet, importPkg func(string) (*Archive, error)) (*Archive, error) { info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), Selections: make(map[*ast.SelectorExpr]*types.Selection), } var errList ErrorList var previousErr error config := &types.Config{ Packages: t.typesPackages, Import: func(_ map[string]*types.Package, path string) (*types.Package, error) { if _, err := importPkg(path); err != nil { return nil, err } return t.typesPackages[path], nil }, Sizes: sizes32, Error: func(err error) { if previousErr != nil && previousErr.Error() == err.Error() { return } errList = append(errList, err) previousErr = err }, } typesPkg, err := config.Check(importPath, fileSet, files, info) if errList != nil { return nil, errList } if err != nil { return nil, err } t.typesPackages[importPath] = typesPkg c := &funcContext{ p: &pkgContext{ pkg: typesPkg, info: info, pkgVars: make(map[string]string), objectVars: make(map[types.Object]string), indentation: 1, dependencies: make(map[types.Object]bool), }, allVars: make(map[string]int), flowDatas: map[string]*flowData{"": &flowData{}}, caseCounter: 1, labelCases: make(map[string]int), hasGoto: make(map[ast.Node]bool), } for name := range reservedKeywords { c.allVars[name] = 1 } var functions []*ast.FuncDecl var initStmts []ast.Stmt var toplevelTypes []*types.TypeName var vars []*types.Var for _, file := range files { for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: sig := c.p.info.Defs[d.Name].(*types.Func).Type().(*types.Signature) var recvType types.Type if sig.Recv() != nil { recvType = sig.Recv().Type() if ptr, isPtr := recvType.(*types.Pointer); isPtr { recvType = ptr.Elem() } } if isBlank(d.Name) { continue } if sig.Recv() == nil && d.Name.Name == "init" { initStmts = append(initStmts, d.Body.List...) continue } functions = append(functions, d) if sig.Recv() == nil { c.objectName(c.p.info.Defs[d.Name]) // register toplevel name } case *ast.GenDecl: switch d.Tok { case token.TYPE: for _, spec := range d.Specs { o := c.p.info.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) toplevelTypes = append(toplevelTypes, o) c.objectName(o) // register toplevel name } case token.VAR: for _, spec := range d.Specs { for _, name := range spec.(*ast.ValueSpec).Names { if !isBlank(name) { o := c.p.info.Defs[name].(*types.Var) vars = append(vars, o) c.objectName(o) // register toplevel name } } } case token.CONST: // skip, constants are inlined } } } } collectDependencies := func(self types.Object, f func()) []DepId { c.p.dependencies = make(map[types.Object]bool) f() var deps []string for dep := range c.p.dependencies { if dep != self { deps = append(deps, dep.Pkg().Path()+":"+dep.Name()) } } sort.Strings(deps) depIds := make([]DepId, len(deps)) for i, dep := range deps { depIds[i] = DepId(dep) } return depIds } gcData := bytes.NewBuffer(nil) gcexporter.Write(typesPkg, gcData, sizes32) encodedFileSet := bytes.NewBuffer(nil) if err := fileSet.Write(json.NewEncoder(encodedFileSet).Encode); err != nil { return nil, err } archive := &Archive{ ImportPath: importPath, GcData: gcData.Bytes(), Dependencies: []string{"runtime"}, // all packages depend on runtime FileSet: encodedFileSet.Bytes(), } // imports for _, importedPkg := range typesPkg.Imports() { varName := c.newVariable(importedPkg.Name()) c.p.pkgVars[importedPkg.Path()] = varName archive.Imports = append(archive.Imports, Import{Path: importedPkg.Path(), VarName: varName}) } // types for _, o := range toplevelTypes { typeName := c.objectName(o) var d Decl d.Var = typeName d.DceFilters = []DepId{DepId(o.Name())} d.DceDeps = collectDependencies(o, func() { d.BodyCode = c.CatchOutput(0, func() { c.translateType(o, true) }) d.InitCode = c.CatchOutput(1, func() { c.initType(o) }) }) archive.Declarations = append(archive.Declarations, d) } // functions nativesOrig := pkgNatives[importPath] natives := make(map[string]string, len(nativesOrig)) for k, v := range nativesOrig { natives[k] = v } for _, fun := range functions { var d Decl o := c.p.info.Defs[fun.Name].(*types.Func) funName := fun.Name.Name if fun.Recv == nil { d.Var = c.objectName(o) if o.Name() != "main" { d.DceFilters = []DepId{DepId(o.Name())} } } if fun.Recv != nil { recvType := o.Type().(*types.Signature).Recv().Type() ptr, isPointer := recvType.(*types.Pointer) namedRecvType, _ := recvType.(*types.Named) if isPointer { namedRecvType = ptr.Elem().(*types.Named) } funName = namedRecvType.Obj().Name() + "." + funName d.DceFilters = []DepId{DepId(namedRecvType.Obj().Name())} if !fun.Name.IsExported() { d.DceFilters = append(d.DceFilters, DepId(fun.Name.Name)) } } native := natives[funName] delete(natives, funName) d.DceDeps = collectDependencies(o, func() { d.BodyCode = c.translateToplevelFunction(fun, native) }) archive.Declarations = append(archive.Declarations, d) if strings.HasPrefix(fun.Name.String(), "Test") { archive.Tests = append(archive.Tests, fun.Name.String()) } } // variables initOrder := c.p.info.InitOrder // workaround for https://code.google.com/p/go/issues/detail?id=6703#c6 if importPath == "math/rand" { findInit := func(name string) int { for i, init := range initOrder { if init.Lhs[0].Name() == name { return i } } panic("init not found") } i := findInit("rng_cooked") j := findInit("globalRand") if i > j { initOrder[i], initOrder[j] = initOrder[j], initOrder[i] } } varsWithInit := make(map[*types.Var]bool) for _, init := range initOrder { for _, o := range init.Lhs { varsWithInit[o] = true } } for _, o := range vars { var d Decl if !o.Exported() { d.Var = c.objectName(o) } if _, ok := varsWithInit[o]; !ok { d.DceDeps = collectDependencies(nil, func() { value := c.zeroValue(o.Type()) if native, ok := natives[o.Name()]; ok { value = native delete(natives, o.Name()) } d.InitCode = []byte(fmt.Sprintf("\t\t%s = %s;\n", c.objectName(o), value)) }) } d.DceFilters = []DepId{DepId(o.Name())} archive.Declarations = append(archive.Declarations, d) } for _, init := range initOrder { lhs := make([]ast.Expr, len(init.Lhs)) for i, o := range init.Lhs { ident := ast.NewIdent(o.Name()) c.p.info.Types[ident] = types.TypeAndValue{Type: o.Type()} c.p.info.Defs[ident] = o lhs[i] = ident varsWithInit[o] = true } var d Decl d.DceDeps = collectDependencies(nil, func() { d.InitCode = c.translateFunctionBody(1, []ast.Stmt{ &ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: []ast.Expr{init.Rhs}, }, }) }) if len(init.Lhs) == 1 { v := hasCallVisitor{c.p.info, false} ast.Walk(&v, init.Rhs) if !v.hasCall { d.DceFilters = []DepId{DepId(init.Lhs[0].Name())} } } archive.Declarations = append(archive.Declarations, d) } // natives var toplevel Decl toplevel.BodyCode = []byte(natives["toplevel"]) delete(natives, "toplevel") if toplevelDependencies, ok := natives["toplevelDependencies"]; ok { for _, dep := range strings.Split(toplevelDependencies, " ") { toplevel.DceDeps = append(toplevel.DceDeps, DepId(dep)) } delete(natives, "toplevelDependencies") } archive.Declarations = append(archive.Declarations, toplevel) // init functions var init Decl init.DceDeps = collectDependencies(nil, func() { init.InitCode = c.translateFunctionBody(1, initStmts) }) archive.Declarations = append(archive.Declarations, init) if len(natives) != 0 { panic("not all natives used: " + importPath) } var importedPaths []string for _, imp := range typesPkg.Imports() { importedPaths = append(importedPaths, imp.Path()) } sort.Strings(importedPaths) for _, impPath := range importedPaths { impOutput, err := importPkg(impPath) if err != nil { return nil, err } archive.AddDependenciesOf(impOutput) } archive.AddDependency(importPath) return archive, nil }
func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, importContext *ImportContext, minify bool) (*Archive, error) { typesInfo := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), Selections: make(map[*ast.SelectorExpr]*types.Selection), Scopes: make(map[ast.Node]*types.Scope), } var importError error var errList ErrorList var previousErr error config := &types.Config{ Importer: packageImporter{ importContext: importContext, importError: &importError, }, Sizes: sizes32, Error: func(err error) { if previousErr != nil && previousErr.Error() == err.Error() { return } errList = append(errList, err) previousErr = err }, } typesPkg, err := config.Check(importPath, fileSet, files, typesInfo) if importError != nil { return nil, importError } if errList != nil { if len(errList) > 10 { pos := token.NoPos if last, ok := errList[9].(types.Error); ok { pos = last.Pos } errList = append(errList[:10], types.Error{Fset: fileSet, Pos: pos, Msg: "too many errors"}) } return nil, errList } if err != nil { return nil, err } importContext.Packages[importPath] = typesPkg exportData := importer.ExportData(typesPkg) encodedFileSet := bytes.NewBuffer(nil) if err := fileSet.Write(json.NewEncoder(encodedFileSet).Encode); err != nil { return nil, err } isBlocking := func(f *types.Func) bool { archive, err := importContext.Import(f.Pkg().Path()) if err != nil { panic(err) } fullName := f.FullName() for _, d := range archive.Declarations { if string(d.FullName) == fullName { return d.Blocking } } panic(fullName) } pkgInfo := analysis.AnalyzePkg(files, fileSet, typesInfo, typesPkg, isBlocking) c := &funcContext{ FuncInfo: pkgInfo.InitFuncInfo, p: &pkgContext{ Info: pkgInfo, additionalSelections: make(map[*ast.SelectorExpr]selection), pkgVars: make(map[string]string), objectNames: make(map[types.Object]string), varPtrNames: make(map[*types.Var]string), escapingVars: make(map[*types.Var]bool), indentation: 1, dependencies: make(map[types.Object]bool), minify: minify, fileSet: fileSet, }, allVars: make(map[string]int), flowDatas: map[*types.Label]*flowData{nil: {}}, caseCounter: 1, labelCases: make(map[*types.Label]int), } for name := range reservedKeywords { c.allVars[name] = 1 } // imports var importDecls []*Decl var importedPaths []string for _, importedPkg := range typesPkg.Imports() { c.p.pkgVars[importedPkg.Path()] = c.newVariableWithLevel(importedPkg.Name(), true) importedPaths = append(importedPaths, importedPkg.Path()) } sort.Strings(importedPaths) for _, impPath := range importedPaths { id := c.newIdent(fmt.Sprintf(`%s.$init`, c.p.pkgVars[impPath]), types.NewSignature(nil, nil, nil, false)) call := &ast.CallExpr{Fun: id} c.Blocking[call] = true c.Flattened[call] = true importDecls = append(importDecls, &Decl{ Vars: []string{c.p.pkgVars[impPath]}, DeclCode: []byte(fmt.Sprintf("\t%s = $packages[\"%s\"];\n", c.p.pkgVars[impPath], impPath)), InitCode: c.CatchOutput(1, func() { c.translateStmt(&ast.ExprStmt{X: call}, nil) }), }) } var functions []*ast.FuncDecl var vars []*types.Var for _, file := range files { for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: sig := c.p.Defs[d.Name].(*types.Func).Type().(*types.Signature) var recvType types.Type if sig.Recv() != nil { recvType = sig.Recv().Type() if ptr, isPtr := recvType.(*types.Pointer); isPtr { recvType = ptr.Elem() } } if sig.Recv() == nil { c.objectName(c.p.Defs[d.Name].(*types.Func)) // register toplevel name } if !isBlank(d.Name) { functions = append(functions, d) } case *ast.GenDecl: switch d.Tok { case token.TYPE: for _, spec := range d.Specs { o := c.p.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) c.p.typeNames = append(c.p.typeNames, o) c.objectName(o) // register toplevel name } case token.VAR: for _, spec := range d.Specs { for _, name := range spec.(*ast.ValueSpec).Names { if !isBlank(name) { o := c.p.Defs[name].(*types.Var) vars = append(vars, o) c.objectName(o) // register toplevel name } } } case token.CONST: // skip, constants are inlined } } } } collectDependencies := func(f func()) []string { c.p.dependencies = make(map[types.Object]bool) f() var deps []string for o := range c.p.dependencies { qualifiedName := o.Pkg().Path() + "." + o.Name() if f, ok := o.(*types.Func); ok && f.Type().(*types.Signature).Recv() != nil { deps = append(deps, qualifiedName+"~") continue } deps = append(deps, qualifiedName) } sort.Strings(deps) return deps } // variables var varDecls []*Decl varsWithInit := make(map[*types.Var]bool) for _, init := range c.p.InitOrder { for _, o := range init.Lhs { varsWithInit[o] = true } } for _, o := range vars { var d Decl if !o.Exported() { d.Vars = []string{c.objectName(o)} } if c.p.HasPointer[o] && !o.Exported() { d.Vars = append(d.Vars, c.varPtrName(o)) } if _, ok := varsWithInit[o]; !ok { d.DceDeps = collectDependencies(func() { d.InitCode = []byte(fmt.Sprintf("\t\t%s = %s;\n", c.objectName(o), c.translateExpr(c.zeroValue(o.Type())).String())) }) } d.DceObjectFilter = o.Name() varDecls = append(varDecls, &d) } for _, init := range c.p.InitOrder { lhs := make([]ast.Expr, len(init.Lhs)) for i, o := range init.Lhs { ident := ast.NewIdent(o.Name()) c.p.Defs[ident] = o lhs[i] = c.setType(ident, o.Type()) varsWithInit[o] = true } var d Decl d.DceDeps = collectDependencies(func() { c.localVars = nil d.InitCode = c.CatchOutput(1, func() { c.translateStmt(&ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: []ast.Expr{init.Rhs}, }, nil) }) d.Vars = append(d.Vars, c.localVars...) }) if len(init.Lhs) == 1 { if !analysis.HasSideEffect(init.Rhs, c.p.Info.Info) { d.DceObjectFilter = init.Lhs[0].Name() } } varDecls = append(varDecls, &d) } // functions var funcDecls []*Decl var mainFunc *types.Func for _, fun := range functions { o := c.p.Defs[fun.Name].(*types.Func) funcInfo := c.p.FuncDeclInfos[o] d := Decl{ FullName: o.FullName(), Blocking: len(funcInfo.Blocking) != 0, } if fun.Recv == nil { d.Vars = []string{c.objectName(o)} d.DceObjectFilter = o.Name() switch o.Name() { case "main": mainFunc = o d.DceObjectFilter = "" case "init": d.InitCode = c.CatchOutput(1, func() { id := c.newIdent("", types.NewSignature(nil, nil, nil, false)) c.p.Uses[id] = o call := &ast.CallExpr{Fun: id} if len(c.p.FuncDeclInfos[o].Blocking) != 0 { c.Blocking[call] = true } c.translateStmt(&ast.ExprStmt{X: call}, nil) }) d.DceObjectFilter = "" } } if fun.Recv != nil { recvType := o.Type().(*types.Signature).Recv().Type() ptr, isPointer := recvType.(*types.Pointer) namedRecvType, _ := recvType.(*types.Named) if isPointer { namedRecvType = ptr.Elem().(*types.Named) } d.DceObjectFilter = namedRecvType.Obj().Name() if !fun.Name.IsExported() { d.DceMethodFilter = o.Name() + "~" } } d.DceDeps = collectDependencies(func() { d.DeclCode = c.translateToplevelFunction(fun, funcInfo) }) funcDecls = append(funcDecls, &d) } if typesPkg.Name() == "main" { if mainFunc == nil { return nil, fmt.Errorf("missing main function") } id := c.newIdent("", types.NewSignature(nil, nil, nil, false)) c.p.Uses[id] = mainFunc call := &ast.CallExpr{Fun: id} ifStmt := &ast.IfStmt{ Cond: c.newIdent("$pkg === $mainPkg", types.Typ[types.Bool]), Body: &ast.BlockStmt{ List: []ast.Stmt{ &ast.ExprStmt{X: call}, &ast.AssignStmt{ Lhs: []ast.Expr{c.newIdent("$mainFinished", types.Typ[types.Bool])}, Tok: token.ASSIGN, Rhs: []ast.Expr{c.newConst(types.Typ[types.Bool], constant.MakeBool(true))}, }, }, }, } if len(c.p.FuncDeclInfos[mainFunc].Blocking) != 0 { c.Blocking[call] = true c.Flattened[ifStmt] = true } funcDecls = append(funcDecls, &Decl{ InitCode: c.CatchOutput(1, func() { c.translateStmt(ifStmt, nil) }), }) } // named types var typeDecls []*Decl for _, o := range c.p.typeNames { typeName := c.objectName(o) d := Decl{ Vars: []string{typeName}, DceObjectFilter: o.Name(), } d.DceDeps = collectDependencies(func() { d.DeclCode = c.CatchOutput(0, func() { typeName := c.objectName(o) lhs := typeName if isPkgLevel(o) { lhs += " = $pkg." + encodeIdent(o.Name()) } size := int64(0) constructor := "null" switch t := o.Type().Underlying().(type) { case *types.Struct: params := make([]string, t.NumFields()) for i := 0; i < t.NumFields(); i++ { params[i] = fieldName(t, i) + "_" } constructor = fmt.Sprintf("function(%s) {\n\t\tthis.$val = this;\n\t\tif (arguments.length === 0) {\n", strings.Join(params, ", ")) for i := 0; i < t.NumFields(); i++ { constructor += fmt.Sprintf("\t\t\tthis.%s = %s;\n", fieldName(t, i), c.translateExpr(c.zeroValue(t.Field(i).Type())).String()) } constructor += "\t\t\treturn;\n\t\t}\n" for i := 0; i < t.NumFields(); i++ { constructor += fmt.Sprintf("\t\tthis.%[1]s = %[1]s_;\n", fieldName(t, i)) } constructor += "\t}" case *types.Basic, *types.Array, *types.Slice, *types.Chan, *types.Signature, *types.Interface, *types.Pointer, *types.Map: size = sizes32.Sizeof(t) } c.Printf(`%s = $newType(%d, %s, "%s.%s", "%s", "%s", %s);`, lhs, size, typeKind(o.Type()), o.Pkg().Name(), o.Name(), o.Name(), o.Pkg().Path(), constructor) }) d.MethodListCode = c.CatchOutput(0, func() { if _, isInterface := o.Type().Underlying().(*types.Interface); !isInterface { named := o.Type().(*types.Named) var methods []string var ptrMethods []string for i := 0; i < named.NumMethods(); i++ { method := named.Method(i) name := method.Name() if reservedKeywords[name] { name += "$" } pkgPath := "" if !method.Exported() { pkgPath = method.Pkg().Path() } t := method.Type().(*types.Signature) entry := fmt.Sprintf(`{prop: "%s", name: "%s", pkg: "%s", typ: $funcType(%s)}`, name, method.Name(), pkgPath, c.initArgs(t)) if _, isPtr := t.Recv().Type().(*types.Pointer); isPtr { ptrMethods = append(ptrMethods, entry) continue } methods = append(methods, entry) } if len(methods) > 0 { c.Printf("%s.methods = [%s];", c.typeName(o.Type()), strings.Join(methods, ", ")) } if len(ptrMethods) > 0 { c.Printf("%s.methods = [%s];", c.typeName(types.NewPointer(o.Type())), strings.Join(ptrMethods, ", ")) } } }) switch t := o.Type().Underlying().(type) { case *types.Array, *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Slice, *types.Signature, *types.Struct: d.TypeInitCode = c.CatchOutput(0, func() { c.Printf("%s.init(%s);", c.objectName(o), c.initArgs(t)) }) } }) typeDecls = append(typeDecls, &d) } // anonymous types for _, t := range c.p.anonTypes { d := Decl{ Vars: []string{t.Name()}, DceObjectFilter: t.Name(), } d.DceDeps = collectDependencies(func() { d.DeclCode = []byte(fmt.Sprintf("\t%s = $%sType(%s);\n", t.Name(), strings.ToLower(typeKind(t.Type())[5:]), c.initArgs(t.Type()))) }) typeDecls = append(typeDecls, &d) } var allDecls []*Decl for _, d := range append(append(append(importDecls, typeDecls...), varDecls...), funcDecls...) { d.DeclCode = removeWhitespace(d.DeclCode, minify) d.MethodListCode = removeWhitespace(d.MethodListCode, minify) d.TypeInitCode = removeWhitespace(d.TypeInitCode, minify) d.InitCode = removeWhitespace(d.InitCode, minify) allDecls = append(allDecls, d) } if len(c.p.errList) != 0 { return nil, c.p.errList } return &Archive{ ImportPath: importPath, Name: typesPkg.Name(), Imports: importedPaths, ExportData: exportData, Declarations: allDecls, FileSet: encodedFileSet.Bytes(), Minified: minify, types: typesPkg, }, nil }
func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, importContext *ImportContext, minify bool) (*Archive, error) { info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), Selections: make(map[*ast.SelectorExpr]*types.Selection), } var errList ErrorList var previousErr error config := &types.Config{ Packages: importContext.Packages, Import: func(_ map[string]*types.Package, path string) (*types.Package, error) { if _, err := importContext.Import(path); err != nil { return nil, err } return importContext.Packages[path], nil }, Sizes: sizes32, Error: func(err error) { if previousErr != nil && previousErr.Error() == err.Error() { return } errList = append(errList, err) previousErr = err }, } typesPkg, err := config.Check(importPath, fileSet, files, info) if errList != nil { return nil, errList } if err != nil { return nil, err } importContext.Packages[importPath] = typesPkg gcData := bytes.NewBuffer(nil) gcexporter.Write(typesPkg, gcData, sizes32) encodedFileSet := bytes.NewBuffer(nil) if err := fileSet.Write(json.NewEncoder(encodedFileSet).Encode); err != nil { return nil, err } archive := &Archive{ ImportPath: PkgPath(importPath), GcData: gcData.Bytes(), Dependencies: []PkgPath{PkgPath("github.com/gopherjs/gopherjs/js"), PkgPath("runtime")}, // all packages depend on those FileSet: encodedFileSet.Bytes(), Minified: minify, } c := &funcContext{ p: &pkgContext{ pkg: typesPkg, info: info, importContext: importContext, comments: make(ast.CommentMap), funcContexts: make(map[*types.Func]*funcContext), pkgVars: make(map[string]string), objectVars: make(map[types.Object]string), escapingVars: make(map[types.Object]bool), indentation: 1, dependencies: make(map[types.Object]bool), minify: minify, }, allVars: make(map[string]int), flowDatas: map[string]*flowData{"": &flowData{}}, flattened: make(map[ast.Node]bool), blocking: make(map[ast.Node]bool), caseCounter: 1, labelCases: make(map[string]int), localCalls: make(map[*types.Func][][]ast.Node), } for name := range reservedKeywords { c.allVars[name] = 1 } // imports var importedPaths []string for _, importedPkg := range typesPkg.Imports() { varName := c.newVariableWithLevel(importedPkg.Name(), true, "") c.p.pkgVars[importedPkg.Path()] = varName archive.Imports = append(archive.Imports, PkgImport{Path: PkgPath(importedPkg.Path()), VarName: varName}) importedPaths = append(importedPaths, importedPkg.Path()) } sort.Strings(importedPaths) for _, impPath := range importedPaths { impOutput, err := importContext.Import(impPath) if err != nil { return nil, err } archive.AddDependenciesOf(impOutput) } var functions []*ast.FuncDecl var toplevelTypes []*types.TypeName var vars []*types.Var for _, file := range files { for k, v := range ast.NewCommentMap(fileSet, file, file.Comments) { c.p.comments[k] = v } for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: sig := c.p.info.Defs[d.Name].(*types.Func).Type().(*types.Signature) var recvType types.Type if sig.Recv() != nil { recvType = sig.Recv().Type() if ptr, isPtr := recvType.(*types.Pointer); isPtr { recvType = ptr.Elem() } } o := c.p.info.Defs[d.Name].(*types.Func) c.p.funcContexts[o] = c.p.analyzeFunction(sig, d.Body) if sig.Recv() == nil { c.objectName(o) // register toplevel name } if !isBlank(d.Name) { functions = append(functions, d) } case *ast.GenDecl: switch d.Tok { case token.TYPE: for _, spec := range d.Specs { o := c.p.info.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) toplevelTypes = append(toplevelTypes, o) c.objectName(o) // register toplevel name } case token.VAR: for _, spec := range d.Specs { for _, name := range spec.(*ast.ValueSpec).Names { if !isBlank(name) { o := c.p.info.Defs[name].(*types.Var) vars = append(vars, o) c.objectName(o) // register toplevel name } } } case token.CONST: // skip, constants are inlined } } } } for { done := true for _, context := range c.p.funcContexts { for obj, calls := range context.localCalls { if len(c.p.funcContexts[obj].blocking) != 0 { for _, call := range calls { context.markBlocking(call) } delete(context.localCalls, obj) done = false } } } if done { break } } collectDependencies := func(self types.Object, f func()) []DepId { c.p.dependencies = make(map[types.Object]bool) f() var deps []string for dep := range c.p.dependencies { if dep != self { deps = append(deps, dep.Pkg().Path()+":"+dep.Name()) } } sort.Strings(deps) depIds := make([]DepId, len(deps)) for i, dep := range deps { depIds[i] = DepId(dep) } return depIds } // types for _, o := range toplevelTypes { typeName := c.objectName(o) var d Decl d.Vars = []string{typeName} d.DceFilters = []DepId{DepId(o.Name())} d.DceDeps = collectDependencies(o, func() { d.BodyCode = removeWhitespace(c.CatchOutput(0, func() { c.translateType(o, true) }), minify) d.InitCode = removeWhitespace(c.CatchOutput(1, func() { c.initType(o) }), minify) }) archive.Declarations = append(archive.Declarations, d) } // variables varsWithInit := make(map[*types.Var]bool) for _, init := range c.p.info.InitOrder { for _, o := range init.Lhs { varsWithInit[o] = true } } for _, o := range vars { var d Decl if !o.Exported() { d.Vars = []string{c.objectName(o)} } if _, ok := varsWithInit[o]; !ok { d.DceDeps = collectDependencies(nil, func() { value := c.zeroValue(o.Type()) if importPath == "runtime" && o.Name() == "sizeof_C_MStats" { value = "3712" } d.InitCode = removeWhitespace([]byte(fmt.Sprintf("\t\t%s = %s;\n", c.objectName(o), value)), minify) }) } d.DceFilters = []DepId{DepId(o.Name())} archive.Declarations = append(archive.Declarations, d) } for _, init := range c.p.info.InitOrder { lhs := make([]ast.Expr, len(init.Lhs)) for i, o := range init.Lhs { ident := ast.NewIdent(o.Name()) c.p.info.Defs[ident] = o lhs[i] = c.setType(ident, o.Type()) varsWithInit[o] = true } var d Decl d.DceDeps = collectDependencies(nil, func() { c.localVars = nil d.InitCode = removeWhitespace(c.CatchOutput(1, func() { ast.Walk(c, init.Rhs) c.translateStmt(&ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: []ast.Expr{init.Rhs}, }, "") }), minify) d.Vars = append(d.Vars, c.localVars...) }) if len(init.Lhs) == 1 { v := hasCallVisitor{c.p.info, false} ast.Walk(&v, init.Rhs) if !v.hasCall { d.DceFilters = []DepId{DepId(init.Lhs[0].Name())} } } archive.Declarations = append(archive.Declarations, d) } // functions var mainFunc *types.Func for _, fun := range functions { o := c.p.info.Defs[fun.Name].(*types.Func) context := c.p.funcContexts[o] d := Decl{ FullName: []byte(o.FullName()), Blocking: len(context.blocking) != 0, } if fun.Recv == nil { d.Vars = []string{c.objectName(o)} switch o.Name() { case "main": mainFunc = o case "init": d.InitCode = removeWhitespace(c.CatchOutput(1, func() { id := c.newIdent("", types.NewSignature(nil, nil, nil, nil, false)) c.p.info.Uses[id] = o call := &ast.CallExpr{Fun: id} c.Visit(call) c.translateStmt(&ast.ExprStmt{X: call}, "") }), minify) default: d.DceFilters = []DepId{DepId(o.Name())} } } if fun.Recv != nil { recvType := o.Type().(*types.Signature).Recv().Type() ptr, isPointer := recvType.(*types.Pointer) namedRecvType, _ := recvType.(*types.Named) if isPointer { namedRecvType = ptr.Elem().(*types.Named) } d.DceFilters = []DepId{DepId(namedRecvType.Obj().Name())} if !fun.Name.IsExported() { d.DceFilters = append(d.DceFilters, DepId(fun.Name.Name)) } } d.DceDeps = collectDependencies(o, func() { d.BodyCode = removeWhitespace(c.translateToplevelFunction(fun, context), minify) }) archive.Declarations = append(archive.Declarations, d) if fun.Recv == nil && strings.HasPrefix(fun.Name.String(), "Test") { archive.Tests = append(archive.Tests, fun.Name.String()) } } archive.BlockingInit = len(c.blocking) != 0 // $run function if typesPkg.Name() == "main" { var stmts []ast.Stmt for _, dep := range archive.Dependencies { id := c.newIdent(fmt.Sprintf(`$packages["%s"].$init`, dep), types.NewSignature(nil, nil, nil, nil, false)) call := &ast.CallExpr{Fun: id} depArchive, err := importContext.Import(string(dep)) if err != nil { panic(err) } if depArchive.BlockingInit { c.blocking[call] = true c.flattened[call] = true } stmts = append(stmts, &ast.ExprStmt{X: call}) } { id := c.newIdent("$pkg.$init", types.NewSignature(nil, nil, nil, nil, false)) call := &ast.CallExpr{Fun: id} if archive.BlockingInit { c.blocking[call] = true c.flattened[call] = true } stmts = append(stmts, &ast.ExprStmt{X: call}) } { id := c.newIdent("", types.NewSignature(nil, nil, nil, nil, false)) c.p.info.Uses[id] = mainFunc call := &ast.CallExpr{Fun: id} c.Visit(call) stmts = append(stmts, &ast.ExprStmt{X: call}) } archive.Declarations = append(archive.Declarations, Decl{ BodyCode: removeWhitespace(append(append([]byte("\t$pkg.$run = function($b) {\n"), c.translateFunctionBody(stmts)...), []byte("\t};\n")...), minify), }) } return archive, nil }