func (p *importer) obj(tag int) { switch tag { case constTag: pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) val := p.value() p.declare(types.NewConst(pos, pkg, name, typ, val)) case typeTag: _ = p.typ(nil) case varTag: pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) p.declare(types.NewVar(pos, pkg, name, typ)) case funcTag: pos := p.pos() pkg, name := p.qualifiedName() params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) p.declare(types.NewFunc(pos, pkg, name, sig)) default: panic(fmt.Sprintf("unexpected object tag %d", tag)) } }
func (p *importer) signature() *types.Signature { var recv *types.Var if p.int() != 0 { recv = p.param() } return types.NewSignature(recv, p.tuple(), p.tuple(), p.int() != 0) }
// makeLen returns the len builtin specialized to type func(T)int. func makeLen(T types.Type) *Builtin { lenParams := types.NewTuple(anonVar(T)) return &Builtin{ name: "len", sig: types.NewSignature(nil, lenParams, lenResults, false), } }
func (p *importer) method(parent *types.Package) *types.Func { pos := p.pos() pkg, name := p.fieldName(parent) params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) return types.NewFunc(pos, pkg, name, sig) }
// newMethod creates a new method of the specified name, package and receiver type. func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function { // TODO(adonovan): fix: hack: currently the only part of Signature // that is needed is the "pointerness" of Recv.Type, and for // now, we'll set it to always be false since we're only // concerned with rtype. Encapsulate this better. sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", recvType), nil, nil, false) fn := pkg.Prog.NewFunction(name, sig, "fake reflect method") fn.Pkg = pkg return fn }
func ParseFuncDecl(fnDecl *gst.FuncDecl) (*types.Func, bool) { var fn *types.Func var pkg *types.Package pkg = nil name := fnDecl.Name var sig *types.Signature sig = types.NewSignature(nil, nil, nil, false) var pos token.Pos fn = types.NewFunc(pos, pkg, name, sig) return fn, true }
// FindTests returns the list of packages that define at least one Test, // Example or Benchmark function (as defined by "go test"), and the // lists of all such functions. // func FindTests(pkgs []*Package) (testpkgs []*Package, tests, benchmarks, examples []*Function) { if len(pkgs) == 0 { return } prog := pkgs[0].Prog // The first two of these may be nil: if the program doesn't import "testing", // it can't contain any tests, but it may yet contain Examples. var testSig *types.Signature // func(*testing.T) var benchmarkSig *types.Signature // func(*testing.B) var exampleSig = types.NewSignature(nil, nil, nil, false) // func() // Obtain the types from the parameters of testing.Main(). if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { params := testingPkg.Func("Main").Signature.Params() testSig = funcField(params.At(1).Type()) benchmarkSig = funcField(params.At(2).Type()) } seen := make(map[*Package]bool) for _, pkg := range pkgs { if pkg.Prog != prog { panic("wrong Program") } // TODO(adonovan): use a stable order, e.g. lexical. for _, mem := range pkg.Members { if f, ok := mem.(*Function); ok && ast.IsExported(f.Name()) && strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") { switch { case testSig != nil && isTestSig(f, "Test", testSig): tests = append(tests, f) case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig): benchmarks = append(benchmarks, f) case isTestSig(f, "Example", exampleSig): examples = append(examples, f) default: continue } if !seen[pkg] { seen[pkg] = true testpkgs = append(testpkgs, pkg) } } } } return }
// FindTests returns the Test, Benchmark, and Example functions // (as defined by "go test") defined in the specified package, // and its TestMain function, if any. func FindTests(pkg *Package) (tests, benchmarks, examples []*Function, main *Function) { prog := pkg.Prog // The first two of these may be nil: if the program doesn't import "testing", // it can't contain any tests, but it may yet contain Examples. var testSig *types.Signature // func(*testing.T) var benchmarkSig *types.Signature // func(*testing.B) var exampleSig = types.NewSignature(nil, nil, nil, false) // func() // Obtain the types from the parameters of testing.MainStart. if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { mainStart := testingPkg.Func("MainStart") params := mainStart.Signature.Params() testSig = funcField(params.At(1).Type()) benchmarkSig = funcField(params.At(2).Type()) // Does the package define this function? // func TestMain(*testing.M) if f := pkg.Func("TestMain"); f != nil { sig := f.Type().(*types.Signature) starM := mainStart.Signature.Results().At(0).Type() // *testing.M if sig.Results().Len() == 0 && sig.Params().Len() == 1 && types.Identical(sig.Params().At(0).Type(), starM) { main = f } } } // TODO(adonovan): use a stable order, e.g. lexical. for _, mem := range pkg.Members { if f, ok := mem.(*Function); ok && ast.IsExported(f.Name()) && strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") { switch { case testSig != nil && isTestSig(f, "Test", testSig): tests = append(tests, f) case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig): benchmarks = append(benchmarks, f) case isTestSig(f, "Example", exampleSig): examples = append(examples, f) default: continue } } } return }
// Signature = Parameters [ Result ] . // Result = Type | Parameters . // func (p *parser) parseSignature(recv *types.Var) *types.Signature { params, isVariadic := p.parseParameters() // optional result type var results []*types.Var if p.tok == '(' { var variadic bool results, variadic = p.parseParameters() if variadic { p.error("... not permitted on result type") } } return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic) }
func (p *importer) obj(tag int) { switch tag { case constTag: pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) val := p.value() p.declare(types.NewConst(pos, pkg, name, typ, val)) case typeTag: p.typ(nil) case varTag: pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) p.declare(types.NewVar(pos, pkg, name, typ)) case funcTag: pos := p.pos() pkg, name := p.qualifiedName() params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) p.declare(types.NewFunc(pos, pkg, name, sig)) case aliasTag: pos := p.pos() name := p.string() var orig types.Object if pkg, name := p.qualifiedName(); pkg != nil { orig = pkg.Scope().Lookup(name) } // Alias-related code. Keep for now. _ = pos _ = name _ = orig // p.declare(types.NewAlias(pos, p.pkgList[0], name, orig)) default: errorf("unexpected object tag %d", tag) } }
// NamedType = TypeName Type { Method } . // Method = "func" "(" Param ")" Name ParamList ResultList ";" . func (p *parser) parseNamedType(n int) types.Type { obj := p.parseTypeName() pkg := obj.Pkg() typ := obj.Type() p.typeMap[n] = typ nt, ok := typ.(*types.Named) if !ok { // This can happen for unsafe.Pointer, which is a TypeName holding a Basic type. pt := p.parseType(pkg) if pt != typ { p.error("unexpected underlying type for non-named TypeName") } return typ } underlying := p.parseType(pkg) if nt.Underlying() == nil { nt.SetUnderlying(underlying.Underlying()) } for p.tok == scanner.Ident { // collect associated methods p.expectKeyword("func") p.expect('(') receiver, _ := p.parseParam(pkg) p.expect(')') name := p.parseName() params, isVariadic := p.parseParamList(pkg) results := p.parseResultList(pkg) p.expect(';') sig := types.NewSignature(receiver, params, results, isVariadic) nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) } return nt }
// parent is the package which declared the type; parent == nil means // the package currently imported. The parent package is needed for // exported struct fields and interface methods which don't contain // explicit package information in the export data. func (p *importer) typ(parent *types.Package) types.Type { // if the type was seen before, i is its index (>= 0) i := p.tagOrIndex() if i >= 0 { return p.typList[i] } // otherwise, i is the type tag (< 0) switch i { case namedTag: // read type object name := p.string() parent = p.pkg() scope := parent.Scope() obj := scope.Lookup(name) // if the object doesn't exist yet, create and insert it if obj == nil { obj = types.NewTypeName(token.NoPos, parent, name, nil) scope.Insert(obj) } if _, ok := obj.(*types.TypeName); !ok { panic(fmt.Sprintf("pkg = %s, name = %s => %s", parent, name, obj)) } // associate new named type with obj if it doesn't exist yet t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) // but record the existing type, if any t := obj.Type().(*types.Named) p.record(t) // read underlying type t0.SetUnderlying(p.typ(parent)) // interfaces don't have associated methods if _, ok := t0.Underlying().(*types.Interface); ok { return t } // read associated methods for i := p.int(); i > 0; i-- { name := p.string() recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? params, isddd := p.paramList() result, _ := p.paramList() p.int() // read and discard index of inlined function body sig := types.NewSignature(recv.At(0), params, result, isddd) t0.AddMethod(types.NewFunc(token.NoPos, parent, name, sig)) } return t case arrayTag: t := new(types.Array) p.record(t) n := p.int64() *t = *types.NewArray(p.typ(parent), n) return t case sliceTag: t := new(types.Slice) p.record(t) *t = *types.NewSlice(p.typ(parent)) return t case dddTag: t := new(dddSlice) p.record(t) t.elem = p.typ(parent) return t case structTag: t := new(types.Struct) p.record(t) n := p.int() fields := make([]*types.Var, n) tags := make([]string, n) for i := range fields { fields[i] = p.field(parent) tags[i] = p.string() } *t = *types.NewStruct(fields, tags) return t case pointerTag: t := new(types.Pointer) p.record(t) *t = *types.NewPointer(p.typ(parent)) return t case signatureTag: t := new(types.Signature) p.record(t) params, isddd := p.paramList() result, _ := p.paramList() *t = *types.NewSignature(nil, params, result, isddd) return t case interfaceTag: // Create a dummy entry in the type list. This is safe because we // cannot expect the interface type to appear in a cycle, as any // such cycle must contain a named type which would have been // first defined earlier. n := len(p.typList) p.record(nil) // no embedded interfaces with gc compiler if p.int() != 0 { panic("unexpected embedded interface") } // read methods methods := make([]*types.Func, p.int()) for i := range methods { pkg, name := p.fieldName(parent) params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) methods[i] = types.NewFunc(token.NoPos, pkg, name, sig) } t := types.NewInterface(methods, nil) p.typList[n] = t return t case mapTag: t := new(types.Map) p.record(t) key := p.typ(parent) val := p.typ(parent) *t = *types.NewMap(key, val) return t case chanTag: t := new(types.Chan) p.record(t) var dir types.ChanDir // tag values must match the constants in cmd/compile/internal/gc/go.go switch d := p.int(); d { case 1 /* Crecv */ : dir = types.RecvOnly case 2 /* Csend */ : dir = types.SendOnly case 3 /* Cboth */ : dir = types.SendRecv default: panic(fmt.Sprintf("unexpected channel dir %d", d)) } val := p.typ(parent) *t = *types.NewChan(dir, val) return t default: panic(fmt.Sprintf("unexpected type tag %d", i)) } }
func changeRecv(s *types.Signature, recv *types.Var) *types.Signature { return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic()) }
// makeWrapper returns a synthetic method that delegates to the // declared method denoted by meth.Obj(), first performing any // necessary pointer indirections or field selections implied by meth. // // The resulting method's receiver type is meth.Recv(). // // This function is versatile but quite subtle! Consider the // following axes of variation when making changes: // - optional receiver indirection // - optional implicit field selections // - meth.Obj() may denote a concrete or an interface method // - the result may be a thunk or a wrapper. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func makeWrapper(prog *Program, sel *types.Selection) *Function { obj := sel.Obj().(*types.Func) // the declared function sig := sel.Type().(*types.Signature) // type of this wrapper var recv *types.Var // wrapper's receiver or thunk's params[0] name := obj.Name() var description string var start int // first regular param if sel.Kind() == types.MethodExpr { name += "$thunk" description = "thunk" recv = sig.Params().At(0) start = 1 } else { description = "wrapper" recv = sig.Recv() } description = fmt.Sprintf("%s for %s", description, sel.Obj()) if prog.mode&LogSource != 0 { defer logStack("make %s to (%s)", description, recv.Type())() } fn := &Function{ name: name, method: sel, object: obj, Signature: sig, Synthetic: description, Prog: prog, pos: obj.Pos(), } fn.startBody() fn.addSpilledParam(recv) createParams(fn, start) indices := sel.Index() var v Value = fn.Locals[0] // spilled receiver if isPointer(sel.Recv()) { v = emitLoad(fn, v) // For simple indirection wrappers, perform an informative nil-check: // "value method (T).f called using nil *T pointer" if len(indices) == 1 && !isPointer(recvType(obj)) { var c Call c.Call.Value = &Builtin{ name: "ssa:wrapnilchk", sig: types.NewSignature(nil, types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)), types.NewTuple(anonVar(sel.Recv())), false), } c.Call.Args = []Value{ v, stringConst(deref(sel.Recv()).String()), stringConst(sel.Obj().Name()), } c.setType(v.Type()) v = fn.emit(&c) } } // Invariant: v is a pointer, either // value of *A receiver param, or // address of A spilled receiver. // We use pointer arithmetic (FieldAddr possibly followed by // Load) in preference to value extraction (Field possibly // preceded by Load). v = emitImplicitSelections(fn, v, indices[:len(indices)-1]) // Invariant: v is a pointer, either // value of implicit *C field, or // address of implicit C field. var c Call if r := recvType(obj); !isInterface(r) { // concrete method if !isPointer(r) { v = emitLoad(fn, v) } c.Call.Value = prog.declaredFunc(obj) c.Call.Args = append(c.Call.Args, v) } else { c.Call.Method = obj c.Call.Value = emitLoad(fn, v) } for _, arg := range fn.Params[1:] { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() return fn }
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 }
// parent is the package which declared the type; parent == nil means // the package currently imported. The parent package is needed for // exported struct fields and interface methods which don't contain // explicit package information in the export data. func (p *importer) typ(parent *types.Package) types.Type { // if the type was seen before, i is its index (>= 0) i := p.tagOrIndex() if i >= 0 { return p.typList[i] } // otherwise, i is the type tag (< 0) switch i { case namedTag: // read type object pos := p.pos() parent, name := p.qualifiedName() scope := parent.Scope() obj := scope.Lookup(name) // if the object doesn't exist yet, create and insert it if obj == nil { obj = types.NewTypeName(pos, parent, name, nil) scope.Insert(obj) } if _, ok := obj.(*types.TypeName); !ok { errorf("pkg = %s, name = %s => %s", parent, name, obj) } // associate new named type with obj if it doesn't exist yet t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) // but record the existing type, if any t := obj.Type().(*types.Named) p.record(t) // read underlying type t0.SetUnderlying(p.typ(parent)) // interfaces don't have associated methods if types.IsInterface(t0) { return t } // read associated methods for i := p.int(); i > 0; i-- { // TODO(gri) replace this with something closer to fieldName pos := p.pos() name := p.string() if !exported(name) { p.pkg() } recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? params, isddd := p.paramList() result, _ := p.paramList() p.int() // go:nointerface pragma - discarded sig := types.NewSignature(recv.At(0), params, result, isddd) t0.AddMethod(types.NewFunc(pos, parent, name, sig)) } return t case arrayTag: t := new(types.Array) if p.trackAllTypes { p.record(t) } n := p.int64() *t = *types.NewArray(p.typ(parent), n) return t case sliceTag: t := new(types.Slice) if p.trackAllTypes { p.record(t) } *t = *types.NewSlice(p.typ(parent)) return t case dddTag: t := new(dddSlice) if p.trackAllTypes { p.record(t) } t.elem = p.typ(parent) return t case structTag: t := new(types.Struct) if p.trackAllTypes { p.record(t) } *t = *types.NewStruct(p.fieldList(parent)) return t case pointerTag: t := new(types.Pointer) if p.trackAllTypes { p.record(t) } *t = *types.NewPointer(p.typ(parent)) return t case signatureTag: t := new(types.Signature) if p.trackAllTypes { p.record(t) } params, isddd := p.paramList() result, _ := p.paramList() *t = *types.NewSignature(nil, params, result, isddd) return t case interfaceTag: // Create a dummy entry in the type list. This is safe because we // cannot expect the interface type to appear in a cycle, as any // such cycle must contain a named type which would have been // first defined earlier. n := len(p.typList) if p.trackAllTypes { p.record(nil) } // no embedded interfaces with gc compiler if p.int() != 0 { errorf("unexpected embedded interface") } t := types.NewInterface(p.methodList(parent), nil) if p.trackAllTypes { p.typList[n] = t } return t case mapTag: t := new(types.Map) if p.trackAllTypes { p.record(t) } key := p.typ(parent) val := p.typ(parent) *t = *types.NewMap(key, val) return t case chanTag: t := new(types.Chan) if p.trackAllTypes { p.record(t) } var dir types.ChanDir // tag values must match the constants in cmd/compile/internal/gc/go.go switch d := p.int(); d { case 1 /* Crecv */ : dir = types.RecvOnly case 2 /* Csend */ : dir = types.SendOnly case 3 /* Cboth */ : dir = types.SendRecv default: errorf("unexpected channel dir %d", d) } val := p.typ(parent) *t = *types.NewChan(dir, val) return t default: errorf("unexpected type tag %d", i) // panics panic("unreachable") } }
func (c *funcContext) translateExpr(expr ast.Expr) *expression { exprType := c.p.TypeOf(expr) if value := c.p.Types[expr].Value; value != nil { basic := exprType.Underlying().(*types.Basic) switch { case isBoolean(basic): return c.formatExpr("%s", strconv.FormatBool(constant.BoolVal(value))) case isInteger(basic): if is64Bit(basic) { if basic.Kind() == types.Int64 { d, ok := constant.Int64Val(constant.ToInt(value)) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatInt(d>>32, 10), strconv.FormatUint(uint64(d)&(1<<32-1), 10)) } d, ok := constant.Uint64Val(constant.ToInt(value)) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } d, ok := constant.Int64Val(constant.ToInt(value)) if !ok { panic("could not get exact int") } return c.formatExpr("%s", strconv.FormatInt(d, 10)) case isFloat(basic): f, _ := constant.Float64Val(value) return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case isComplex(basic): r, _ := constant.Float64Val(constant.Real(value)) i, _ := constant.Float64Val(constant.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case isString(basic): return c.formatExpr("%s", encodeString(constant.StringVal(value))) default: panic("Unhandled constant type: " + basic.String()) } } var obj types.Object switch e := expr.(type) { case *ast.SelectorExpr: obj = c.p.Uses[e.Sel] case *ast.Ident: obj = c.p.Defs[e] if obj == nil { obj = c.p.Uses[e] } } if obj != nil && typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Global": return c.formatExpr("$global") case "Module": return c.formatExpr("$module") case "Undefined": return c.formatExpr("undefined") } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { var elements []string i := 0 zero := c.translateExpr(c.zeroValue(elementType)).String() for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, ok := constant.Int64Val(constant.ToInt(c.p.Types[kve.Key].Value)) if !ok { panic("could not get exact int") } i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateImplicitConversionWithCloning(element, elementType).String() i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) == 0 { return c.formatExpr("%s.zero()", c.typeName(t)) } zero := c.translateExpr(c.zeroValue(t.Elem())).String() for len(elements) < int(t.Len()) { elements = append(elements, zero) } return c.formatExpr(`$toNativeArray(%s, [%s])`, typeKind(t.Elem()), strings.Join(elements, ", ")) case *types.Slice: return c.formatExpr("new %s([%s])", c.typeName(exprType), strings.Join(collectIndexedElements(t.Elem()), ", ")) case *types.Map: entries := make([]string, len(e.Elts)) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) entries[i] = fmt.Sprintf("{ k: %s, v: %s }", c.translateImplicitConversionWithCloning(kve.Key, t.Key()), c.translateImplicitConversionWithCloning(kve.Value, t.Elem())) } return c.formatExpr("$makeMap(%s.keyFor, [%s])", c.typeName(t.Key()), strings.Join(entries, ", ")) case *types.Struct: elements := make([]string, t.NumFields()) isKeyValue := true if len(e.Elts) != 0 { _, isKeyValue = e.Elts[0].(*ast.KeyValueExpr) } if !isKeyValue { for i, element := range e.Elts { elements[i] = c.translateImplicitConversionWithCloning(element, t.Field(i).Type()).String() } } if isKeyValue { for i := range elements { elements[i] = c.translateExpr(c.zeroValue(t.Field(i).Type())).String() } for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) for j := range elements { if kve.Key.(*ast.Ident).Name == t.Field(j).Name() { elements[j] = c.translateImplicitConversionWithCloning(kve.Value, t.Field(j).Type()).String() break } } } } return c.formatExpr("new %s.ptr(%s)", c.typeName(exprType), strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: _, fun := translateFunction(e.Type, nil, e.Body, c, exprType.(*types.Signature), c.p.FuncLitInfos[e], "") if len(c.p.escapingVars) != 0 { names := make([]string, 0, len(c.p.escapingVars)) for obj := range c.p.escapingVars { names = append(names, c.p.objectNames[obj]) } sort.Strings(names) list := strings.Join(names, ", ") return c.formatExpr("(function(%s) { return %s; })(%s)", list, fun, list) } return c.formatExpr("(%s)", fun) case *ast.UnaryExpr: t := c.p.TypeOf(e.X) switch e.Op { case token.AND: if typesutil.IsJsObject(exprType) { return c.formatExpr("%e.object", e.X) } switch t.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } switch x := astutil.RemoveParens(e.X).(type) { case *ast.CompositeLit: return c.formatExpr("$newDataPointer(%e, %s)", x, c.typeName(c.p.TypeOf(e))) case *ast.Ident: obj := c.p.Uses[x].(*types.Var) if c.p.escapingVars[obj] { return c.formatExpr("(%1s.$ptr || (%1s.$ptr = new %2s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %1s)))", c.p.objectNames[obj], c.typeName(exprType)) } return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) case *ast.SelectorExpr: sel, ok := c.p.SelectionOf(x) if !ok { // qualified identifier obj := c.p.Uses[x.Sel].(*types.Var) return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) } newSel := &ast.SelectorExpr{X: c.newIdent("this.$target", c.p.TypeOf(x.X)), Sel: x.Sel} c.setType(newSel, exprType) c.p.additionalSelections[newSel] = sel return c.formatExpr("(%1e.$ptr_%2s || (%1e.$ptr_%2s = new %3s(function() { return %4e; }, function($v) { %5s }, %1e)))", x.X, x.Sel.Name, c.typeName(exprType), newSel, c.translateAssign(newSel, c.newIdent("$v", exprType), false)) case *ast.IndexExpr: if _, ok := c.p.TypeOf(x.X).Underlying().(*types.Slice); ok { return c.formatExpr("$indexPtr(%1e.$array, %1e.$offset + %2e, %3s)", x.X, x.Index, c.typeName(exprType)) } return c.formatExpr("$indexPtr(%e, %e, %s)", x.X, x.Index, c.typeName(exprType)) case *ast.StarExpr: return c.translateExpr(x.X) default: panic(fmt.Sprintf("Unhandled: %T\n", x)) } case token.ARROW: call := &ast.CallExpr{ Fun: c.newIdent("$recv", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", t)), types.NewTuple(types.NewVar(0, nil, "", exprType), types.NewVar(0, nil, "", types.Typ[types.Bool])), false)), Args: []ast.Expr{e.X}, } c.Blocking[call] = true if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("%e", call) } return c.formatExpr("%e[0]", call) } basic := t.Underlying().(*types.Basic) switch e.Op { case token.ADD: return c.translateExpr(e.X) case token.SUB: switch { case is64Bit(basic): return c.formatExpr("new %1s(-%2h, -%2l)", c.typeName(t), e.X) case isComplex(basic): return c.formatExpr("new %1s(-%2r, -%2i)", c.typeName(t), e.X) case isUnsigned(basic): return c.fixNumber(c.formatExpr("-%e", e.X), basic) default: return c.formatExpr("-%e", e.X) } case token.XOR: if is64Bit(basic) { return c.formatExpr("new %1s(~%2h, ~%2l >>> 0)", c.typeName(t), e.X) } return c.fixNumber(c.formatExpr("~%e", e.X), basic) case token.NOT: return c.formatExpr("!%e", e.X) default: panic(e.Op) } case *ast.BinaryExpr: if e.Op == token.NEQ { return c.formatExpr("!(%s)", c.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } t := c.p.TypeOf(e.X) t2 := c.p.TypeOf(e.Y) _, isInterface := t2.Underlying().(*types.Interface) if isInterface || types.Identical(t, types.Typ[types.UntypedNil]) { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && isNumeric(basic) { if is64Bit(basic) { switch e.Op { case token.MUL: return c.formatExpr("$mul64(%e, %e)", e.X, e.Y) case token.QUO: return c.formatExpr("$div64(%e, %e, false)", e.X, e.Y) case token.REM: return c.formatExpr("$div64(%e, %e, true)", e.X, e.Y) case token.SHL: return c.formatExpr("$shiftLeft64(%e, %f)", e.X, e.Y) case token.SHR: return c.formatExpr("$shiftRight%s(%e, %f)", toJavaScriptType(basic), e.X, e.Y) case token.EQL: return c.formatExpr("(%1h === %2h && %1l === %2l)", e.X, e.Y) case token.LSS: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l < %2l))", e.X, e.Y) case token.LEQ: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l <= %2l))", e.X, e.Y) case token.GTR: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l > %2l))", e.X, e.Y) case token.GEQ: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l >= %2l))", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1h %4t %2h, %1l %4t %2l)", e.X, e.Y, c.typeName(t), e.Op) case token.AND, token.OR, token.XOR: return c.formatExpr("new %3s(%1h %4t %2h, (%1l %4t %2l) >>> 0)", e.X, e.Y, c.typeName(t), e.Op) case token.AND_NOT: return c.formatExpr("new %3s(%1h & ~%2h, (%1l & ~%2l) >>> 0)", e.X, e.Y, c.typeName(t)) default: panic(e.Op) } } if isComplex(basic) { switch e.Op { case token.EQL: return c.formatExpr("(%1r === %2r && %1i === %2i)", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1r %4t %2r, %1i %4t %2i)", e.X, e.Y, c.typeName(t), e.Op) case token.MUL: return c.formatExpr("new %3s(%1r * %2r - %1i * %2i, %1r * %2i + %1i * %2r)", e.X, e.Y, c.typeName(t)) case token.QUO: return c.formatExpr("$divComplex(%e, %e)", e.X, e.Y) default: panic(e.Op) } } switch e.Op { case token.EQL: return c.formatParenExpr("%e === %e", e.X, e.Y) case token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.ADD, token.SUB: return c.fixNumber(c.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) case token.MUL: switch basic.Kind() { case types.Int32, types.Int: return c.formatParenExpr("$imul(%e, %e)", e.X, e.Y) case types.Uint32, types.Uintptr: return c.formatParenExpr("$imul(%e, %e) >>> 0", e.X, e.Y) } return c.fixNumber(c.formatExpr("%e * %e", e.X, e.Y), basic) case token.QUO: if isInteger(basic) { // cut off decimals shift := ">>" if isUnsigned(basic) { shift = ">>>" } return c.formatExpr(`(%1s = %2e / %3e, (%1s === %1s && %1s !== 1/0 && %1s !== -1/0) ? %1s %4s 0 : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_q"), e.X, e.Y, shift) } if basic.Kind() == types.Float32 { return c.fixNumber(c.formatExpr("%e / %e", e.X, e.Y), basic) } return c.formatExpr("%e / %e", e.X, e.Y) case token.REM: return c.formatExpr(`(%1s = %2e %% %3e, %1s === %1s ? %1s : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_r"), e.X, e.Y) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && isUnsigned(basic) { op = ">>>" } if v := c.p.Types[e.Y].Value; v != nil { i, _ := constant.Uint64Val(constant.ToInt(v)) if i >= 32 { return c.formatExpr("0") } return c.fixNumber(c.formatExpr("%e %s %s", e.X, op, strconv.FormatUint(i, 10)), basic) } if e.Op == token.SHR && !isUnsigned(basic) { return c.fixNumber(c.formatParenExpr("%e >> $min(%f, 31)", e.X, e.Y), basic) } y := c.newVariable("y") return c.fixNumber(c.formatExpr("(%s = %f, %s < 32 ? (%e %s %s) : 0)", y, e.Y, y, e.X, op, y), basic) case token.AND, token.OR: if isUnsigned(basic) { return c.formatParenExpr("(%e %t %e) >>> 0", e.X, e.Op, e.Y) } return c.formatParenExpr("%e %t %e", e.X, e.Op, e.Y) case token.AND_NOT: return c.fixNumber(c.formatParenExpr("%e & ~%e", e.X, e.Y), basic) case token.XOR: return c.fixNumber(c.formatParenExpr("%e ^ %e", e.X, e.Y), basic) default: panic(e.Op) } } switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.LAND: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (!(%s)) { %s = false; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e && %e", e.X, e.Y) case token.LOR: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (%s) { %s = true; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e || %e", e.X, e.Y) case token.EQL: switch u := t.Underlying().(type) { case *types.Array, *types.Struct: return c.formatExpr("$equal(%e, %e, %s)", e.X, e.Y, c.typeName(t)) case *types.Interface: return c.formatExpr("$interfaceIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Pointer: if _, ok := u.Elem().Underlying().(*types.Array); ok { return c.formatExpr("$equal(%s, %s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t), c.typeName(u.Elem())) } case *types.Basic: if isBoolean(u) { if b, ok := analysis.BoolValue(e.X, c.p.Info.Info); ok && b { return c.translateExpr(e.Y) } if b, ok := analysis.BoolValue(e.Y, c.p.Info.Info); ok && b { return c.translateExpr(e.X) } } } return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) default: panic(e.Op) } case *ast.ParenExpr: return c.formatParenExpr("%e", e.X) case *ast.IndexExpr: switch t := c.p.TypeOf(e.X).Underlying().(type) { case *types.Array, *types.Pointer: pattern := rangeCheck("%1e[%2f]", c.p.Types[e.Index].Value != nil, true) if _, ok := t.(*types.Pointer); ok { // check pointer for nix (attribute getter causes a panic) pattern = `(%1e.nilCheck, ` + pattern + `)` } return c.formatExpr(pattern, e.X, e.Index) case *types.Slice: return c.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f]", c.p.Types[e.Index].Value != nil, false), e.X, e.Index) case *types.Map: if typesutil.IsJsObject(c.p.TypeOf(e.Index)) { c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: e.Index.Pos(), Msg: "cannot use js.Object as map key"}) } key := fmt.Sprintf("%s.keyFor(%s)", c.typeName(t.Key()), c.translateImplicitConversion(e.Index, t.Key())) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? [%1s.v, true] : [%4e, false])`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) } return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? %1s.v : %4e)`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) case *types.Basic: return c.formatExpr("%e.charCodeAt(%f)", e.X, e.Index) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: if b, isBasic := c.p.TypeOf(e.X).Underlying().(*types.Basic); isBasic && isString(b) { switch { case e.Low == nil && e.High == nil: return c.translateExpr(e.X) case e.Low == nil: return c.formatExpr("%e.substring(0, %f)", e.X, e.High) case e.High == nil: return c.formatExpr("%e.substring(%f)", e.X, e.Low) default: return c.formatExpr("%e.substring(%f, %f)", e.X, e.Low, e.High) } } slice := c.translateConversionToSlice(e.X, exprType) switch { case e.Low == nil && e.High == nil: return c.formatExpr("%s", slice) case e.Low == nil: if e.Max != nil { return c.formatExpr("$subslice(%s, 0, %f, %f)", slice, e.High, e.Max) } return c.formatExpr("$subslice(%s, 0, %f)", slice, e.High) case e.High == nil: return c.formatExpr("$subslice(%s, %f)", slice, e.Low) default: if e.Max != nil { return c.formatExpr("$subslice(%s, %f, %f, %f)", slice, e.Low, e.High, e.Max) } return c.formatExpr("$subslice(%s, %f, %f)", slice, e.Low, e.High) } case *ast.SelectorExpr: sel, ok := c.p.SelectionOf(e) if !ok { // qualified identifier return c.formatExpr("%s", c.objectName(obj)) } switch sel.Kind() { case types.FieldVal: fields, jsTag := c.translateSelection(sel, e.Pos()) if jsTag != "" { if _, ok := sel.Type().(*types.Signature); ok { return c.formatExpr("$internalize(%1e.%2s.%3s, %4s, %1e.%2s)", e.X, strings.Join(fields, "."), jsTag, c.typeName(sel.Type())) } return c.internalize(c.formatExpr("%e.%s.%s", e.X, strings.Join(fields, "."), jsTag), sel.Type()) } return c.formatExpr("%e.%s", e.X, strings.Join(fields, ".")) case types.MethodVal: return c.formatExpr(`$methodVal(%s, "%s")`, c.makeReceiver(e), sel.Obj().(*types.Func).Name()) case types.MethodExpr: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } if _, ok := sel.Recv().Underlying().(*types.Interface); ok { return c.formatExpr(`$ifaceMethodExpr("%s")`, sel.Obj().(*types.Func).Name()) } return c.formatExpr(`$methodExpr(%s, "%s")`, c.typeName(sel.Recv()), sel.Obj().(*types.Func).Name()) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } case *ast.CallExpr: plainFun := astutil.RemoveParens(e.Fun) if astutil.IsTypeExpr(plainFun, c.p.Info.Info) { return c.formatExpr("%s", c.translateConversion(e.Args[0], c.p.TypeOf(plainFun))) } sig := c.p.TypeOf(plainFun).Underlying().(*types.Signature) switch f := plainFun.(type) { case *ast.Ident: obj := c.p.Uses[f] if o, ok := obj.(*types.Builtin); ok { return c.translateBuiltin(o.Name(), sig, e.Args, e.Ellipsis.IsValid()) } if typesutil.IsJsPackage(obj.Pkg()) && obj.Name() == "InternalObject" { return c.translateExpr(e.Args[0]) } return c.translateCall(e, sig, c.translateExpr(f)) case *ast.SelectorExpr: sel, ok := c.p.SelectionOf(f) if !ok { // qualified identifier obj := c.p.Uses[f.Sel] if typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Debugger": return c.formatExpr("debugger") case "InternalObject": return c.translateExpr(e.Args[0]) } } return c.translateCall(e, sig, c.translateExpr(f)) } externalizeExpr := func(e ast.Expr) string { t := c.p.TypeOf(e) if types.Identical(t, types.Typ[types.UntypedNil]) { return "null" } return c.externalize(c.translateExpr(e).String(), t) } externalizeArgs := func(args []ast.Expr) string { s := make([]string, len(args)) for i, arg := range args { s[i] = externalizeExpr(arg) } return strings.Join(s, ", ") } switch sel.Kind() { case types.MethodVal: recv := c.makeReceiver(f) declaredFuncRecv := sel.Obj().(*types.Func).Type().(*types.Signature).Recv().Type() if typesutil.IsJsObject(declaredFuncRecv) { globalRef := func(id string) string { if recv.String() == "$global" && id[0] == '$' && len(id) > 1 { return id } return recv.String() + "." + id } switch sel.Obj().Name() { case "Get": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s", globalRef(id)) } return c.formatExpr("%s[$externalize(%e, $String)]", recv, e.Args[0]) case "Set": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s = %s", globalRef(id), externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Delete": return c.formatExpr("delete %s[$externalize(%e, $String)]", recv, e.Args[0]) case "Length": return c.formatExpr("$parseInt(%s.length)", recv) case "Index": return c.formatExpr("%s[%e]", recv, e.Args[0]) case "SetIndex": return c.formatExpr("%s[%e] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Call": if id, ok := c.identifierConstant(e.Args[0]); ok { if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s.%s.apply(%s, %s))", objVar, recv, objVar, id, objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s(%s)", globalRef(id), externalizeArgs(e.Args[1:])) } if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s[$externalize(%e, $String)].apply(%s, %s))", objVar, recv, objVar, e.Args[0], objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)](%s)", recv, e.Args[0], externalizeArgs(e.Args[1:])) case "Invoke": if e.Ellipsis.IsValid() { return c.formatExpr("%s.apply(undefined, %s)", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("%s(%s)", recv, externalizeArgs(e.Args)) case "New": if e.Ellipsis.IsValid() { return c.formatExpr("new ($global.Function.prototype.bind.apply(%s, [undefined].concat(%s)))", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("new (%s)(%s)", recv, externalizeArgs(e.Args)) case "Bool": return c.internalize(recv, types.Typ[types.Bool]) case "String": return c.internalize(recv, types.Typ[types.String]) case "Int": return c.internalize(recv, types.Typ[types.Int]) case "Int64": return c.internalize(recv, types.Typ[types.Int64]) case "Uint64": return c.internalize(recv, types.Typ[types.Uint64]) case "Float": return c.internalize(recv, types.Typ[types.Float64]) case "Interface": return c.internalize(recv, types.NewInterface(nil, nil)) case "Unsafe": return recv default: panic("Invalid js package object: " + sel.Obj().Name()) } } methodName := sel.Obj().Name() if reservedKeywords[methodName] { methodName += "$" } return c.translateCall(e, sig, c.formatExpr("%s.%s", recv, methodName)) case types.FieldVal: fields, jsTag := c.translateSelection(sel, f.Pos()) if jsTag != "" { call := c.formatExpr("%e.%s.%s(%s)", f.X, strings.Join(fields, "."), jsTag, externalizeArgs(e.Args)) switch sig.Results().Len() { case 0: return call case 1: return c.internalize(call, sig.Results().At(0).Type()) default: c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: f.Pos(), Msg: "field with js tag can not have func type with multiple results"}) } } return c.translateCall(e, sig, c.formatExpr("%e.%s", f.X, strings.Join(fields, "."))) case types.MethodExpr: return c.translateCall(e, sig, c.translateExpr(f)) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } default: return c.translateCall(e, sig, c.translateExpr(plainFun)) } case *ast.StarExpr: if typesutil.IsJsObject(c.p.TypeOf(e.X)) { return c.formatExpr("new $jsObjectPtr(%e)", e.X) } if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 { if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.Identical(c.p.TypeOf(c2.Fun), types.Typ[types.UnsafePointer]) { if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND { return c.translateExpr(unary.X) // unsafe conversion } } } switch exprType.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } return c.formatExpr("%e.$get()", e.X) case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.p.TypeOf(e.Type) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("$assertType(%e, %s, true)", e.X, c.typeName(t)) } return c.formatExpr("$assertType(%e, %s)", e.X, c.typeName(t)) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := obj.(type) { case *types.Var, *types.Const: return c.formatExpr("%s", c.objectName(o)) case *types.Func: return c.formatExpr("%s", c.objectName(o)) case *types.TypeName: return c.formatExpr("%s", c.typeName(o.Type())) case *types.Nil: if typesutil.IsJsObject(exprType) { return c.formatExpr("null") } switch t := exprType.Underlying().(type) { case *types.Basic: if t.Kind() != types.UnsafePointer { panic("unexpected basic type") } return c.formatExpr("0") case *types.Slice, *types.Pointer: return c.formatExpr("%s.nil", c.typeName(exprType)) case *types.Chan: return c.formatExpr("$chanNil") case *types.Map: return c.formatExpr("false") case *types.Interface: return c.formatExpr("$ifaceNil") case *types.Signature: return c.formatExpr("$throwNilPointerError") default: panic(fmt.Sprintf("unexpected type: %T", t)) } default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case *this: if isWrapped(c.p.TypeOf(e)) { return c.formatExpr("this.$val") } return c.formatExpr("this") case nil: return c.formatExpr("") default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { c.SetPos(stmt.Pos()) stmt = filter.IncDecStmt(stmt, c.p.Info.Info) stmt = filter.Assign(stmt, c.p.Info.Info, c.p.Info.Pkg) switch s := stmt.(type) { case *ast.BlockStmt: c.translateStmtList(s.List) case *ast.IfStmt: var caseClauses []*ast.CaseClause ifStmt := s for { if ifStmt.Init != nil { panic("simplification error") } caseClauses = append(caseClauses, &ast.CaseClause{List: []ast.Expr{ifStmt.Cond}, Body: ifStmt.Body.List}) elseStmt, ok := ifStmt.Else.(*ast.IfStmt) if !ok { break } ifStmt = elseStmt } var defaultClause *ast.CaseClause if block, ok := ifStmt.Else.(*ast.BlockStmt); ok { defaultClause = &ast.CaseClause{Body: block.List} } c.translateBranchingStmt(caseClauses, defaultClause, false, c.translateExpr, nil, c.Flattened[s]) case *ast.SwitchStmt: if s.Init != nil || s.Tag != nil || len(s.Body.List) != 1 { panic("simplification error") } clause := s.Body.List[0].(*ast.CaseClause) if len(clause.List) != 0 { panic("simplification error") } prevFlowData := c.flowDatas[nil] data := &flowData{ postStmt: prevFlowData.postStmt, // for "continue" of outer loop beginCase: prevFlowData.beginCase, // same } c.flowDatas[nil] = data c.flowDatas[label] = data defer func() { delete(c.flowDatas, label) c.flowDatas[nil] = prevFlowData }() if c.Flattened[s] { data.endCase = c.caseCounter c.caseCounter++ c.Indent(func() { c.translateStmtList(clause.Body) }) c.Printf("case %d:", data.endCase) return } if label != nil || analysis.HasBreak(clause) { if label != nil { c.Printf("%s:", label.Name()) } c.Printf("switch (0) { default:") c.Indent(func() { c.translateStmtList(clause.Body) }) c.Printf("}") return } c.translateStmtList(clause.Body) case *ast.TypeSwitchStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } refVar := c.newVariable("_ref") var expr ast.Expr switch a := s.Assign.(type) { case *ast.AssignStmt: expr = a.Rhs[0].(*ast.TypeAssertExpr).X case *ast.ExprStmt: expr = a.X.(*ast.TypeAssertExpr).X } c.Printf("%s = %s;", refVar, c.translateExpr(expr)) translateCond := func(cond ast.Expr) *expression { if types.Identical(c.p.TypeOf(cond), types.Typ[types.UntypedNil]) { return c.formatExpr("%s === $ifaceNil", refVar) } return c.formatExpr("$assertType(%s, %s, true)[1]", refVar, c.typeName(c.p.TypeOf(cond))) } var caseClauses []*ast.CaseClause var defaultClause *ast.CaseClause for _, cc := range s.Body.List { clause := cc.(*ast.CaseClause) var bodyPrefix []ast.Stmt if implicit := c.p.Implicits[clause]; implicit != nil { value := refVar if _, isInterface := implicit.Type().Underlying().(*types.Interface); !isInterface { value += ".$val" } bodyPrefix = []ast.Stmt{&ast.AssignStmt{ Lhs: []ast.Expr{c.newIdent(c.objectName(implicit), implicit.Type())}, Tok: token.DEFINE, Rhs: []ast.Expr{c.newIdent(value, implicit.Type())}, }} } c := &ast.CaseClause{ List: clause.List, Body: append(bodyPrefix, clause.Body...), } if len(c.List) == 0 { defaultClause = c continue } caseClauses = append(caseClauses, c) } c.translateBranchingStmt(caseClauses, defaultClause, true, translateCond, label, c.Flattened[s]) case *ast.ForStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } cond := func() string { if s.Cond == nil { return "true" } return c.translateExpr(s.Cond).String() } c.translateLoopingStmt(cond, s.Body, nil, func() { if s.Post != nil { c.translateStmt(s.Post, nil) } }, label, c.Flattened[s]) case *ast.RangeStmt: refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(s.X)) switch t := c.p.TypeOf(s.X).Underlying().(type) { case *types.Basic: iVar := c.newVariable("_i") c.Printf("%s = 0;", iVar) runeVar := c.newVariable("_rune") c.translateLoopingStmt(func() string { return iVar + " < " + refVar + ".length" }, s.Body, func() { c.Printf("%s = $decodeRune(%s, %s);", runeVar, refVar, iVar) if !isBlank(s.Key) { c.Printf("%s", c.translateAssign(s.Key, c.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, c.newIdent(runeVar+"[0]", types.Typ[types.Rune]), s.Tok == token.DEFINE)) } }, func() { c.Printf("%s += %s[1];", iVar, runeVar) }, label, c.Flattened[s]) case *types.Map: iVar := c.newVariable("_i") c.Printf("%s = 0;", iVar) keysVar := c.newVariable("_keys") c.Printf("%s = $keys(%s);", keysVar, refVar) c.translateLoopingStmt(func() string { return iVar + " < " + keysVar + ".length" }, s.Body, func() { entryVar := c.newVariable("_entry") c.Printf("%s = %s[%s[%s]];", entryVar, refVar, keysVar, iVar) c.translateStmt(&ast.IfStmt{ Cond: c.newIdent(entryVar+" === undefined", types.Typ[types.Bool]), Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.CONTINUE}}}, }, nil) if !isBlank(s.Key) { c.Printf("%s", c.translateAssign(s.Key, c.newIdent(entryVar+".k", t.Key()), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, c.newIdent(entryVar+".v", t.Elem()), s.Tok == token.DEFINE)) } }, func() { c.Printf("%s++;", iVar) }, label, c.Flattened[s]) case *types.Array, *types.Pointer, *types.Slice: var length string var elemType types.Type switch t2 := t.(type) { case *types.Array: length = fmt.Sprintf("%d", t2.Len()) elemType = t2.Elem() case *types.Pointer: length = fmt.Sprintf("%d", t2.Elem().Underlying().(*types.Array).Len()) elemType = t2.Elem().Underlying().(*types.Array).Elem() case *types.Slice: length = refVar + ".$length" elemType = t2.Elem() } iVar := c.newVariable("_i") c.Printf("%s = 0;", iVar) c.translateLoopingStmt(func() string { return iVar + " < " + length }, s.Body, func() { if !isBlank(s.Key) { c.Printf("%s", c.translateAssign(s.Key, c.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, c.setType(&ast.IndexExpr{ X: c.newIdent(refVar, t), Index: c.newIdent(iVar, types.Typ[types.Int]), }, elemType), s.Tok == token.DEFINE)) } }, func() { c.Printf("%s++;", iVar) }, label, c.Flattened[s]) case *types.Chan: okVar := c.newIdent(c.newVariable("_ok"), types.Typ[types.Bool]) key := s.Key tok := s.Tok if key == nil { key = ast.NewIdent("_") tok = token.ASSIGN } forStmt := &ast.ForStmt{ Body: &ast.BlockStmt{ List: []ast.Stmt{ &ast.AssignStmt{ Lhs: []ast.Expr{ key, okVar, }, Rhs: []ast.Expr{ c.setType(&ast.UnaryExpr{X: c.newIdent(refVar, t), Op: token.ARROW}, types.NewTuple(types.NewVar(0, nil, "", t.Elem()), types.NewVar(0, nil, "", types.Typ[types.Bool]))), }, Tok: tok, }, &ast.IfStmt{ Cond: &ast.UnaryExpr{X: okVar, Op: token.NOT}, Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.BREAK}}}, }, s.Body, }, }, } c.Flattened[forStmt] = true c.translateStmt(forStmt, label) default: panic("") } case *ast.BranchStmt: normalLabel := "" blockingLabel := "" data := c.flowDatas[nil] if s.Label != nil { normalLabel = " " + s.Label.Name blockingLabel = " s" // use explicit label "s", because surrounding loop may not be flattened data = c.flowDatas[c.p.Uses[s.Label].(*types.Label)] } switch s.Tok { case token.BREAK: c.PrintCond(data.endCase == 0, fmt.Sprintf("break%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.endCase, blockingLabel)) case token.CONTINUE: data.postStmt() c.PrintCond(data.beginCase == 0, fmt.Sprintf("continue%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.beginCase, blockingLabel)) case token.GOTO: c.PrintCond(false, "goto "+s.Label.Name, fmt.Sprintf("$s = %d; continue;", c.labelCase(c.p.Uses[s.Label].(*types.Label)))) case token.FALLTHROUGH: // handled in CaseClause default: panic("Unhandled branch statment: " + s.Tok.String()) } case *ast.ReturnStmt: results := s.Results if c.resultNames != nil { if len(s.Results) != 0 { c.translateStmt(&ast.AssignStmt{ Lhs: c.resultNames, Tok: token.ASSIGN, Rhs: s.Results, }, nil) } results = c.resultNames } rVal := c.translateResults(results) if c.Flattened[s] { resumeCase := c.caseCounter c.caseCounter++ c.Printf("/* */ $s = %[1]d; case %[1]d:", resumeCase) } c.Printf("return%s;", rVal) case *ast.DeferStmt: isBuiltin := false isJs := false switch fun := s.Call.Fun.(type) { case *ast.Ident: var builtin *types.Builtin builtin, isBuiltin = c.p.Uses[fun].(*types.Builtin) if isBuiltin && builtin.Name() == "recover" { c.Printf("$deferred.push([$recover, []]);") return } case *ast.SelectorExpr: isJs = typesutil.IsJsPackage(c.p.Uses[fun.Sel].Pkg()) } sig := c.p.TypeOf(s.Call.Fun).Underlying().(*types.Signature) args := c.translateArgs(sig, s.Call.Args, s.Call.Ellipsis.IsValid(), true) if isBuiltin || isJs { vars := make([]string, len(s.Call.Args)) callArgs := make([]ast.Expr, len(s.Call.Args)) for i, arg := range s.Call.Args { v := c.newVariable("_arg") vars[i] = v callArgs[i] = c.newIdent(v, c.p.TypeOf(arg)) } call := c.translateExpr(&ast.CallExpr{ Fun: s.Call.Fun, Args: callArgs, Ellipsis: s.Call.Ellipsis, }) c.Printf("$deferred.push([function(%s) { %s; }, [%s]]);", strings.Join(vars, ", "), call, strings.Join(args, ", ")) return } c.Printf("$deferred.push([%s, [%s]]);", c.translateExpr(s.Call.Fun), strings.Join(args, ", ")) case *ast.AssignStmt: if s.Tok != token.ASSIGN && s.Tok != token.DEFINE { panic(s.Tok) } switch { case len(s.Lhs) == 1 && len(s.Rhs) == 1: lhs := astutil.RemoveParens(s.Lhs[0]) if isBlank(lhs) { if analysis.HasSideEffect(s.Rhs[0], c.p.Info.Info) { c.Printf("%s;", c.translateExpr(s.Rhs[0])) } return } c.Printf("%s", c.translateAssign(lhs, s.Rhs[0], s.Tok == token.DEFINE)) case len(s.Lhs) > 1 && len(s.Rhs) == 1: tupleVar := c.newVariable("_tuple") c.Printf("%s = %s;", tupleVar, c.translateExpr(s.Rhs[0])) tuple := c.p.TypeOf(s.Rhs[0]).(*types.Tuple) for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { c.Printf("%s", c.translateAssign(lhs, c.newIdent(fmt.Sprintf("%s[%d]", tupleVar, i), tuple.At(i).Type()), s.Tok == token.DEFINE)) } } case len(s.Lhs) == len(s.Rhs): tmpVars := make([]string, len(s.Rhs)) for i, rhs := range s.Rhs { tmpVars[i] = c.newVariable("_tmp") if isBlank(astutil.RemoveParens(s.Lhs[i])) { if analysis.HasSideEffect(rhs, c.p.Info.Info) { c.Printf("%s;", c.translateExpr(rhs)) } continue } c.Printf("%s", c.translateAssign(c.newIdent(tmpVars[i], c.p.TypeOf(s.Lhs[i])), rhs, true)) } for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { c.Printf("%s", c.translateAssign(lhs, c.newIdent(tmpVars[i], c.p.TypeOf(lhs)), s.Tok == token.DEFINE)) } } default: panic("Invalid arity of AssignStmt.") } case *ast.DeclStmt: decl := s.Decl.(*ast.GenDecl) switch decl.Tok { case token.VAR: for _, spec := range s.Decl.(*ast.GenDecl).Specs { valueSpec := spec.(*ast.ValueSpec) lhs := make([]ast.Expr, len(valueSpec.Names)) for i, name := range valueSpec.Names { lhs[i] = name } rhs := valueSpec.Values if len(rhs) == 0 { rhs = make([]ast.Expr, len(lhs)) for i, e := range lhs { rhs[i] = c.zeroValue(c.p.TypeOf(e)) } } c.translateStmt(&ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: rhs, }, nil) } case token.TYPE: for _, spec := range decl.Specs { o := c.p.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) c.p.typeNames = append(c.p.typeNames, o) c.p.objectNames[o] = c.newVariableWithLevel(o.Name(), true) c.p.dependencies[o] = true } case token.CONST: // skip, constants are inlined } case *ast.ExprStmt: expr := c.translateExpr(s.X) if expr != nil && expr.String() != "" { c.Printf("%s;", expr) } case *ast.LabeledStmt: label := c.p.Defs[s.Label].(*types.Label) if c.GotoLabel[label] { c.PrintCond(false, s.Label.Name+":", fmt.Sprintf("case %d:", c.labelCase(label))) } c.translateStmt(s.Stmt, label) case *ast.GoStmt: c.Printf("$go(%s, [%s]);", c.translateExpr(s.Call.Fun), strings.Join(c.translateArgs(c.p.TypeOf(s.Call.Fun).Underlying().(*types.Signature), s.Call.Args, s.Call.Ellipsis.IsValid(), true), ", ")) case *ast.SendStmt: chanType := c.p.TypeOf(s.Chan).Underlying().(*types.Chan) call := &ast.CallExpr{ Fun: c.newIdent("$send", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", chanType), types.NewVar(0, nil, "", chanType.Elem())), nil, false)), Args: []ast.Expr{s.Chan, c.newIdent(c.translateImplicitConversionWithCloning(s.Value, chanType.Elem()).String(), chanType.Elem())}, } c.Blocking[call] = true c.translateStmt(&ast.ExprStmt{X: call}, label) case *ast.SelectStmt: selectionVar := c.newVariable("_selection") var channels []string var caseClauses []*ast.CaseClause flattened := false hasDefault := false for i, cc := range s.Body.List { clause := cc.(*ast.CommClause) switch comm := clause.Comm.(type) { case nil: channels = append(channels, "[]") hasDefault = true case *ast.ExprStmt: channels = append(channels, c.formatExpr("[%e]", astutil.RemoveParens(comm.X).(*ast.UnaryExpr).X).String()) case *ast.AssignStmt: channels = append(channels, c.formatExpr("[%e]", astutil.RemoveParens(comm.Rhs[0]).(*ast.UnaryExpr).X).String()) case *ast.SendStmt: chanType := c.p.TypeOf(comm.Chan).Underlying().(*types.Chan) channels = append(channels, c.formatExpr("[%e, %s]", comm.Chan, c.translateImplicitConversionWithCloning(comm.Value, chanType.Elem())).String()) default: panic(fmt.Sprintf("unhandled: %T", comm)) } indexLit := &ast.BasicLit{Kind: token.INT} c.p.Types[indexLit] = types.TypeAndValue{Type: types.Typ[types.Int], Value: constant.MakeInt64(int64(i))} var bodyPrefix []ast.Stmt if assign, ok := clause.Comm.(*ast.AssignStmt); ok { switch rhsType := c.p.TypeOf(assign.Rhs[0]).(type) { case *types.Tuple: bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1]", rhsType)}, Tok: assign.Tok}} default: bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}} } } caseClauses = append(caseClauses, &ast.CaseClause{ List: []ast.Expr{indexLit}, Body: append(bodyPrefix, clause.Body...), }) flattened = flattened || c.Flattened[clause] } selectCall := c.setType(&ast.CallExpr{ Fun: c.newIdent("$select", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", types.NewInterface(nil, nil))), types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])), false)), Args: []ast.Expr{c.newIdent(fmt.Sprintf("[%s]", strings.Join(channels, ", ")), types.NewInterface(nil, nil))}, }, types.Typ[types.Int]) c.Blocking[selectCall] = !hasDefault c.Printf("%s = %s;", selectionVar, c.translateExpr(selectCall)) if len(caseClauses) != 0 { translateCond := func(cond ast.Expr) *expression { return c.formatExpr("%s[0] === %e", selectionVar, cond) } c.translateBranchingStmt(caseClauses, nil, true, translateCond, label, flattened) } case *ast.EmptyStmt: // skip default: panic(fmt.Sprintf("Unhandled statement: %T\n", s)) } }
// BImportData imports a package from the serialized package data // and returns the number of bytes consumed and a reference to the package. // If data is obviously malformed, an error is returned but in // general it is not recommended to call BImportData on untrusted data. func BImportData(imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) { p := importer{ imports: imports, data: data, } p.buf = p.bufarray[:] // read low-level encoding format switch format := p.byte(); format { case 'c': // compact format - nothing to do case 'd': p.debugFormat = true default: return p.read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format) } // --- generic export data --- if v := p.string(); v != "v0" { return p.read, nil, fmt.Errorf("unknown version: %s", v) } // populate typList with predeclared "known" types p.typList = append(p.typList, predeclared...) // read package data // TODO(gri) clean this up i := p.tagOrIndex() if i != packageTag { panic(fmt.Sprintf("package tag expected, got %d", i)) } name := p.string() if s := p.string(); s != "" { panic(fmt.Sprintf("empty path expected, got %s", s)) } pkg := p.imports[path] if pkg == nil { pkg = types.NewPackage(path, name) p.imports[path] = pkg } p.pkgList = append(p.pkgList, pkg) if debug && p.pkgList[0] != pkg { panic("imported packaged not found in pkgList[0]") } // read compiler-specific flags p.string() // discard // read consts for i := p.int(); i > 0; i-- { name := p.string() typ := p.typ(nil) val := p.value() p.declare(types.NewConst(token.NoPos, pkg, name, typ, val)) } // read vars for i := p.int(); i > 0; i-- { name := p.string() typ := p.typ(nil) p.declare(types.NewVar(token.NoPos, pkg, name, typ)) } // read funcs for i := p.int(); i > 0; i-- { name := p.string() params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) p.int() // read and discard index of inlined function body p.declare(types.NewFunc(token.NoPos, pkg, name, sig)) } // read types for i := p.int(); i > 0; i-- { // name is parsed as part of named type and the // type object is added to scope via respective // named type _ = p.typ(nil).(*types.Named) } // ignore compiler-specific import data // complete interfaces for _, typ := range p.typList { if it, ok := typ.(*types.Interface); ok { it.Complete() } } // record all referenced packages as imports list := append(([]*types.Package)(nil), p.pkgList[1:]...) sort.Sort(byPath(list)) pkg.SetImports(list) // package was imported completely and without errors pkg.MarkComplete() return p.read, pkg, nil }
"comma-separated list of functions whose results must be used") var unusedStringMethodsFlag = flag.String("unusedstringmethods", "Error,String", "comma-separated list of names of methods of type func() string whose results must be used") func init() { register("unusedresult", "check for unused result of calls to functions in -unusedfuncs list and methods in -unusedstringmethods list", checkUnusedResult, exprStmt) } // func() string var sigNoArgsStringResult = types.NewSignature(nil, nil, nil, types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])), false) var unusedFuncs = make(map[string]bool) var unusedStringMethods = make(map[string]bool) func initUnusedFlags() { commaSplit := func(s string, m map[string]bool) { if s != "" { for _, name := range strings.Split(s, ",") { if len(name) == 0 { flag.Usage() } m[name] = true } }
// FunctionType = ParamList ResultList . func (p *parser) parseFunctionType(pkg *types.Package) *types.Signature { params, isVariadic := p.parseParamList(pkg) results := p.parseResultList(pkg) return types.NewSignature(nil, params, results, isVariadic) }