func TranslatePackage(importPath string, files []*ast.File, fileSet *token.FileSet, config *types.Config) ([]byte, error) { info := &types.Info{ Types: make(map[ast.Expr]types.Type), Values: make(map[ast.Expr]exact.Value), Objects: 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.Error = func(err error) { if previousErr != nil && previousErr.Error() == err.Error() { return } errList = append(errList, err) previousErr = err } config.Sizes = sizes32 typesPkg, err := config.Check(importPath, fileSet, files, info) if errList != nil { return nil, errList } if err != nil { return nil, err } config.Packages[importPath] = typesPkg c := &PkgContext{ pkg: typesPkg, info: info, pkgVars: make(map[string]string), objectVars: make(map[types.Object]string), allVarNames: make(map[string]int), postLoopStmt: make(map[string]ast.Stmt), positions: make(map[int]token.Pos), } for _, name := range ReservedKeywords { c.allVarNames[name] = 1 } functionsByType := make(map[types.Type][]*ast.FuncDecl) functionsByObject := make(map[types.Object]*ast.FuncDecl) var initStmts []ast.Stmt var typeSpecs []*ast.TypeSpec var constSpecs []*ast.ValueSpec var varSpecs []*ast.ValueSpec for _, file := range files { for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: sig := c.info.Objects[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 && d.Name.Name == "init" { initStmts = append(initStmts, d.Body.List...) continue } functionsByType[recvType] = append(functionsByType[recvType], d) o := c.info.Objects[d.Name] functionsByObject[o] = d if sig.Recv() == nil { c.objectName(o) // register toplevel name } case *ast.GenDecl: switch d.Tok { case token.TYPE: for _, spec := range d.Specs { s := spec.(*ast.TypeSpec) typeSpecs = append(typeSpecs, s) c.objectName(c.info.Objects[s.Name]) // register toplevel name } case token.CONST: for _, spec := range d.Specs { s := spec.(*ast.ValueSpec) constSpecs = append(constSpecs, s) for _, name := range s.Names { if !isBlank(name) { c.objectName(c.info.Objects[name]) // register toplevel name } } } case token.VAR: for _, spec := range d.Specs { s := spec.(*ast.ValueSpec) varSpecs = append(varSpecs, s) for _, name := range s.Names { if !isBlank(name) { c.objectName(c.info.Objects[name]) // register toplevel name } } } } } } } // resolve var dependencies var unorderedSingleVarSpecs []*ast.ValueSpec pendingObjects := make(map[types.Object]bool) for _, spec := range varSpecs { for _, singleSpec := range c.splitValueSpec(spec) { if singleSpec.Values[0] == nil { continue } unorderedSingleVarSpecs = append(unorderedSingleVarSpecs, singleSpec) for _, name := range singleSpec.Names { pendingObjects[c.info.Objects[name]] = true } } } complete := false var intVarStmts []ast.Stmt for !complete { complete = true for i, spec := range unorderedSingleVarSpecs { if spec == nil { continue } v := DependencyCollector{info: c.info, functions: functionsByObject} ast.Walk(&v, spec.Values[0]) currentObjs := make(map[types.Object]bool) for _, name := range spec.Names { currentObjs[c.info.Objects[name]] = true } ready := true for _, dep := range v.dependencies { if currentObjs[dep] { return nil, fmt.Errorf("%s: initialization loop", fileSet.Position(dep.Pos()).String()) } ready = ready && !pendingObjects[dep] } if !ready { complete = false continue } lhs := make([]ast.Expr, len(spec.Names)) for i, name := range spec.Names { lhs[i] = name delete(pendingObjects, c.info.Objects[name]) } intVarStmts = append(intVarStmts, &ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: spec.Values, }) unorderedSingleVarSpecs[i] = nil } } c.Indent(func() { for _, importedPkg := range typesPkg.Imports() { varName := c.newVariable(importedPkg.Name()) c.Printf(`var %s = Go$packages["%s"];`, varName, importedPkg.Path()) c.pkgVars[importedPkg.Path()] = varName } // types and their functions for _, spec := range typeSpecs { obj := c.info.Objects[spec.Name] typeName := c.objectName(obj) c.Printf("var %s;", typeName) c.translateTypeSpec(spec) for _, fun := range functionsByType[obj.Type()] { _, isStruct := obj.Type().Underlying().(*types.Struct) c.translateMethod(typeName, isStruct, fun) } c.Printf("Go$pkg.%s = %s;", typeName, typeName) } // package functions for _, fun := range functionsByType[nil] { if isBlank(fun.Name) { continue } c.newScope(func() { name := c.objectName(c.info.Objects[fun.Name]) params := c.translateParams(fun.Type) c.Printf("var %s = function(%s) {", name, strings.Join(params, ", ")) c.Indent(func() { jsCode, _ := typesPkg.Scope().Lookup("js_" + name).(*types.Const) if jsCode != nil { c.Write([]byte(exact.StringVal(jsCode.Val()))) c.Write([]byte{'\n'}) return } if fun.Body == nil { c.Printf(`throw new Go$Panic("Native function not implemented: %s");`, name) return } c.translateFunctionBody(fun.Body.List, c.info.Objects[fun.Name].Type().(*types.Signature)) }) c.Printf("};") }) } // constants for _, spec := range constSpecs { for _, name := range spec.Names { if isBlank(name) || strings.HasPrefix(name.Name, "js_") { continue } o := c.info.Objects[name].(*types.Const) c.info.Types[name] = o.Type() c.info.Values[name] = o.Val() c.Printf("%s = %s;", c.objectName(o), c.translateExpr(name)) } } // variables for _, spec := range varSpecs { for _, name := range spec.Names { o := c.info.Objects[name].(*types.Var) c.Printf("%s = %s;", c.objectName(o), c.zeroValue(o.Type())) } } // native implementations if native, hasNative := natives[importPath]; hasNative { c.Write([]byte(strings.TrimSpace(native))) c.Write([]byte{'\n'}) } // exports for package functions for _, fun := range functionsByType[nil] { name := fun.Name.Name if fun.Name.IsExported() || name == "main" { c.Printf("Go$pkg.%s = %s;", name, name) } } // init function c.Printf("Go$pkg.init = function() {") c.Indent(func() { c.translateFunctionBody(append(intVarStmts, initStmts...), nil) }) c.Printf("};") }) return c.output, nil }