func (fr *frame) callRecover(isDeferredRecover bool) *govalue { startbb := fr.builder.GetInsertBlock() recoverbb := llvm.AddBasicBlock(fr.function, "") contbb := llvm.AddBasicBlock(fr.function, "") canRecover := fr.builder.CreateTrunc(fr.canRecover, llvm.Int1Type(), "") fr.builder.CreateCondBr(canRecover, recoverbb, contbb) fr.builder.SetInsertPointAtEnd(recoverbb) var recovered llvm.Value if isDeferredRecover { recovered = fr.runtime.deferredRecover.call(fr)[0] } else { recovered = fr.runtime.recover.call(fr)[0] } recoverbb = fr.builder.GetInsertBlock() fr.builder.CreateBr(contbb) fr.builder.SetInsertPointAtEnd(contbb) eface := types.NewInterface(nil, nil) llv := fr.builder.CreatePHI(fr.types.ToLLVM(eface), "") llv.AddIncoming( []llvm.Value{llvm.ConstNull(llv.Type()), recovered}, []llvm.BasicBlock{startbb, recoverbb}, ) return newValue(llv, eface) }
// convertI2E converts a non-empty interface value to an empty interface. func (fr *frame) convertI2E(v *govalue) *govalue { td := fr.getInterfaceTypeDescriptor(v) val := fr.builder.CreateExtractValue(v.value, 1, "") typ := types.NewInterface(nil, nil) intf := llvm.Undef(fr.types.ToLLVM(typ)) intf = fr.builder.CreateInsertValue(intf, td, 0, "") intf = fr.builder.CreateInsertValue(intf, val, 1, "") return newValue(intf, typ) }
func initReflect(i *interpreter) { i.reflectPackage = &ssa.Package{ Prog: i.prog, Pkg: reflectTypesPackage, Members: make(map[string]ssa.Member), } // Clobber the type-checker's notion of reflect.Value's // underlying type so that it more closely matches the fake one // (at least in the number of fields---we lie about the type of // the rtype field). // // We must ensure that calls to (ssa.Value).Type() return the // fake type so that correct "shape" is used when allocating // variables, making zero values, loading, and storing. // // TODO(adonovan): obviously this is a hack. We need a cleaner // way to fake the reflect package (almost---DeepEqual is fine). // One approach would be not to even load its source code, but // provide fake source files. This would guarantee that no bad // information leaks into other packages. if r := i.prog.ImportedPackage("reflect"); r != nil { rV := r.Pkg.Scope().Lookup("Value").Type().(*types.Named) // delete bodies of the old methods mset := i.prog.MethodSets.MethodSet(rV) for j := 0; j < mset.Len(); j++ { i.prog.MethodValue(mset.At(j)).Blocks = nil } tEface := types.NewInterface(nil, nil).Complete() rV.SetUnderlying(types.NewStruct([]*types.Var{ types.NewField(token.NoPos, r.Pkg, "t", tEface, false), // a lie types.NewField(token.NoPos, r.Pkg, "v", tEface, false), }, nil)) } i.rtypeMethods = methodSet{ "Bits": newMethod(i.reflectPackage, rtypeType, "Bits"), "Elem": newMethod(i.reflectPackage, rtypeType, "Elem"), "Field": newMethod(i.reflectPackage, rtypeType, "Field"), "In": newMethod(i.reflectPackage, rtypeType, "In"), "Kind": newMethod(i.reflectPackage, rtypeType, "Kind"), "NumField": newMethod(i.reflectPackage, rtypeType, "NumField"), "NumIn": newMethod(i.reflectPackage, rtypeType, "NumIn"), "NumMethod": newMethod(i.reflectPackage, rtypeType, "NumMethod"), "NumOut": newMethod(i.reflectPackage, rtypeType, "NumOut"), "Out": newMethod(i.reflectPackage, rtypeType, "Out"), "Size": newMethod(i.reflectPackage, rtypeType, "Size"), "String": newMethod(i.reflectPackage, rtypeType, "String"), } i.errorMethods = methodSet{ "Error": newMethod(i.reflectPackage, errorType, "Error"), } }
// InterfaceType = "interface" "{" [ MethodList ] "}" . // MethodList = Method { ";" Method } . // Method = Name Signature . // // The methods of embedded interfaces are always "inlined" // by the compiler and thus embedded interfaces are never // visible in the export data. // func (p *parser) parseInterfaceType() types.Type { var methods []*types.Func p.expectKeyword("interface") p.expect('{') for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ { if i > 0 { p.expect(';') } pkg, name := p.parseName(true) sig := p.parseSignature(nil) methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig)) } p.expect('}') // Complete requires the type's embedded interfaces to be fully defined, // but we do not define any return types.NewInterface(methods, nil).Complete() }
// InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" . func (p *parser) parseInterfaceType(pkg *types.Package) types.Type { p.expectKeyword("interface") var methods []*types.Func var typs []*types.Named p.expect('{') for p.tok != '}' && p.tok != scanner.EOF { if p.tok == '?' { p.next() typs = append(typs, p.parseType(pkg).(*types.Named)) } else { method := p.parseFunc(pkg) methods = append(methods, method) } p.expect(';') } p.expect('}') return types.NewInterface(methods, typs) }
func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { c.SetPos(stmt.Pos()) stmt = filter.IncDecStmt(stmt, c.p.Info) stmt = filter.Assign(stmt, c.p.Info) switch s := stmt.(type) { case *ast.BlockStmt: c.translateStmtList(s.List) case *ast.IfStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } var caseClauses []ast.Stmt ifStmt := s for { caseClauses = append(caseClauses, &ast.CaseClause{List: []ast.Expr{ifStmt.Cond}, Body: ifStmt.Body.List}) switch elseStmt := ifStmt.Else.(type) { case *ast.IfStmt: if elseStmt.Init != nil { caseClauses = append(caseClauses, &ast.CaseClause{List: nil, Body: []ast.Stmt{elseStmt}}) break } ifStmt = elseStmt continue case *ast.BlockStmt: caseClauses = append(caseClauses, &ast.CaseClause{List: nil, Body: elseStmt.List}) case *ast.EmptyStmt, nil: // no else clause default: panic(fmt.Sprintf("Unhandled else: %T\n", elseStmt)) } break } c.translateBranchingStmt(caseClauses, false, nil, nil, nil, c.Flattened[s]) case *ast.SwitchStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } tag := s.Tag if tag == nil { tag = ast.NewIdent("true") c.p.Types[tag] = types.TypeAndValue{Type: types.Typ[types.Bool], Value: exact.MakeBool(true)} } if c.p.Types[tag].Value == nil { refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(tag)) tag = c.newIdent(refVar, c.p.Types[tag].Type) } translateCond := func(cond ast.Expr) *expression { return c.translateExpr(&ast.BinaryExpr{ X: tag, Op: token.EQL, Y: cond, }) } c.translateBranchingStmt(s.Body.List, true, translateCond, nil, label, c.Flattened[s]) case *ast.TypeSwitchStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } refVar := c.newVariable("_ref") var expr ast.Expr var printCaseBodyPrefix func(index int) switch a := s.Assign.(type) { case *ast.AssignStmt: expr = a.Rhs[0].(*ast.TypeAssertExpr).X printCaseBodyPrefix = func(index int) { value := refVar caseClause := s.Body.List[index].(*ast.CaseClause) if len(caseClause.List) == 1 { t := c.p.Types[caseClause.List[0]].Type if _, isInterface := t.Underlying().(*types.Interface); !isInterface && !types.Identical(t, types.Typ[types.UntypedNil]) { value += ".$val" } } c.Printf("%s = %s;", c.objectName(c.p.Implicits[caseClause]), value) } 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.Types[cond].Type, types.Typ[types.UntypedNil]) { return c.formatExpr("%s === $ifaceNil", refVar) } return c.formatExpr("$assertType(%s, %s, true)[1]", refVar, c.typeName(c.p.Types[cond].Type)) } c.translateBranchingStmt(s.Body.List, true, translateCond, printCaseBodyPrefix, 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.Types[s.X].Type.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, iVar, types.Typ[types.Int], s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, 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, entryVar+".k", t.Key(), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, 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, iVar, types.Typ[types.Int], s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, c.translateImplicitConversion(c.setType(&ast.IndexExpr{ X: c.newIdent(refVar, t), Index: c.newIdent(iVar, types.Typ[types.Int]), }, elemType), elemType).String(), 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 } c.Printf("return%s;", c.translateResults(results)) 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.Types[s.Call.Fun].Type.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.Types[arg].Type) } 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) } if s.Tok == token.DEFINE { for _, lhs := range s.Lhs { if !isBlank(lhs) { obj := c.p.Defs[lhs.(*ast.Ident)] if obj == nil { obj = c.p.Uses[lhs.(*ast.Ident)] } c.setType(lhs, obj.Type()) } } } 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]).String()) } return } lhsType := c.p.Types[s.Lhs[0]].Type c.Printf("%s", c.translateAssignOfExpr(lhs, s.Rhs[0], lhsType, s.Tok == token.DEFINE)) case len(s.Lhs) > 1 && len(s.Rhs) == 1: tupleVar := c.newVariable("_tuple") out := tupleVar + " = " + c.translateExpr(s.Rhs[0]).String() + ";" tuple := c.p.Types[s.Rhs[0]].Type.(*types.Tuple) for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { lhsType := c.p.Types[s.Lhs[i]].Type out += " " + c.translateAssignOfExpr(lhs, c.newIdent(fmt.Sprintf("%s[%d]", tupleVar, i), tuple.At(i).Type()), lhsType, s.Tok == token.DEFINE) } } c.Printf("%s", out) case len(s.Lhs) == len(s.Rhs): tmpVars := make([]string, len(s.Rhs)) var parts []string 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).String()) } continue } lhsType := c.p.Types[s.Lhs[i]].Type parts = append(parts, c.translateAssignOfExpr(c.newIdent(tmpVars[i], c.p.Types[s.Lhs[i]].Type), rhs, lhsType, true)) } for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { t := c.p.Types[lhs].Type parts = append(parts, c.translateAssignOfExpr(lhs, c.newIdent(tmpVars[i], t), t, s.Tok == token.DEFINE)) } } c.Printf("%s", strings.Join(parts, " ")) 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 isTuple := false if len(rhs) == 1 { _, isTuple = c.p.Types[rhs[0]].Type.(*types.Tuple) } for len(rhs) < len(lhs) && !isTuple { rhs = append(rhs, nil) } 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.Types[s.Call.Fun].Type.Underlying().(*types.Signature), s.Call.Args, s.Call.Ellipsis.IsValid(), false), ", ")) case *ast.SendStmt: chanType := c.p.Types[s.Chan].Type.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, s.Value}, } c.Blocking[call] = true c.translateStmt(&ast.ExprStmt{X: call}, label) case *ast.SelectStmt: var channels []string var caseClauses []ast.Stmt flattened := false hasDefault := false for i, s := range s.Body.List { clause := s.(*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: channels = append(channels, c.formatExpr("[%e, %e]", comm.Chan, comm.Value).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: exact.MakeInt64(int64(i))} caseClauses = append(caseClauses, &ast.CaseClause{ List: []ast.Expr{indexLit}, Body: 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 selectionVar := c.newVariable("_selection") c.Printf("%s = %s;", selectionVar, c.translateExpr(selectCall)) translateCond := func(cond ast.Expr) *expression { return c.formatExpr("%s[0] === %e", selectionVar, cond) } printCaseBodyPrefix := func(index int) { if assign, ok := s.Body.List[index].(*ast.CommClause).Comm.(*ast.AssignStmt); ok { switch rhsType := c.p.Types[assign.Rhs[0]].Type.(type) { case *types.Tuple: c.translateStmt(&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1]", rhsType)}, Tok: assign.Tok}, nil) default: c.translateStmt(&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}, nil) } } } c.translateBranchingStmt(caseClauses, true, translateCond, printCaseBodyPrefix, label, flattened) case *ast.EmptyStmt: // skip default: panic(fmt.Sprintf("Unhandled statement: %T\n", s)) } }
func (c *funcContext) translateBuiltin(name string, sig *types.Signature, args []ast.Expr, ellipsis bool) *expression { switch name { case "new": t := sig.Results().At(0).Type().(*types.Pointer) if c.p.Pkg.Path() == "syscall" && types.Identical(t.Elem().Underlying(), types.Typ[types.Uintptr]) { return c.formatExpr("new Uint8Array(8)") } switch t.Elem().Underlying().(type) { case *types.Struct, *types.Array: return c.formatExpr("%e", c.zeroValue(t.Elem())) default: return c.formatExpr("$newDataPointer(%e, %s)", c.zeroValue(t.Elem()), c.typeName(t)) } case "make": switch argType := c.p.TypeOf(args[0]).Underlying().(type) { case *types.Slice: t := c.typeName(c.p.TypeOf(args[0])) if len(args) == 3 { return c.formatExpr("$makeSlice(%s, %f, %f)", t, args[1], args[2]) } return c.formatExpr("$makeSlice(%s, %f)", t, args[1]) case *types.Map: return c.formatExpr("{}") case *types.Chan: length := "0" if len(args) == 2 { length = c.translateExpr(args[1]).String() } return c.formatExpr("new %s(%s)", c.typeName(c.p.TypeOf(args[0])), length) default: panic(fmt.Sprintf("Unhandled make type: %T\n", argType)) } case "len": switch argType := c.p.TypeOf(args[0]).Underlying().(type) { case *types.Basic: return c.formatExpr("%e.length", args[0]) case *types.Slice: return c.formatExpr("%e.$length", args[0]) case *types.Pointer: return c.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len()) case *types.Map: return c.formatExpr("$keys(%e).length", args[0]) case *types.Chan: return c.formatExpr("%e.$buffer.length", args[0]) // length of array is constant default: panic(fmt.Sprintf("Unhandled len type: %T\n", argType)) } case "cap": switch argType := c.p.TypeOf(args[0]).Underlying().(type) { case *types.Slice, *types.Chan: return c.formatExpr("%e.$capacity", args[0]) case *types.Pointer: return c.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len()) // capacity of array is constant default: panic(fmt.Sprintf("Unhandled cap type: %T\n", argType)) } case "panic": return c.formatExpr("$panic(%s)", c.translateImplicitConversion(args[0], types.NewInterface(nil, nil))) case "append": if ellipsis || len(args) == 1 { argStr := c.translateArgs(sig, args, ellipsis, false) return c.formatExpr("$appendSlice(%s, %s)", argStr[0], argStr[1]) } sliceType := sig.Results().At(0).Type().Underlying().(*types.Slice) return c.formatExpr("$append(%e, %s)", args[0], strings.Join(c.translateExprSlice(args[1:], sliceType.Elem()), ", ")) case "delete": keyType := c.p.TypeOf(args[0]).Underlying().(*types.Map).Key() return c.formatExpr(`delete %e[%s.keyFor(%s)]`, args[0], c.typeName(keyType), c.translateImplicitConversion(args[1], keyType)) case "copy": if basic, isBasic := c.p.TypeOf(args[1]).Underlying().(*types.Basic); isBasic && isString(basic) { return c.formatExpr("$copyString(%e, %e)", args[0], args[1]) } return c.formatExpr("$copySlice(%e, %e)", args[0], args[1]) case "print", "println": return c.formatExpr("console.log(%s)", strings.Join(c.translateExprSlice(args, nil), ", ")) case "complex": argStr := c.translateArgs(sig, args, ellipsis, false) return c.formatExpr("new %s(%s, %s)", c.typeName(sig.Results().At(0).Type()), argStr[0], argStr[1]) case "real": return c.formatExpr("%e.$real", args[0]) case "imag": return c.formatExpr("%e.$imag", args[0]) case "recover": return c.formatExpr("$recover()") case "close": return c.formatExpr(`$close(%e)`, args[0]) default: panic(fmt.Sprintf("Unhandled builtin: %s\n", name)) } }
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(exact.BoolVal(value))) case isInteger(basic): if is64Bit(basic) { if basic.Kind() == types.Int64 { d, ok := exact.Int64Val(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 := exact.Uint64Val(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 := exact.Int64Val(value) if !ok { panic("could not get exact int") } return c.formatExpr("%s", strconv.FormatInt(d, 10)) case isFloat(basic): f, _ := exact.Float64Val(value) return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case isComplex(basic): r, _ := exact.Float64Val(exact.Real(value)) i, _ := exact.Float64Val(exact.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(exact.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 := exact.Int64Val(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.Selections[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.Selections[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: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >> 0) + (%1e << 16 >>> 16) * %2e) >> 0", e.X, e.Y) case types.Uint32, types.Uintptr: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >>> 0) + (%1e << 16 >>> 16) * %2e) >>> 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 c.p.Types[e.Y].Value != nil { return c.fixNumber(c.formatExpr("%e %s %e", e.X, op, e.Y), basic) } if e.Op == token.SHR && !isUnsigned(basic) { return c.fixNumber(c.formatParenExpr("%e >> $min(%e, 31)", e.X, e.Y), basic) } y := c.newVariable("y") return c.fixNumber(c.formatExpr("(%s = %s, %s < 32 ? (%e %s %s) : 0)", y, c.translateImplicitConversion(e.Y, types.Typ[types.Uint]), 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.Selections[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: recv := c.makeReceiver(e.X, sel) return c.formatExpr(`$methodVal(%s, "%s")`, recv, sel.Obj().(*types.Func).Name()) case types.MethodExpr: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } 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.Selections[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]) case "MakeFunc": return c.formatExpr("(function() { return $externalize(%e(this, new ($sliceType($jsObjectPtr))($global.Array.prototype.slice.call(arguments, []))), $emptyInterface); })", 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.X, sel) if typesutil.IsJsPackage(sel.Obj().Pkg()) { globalRef := func(id string) string { if recv.String() == "$global" && id[0] == '$' { 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, *types.Chan: return c.formatExpr("%s.nil", c.typeName(exprType)) 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 (p *importer) typ() types.Type { // if the type was seen before, i is its index (>= 0) i := p.int() if i >= 0 { return p.typList[i] } // otherwise, i is the type tag (< 0) switch i { case arrayTag: t := new(types.Array) p.record(t) n := p.int64() *t = *types.NewArray(p.typ(), n) return t case sliceTag: t := new(types.Slice) p.record(t) *t = *types.NewSlice(p.typ()) 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() 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()) return t case signatureTag: t := new(types.Signature) p.record(t) *t = *p.signature() 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) // read embedded interfaces embeddeds := make([]*types.Named, p.int()) for i := range embeddeds { embeddeds[i] = p.typ().(*types.Named) } // read methods methods := make([]*types.Func, p.int()) for i := range methods { pkg, name := p.qualifiedName() methods[i] = types.NewFunc(token.NoPos, pkg, name, p.typ().(*types.Signature)) } t := types.NewInterface(methods, embeddeds) p.typList[n] = t return t case mapTag: t := new(types.Map) p.record(t) *t = *types.NewMap(p.typ(), p.typ()) return t case chanTag: t := new(types.Chan) p.record(t) *t = *types.NewChan(types.ChanDir(p.int()), p.typ()) return t case namedTag: // read type object name := p.string() pkg := p.pkg() scope := pkg.Scope() obj := scope.Lookup(name) // if the object doesn't exist yet, create and insert it if obj == nil { obj = types.NewTypeName(token.NoPos, pkg, name, nil) scope.Insert(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()) // read associated methods for i, n := 0, p.int(); i < n; i++ { t0.AddMethod(types.NewFunc(token.NoPos, pkg, p.string(), p.typ().(*types.Signature))) } return t default: panic(fmt.Sprintf("unexpected type tag %d", i)) } }
// TODO(adonovan): move the constraint definitions and the store() etc // functions which add them (and are also used by the solver) into a // new file, constraints.go. import ( "fmt" "go/token" "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/types" ) var ( tEface = types.NewInterface(nil, nil).Complete() tInvalid = types.Typ[types.Invalid] tUnsafePtr = types.Typ[types.UnsafePointer] ) // ---------- Node creation ---------- // nextNode returns the index of the next unused node. func (a *analysis) nextNode() nodeid { return nodeid(len(a.nodes)) } // addNodes creates nodes for all scalar elements in type typ, and // returns the id of the first one, or zero if the type was // analytically uninteresting. //
func newRuntimeInterface(module llvm.Module, tm *llvmTypeMap) (*runtimeInterface, error) { var ri runtimeInterface Bool := types.Typ[types.Bool] Complex128 := types.Typ[types.Complex128] Float64 := types.Typ[types.Float64] Int32 := types.Typ[types.Int32] Int64 := types.Typ[types.Int64] Int := types.Typ[types.Int] Rune := types.Typ[types.Rune] String := types.Typ[types.String] Uintptr := types.Typ[types.Uintptr] UnsafePointer := types.Typ[types.UnsafePointer] EmptyInterface := types.NewInterface(nil, nil) IntSlice := types.NewSlice(types.Typ[types.Int]) for _, rt := range [...]struct { name string rfi *runtimeFnInfo args, res []types.Type attrs []llvm.Attribute }{ { name: "__go_append", rfi: &ri.append, args: []types.Type{IntSlice, UnsafePointer, Uintptr, Uintptr}, res: []types.Type{IntSlice}, }, { name: "__go_assert_interface", rfi: &ri.assertInterface, args: []types.Type{UnsafePointer, UnsafePointer}, res: []types.Type{UnsafePointer}, }, { name: "__go_can_recover", rfi: &ri.canRecover, args: []types.Type{UnsafePointer}, res: []types.Type{Bool}, }, { name: "__go_chan_cap", rfi: &ri.chanCap, args: []types.Type{UnsafePointer}, res: []types.Type{Int}, }, { name: "__go_chan_len", rfi: &ri.chanLen, args: []types.Type{UnsafePointer}, res: []types.Type{Int}, }, { name: "runtime.chanrecv2", rfi: &ri.chanrecv2, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, res: []types.Type{Bool}, }, { name: "__go_check_defer", rfi: &ri.checkDefer, args: []types.Type{UnsafePointer}, }, { name: "__go_check_interface_type", rfi: &ri.checkInterfaceType, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "__go_builtin_close", rfi: &ri.builtinClose, args: []types.Type{UnsafePointer}, }, { name: "__go_convert_interface", rfi: &ri.convertInterface, args: []types.Type{UnsafePointer, UnsafePointer}, res: []types.Type{UnsafePointer}, }, { name: "__go_copy", rfi: &ri.copy, args: []types.Type{UnsafePointer, UnsafePointer, Uintptr}, }, { name: "__go_defer", rfi: &ri.Defer, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "__go_deferred_recover", rfi: &ri.deferredRecover, res: []types.Type{EmptyInterface}, }, { name: "__go_empty_interface_compare", rfi: &ri.emptyInterfaceCompare, args: []types.Type{EmptyInterface, EmptyInterface}, res: []types.Type{Int}, }, { name: "__go_get_closure", rfi: &ri.getClosure, res: []types.Type{UnsafePointer}, }, { name: "__go_go", rfi: &ri.Go, args: []types.Type{UnsafePointer, UnsafePointer}, }, { name: "runtime.ifaceE2I2", rfi: &ri.ifaceE2I2, args: []types.Type{UnsafePointer, EmptyInterface}, res: []types.Type{EmptyInterface, Bool}, }, { name: "runtime.ifaceI2I2", rfi: &ri.ifaceI2I2, args: []types.Type{UnsafePointer, EmptyInterface}, res: []types.Type{EmptyInterface, Bool}, }, { name: "__go_int_array_to_string", rfi: &ri.intArrayToString, args: []types.Type{UnsafePointer, Int}, res: []types.Type{String}, }, { name: "__go_int_to_string", rfi: &ri.intToString, args: []types.Type{Int}, res: []types.Type{String}, }, { name: "__go_interface_compare", rfi: &ri.interfaceCompare, args: []types.Type{EmptyInterface, EmptyInterface}, res: []types.Type{Int}, }, { name: "__go_make_slice2", rfi: &ri.makeSlice, args: []types.Type{UnsafePointer, Uintptr, Uintptr}, res: []types.Type{IntSlice}, }, { name: "runtime.mapdelete", rfi: &ri.mapdelete, args: []types.Type{UnsafePointer, UnsafePointer}, }, { name: "runtime.mapiter2", rfi: &ri.mapiter2, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "runtime.mapiterinit", rfi: &ri.mapiterinit, args: []types.Type{UnsafePointer, UnsafePointer}, }, { name: "runtime.mapiternext", rfi: &ri.mapiternext, args: []types.Type{UnsafePointer}, }, { name: "__go_map_index", rfi: &ri.mapIndex, args: []types.Type{UnsafePointer, UnsafePointer, Bool}, res: []types.Type{UnsafePointer}, }, { name: "__go_map_len", rfi: &ri.mapLen, args: []types.Type{UnsafePointer}, res: []types.Type{Int}, }, { name: "__go_new", rfi: &ri.New, args: []types.Type{Uintptr}, res: []types.Type{UnsafePointer}, }, { name: "__go_new_channel", rfi: &ri.newChannel, args: []types.Type{UnsafePointer, Uintptr}, res: []types.Type{UnsafePointer}, }, { name: "__go_new_map", rfi: &ri.newMap, args: []types.Type{UnsafePointer, Uintptr}, res: []types.Type{UnsafePointer}, }, { name: "__go_new_nopointers", rfi: &ri.NewNopointers, args: []types.Type{Uintptr}, res: []types.Type{UnsafePointer}, }, { name: "runtime.newselect", rfi: &ri.newSelect, args: []types.Type{Int32}, res: []types.Type{UnsafePointer}, }, { name: "__go_panic", rfi: &ri.panic, args: []types.Type{EmptyInterface}, attrs: []llvm.Attribute{llvm.NoReturnAttribute}, }, { name: "__go_print_bool", rfi: &ri.printBool, args: []types.Type{Bool}, }, { name: "__go_print_complex", rfi: &ri.printComplex, args: []types.Type{Complex128}, }, { name: "__go_print_double", rfi: &ri.printDouble, args: []types.Type{Float64}, }, { name: "__go_print_empty_interface", rfi: &ri.printEmptyInterface, args: []types.Type{EmptyInterface}, }, { name: "__go_print_interface", rfi: &ri.printInterface, args: []types.Type{EmptyInterface}, }, { name: "__go_print_int64", rfi: &ri.printInt64, args: []types.Type{Int64}, }, { name: "__go_print_nl", rfi: &ri.printNl, }, { name: "__go_print_pointer", rfi: &ri.printPointer, args: []types.Type{UnsafePointer}, }, { name: "__go_print_slice", rfi: &ri.printSlice, args: []types.Type{IntSlice}, }, { name: "__go_print_space", rfi: &ri.printSpace, }, { name: "__go_print_string", rfi: &ri.printString, args: []types.Type{String}, }, { name: "__go_print_uint64", rfi: &ri.printUint64, args: []types.Type{Int64}, }, { name: "__go_receive", rfi: &ri.receive, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "__go_recover", rfi: &ri.recover, res: []types.Type{EmptyInterface}, }, { name: "__go_register_gc_roots", rfi: &ri.registerGcRoots, args: []types.Type{UnsafePointer}, }, { name: "__go_runtime_error", rfi: &ri.runtimeError, args: []types.Type{Int32}, attrs: []llvm.Attribute{llvm.NoReturnAttribute}, }, { name: "runtime.selectdefault", rfi: &ri.selectdefault, args: []types.Type{UnsafePointer, Int32}, }, { name: "runtime.selectgo", rfi: &ri.selectgo, args: []types.Type{UnsafePointer}, res: []types.Type{Int}, }, { name: "runtime.selectrecv2", rfi: &ri.selectrecv2, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer, UnsafePointer, Int32}, }, { name: "runtime.selectsend", rfi: &ri.selectsend, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer, Int32}, }, { name: "__go_send_big", rfi: &ri.sendBig, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "__go_set_closure", rfi: &ri.setClosure, args: []types.Type{UnsafePointer}, }, { name: "__go_set_defer_retaddr", rfi: &ri.setDeferRetaddr, args: []types.Type{UnsafePointer}, res: []types.Type{Bool}, }, { name: "__go_strcmp", rfi: &ri.strcmp, args: []types.Type{String, String}, res: []types.Type{Int}, }, { name: "__go_string_plus", rfi: &ri.stringPlus, args: []types.Type{String, String}, res: []types.Type{String}, }, { name: "__go_string_slice", rfi: &ri.stringSlice, args: []types.Type{String, Int, Int}, res: []types.Type{String}, }, { name: "__go_string_to_int_array", rfi: &ri.stringToIntArray, args: []types.Type{String}, res: []types.Type{IntSlice}, }, { name: "runtime.stringiter2", rfi: &ri.stringiter2, args: []types.Type{String, Int}, res: []types.Type{Int, Rune}, }, { name: "__go_type_descriptors_equal", rfi: &ri.typeDescriptorsEqual, args: []types.Type{UnsafePointer, UnsafePointer}, res: []types.Type{Bool}, }, { name: "__go_undefer", rfi: &ri.undefer, args: []types.Type{UnsafePointer}, }, } { rt.rfi.init(tm, module, rt.name, rt.args, rt.res) for _, attr := range rt.attrs { rt.rfi.fn.AddFunctionAttr(attr) } } memsetName := "llvm.memset.p0i8.i" + strconv.Itoa(tm.target.IntPtrType().IntTypeWidth()) memsetType := llvm.FunctionType( llvm.VoidType(), []llvm.Type{ llvm.PointerType(llvm.Int8Type(), 0), llvm.Int8Type(), tm.target.IntPtrType(), llvm.Int32Type(), llvm.Int1Type(), }, false, ) ri.memset = llvm.AddFunction(module, memsetName, memsetType) memcpyName := "llvm.memcpy.p0i8.p0i8.i" + strconv.Itoa(tm.target.IntPtrType().IntTypeWidth()) memcpyType := llvm.FunctionType( llvm.VoidType(), []llvm.Type{ llvm.PointerType(llvm.Int8Type(), 0), llvm.PointerType(llvm.Int8Type(), 0), tm.target.IntPtrType(), llvm.Int32Type(), llvm.Int1Type(), }, false, ) ri.memcpy = llvm.AddFunction(module, memcpyName, memcpyType) returnaddressType := llvm.FunctionType( llvm.PointerType(llvm.Int8Type(), 0), []llvm.Type{llvm.Int32Type()}, false, ) ri.returnaddress = llvm.AddFunction(module, "llvm.returnaddress", returnaddressType) gccgoPersonalityType := llvm.FunctionType( llvm.Int32Type(), []llvm.Type{ llvm.Int32Type(), llvm.Int64Type(), llvm.PointerType(llvm.Int8Type(), 0), llvm.PointerType(llvm.Int8Type(), 0), }, false, ) ri.gccgoPersonality = llvm.AddFunction(module, "__gccgo_personality_v0", gccgoPersonalityType) ri.gccgoExceptionType = llvm.StructType( []llvm.Type{ llvm.PointerType(llvm.Int8Type(), 0), llvm.Int32Type(), }, false, ) return &ri, nil }