func (c *compiler) makeInterface(v *LLVMValue, iface types.Type) *LLVMValue { llv := v.LLVMValue() lltyp := llv.Type() i8ptr := llvm.PointerType(llvm.Int8Type(), 0) if lltyp.TypeKind() == llvm.PointerTypeKind { llv = c.builder.CreateBitCast(llv, i8ptr, "") } else { // If the value fits exactly in a pointer, then we can just // bitcast it. Otherwise we need to malloc. if c.target.TypeStoreSize(lltyp) <= uint64(c.target.PointerSize()) { bits := c.target.TypeSizeInBits(lltyp) if bits > 0 { llv = coerce(c.builder, llv, llvm.IntType(int(bits))) llv = c.builder.CreateIntToPtr(llv, i8ptr, "") } else { llv = llvm.ConstNull(i8ptr) } } else { ptr := c.createTypeMalloc(lltyp) c.builder.CreateStore(llv, ptr) llv = c.builder.CreateBitCast(ptr, i8ptr, "") } } value := llvm.Undef(c.types.ToLLVM(iface)) rtype := c.types.ToRuntime(v.Type()) rtype = c.builder.CreateBitCast(rtype, llvm.PointerType(llvm.Int8Type(), 0), "") value = c.builder.CreateInsertValue(value, rtype, 0, "") value = c.builder.CreateInsertValue(value, llv, 1, "") if iface.Underlying().(*types.Interface).NumMethods() > 0 { result := c.NewValue(value, types.NewInterface(nil, nil)) result, _ = result.convertE2I(iface) return result } return c.NewValue(value, iface) }
// convertI2E converts a non-empty interface value to an empty interface. func (v *LLVMValue) convertI2E() *LLVMValue { c := v.compiler f := c.runtime.convertI2E.LLVMValue() args := []llvm.Value{coerce(c.builder, v.LLVMValue(), c.runtime.iface.llvm)} typ := types.NewInterface(nil, nil) return c.NewValue(coerce(c.builder, c.builder.CreateCall(f, args, ""), c.llvmtypes.ToLLVM(typ)), typ) }
// 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('}') return types.NewInterface(methods, nil) }
// 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 string) { c.WritePos(stmt.Pos()) switch s := stmt.(type) { case *ast.BlockStmt: c.printLabel(label) c.translateStmtList(s.List) case *ast.IfStmt: c.printLabel(label) if s.Init != nil { c.translateStmt(s.Init, "") } 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, c.translateExpr, nil, "", c.flattened[s]) case *ast.SwitchStmt: if s.Init != nil { c.translateStmt(s.Init, "") } translateCond := func(cond ast.Expr) *expression { return c.translateExpr(cond) } if s.Tag != nil { refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(s.Tag)) translateCond = func(cond ast.Expr) *expression { return c.translateExpr(&ast.BinaryExpr{ X: c.newIdent(refVar, c.p.info.Types[s.Tag].Type), 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, "") } var expr ast.Expr var typeSwitchVar string switch a := s.Assign.(type) { case *ast.AssignStmt: expr = a.Rhs[0].(*ast.TypeAssertExpr).X typeSwitchVar = c.newVariable(a.Lhs[0].(*ast.Ident).Name) for _, caseClause := range s.Body.List { c.p.objectVars[c.p.info.Implicits[caseClause]] = typeSwitchVar } case *ast.ExprStmt: expr = a.X.(*ast.TypeAssertExpr).X } refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(expr)) translateCond := func(cond ast.Expr) *expression { if types.Identical(c.p.info.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.info.Types[cond].Type)) } printCaseBodyPrefix := func(index int) { if typeSwitchVar == "" { return } value := refVar if conds := s.Body.List[index].(*ast.CaseClause).List; len(conds) == 1 { t := c.p.info.Types[conds[0]].Type if _, isInterface := t.Underlying().(*types.Interface); !isInterface && !types.Identical(t, types.Typ[types.UntypedNil]) { value += ".$val" } } c.Printf("%s = %s;", typeSwitchVar, value) } c.translateBranchingStmt(s.Body.List, true, translateCond, printCaseBodyPrefix, label, c.flattened[s]) case *ast.ForStmt: if s.Init != nil { c.translateStmt(s.Init, "") } cond := "true" if s.Cond != nil { cond = c.translateExpr(s.Cond).String() } c.translateLoopingStmt(cond, s.Body, nil, func() { if s.Post != nil { c.translateStmt(s.Post, "") } }, label, c.flattened[s]) case *ast.RangeStmt: refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(s.X)) switch t := c.p.info.Types[s.X].Type.Underlying().(type) { case *types.Basic: iVar := c.newVariable("_i") c.Printf("%s = 0;", iVar) runeVar := c.newVariable("_rune") c.translateLoopingStmt(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(iVar+" < "+keysVar+".length", s.Body, func() { entryVar := c.newVariable("_entry") c.Printf("%s = %s[%s[%s]];", entryVar, refVar, keysVar, iVar) 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(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]) forStmt := &ast.ForStmt{ Body: &ast.BlockStmt{ List: []ast.Stmt{ &ast.AssignStmt{ Lhs: []ast.Expr{ s.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: s.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: c.printLabel(label) labelSuffix := "" data := c.flowDatas[""] if s.Label != nil { labelSuffix = " " + s.Label.Name data = c.flowDatas[s.Label.Name] } switch s.Tok { case token.BREAK: c.PrintCond(data.endCase == 0, fmt.Sprintf("break%s;", labelSuffix), fmt.Sprintf("$s = %d; continue;", data.endCase)) case token.CONTINUE: data.postStmt() c.PrintCond(data.beginCase == 0, fmt.Sprintf("continue%s;", labelSuffix), fmt.Sprintf("$s = %d; continue;", data.beginCase)) case token.GOTO: c.PrintCond(false, "goto "+s.Label.Name, fmt.Sprintf("$s = %d; continue;", c.labelCases[s.Label.Name])) case token.FALLTHROUGH: // handled in CaseClause default: panic("Unhandled branch statment: " + s.Tok.String()) } case *ast.ReturnStmt: c.printLabel(label) 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, }, "") } results = c.resultNames } switch len(results) { case 0: c.Printf("return;") case 1: if c.sig.Results().Len() > 1 { c.Printf("return %s;", c.translateExpr(results[0])) return } v := c.translateImplicitConversion(results[0], c.sig.Results().At(0).Type()) c.delayedOutput = nil c.Printf("return %s;", v) default: values := make([]string, len(results)) for i, result := range results { values[i] = c.translateImplicitConversion(result, c.sig.Results().At(i).Type()).String() } c.delayedOutput = nil c.Printf("return [%s];", strings.Join(values, ", ")) } case *ast.DeferStmt: c.printLabel(label) isBuiltin := false isJs := false switch fun := s.Call.Fun.(type) { case *ast.Ident: var builtin *types.Builtin builtin, isBuiltin = c.p.info.Uses[fun].(*types.Builtin) if isBuiltin && builtin.Name() == "recover" { c.Printf("$deferred.push([$recover, []]);") return } case *ast.SelectorExpr: isJs = isJsPackage(c.p.info.Uses[fun.Sel].Pkg()) } if isBuiltin || isJs { args := make([]ast.Expr, len(s.Call.Args)) for i, arg := range s.Call.Args { args[i] = c.newIdent(c.newVariable("_arg"), c.p.info.Types[arg].Type) } call := c.translateExpr(&ast.CallExpr{ Fun: s.Call.Fun, Args: args, Ellipsis: s.Call.Ellipsis, }) c.Printf("$deferred.push([function(%s) { %s; }, [%s]]);", strings.Join(c.translateExprSlice(args, nil), ", "), call, strings.Join(c.translateExprSlice(s.Call.Args, nil), ", ")) return } sig := c.p.info.Types[s.Call.Fun].Type.Underlying().(*types.Signature) args := c.translateArgs(sig, s.Call.Args, s.Call.Ellipsis.IsValid()) if len(c.blocking) != 0 { args = append(args, "true") } c.Printf("$deferred.push([%s, [%s]]);", c.translateExpr(s.Call.Fun), strings.Join(args, ", ")) case *ast.AssignStmt: c.printLabel(label) if s.Tok != token.ASSIGN && s.Tok != token.DEFINE { var op token.Token switch s.Tok { case token.ADD_ASSIGN: op = token.ADD case token.SUB_ASSIGN: op = token.SUB case token.MUL_ASSIGN: op = token.MUL case token.QUO_ASSIGN: op = token.QUO case token.REM_ASSIGN: op = token.REM case token.AND_ASSIGN: op = token.AND case token.OR_ASSIGN: op = token.OR case token.XOR_ASSIGN: op = token.XOR case token.SHL_ASSIGN: op = token.SHL case token.SHR_ASSIGN: op = token.SHR case token.AND_NOT_ASSIGN: op = token.AND_NOT default: panic(s.Tok) } var parts []string lhs := s.Lhs[0] switch l := lhs.(type) { case *ast.IndexExpr: lhsVar := c.newVariable("_lhs") indexVar := c.newVariable("_index") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X).String()+";") parts = append(parts, indexVar+" = "+c.translateExpr(l.Index).String()+";") lhs = c.setType(&ast.IndexExpr{ X: c.newIdent(lhsVar, c.p.info.Types[l.X].Type), Index: c.newIdent(indexVar, c.p.info.Types[l.Index].Type), }, c.p.info.Types[l].Type) case *ast.StarExpr: lhsVar := c.newVariable("_lhs") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X).String()+";") lhs = c.setType(&ast.StarExpr{ X: c.newIdent(lhsVar, c.p.info.Types[l.X].Type), }, c.p.info.Types[l].Type) case *ast.SelectorExpr: v := hasCallVisitor{c.p.info, false} ast.Walk(&v, l.X) if v.hasCall { lhsVar := c.newVariable("_lhs") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X).String()+";") lhs = c.setType(&ast.SelectorExpr{ X: c.newIdent(lhsVar, c.p.info.Types[l.X].Type), Sel: l.Sel, }, c.p.info.Types[l].Type) c.p.info.Selections[lhs.(*ast.SelectorExpr)] = c.p.info.Selections[l] } } lhsType := c.p.info.Types[s.Lhs[0]].Type parts = append(parts, c.translateAssign(lhs, c.translateExpr(c.setType(&ast.BinaryExpr{ X: lhs, Op: op, Y: c.setType(&ast.ParenExpr{X: s.Rhs[0]}, c.p.info.Types[s.Rhs[0]].Type), }, lhsType)).String(), lhsType, s.Tok == token.DEFINE)) c.Printf("%s", strings.Join(parts, " ")) return } if s.Tok == token.DEFINE { for _, lhs := range s.Lhs { if !isBlank(lhs) { obj := c.p.info.Defs[lhs.(*ast.Ident)] if obj == nil { obj = c.p.info.Uses[lhs.(*ast.Ident)] } c.setType(lhs, obj.Type()) } } } switch { case len(s.Lhs) == 1 && len(s.Rhs) == 1: lhs := removeParens(s.Lhs[0]) if isBlank(lhs) { v := hasCallVisitor{c.p.info, false} ast.Walk(&v, s.Rhs[0]) if v.hasCall { c.Printf("%s;", c.translateExpr(s.Rhs[0]).String()) } return } lhsType := c.p.info.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.info.Types[s.Rhs[0]].Type.(*types.Tuple) for i, lhs := range s.Lhs { lhs = removeParens(lhs) if !isBlank(lhs) { lhsType := c.p.info.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(removeParens(s.Lhs[i])) { v := hasCallVisitor{c.p.info, false} ast.Walk(&v, rhs) if v.hasCall { c.Printf("%s;", c.translateExpr(rhs).String()) } continue } lhsType := c.p.info.Types[s.Lhs[i]].Type parts = append(parts, c.translateAssignOfExpr(c.newIdent(tmpVars[i], c.p.info.Types[s.Lhs[i]].Type), rhs, lhsType, true)) } for i, lhs := range s.Lhs { lhs = removeParens(lhs) if !isBlank(lhs) { parts = append(parts, c.translateAssign(lhs, tmpVars[i], c.p.info.Types[lhs].Type, s.Tok == token.DEFINE)) } } c.Printf("%s", strings.Join(parts, " ")) default: panic("Invalid arity of AssignStmt.") } case *ast.IncDecStmt: t := c.p.info.Types[s.X].Type if iExpr, isIExpr := s.X.(*ast.IndexExpr); isIExpr { switch u := c.p.info.Types[iExpr.X].Type.Underlying().(type) { case *types.Array: t = u.Elem() case *types.Slice: t = u.Elem() case *types.Map: t = u.Elem() } } tok := token.ADD_ASSIGN if s.Tok == token.DEC { tok = token.SUB_ASSIGN } c.translateStmt(&ast.AssignStmt{ Lhs: []ast.Expr{s.X}, Tok: tok, Rhs: []ast.Expr{c.newInt(1, t)}, }, label) case *ast.DeclStmt: c.printLabel(label) 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.info.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, }, "") } case token.TYPE: for _, spec := range decl.Specs { o := c.p.info.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) c.translateType(o, false) c.initType(o) } case token.CONST: // skip, constants are inlined } case *ast.ExprStmt: c.printLabel(label) expr := c.translateExpr(s.X) if expr != nil { c.Printf("%s;", expr) } case *ast.LabeledStmt: c.printLabel(label) c.translateStmt(s.Stmt, s.Label.Name) case *ast.GoStmt: c.printLabel(label) c.Printf("$go(%s, [%s]);", c.translateExpr(s.Call.Fun), strings.Join(c.translateArgs(c.p.info.Types[s.Call.Fun].Type.Underlying().(*types.Signature), s.Call.Args, s.Call.Ellipsis.IsValid()), ", ")) case *ast.SendStmt: chanType := c.p.info.Types[s.Chan].Type.Underlying().(*types.Chan) call := &ast.CallExpr{ Fun: c.newIdent("$send", types.NewSignature(nil, 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{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]", removeParens(comm.X).(*ast.UnaryExpr).X).String()) case *ast.AssignStmt: channels = append(channels, c.formatExpr("[%e]", 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)) } caseClauses = append(caseClauses, &ast.CaseClause{ List: []ast.Expr{c.newInt(i, types.Typ[types.Int])}, Body: clause.Body, }) flattened = flattened || c.flattened[clause] } selectCall := c.setType(&ast.CallExpr{ Fun: c.newIdent("$select", types.NewSignature(nil, 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.info.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}, "") default: c.translateStmt(&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}, "") } } } 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, args []ast.Expr, ellipsis bool, typ types.Type) *expression { switch name { case "new": t := typ.(*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("%s", c.zeroValue(t.Elem())) default: return c.formatExpr("$newDataPointer(%s, %s)", c.zeroValue(t.Elem()), c.typeName(t)) } case "make": switch argType := c.p.info.Types[args[0]].Type.Underlying().(type) { case *types.Slice: t := c.typeName(c.p.info.Types[args[0]].Type) if len(args) == 3 { return c.formatExpr("%s.make(%f, %f)", t, args[1], args[2]) } return c.formatExpr("%s.make(%f)", t, args[1]) case *types.Map: return c.formatExpr("new $Map()") 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.info.Types[args[0]].Type), length) default: panic(fmt.Sprintf("Unhandled make type: %T\n", argType)) } case "len": switch argType := c.p.info.Types[args[0]].Type.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.info.Types[args[0]].Type.Underlying().(type) { case *types.Slice: return c.formatExpr("%e.$capacity", args[0]) case *types.Pointer: return c.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len()) case *types.Chan: return c.formatExpr("%e.$capacity", args[0]) // 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 len(args) == 1 { return c.translateExpr(args[0]) } if ellipsis { return c.formatExpr("$appendSlice(%e, %s)", args[0], c.translateConversionToSlice(args[1], typ)) } sliceType := typ.Underlying().(*types.Slice) return c.formatExpr("$append(%e, %s)", args[0], strings.Join(c.translateExprSlice(args[1:], sliceType.Elem()), ", ")) case "delete": return c.formatExpr(`delete %e[%s]`, args[0], c.makeKey(args[1], c.p.info.Types[args[0]].Type.Underlying().(*types.Map).Key())) case "copy": if basic, isBasic := c.p.info.Types[args[1]].Type.Underlying().(*types.Basic); isBasic && basic.Info()&types.IsString != 0 { 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": return c.formatExpr("new %s(%e, %e)", c.typeName(typ), args[0], args[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.info.Types[expr].Type if value := c.p.info.Types[expr].Value; value != nil { basic := types.Typ[types.String] if value.Kind() != exact.String { // workaround for bug in go/types basic = exprType.Underlying().(*types.Basic) } switch { case basic.Info()&types.IsBoolean != 0: return c.formatExpr("%s", strconv.FormatBool(exact.BoolVal(value))) case basic.Info()&types.IsInteger != 0: if is64Bit(basic) { d, _ := exact.Uint64Val(value) if basic.Kind() == types.Int64 { return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatInt(int64(d)>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } d, _ := exact.Int64Val(value) return c.formatExpr("%s", strconv.FormatInt(d, 10)) case basic.Info()&types.IsFloat != 0: f, _ := exact.Float64Val(value) return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case basic.Info()&types.IsComplex != 0: 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 basic.Info()&types.IsString != 0: return c.formatExpr("%s", encodeString(exact.StringVal(value))) default: panic("Unhandled constant type: " + basic.String()) } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { elements := make([]string, 0) i := 0 zero := c.zeroValue(elementType) for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, _ := exact.Int64Val(c.p.info.Types[kve.Key].Value) 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", c.zeroValue(t)) } zero := c.zeroValue(t.Elem()) 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: mapVar := c.newVariable("_map") keyVar := c.newVariable("_key") assignments := "" for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) assignments += c.formatExpr(`%s = %s, %s[%s] = { k: %s, v: %s }, `, keyVar, c.translateImplicitConversion(kve.Key, t.Key()), mapVar, c.makeKey(c.newIdent(keyVar, t.Key()), t.Key()), keyVar, c.translateImplicitConversion(kve.Value, t.Elem())).String() } return c.formatExpr("(%s = new $Map(), %s%s)", mapVar, assignments, mapVar) 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.translateImplicitConversion(element, t.Field(i).Type()).String() } } if isKeyValue { for i := range elements { elements[i] = c.zeroValue(t.Field(i).Type()) } 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.translateImplicitConversion(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: innerContext := c.p.analyzeFunction(exprType.(*types.Signature), e.Body) params, body := innerContext.translateFunction(e.Type, e.Body.List, c.allVars) 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.objectVars[obj]) } list := strings.Join(names, ", ") return c.formatExpr("(function(%s) { return function(%s) {\n%s%s}; })(%s)", list, strings.Join(params, ", "), string(body), strings.Repeat("\t", c.p.indentation), list) } return c.formatExpr("(function(%s) {\n%s%s})", strings.Join(params, ", "), string(body), strings.Repeat("\t", c.p.indentation)) case *ast.UnaryExpr: t := c.p.info.Types[e.X].Type switch e.Op { case token.AND: switch t.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } switch x := removeParens(e.X).(type) { case *ast.CompositeLit: return c.formatExpr("$newDataPointer(%e, %s)", x, c.typeName(c.p.info.Types[e].Type)) case *ast.Ident: if obj := c.p.info.Uses[x]; c.p.escapingVars[obj] { return c.formatExpr("new %s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %s)", c.typeName(exprType), c.p.objectVars[obj]) } return c.formatExpr("new %s(function() { return %e; }, function($v) { %s })", c.typeName(exprType), x, c.translateAssign(x, "$v", exprType, false)) case *ast.SelectorExpr: newSel := &ast.SelectorExpr{X: c.newIdent("this.$target", c.p.info.Types[x.X].Type), Sel: x.Sel} c.p.info.Selections[newSel] = c.p.info.Selections[x] return c.formatExpr("new %s(function() { return %e; }, function($v) { %s }, %e)", c.typeName(exprType), newSel, c.translateAssign(newSel, "$v", exprType, false), x.X) case *ast.IndexExpr: newIndex := &ast.IndexExpr{X: c.newIdent("this.$target", c.p.info.Types[x.X].Type), Index: x.Index} return c.formatExpr("new %s(function() { return %e; }, function($v) { %s }, %e)", c.typeName(exprType), newIndex, c.translateAssign(newIndex, "$v", exprType, false), x.X) default: panic(fmt.Sprintf("Unhandled: %T\n", x)) } case token.ARROW: call := &ast.CallExpr{ Fun: c.newIdent("$recv", types.NewSignature(nil, 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 basic.Info()&types.IsComplex != 0: return c.formatExpr("new %1s(-%2r, -%2i)", c.typeName(t), e.X) case basic.Info()&types.IsUnsigned != 0: 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: x := c.translateExpr(e.X) if x.String() == "true" { return c.formatExpr("false") } if x.String() == "false" { return c.formatExpr("true") } return c.formatExpr("!%s", 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.info.Types[e.X].Type t2 := c.p.info.Types[e.Y].Type _, isInterface := t2.Underlying().(*types.Interface) if isInterface { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && basic.Info()&types.IsNumeric != 0 { 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 basic.Info()&types.IsComplex != 0 { switch e.Op { case token.EQL: if basic.Kind() == types.Complex64 { return c.formatExpr("($float32IsEqual(%1r, %2r) && $float32IsEqual(%1i, %2i))", e.X, e.Y) } 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: if basic.Kind() == types.Float32 { return c.formatParenExpr("$float32IsEqual(%e, %e)", e.X, e.Y) } 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: if basic.Info()&types.IsInteger != 0 { return c.fixNumber(c.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) } return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.MUL: switch basic.Kind() { case types.Int32, types.Int: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >> 0) + (%1e << 16 >>> 16) * %2e) >> 0", e.X, e.Y) case types.Uint32, types.Uint, types.Uintptr: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >>> 0) + (%1e << 16 >>> 16) * %2e) >>> 0", e.X, e.Y) case types.Float32, types.Float64: return c.formatExpr("%e * %e", e.X, e.Y) default: return c.fixNumber(c.formatExpr("%e * %e", e.X, e.Y), basic) } case token.QUO: if basic.Info()&types.IsInteger != 0 { // cut off decimals shift := ">>" if basic.Info()&types.IsUnsigned != 0 { 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) } 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 && basic.Info()&types.IsUnsigned != 0 { op = ">>>" } if c.p.info.Types[e.Y].Value != nil { return c.fixNumber(c.formatExpr("%e %s %e", e.X, op, e.Y), basic) } if e.Op == token.SHR && basic.Info()&types.IsUnsigned == 0 { 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 basic.Info()&types.IsUnsigned != 0 { 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.formatParenExpr("%e & ~%e", e.X, e.Y) 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: x := c.translateExpr(e.X) y := c.translateExpr(e.Y) if x.String() == "false" { return c.formatExpr("false") } return c.formatExpr("%s && %s", x, y) case token.LOR: x := c.translateExpr(e.X) y := c.translateExpr(e.Y) if x.String() == "true" { return c.formatExpr("true") } return c.formatExpr("%s || %s", x, 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: if isJsObject(t) { return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) } return c.formatExpr("$interfaceIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Pointer: xUnary, xIsUnary := e.X.(*ast.UnaryExpr) yUnary, yIsUnary := e.Y.(*ast.UnaryExpr) if xIsUnary && xUnary.Op == token.AND && yIsUnary && yUnary.Op == token.AND { xIndex, xIsIndex := xUnary.X.(*ast.IndexExpr) yIndex, yIsIndex := yUnary.X.(*ast.IndexExpr) if xIsIndex && yIsIndex { return c.formatExpr("$sliceIsEqual(%e, %f, %e, %f)", xIndex.X, xIndex.Index, yIndex.X, yIndex.Index) } } switch u.Elem().Underlying().(type) { case *types.Struct, *types.Interface: return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Array: return c.formatExpr("$equal(%s, %s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t), c.typeName(u.Elem())) default: return c.formatExpr("$pointerIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) } default: return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) } default: panic(e.Op) } case *ast.ParenExpr: x := c.translateExpr(e.X) if x.String() == "true" || x.String() == "false" { return x } return c.formatParenExpr("%s", x) case *ast.IndexExpr: switch t := c.p.info.Types[e.X].Type.Underlying().(type) { case *types.Array, *types.Pointer: if c.p.info.Types[e.Index].Value != nil { return c.formatExpr("%e[%f]", e.X, e.Index) } return c.formatExpr(`((%2f < 0 || %2f >= %1e.length) ? $throwRuntimeError("index out of range") : %1e[%2f])`, e.X, e.Index) case *types.Slice: return c.formatExpr(`((%2f < 0 || %2f >= %1e.$length) ? $throwRuntimeError("index out of range") : %1e.$array[%1e.$offset + %2f])`, e.X, e.Index) case *types.Map: key := c.makeKey(e.Index, t.Key()) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? [%1s.v, true] : [%4s, false])`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) } return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? %1s.v : %4s)`, 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.info.Types[e.X].Type.Underlying().(*types.Basic); isBasic && b.Info()&types.IsString != 0 { 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.info.Selections[e] if !ok { // qualified identifier obj := c.p.info.Uses[e.Sel] if isJsPackage(obj.Pkg()) { switch obj.Name() { case "Global": return c.formatExpr("$global") case "This": if len(c.flattened) != 0 { return c.formatExpr("$this") } return c.formatExpr("this") case "Arguments": args := "arguments" if len(c.flattened) != 0 { args = "$args" } return c.formatExpr(`new ($sliceType(%s.Object))($global.Array.prototype.slice.call(%s, []))`, c.p.pkgVars["github.com/gopherjs/gopherjs/js"], args) case "Module": return c.formatExpr("$module") default: panic("Invalid js package object: " + obj.Name()) } } return c.formatExpr("%s", c.objectName(obj)) } parameterName := func(v *types.Var) string { if v.Anonymous() || v.Name() == "" { return c.newVariable("param") } return c.newVariable(v.Name()) } makeParametersList := func() []string { params := sel.Obj().Type().(*types.Signature).Params() names := make([]string, params.Len()) for i := 0; i < params.Len(); i++ { names[i] = parameterName(params.At(i)) } return names } switch sel.Kind() { case types.FieldVal: fields, jsTag := c.translateSelection(sel) 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: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } parameters := makeParametersList() target := c.translateExpr(e.X) if isWrapped(sel.Recv()) { target = c.formatParenExpr("new %s(%s)", c.typeName(sel.Recv()), target) } recv := c.newVariable("_recv") return c.formatExpr("(%s = %s, function(%s) { $stackDepthOffset--; try { return %s.%s(%s); } finally { $stackDepthOffset++; } })", recv, target, strings.Join(parameters, ", "), recv, e.Sel.Name, strings.Join(parameters, ", ")) case types.MethodExpr: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } recv := "recv" if isWrapped(sel.Recv()) { recv = fmt.Sprintf("(new %s(recv))", c.typeName(sel.Recv())) } parameters := makeParametersList() return c.formatExpr("(function(%s) { $stackDepthOffset--; try { return %s.%s(%s); } finally { $stackDepthOffset++; } })", strings.Join(append([]string{"recv"}, parameters...), ", "), recv, sel.Obj().(*types.Func).Name(), strings.Join(parameters, ", ")) } panic("") case *ast.CallExpr: plainFun := e.Fun for { if p, isParen := plainFun.(*ast.ParenExpr); isParen { plainFun = p.X continue } break } var isType func(ast.Expr) bool isType = func(expr ast.Expr) bool { switch e := expr.(type) { case *ast.ArrayType, *ast.ChanType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.StructType: return true case *ast.StarExpr: return isType(e.X) case *ast.Ident: _, ok := c.p.info.Uses[e].(*types.TypeName) return ok case *ast.SelectorExpr: _, ok := c.p.info.Uses[e.Sel].(*types.TypeName) return ok case *ast.ParenExpr: return isType(e.X) default: return false } } if isType(plainFun) { return c.formatExpr("%s", c.translateConversion(e.Args[0], c.p.info.Types[plainFun].Type)) } var fun *expression switch f := plainFun.(type) { case *ast.Ident: if o, ok := c.p.info.Uses[f].(*types.Builtin); ok { return c.translateBuiltin(o.Name(), e.Args, e.Ellipsis.IsValid(), exprType) } fun = c.translateExpr(plainFun) case *ast.SelectorExpr: sel, ok := c.p.info.Selections[f] if !ok { // qualified identifier obj := c.p.info.Uses[f.Sel] if isJsPackage(obj.Pkg()) { switch obj.Name() { case "InternalObject": return c.translateExpr(e.Args[0]) } } fun = c.translateExpr(f) break } externalizeExpr := func(e ast.Expr) string { t := c.p.info.Types[e].Type 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: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } methodName := sel.Obj().Name() if reservedKeywords[methodName] { methodName += "$" } recvType := sel.Recv() _, isPointer := recvType.Underlying().(*types.Pointer) methodsRecvType := sel.Obj().Type().(*types.Signature).Recv().Type() _, pointerExpected := methodsRecvType.(*types.Pointer) var recv *expression switch { case !isPointer && pointerExpected: recv = c.translateExpr(c.setType(&ast.UnaryExpr{Op: token.AND, X: f.X}, methodsRecvType)) default: recv = c.translateExpr(f.X) } for _, index := range sel.Index()[:len(sel.Index())-1] { if ptr, isPtr := recvType.(*types.Pointer); isPtr { recvType = ptr.Elem() } s := recvType.Underlying().(*types.Struct) recv = c.formatExpr("%s.%s", recv, fieldName(s, index)) recvType = s.Field(index).Type() } if 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 "Str": 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 case "IsUndefined": return c.formatParenExpr("%s === undefined", recv) case "IsNull": return c.formatParenExpr("%s === null", recv) default: panic("Invalid js package object: " + sel.Obj().Name()) } } if isWrapped(methodsRecvType) { fun = c.formatExpr("(new %s(%s)).%s", c.typeName(methodsRecvType), recv, methodName) break } fun = c.formatExpr("%s.%s", recv, methodName) case types.FieldVal: fields, jsTag := c.translateSelection(sel) if jsTag != "" { sig := sel.Type().(*types.Signature) return c.internalize(c.formatExpr("%e.%s.%s(%s)", f.X, strings.Join(fields, "."), jsTag, externalizeArgs(e.Args)), sig.Results().At(0).Type()) } fun = c.formatExpr("%e.%s", f.X, strings.Join(fields, ".")) case types.MethodExpr: fun = c.translateExpr(f) default: panic("") } default: fun = c.translateExpr(plainFun) } sig := c.p.info.Types[plainFun].Type.Underlying().(*types.Signature) if len(e.Args) == 1 { if tuple, isTuple := c.p.info.Types[e.Args[0]].Type.(*types.Tuple); isTuple { tupleVar := c.newVariable("_tuple") args := make([]ast.Expr, tuple.Len()) for i := range args { args[i] = c.newIdent(c.formatExpr("%s[%d]", tupleVar, i).String(), tuple.At(i).Type()) } return c.formatExpr("(%s = %e, %s(%s))", tupleVar, e.Args[0], fun, strings.Join(c.translateArgs(sig, args, false), ", ")) } } args := c.translateArgs(sig, e.Args, e.Ellipsis.IsValid()) if c.blocking[e] { resumeCase := c.caseCounter c.caseCounter++ returnVar := "$r" if sig.Results().Len() != 0 { returnVar = c.newVariable("_r") } c.Printf("%[1]s = %[2]s(%[3]s); /* */ $s = %[4]d; case %[4]d: if (%[1]s && %[1]s.constructor === Function) { %[1]s = %[1]s(); }", returnVar, fun, strings.Join(append(args, "true"), ", "), resumeCase) if sig.Results().Len() != 0 { return c.formatExpr("%s", returnVar) } return nil } return c.formatExpr("%s(%s)", fun, strings.Join(args, ", ")) case *ast.StarExpr: 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.info.Types[c2.Fun].Type, 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.info.Types[e.Type].Type check := "%1e !== null && " + c.typeCheck("%1e.constructor", t) valueSuffix := "" if _, isInterface := t.Underlying().(*types.Interface); !isInterface { valueSuffix = ".$val" } if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("("+check+" ? [%1e%2s, true] : [%3s, false])", e.X, valueSuffix, c.zeroValue(c.p.info.Types[e.Type].Type)) } return c.formatExpr("("+check+" ? %1e%2s : $typeAssertionFailed(%1e, %3s))", e.X, valueSuffix, c.typeName(t)) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } obj := c.p.info.Defs[e] if obj == nil { obj = c.p.info.Uses[e] } switch o := obj.(type) { case *types.PkgName: return c.formatExpr("%s", c.p.pkgVars[o.Pkg().Path()]) 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: return c.formatExpr("%s", c.zeroValue(c.p.info.Types[e].Type)) default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case *This: this := "this" if len(c.flattened) != 0 { this = "$this" } if isWrapped(c.p.info.Types[e].Type) { this += ".$val" } return c.formatExpr(this) case nil: return c.formatExpr("") default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
// 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" "code.google.com/p/go.tools/go/callgraph" "code.google.com/p/go.tools/go/ssa" "code.google.com/p/go.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 (c *PkgContext) translateExpr(expr ast.Expr) string { exprType := c.info.Types[expr] if value, valueFound := c.info.Values[expr]; valueFound { basic := types.Typ[types.String] if value.Kind() != exact.String { // workaround for bug in go/types basic = exprType.Underlying().(*types.Basic) } switch { case basic.Info()&types.IsBoolean != 0: return strconv.FormatBool(exact.BoolVal(value)) case basic.Info()&types.IsInteger != 0: if is64Bit(basic) { d, _ := exact.Uint64Val(value) return fmt.Sprintf("new %s(%d, %d)", c.typeName(exprType), d>>32, d&(1<<32-1)) } d, _ := exact.Int64Val(value) return strconv.FormatInt(d, 10) case basic.Info()&types.IsFloat != 0: f, _ := exact.Float64Val(value) return strconv.FormatFloat(f, 'g', -1, 64) case basic.Info()&types.IsComplex != 0: r, _ := exact.Float64Val(exact.Real(value)) i, _ := exact.Float64Val(exact.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } return fmt.Sprintf("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case basic.Info()&types.IsString != 0: buffer := bytes.NewBuffer(nil) for _, r := range []byte(exact.StringVal(value)) { switch r { case '\b': buffer.WriteString(`\b`) case '\f': buffer.WriteString(`\f`) case '\n': buffer.WriteString(`\n`) case '\r': buffer.WriteString(`\r`) case '\t': buffer.WriteString(`\t`) case '\v': buffer.WriteString(`\v`) case '"': buffer.WriteString(`\"`) case '\\': buffer.WriteString(`\\`) default: if r < 0x20 || r > 0x7E { fmt.Fprintf(buffer, `\x%02X`, r) continue } buffer.WriteByte(r) } } return `"` + buffer.String() + `"` default: panic("Unhandled constant type: " + basic.String()) } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { elements := make([]string, 0) i := 0 zero := c.zeroValue(elementType) for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, _ := exact.Int64Val(c.info.Values[kve.Key]) i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateExprToType(element, elementType) i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) != 0 { zero := c.zeroValue(t.Elem()) for len(elements) < int(t.Len()) { elements = append(elements, zero) } return createListComposite(t.Elem(), elements) } return fmt.Sprintf("Go$makeArray(%s, %d, function() { return %s; })", toArrayType(t.Elem()), t.Len(), c.zeroValue(t.Elem())) case *types.Slice: return fmt.Sprintf("new %s(%s)", c.typeName(exprType), createListComposite(t.Elem(), collectIndexedElements(t.Elem()))) case *types.Map: elements := make([]string, len(e.Elts)*2) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) elements[i*2] = c.translateExprToType(kve.Key, t.Key()) elements[i*2+1] = c.translateExprToType(kve.Value, t.Elem()) } return fmt.Sprintf("new %s([%s])", c.typeName(exprType), strings.Join(elements, ", ")) 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.translateExprToType(element, t.Field(i).Type()) } } if isKeyValue { for i := range elements { elements[i] = c.zeroValue(t.Field(i).Type()) } 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.translateExprToType(kve.Value, t.Field(j).Type()) break } } } } if named, isNamed := exprType.(*types.Named); isNamed { return fmt.Sprintf("new %s(%s)", c.objectName(named.Obj()), strings.Join(elements, ", ")) } structVar := c.newVariable("_struct") c.translateTypeSpec(&ast.TypeSpec{ Name: c.newIdent(structVar, t), Type: e.Type, }) return fmt.Sprintf("new %s(%s)", structVar, strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: return strings.TrimSpace(string(c.CatchOutput(func() { c.newScope(func() { params := c.translateParams(e.Type) closurePrefix := "(" closureSuffix := ")" if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") closurePrefix = "(function(" + list + ") { return " closureSuffix = "; })(" + list + ")" } c.Printf("%sfunction(%s) {", closurePrefix, strings.Join(params, ", ")) c.Indent(func() { c.translateFunctionBody(e.Body.List, exprType.(*types.Signature)) }) c.Printf("}%s", closureSuffix) }) }))) case *ast.UnaryExpr: switch e.Op { case token.AND: switch c.info.Types[e.X].Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) default: if _, isComposite := e.X.(*ast.CompositeLit); isComposite { return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.translateExpr(e.X), c.typeName(c.info.Types[e])) } closurePrefix := "" closureSuffix := "" if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") closurePrefix = "(function(" + list + ") { return " closureSuffix = "; })(" + list + ")" } vVar := c.newVariable("v") return fmt.Sprintf("%snew %s(function() { return %s; }, function(%s) { %s; })%s", closurePrefix, c.typeName(exprType), c.translateExpr(e.X), vVar, c.translateAssign(e.X, vVar), closureSuffix) } case token.ARROW: return "undefined" } t := c.info.Types[e.X] basic := t.Underlying().(*types.Basic) op := e.Op.String() switch e.Op { case token.ADD: return c.translateExpr(e.X) case token.SUB: if is64Bit(basic) { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(-%s.high, -%s.low))", x, c.translateExpr(e.X), c.typeName(t), x, x) } if basic.Info()&types.IsComplex != 0 { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(-%s.real, -%s.imag))", x, c.translateExpr(e.X), c.typeName(t), x, x) } case token.XOR: if is64Bit(basic) { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(~%s.high, ~%s.low >>> 0))", x, c.translateExpr(e.X), c.typeName(t), x, x) } op = "~" } return fixNumber(fmt.Sprintf("%s%s", op, c.translateExpr(e.X)), basic) case *ast.BinaryExpr: if e.Op == token.NEQ { return fmt.Sprintf("!(%s)", c.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } t := c.info.Types[e.X] t2 := c.info.Types[e.Y] _, isInterface := t2.Underlying().(*types.Interface) if isInterface { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && basic.Info()&types.IsNumeric != 0 { if is64Bit(basic) { var expr string switch e.Op { case token.MUL: return fmt.Sprintf("Go$mul64(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.QUO: return fmt.Sprintf("Go$div64(%s, %s, false)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.REM: return fmt.Sprintf("Go$div64(%s, %s, true)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.SHL: return fmt.Sprintf("Go$shiftLeft64(%s, %s)", c.translateExpr(e.X), c.flatten64(e.Y)) case token.SHR: return fmt.Sprintf("Go$shiftRight%s(%s, %s)", toJavaScriptType(basic), c.translateExpr(e.X), c.flatten64(e.Y)) case token.EQL: expr = "x.high === y.high && x.low === y.low" case token.LSS: expr = "x.high < y.high || (x.high === y.high && x.low < y.low)" case token.LEQ: expr = "x.high < y.high || (x.high === y.high && x.low <= y.low)" case token.GTR: expr = "x.high > y.high || (x.high === y.high && x.low > y.low)" case token.GEQ: expr = "x.high > y.high || (x.high === y.high && x.low >= y.low)" case token.ADD, token.SUB: expr = fmt.Sprintf("new %s(x.high %s y.high, x.low %s y.low)", c.typeName(t), e.Op, e.Op) case token.AND, token.OR, token.XOR: expr = fmt.Sprintf("new %s(x.high %s y.high, (x.low %s y.low) >>> 0)", c.typeName(t), e.Op, e.Op) case token.AND_NOT: expr = fmt.Sprintf("new %s(x.high &~ y.high, (x.low &~ y.low) >>> 0)", c.typeName(t)) default: panic(e.Op) } x := c.newVariable("x") y := c.newVariable("y") expr = strings.Replace(expr, "x.", x+".", -1) expr = strings.Replace(expr, "y.", y+".", -1) return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr) } if basic.Info()&types.IsComplex != 0 { var expr string switch e.Op { case token.EQL: expr = "x.real === y.real && x.imag === y.imag" case token.ADD, token.SUB: expr = fmt.Sprintf("new %s(x.real %s y.real, x.imag %s y.imag)", c.typeName(t), e.Op, e.Op) case token.MUL: expr = fmt.Sprintf("new %s(x.real * y.real - x.imag * y.imag, x.real * y.imag + x.imag * y.real)", c.typeName(t)) case token.QUO: return fmt.Sprintf("Go$divComplex(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) default: panic(e.Op) } x := c.newVariable("x") y := c.newVariable("y") expr = strings.Replace(expr, "x.", x+".", -1) expr = strings.Replace(expr, "y.", y+".", -1) return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr) } switch e.Op { case token.EQL: return fmt.Sprintf("%s === %s", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.LSS, token.LEQ, token.GTR, token.GEQ: return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)) case token.ADD, token.SUB: return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic) case token.MUL: if basic.Kind() == types.Int32 { x := c.newVariable("x") y := c.newVariable("y") return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >> 0) + (%s << 16 >>> 16) * %s) >> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y) } if basic.Kind() == types.Uint32 || basic.Kind() == types.Uintptr { x := c.newVariable("x") y := c.newVariable("y") return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >>> 0) + (%s << 16 >>> 16) * %s) >>> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y) } return fixNumber(fmt.Sprintf("%s * %s", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) case token.QUO: value := fmt.Sprintf("%s / %s", c.translateExpr(e.X), c.translateExpr(e.Y)) if basic.Info()&types.IsInteger != 0 { value = "(Go$obj = " + value + `, (Go$obj === Go$obj && Go$obj !== 1/0 && Go$obj !== -1/0) ? Go$obj : Go$throwRuntimeError("integer divide by zero"))` } switch basic.Kind() { case types.Int, types.Uint: return "(" + value + " >> 0)" // cut off decimals default: return fixNumber(value, basic) } case token.REM: return fmt.Sprintf(`(Go$obj = %s %% %s, Go$obj === Go$obj ? Go$obj : Go$throwRuntimeError("integer divide by zero"))`, c.translateExpr(e.X), c.translateExpr(e.Y)) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && basic.Info()&types.IsUnsigned != 0 { op = ">>>" } if c.info.Values[e.Y] != nil { return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), op, c.translateExpr(e.Y)), basic) } if e.Op == token.SHR && basic.Info()&types.IsUnsigned == 0 { return fixNumber(fmt.Sprintf("(%s >> Go$min(%s, 31))", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) } y := c.newVariable("y") return fixNumber(fmt.Sprintf("(%s = %s, %s < 32 ? (%s %s %s) : 0)", y, c.translateExprToType(e.Y, types.Typ[types.Uint]), y, c.translateExpr(e.X), op, y), basic) case token.AND, token.OR, token.XOR: return fixNumber(fmt.Sprintf("(%s %s %s)", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic) case token.AND_NOT: return fixNumber(fmt.Sprintf("(%s &~ %s)", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) default: panic(e.Op) } } switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ, token.LAND, token.LOR: return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)) case token.EQL: switch u := t.Underlying().(type) { case *types.Struct: x := c.newVariable("x") y := c.newVariable("y") var conds []string for i := 0; i < u.NumFields(); i++ { field := u.Field(i) if field.Name() == "_" { continue } conds = append(conds, c.translateExpr(&ast.BinaryExpr{ X: c.newIdent(x+"."+field.Name(), field.Type()), Op: token.EQL, Y: c.newIdent(y+"."+field.Name(), field.Type()), })) } if len(conds) == 0 { conds = []string{"true"} } return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), strings.Join(conds, " && ")) case *types.Interface: return fmt.Sprintf("Go$interfaceIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) case *types.Array: return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) case *types.Pointer: xUnary, xIsUnary := e.X.(*ast.UnaryExpr) yUnary, yIsUnary := e.Y.(*ast.UnaryExpr) if xIsUnary && xUnary.Op == token.AND && yIsUnary && yUnary.Op == token.AND { xIndex, xIsIndex := xUnary.X.(*ast.IndexExpr) yIndex, yIsIndex := yUnary.X.(*ast.IndexExpr) if xIsIndex && yIsIndex { return fmt.Sprintf("Go$sliceIsEqual(%s, %s, %s, %s)", c.translateExpr(xIndex.X), c.flatten64(xIndex.Index), c.translateExpr(yIndex.X), c.flatten64(yIndex.Index)) } } switch u.Elem().Underlying().(type) { case *types.Struct, *types.Interface: return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t) case *types.Array: return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) default: return fmt.Sprintf("Go$pointerIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) } default: return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t) } default: panic(e.Op) } case *ast.ParenExpr: return fmt.Sprintf("(%s)", c.translateExpr(e.X)) case *ast.IndexExpr: xType := c.info.Types[e.X] if ptr, isPointer := xType.(*types.Pointer); isPointer { xType = ptr.Elem() } switch t := xType.Underlying().(type) { case *types.Array: return fmt.Sprintf("%s[%s]", c.translateExpr(e.X), c.flatten64(e.Index)) case *types.Slice: sliceVar := c.newVariable("_slice") indexVar := c.newVariable("_index") return fmt.Sprintf(`(%s = %s, %s = %s, (%s >= 0 && %s < %s.length) ? %s.array[%s.offset + %s] : Go$throwRuntimeError("index out of range"))`, sliceVar, c.translateExpr(e.X), indexVar, c.flatten64(e.Index), indexVar, indexVar, sliceVar, sliceVar, sliceVar, indexVar) case *types.Map: key := c.makeKey(e.Index, t.Key()) if _, isTuple := exprType.(*types.Tuple); isTuple { return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? [Go$obj.v, true] : [%s, false])`, c.translateExpr(e.X), key, c.zeroValue(t.Elem())) } return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? Go$obj.v : %s)`, c.translateExpr(e.X), key, c.zeroValue(t.Elem())) case *types.Basic: return fmt.Sprintf("%s.charCodeAt(%s)", c.translateExpr(e.X), c.flatten64(e.Index)) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: b, isBasic := c.info.Types[e.X].(*types.Basic) isString := isBasic && b.Info()&types.IsString != 0 slice := c.translateExprToType(e.X, exprType) if e.High == nil { if e.Low == nil { return slice } if isString { return fmt.Sprintf("%s.substring(%s)", slice, c.flatten64(e.Low)) } return fmt.Sprintf("Go$subslice(%s, %s)", slice, c.flatten64(e.Low)) } low := "0" if e.Low != nil { low = c.flatten64(e.Low) } if isString { return fmt.Sprintf("%s.substring(%s, %s)", slice, low, c.flatten64(e.High)) } if e.Max != nil { return fmt.Sprintf("Go$subslice(%s, %s, %s, %s)", slice, low, c.flatten64(e.High), c.flatten64(e.Max)) } return fmt.Sprintf("Go$subslice(%s, %s, %s)", slice, low, c.flatten64(e.High)) case *ast.SelectorExpr: sel := c.info.Selections[e] parameterName := func(v *types.Var) string { if v.Anonymous() || v.Name() == "" { return c.newVariable("param") } return c.newVariable(v.Name()) } makeParametersList := func() []string { params := sel.Obj().Type().(*types.Signature).Params() names := make([]string, params.Len()) for i := 0; i < params.Len(); i++ { names[i] = parameterName(params.At(i)) } return names } switch sel.Kind() { case types.FieldVal: return c.translateExpr(e.X) + "." + translateSelection(sel) case types.MethodVal: parameters := makeParametersList() recv := c.newVariable("_recv") return fmt.Sprintf("(%s = %s, function(%s) { return %s.%s(%s); })", recv, c.translateExpr(e.X), strings.Join(parameters, ", "), recv, e.Sel.Name, strings.Join(parameters, ", ")) case types.MethodExpr: recv := "recv" if isWrapped(sel.Recv()) { recv = fmt.Sprintf("(new %s(recv))", c.typeName(sel.Recv())) } parameters := makeParametersList() return fmt.Sprintf("(function(%s) { return %s.%s(%s); })", strings.Join(append([]string{"recv"}, parameters...), ", "), recv, sel.Obj().(*types.Func).Name(), strings.Join(parameters, ", ")) case types.PackageObj: return fmt.Sprintf("%s.%s", c.translateExpr(e.X), e.Sel.Name) } panic("") case *ast.CallExpr: plainFun := e.Fun for { if p, isParen := plainFun.(*ast.ParenExpr); isParen { plainFun = p.X continue } break } switch f := plainFun.(type) { case *ast.Ident: switch o := c.info.Objects[f].(type) { case *types.Builtin: switch o.Name() { case "new": t := c.info.Types[e].(*types.Pointer) if types.IsIdentical(t.Elem().Underlying(), types.Typ[types.Uintptr]) { return "new Uint8Array(8)" } switch t.Elem().Underlying().(type) { case *types.Struct, *types.Array: return c.zeroValue(t.Elem()) default: return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.zeroValue(t.Elem()), c.typeName(t)) } case "make": switch t2 := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Slice: if len(e.Args) == 3 { return fmt.Sprintf("Go$subslice(new %s(Go$makeArray(%s, %s, function() { return %s; })), 0, %s)", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[2], types.Typ[types.Int]), c.zeroValue(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int])) } return fmt.Sprintf("new %s(Go$makeArray(%s, %s, function() { return %s; }))", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int]), c.zeroValue(t2.Elem())) default: args := []string{"undefined"} for _, arg := range e.Args[1:] { args = append(args, c.translateExpr(arg)) } return fmt.Sprintf("new %s(%s)", c.typeName(c.info.Types[e.Args[0]]), strings.Join(args, ", ")) } case "len": arg := c.translateExpr(e.Args[0]) switch argType := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Basic, *types.Slice: return arg + ".length" case *types.Pointer: return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Map: return fmt.Sprintf("(Go$obj = %s, Go$obj !== null ? Go$keys(Go$obj).length : 0)", arg) case *types.Chan: return "0" // length of array is constant default: panic(fmt.Sprintf("Unhandled len type: %T\n", argType)) } case "cap": arg := c.translateExpr(e.Args[0]) switch argType := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Slice: return arg + ".capacity" case *types.Pointer: return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Chan: return "0" // capacity of array is constant default: panic(fmt.Sprintf("Unhandled cap type: %T\n", argType)) } case "panic": return fmt.Sprintf("throw new Go$Panic(%s)", c.translateExprToType(e.Args[0], types.NewInterface(nil, nil))) case "append": if e.Ellipsis.IsValid() { return fmt.Sprintf("Go$append(%s, %s)", c.translateExpr(e.Args[0]), c.translateExprToType(e.Args[1], exprType)) } sliceType := exprType.Underlying().(*types.Slice) toAppend := createListComposite(sliceType.Elem(), c.translateExprSlice(e.Args[1:], sliceType.Elem())) return fmt.Sprintf("Go$append(%s, new %s(%s))", c.translateExpr(e.Args[0]), c.typeName(exprType), toAppend) case "delete": return fmt.Sprintf(`delete (%s || Go$Map.Go$nil)[%s]`, c.translateExpr(e.Args[0]), c.makeKey(e.Args[1], c.info.Types[e.Args[0]].Underlying().(*types.Map).Key())) case "copy": return fmt.Sprintf("Go$copy(%s, %s)", c.translateExprToType(e.Args[0], types.NewSlice(types.Typ[types.Byte])), c.translateExprToType(e.Args[1], types.NewSlice(types.Typ[types.Byte]))) case "print", "println": return fmt.Sprintf("console.log(%s)", strings.Join(c.translateExprSlice(e.Args, nil), ", ")) case "complex": return fmt.Sprintf("new %s(%s, %s)", c.typeName(c.info.Types[e]), c.translateExpr(e.Args[0]), c.translateExpr(e.Args[1])) case "real": return c.translateExpr(e.Args[0]) + ".real" case "imag": return c.translateExpr(e.Args[0]) + ".imag" case "recover": return "Go$recover()" case "close": return `Go$throwRuntimeError("not supported by GopherJS: close")` default: panic(fmt.Sprintf("Unhandled builtin: %s\n", o.Name())) } case *types.TypeName: // conversion if basic, isBasic := o.Type().Underlying().(*types.Basic); isBasic && !types.IsIdentical(c.info.Types[e.Args[0]], types.Typ[types.UnsafePointer]) { return fixNumber(c.translateExprToType(e.Args[0], o.Type()), basic) } return c.translateExprToType(e.Args[0], o.Type()) } case *ast.FuncType: // conversion return c.translateExprToType(e.Args[0], c.info.Types[plainFun]) } funType := c.info.Types[plainFun] sig, isSig := funType.Underlying().(*types.Signature) if !isSig { // conversion if c.pkg.Path() == "reflect" { if call, isCall := e.Args[0].(*ast.CallExpr); isCall && types.IsIdentical(c.info.Types[call.Fun], types.Typ[types.UnsafePointer]) { if named, isNamed := funType.(*types.Pointer).Elem().(*types.Named); isNamed { return c.translateExpr(call.Args[0]) + "." + named.Obj().Name() // unsafe conversion } } } return c.translateExprToType(e.Args[0], funType) } var fun string switch f := plainFun.(type) { case *ast.SelectorExpr: sel := c.info.Selections[f] switch sel.Kind() { case types.MethodVal: methodsRecvType := sel.Obj().(*types.Func).Type().(*types.Signature).Recv().Type() _, pointerExpected := methodsRecvType.(*types.Pointer) _, isPointer := sel.Recv().Underlying().(*types.Pointer) _, isStruct := sel.Recv().Underlying().(*types.Struct) _, isArray := sel.Recv().Underlying().(*types.Array) if pointerExpected && !isPointer && !isStruct && !isArray { target := c.translateExpr(f.X) vVar := c.newVariable("v") fun = fmt.Sprintf("(new %s(function() { return %s; }, function(%s) { %s = %s; })).%s", c.typeName(methodsRecvType), target, vVar, target, vVar, f.Sel.Name) break } if isWrapped(sel.Recv()) { fun = fmt.Sprintf("(new %s(%s)).%s", c.typeName(sel.Recv()), c.translateExpr(f.X), f.Sel.Name) break } fun = fmt.Sprintf("%s.%s", c.translateExpr(f.X), f.Sel.Name) case types.FieldVal, types.MethodExpr, types.PackageObj: fun = c.translateExpr(f) default: panic("") } default: fun = c.translateExpr(plainFun) } if len(e.Args) == 1 { if tuple, isTuple := c.info.Types[e.Args[0]].(*types.Tuple); isTuple { args := make([]ast.Expr, tuple.Len()) for i := range args { args[i] = c.newIdent(fmt.Sprintf("Go$tuple[%d]", i), tuple.At(i).Type()) } return fmt.Sprintf("(Go$tuple = %s, %s(%s))", c.translateExpr(e.Args[0]), fun, c.translateArgs(sig, args, false)) } } return fmt.Sprintf("%s(%s)", fun, c.translateArgs(sig, e.Args, e.Ellipsis.IsValid())) case *ast.StarExpr: 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.IsIdentical(c.info.Types[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.translateExpr(e.X) + ".Go$get()" case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.info.Types[e.Type] check := "Go$obj !== null && " + c.typeCheck("Go$obj.constructor", t) value := "Go$obj" if _, isInterface := t.Underlying().(*types.Interface); !isInterface { value += ".Go$val" } if _, isTuple := exprType.(*types.Tuple); isTuple { return fmt.Sprintf("(Go$obj = %s, %s ? [%s, true] : [%s, false])", c.translateExpr(e.X), check, value, c.zeroValue(c.info.Types[e.Type])) } return fmt.Sprintf("(Go$obj = %s, %s ? %s : Go$typeAssertionFailed(Go$obj))", c.translateExpr(e.X), check, value) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := c.info.Objects[e].(type) { case *types.PkgName: return c.pkgVars[o.Pkg().Path()] case *types.Var, *types.Const: return c.objectName(o) case *types.Func: return c.objectName(o) case *types.TypeName: return c.typeName(o.Type()) case *types.Nil: return c.zeroValue(c.info.Types[e]) case nil: return e.Name default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case nil: return "" 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: t := new(types.Interface) p.record(t) // 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) 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" "code.google.com/p/go.tools/go/callgraph" "code.google.com/p/go.tools/go/ssa" "code.google.com/p/go.tools/go/types" ) var ( tEface = types.NewInterface(nil, nil) 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 (c *funcContext) translateExpr(expr ast.Expr) *expression { exprType := c.p.info.Types[expr].Type if value := c.p.info.Types[expr].Value; value != nil { basic := types.Typ[types.String] if value.Kind() != exact.String { // workaround for bug in go/types basic = exprType.Underlying().(*types.Basic) } switch { case basic.Info()&types.IsBoolean != 0: return c.formatExpr("%s", strconv.FormatBool(exact.BoolVal(value))) case basic.Info()&types.IsInteger != 0: if is64Bit(basic) { d, _ := exact.Uint64Val(value) if basic.Kind() == types.Int64 { return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatInt(int64(d)>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } d, _ := exact.Int64Val(value) return c.formatExpr("%s", strconv.FormatInt(d, 10)) case basic.Info()&types.IsFloat != 0: f, _ := exact.Float64Val(value) return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case basic.Info()&types.IsComplex != 0: 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 basic.Info()&types.IsString != 0: return c.formatExpr("%s", encodeString(exact.StringVal(value))) default: panic("Unhandled constant type: " + basic.String()) } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { elements := make([]string, 0) i := 0 zero := c.zeroValue(elementType) for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, _ := exact.Int64Val(c.p.info.Types[kve.Key].Value) i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateImplicitConversion(element, elementType).String() i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) != 0 { zero := c.zeroValue(t.Elem()) for len(elements) < int(t.Len()) { elements = append(elements, zero) } return c.formatExpr(`go$toNativeArray("%s", [%s])`, typeKind(t.Elem()), strings.Join(elements, ", ")) } return c.formatExpr(`go$makeNativeArray("%s", %d, function() { return %s; })`, typeKind(t.Elem()), int(t.Len()), c.zeroValue(t.Elem())) case *types.Slice: return c.formatExpr("new %s([%s])", c.typeName(exprType), strings.Join(collectIndexedElements(t.Elem()), ", ")) case *types.Map: mapVar := c.newVariable("_map") keyVar := c.newVariable("_key") assignments := "" for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) assignments += c.formatExpr(`%s = %s, %s[%s] = { k: %s, v: %s }, `, keyVar, c.translateImplicitConversion(kve.Key, t.Key()), mapVar, c.makeKey(c.newIdent(keyVar, t.Key()), t.Key()), keyVar, c.translateImplicitConversion(kve.Value, t.Elem())).String() } return c.formatExpr("(%s = new Go$Map(), %s%s)", mapVar, assignments, mapVar) 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.translateImplicitConversion(element, t.Field(i).Type()).String() } } if isKeyValue { for i := range elements { elements[i] = c.zeroValue(t.Field(i).Type()) } 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.translateImplicitConversion(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: params, body := c.translateFunction(e.Type, exprType.(*types.Signature), e.Body.List) if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") return c.formatExpr("(function(%s) { return function(%s) {\n%s%s}; })(%s)", list, strings.Join(params, ", "), string(body), strings.Repeat("\t", c.p.indentation), list) } return c.formatExpr("(function(%s) {\n%s%s})", strings.Join(params, ", "), string(body), strings.Repeat("\t", c.p.indentation)) case *ast.UnaryExpr: switch e.Op { case token.AND: switch c.p.info.Types[e.X].Type.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) default: if _, isComposite := e.X.(*ast.CompositeLit); isComposite { return c.formatExpr("go$newDataPointer(%e, %s)", e.X, c.typeName(c.p.info.Types[e].Type)) } closurePrefix := "" closureSuffix := "" if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") closurePrefix = "(function(" + list + ") { return " closureSuffix = "; })(" + list + ")" } vVar := c.newVariable("v") return c.formatExpr("%snew %s(function() { return %e; }, function(%s) { %s; })%s", closurePrefix, c.typeName(exprType), e.X, vVar, c.translateAssign(e.X, vVar), closureSuffix) } case token.ARROW: return c.formatExpr("undefined") } t := c.p.info.Types[e.X].Type 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 basic.Info()&types.IsComplex != 0: return c.formatExpr("new %1s(-%2r, -%2i)", c.typeName(t), e.X) case basic.Info()&types.IsUnsigned != 0: 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: x := c.translateExpr(e.X) if x.String() == "true" { return c.formatExpr("false") } if x.String() == "false" { return c.formatExpr("true") } return c.formatExpr("!%s", 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.info.Types[e.X].Type t2 := c.p.info.Types[e.Y].Type _, isInterface := t2.Underlying().(*types.Interface) if isInterface { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && basic.Info()&types.IsNumeric != 0 { if is64Bit(basic) { switch e.Op { case token.MUL: return c.formatExpr("go$mul64(%e, %e)", e.X, e.Y) case token.QUO: return c.formatExpr("go$div64(%e, %e, false)", e.X, e.Y) case token.REM: return c.formatExpr("go$div64(%e, %e, true)", e.X, e.Y) case token.SHL: return c.formatExpr("go$shiftLeft64(%e, %f)", e.X, e.Y) case token.SHR: return c.formatExpr("go$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 basic.Info()&types.IsComplex != 0 { switch e.Op { case token.EQL: if basic.Kind() == types.Complex64 { return c.formatExpr("(go$float32IsEqual(%1r, %2r) && go$float32IsEqual(%1i, %2i))", e.X, e.Y) } 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("go$divComplex(%e, %e)", e.X, e.Y) default: panic(e.Op) } } switch e.Op { case token.EQL: if basic.Kind() == types.Float32 { return c.formatParenExpr("go$float32IsEqual(%e, %e)", e.X, e.Y) } 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: if basic.Info()&types.IsInteger != 0 { return c.fixNumber(c.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) } return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.MUL: switch basic.Kind() { case types.Int32, types.Int: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >> 0) + (%1e << 16 >>> 16) * %2e) >> 0", e.X, e.Y) case types.Uint32, types.Uint, types.Uintptr: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >>> 0) + (%1e << 16 >>> 16) * %2e) >>> 0", e.X, e.Y) case types.Float32, types.Float64: return c.formatExpr("%e * %e", e.X, e.Y) default: return c.fixNumber(c.formatExpr("%e * %e", e.X, e.Y), basic) } case token.QUO: if basic.Info()&types.IsInteger != 0 { // cut off decimals shift := ">>" if basic.Info()&types.IsUnsigned != 0 { shift = ">>>" } return c.formatExpr(`(%1s = %2e / %3e, (%1s === %1s && %1s !== 1/0 && %1s !== -1/0) ? %1s %4s 0 : go$throwRuntimeError("integer divide by zero"))`, c.newVariable("_q"), e.X, e.Y, shift) } return c.formatExpr("%e / %e", e.X, e.Y) case token.REM: return c.formatExpr(`(%1s = %2e %% %3e, %1s === %1s ? %1s : go$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 && basic.Info()&types.IsUnsigned != 0 { op = ">>>" } if c.p.info.Types[e.Y].Value != nil { return c.fixNumber(c.formatExpr("%e %s %e", e.X, op, e.Y), basic) } if e.Op == token.SHR && basic.Info()&types.IsUnsigned == 0 { return c.fixNumber(c.formatParenExpr("%e >> go$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 basic.Info()&types.IsUnsigned != 0 { 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.formatParenExpr("%e & ~%e", e.X, e.Y) 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: x := c.translateExpr(e.X) y := c.translateExpr(e.Y) if x.String() == "false" { return c.formatExpr("false") } return c.formatExpr("%s && %s", x, y) case token.LOR: x := c.translateExpr(e.X) y := c.translateExpr(e.Y) if x.String() == "true" { return c.formatExpr("true") } return c.formatExpr("%s || %s", x, y) case token.EQL: switch u := t.Underlying().(type) { case *types.Struct: x := c.newVariable("x") y := c.newVariable("y") var conds []string for i := 0; i < u.NumFields(); i++ { field := u.Field(i) if field.Name() == "_" { continue } conds = append(conds, c.translateExpr(&ast.BinaryExpr{ X: c.newIdent(x+"."+fieldName(u, i), field.Type()), Op: token.EQL, Y: c.newIdent(y+"."+fieldName(u, i), field.Type()), }).String()) } if len(conds) == 0 { conds = []string{"true"} } return c.formatExpr("(%s = %e, %s = %e, %s)", x, e.X, y, e.Y, strings.Join(conds, " && ")) case *types.Interface: if isJsObject(t) { return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) } return c.formatExpr("go$interfaceIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Array: return c.formatExpr("go$arrayIsEqual(%e, %e)", e.X, e.Y) case *types.Pointer: xUnary, xIsUnary := e.X.(*ast.UnaryExpr) yUnary, yIsUnary := e.Y.(*ast.UnaryExpr) if xIsUnary && xUnary.Op == token.AND && yIsUnary && yUnary.Op == token.AND { xIndex, xIsIndex := xUnary.X.(*ast.IndexExpr) yIndex, yIsIndex := yUnary.X.(*ast.IndexExpr) if xIsIndex && yIsIndex { return c.formatExpr("go$sliceIsEqual(%e, %f, %e, %f)", xIndex.X, xIndex.Index, yIndex.X, yIndex.Index) } } switch u.Elem().Underlying().(type) { case *types.Struct, *types.Interface: return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Array: return c.formatExpr("go$arrayIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) default: return c.formatExpr("go$pointerIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) } default: return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) } default: panic(e.Op) } case *ast.ParenExpr: x := c.translateExpr(e.X) if x.String() == "true" || x.String() == "false" { return x } return c.formatParenExpr("%s", x) case *ast.IndexExpr: switch t := c.p.info.Types[e.X].Type.Underlying().(type) { case *types.Array, *types.Pointer: return c.formatExpr("%e[%f]", e.X, e.Index) case *types.Slice: return c.formatExpr(`((%2f < 0 || %2f >= %1e.length) ? go$throwRuntimeError("index out of range") : %1e.array[%1e.offset + %2f])`, e.X, e.Index) case *types.Map: key := c.makeKey(e.Index, t.Key()) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? [%1s.v, true] : [%4s, false])`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) } return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? %1s.v : %4s)`, 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.info.Types[e.X].Type.Underlying().(*types.Basic); isBasic && b.Info()&types.IsString != 0 { 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("go$subslice(%s, 0, %f, %f)", slice, e.High, e.Max) } return c.formatExpr("go$subslice(%s, 0, %f)", slice, e.High) case e.High == nil: return c.formatExpr("go$subslice(%s, %f)", slice, e.Low) default: if e.Max != nil { return c.formatExpr("go$subslice(%s, %f, %f, %f)", slice, e.Low, e.High, e.Max) } return c.formatExpr("go$subslice(%s, %f, %f)", slice, e.Low, e.High) } case *ast.SelectorExpr: sel := c.p.info.Selections[e] parameterName := func(v *types.Var) string { if v.Anonymous() || v.Name() == "" { return c.newVariable("param") } return c.newVariable(v.Name()) } makeParametersList := func() []string { params := sel.Obj().Type().(*types.Signature).Params() names := make([]string, params.Len()) for i := 0; i < params.Len(); i++ { names[i] = parameterName(params.At(i)) } return names } switch sel.Kind() { case types.FieldVal: fields, jsTag := c.translateSelection(sel) if jsTag != "" { if _, ok := sel.Type().(*types.Signature); ok { return c.formatExpr("go$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: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } parameters := makeParametersList() target := c.translateExpr(e.X) if isWrapped(sel.Recv()) { target = c.formatParenExpr("new %s(%s)", c.typeName(sel.Recv()), target) } recv := c.newVariable("_recv") return c.formatExpr("(%s = %s, function(%s) { return %s.%s(%s); })", recv, target, strings.Join(parameters, ", "), recv, e.Sel.Name, strings.Join(parameters, ", ")) case types.MethodExpr: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } recv := "recv" if isWrapped(sel.Recv()) { recv = fmt.Sprintf("(new %s(recv))", c.typeName(sel.Recv())) } parameters := makeParametersList() return c.formatExpr("(function(%s) { return %s.%s(%s); })", strings.Join(append([]string{"recv"}, parameters...), ", "), recv, sel.Obj().(*types.Func).Name(), strings.Join(parameters, ", ")) case types.PackageObj: if isJsPackage(sel.Obj().Pkg()) { switch sel.Obj().Name() { case "Global": return c.formatExpr("go$global") case "This": if c.flattened { return c.formatExpr("go$this") } return c.formatExpr("this") case "Arguments": args := "arguments" if c.flattened { args = "go$args" } return c.formatExpr(`new (go$sliceType(%s.Object))(go$global.Array.prototype.slice.call(%s))`, c.p.pkgVars["github.com/gopherjs/gopherjs/js"], args) default: panic("Invalid js package object: " + sel.Obj().Name()) } } return c.formatExpr("%s", c.objectName(sel.Obj())) } panic("") case *ast.CallExpr: plainFun := e.Fun for { if p, isParen := plainFun.(*ast.ParenExpr); isParen { plainFun = p.X continue } break } var isType func(ast.Expr) bool isType = func(expr ast.Expr) bool { switch e := expr.(type) { case *ast.ArrayType, *ast.ChanType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.StructType: return true case *ast.StarExpr: return isType(e.X) case *ast.Ident: _, ok := c.p.info.Uses[e].(*types.TypeName) return ok case *ast.SelectorExpr: _, ok := c.p.info.Uses[e.Sel].(*types.TypeName) return ok case *ast.ParenExpr: return isType(e.X) default: return false } } if isType(plainFun) { return c.formatExpr("%s", c.translateConversion(e.Args[0], c.p.info.Types[plainFun].Type)) } var fun *expression switch f := plainFun.(type) { case *ast.Ident: if o, ok := c.p.info.Uses[f].(*types.Builtin); ok { switch o.Name() { case "new": t := c.p.info.Types[e].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("%s", c.zeroValue(t.Elem())) default: return c.formatExpr("go$newDataPointer(%s, %s)", c.zeroValue(t.Elem()), c.typeName(t)) } case "make": switch argType := c.p.info.Types[e.Args[0]].Type.Underlying().(type) { case *types.Slice: t := c.typeName(c.p.info.Types[e.Args[0]].Type) if len(e.Args) == 3 { return c.formatExpr("%s.make(%f, %f, function() { return %s; })", t, e.Args[1], e.Args[2], c.zeroValue(argType.Elem())) } return c.formatExpr("%s.make(%f, 0, function() { return %s; })", t, e.Args[1], c.zeroValue(argType.Elem())) case *types.Map: return c.formatExpr("new Go$Map()") case *types.Chan: return c.formatExpr("new %s()", c.typeName(c.p.info.Types[e.Args[0]].Type)) default: panic(fmt.Sprintf("Unhandled make type: %T\n", argType)) } case "len": arg := c.translateExpr(e.Args[0]) switch argType := c.p.info.Types[e.Args[0]].Type.Underlying().(type) { case *types.Basic, *types.Slice: return c.formatExpr("%s.length", arg) case *types.Pointer: return c.formatExpr("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Map: return c.formatExpr("go$keys(%s).length", arg) case *types.Chan: return c.formatExpr("0") // length of array is constant default: panic(fmt.Sprintf("Unhandled len type: %T\n", argType)) } case "cap": arg := c.translateExpr(e.Args[0]) switch argType := c.p.info.Types[e.Args[0]].Type.Underlying().(type) { case *types.Slice: return c.formatExpr("%s.capacity", arg) case *types.Pointer: return c.formatExpr("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Chan: return c.formatExpr("0") // capacity of array is constant default: panic(fmt.Sprintf("Unhandled cap type: %T\n", argType)) } case "panic": return c.formatExpr("throw go$panic(%s)", c.translateImplicitConversion(e.Args[0], types.NewInterface(nil, nil))) case "append": if len(e.Args) == 1 { return c.translateExpr(e.Args[0]) } if e.Ellipsis.IsValid() { return c.formatExpr("go$appendSlice(%e, %s)", e.Args[0], c.translateConversionToSlice(e.Args[1], exprType)) } sliceType := exprType.Underlying().(*types.Slice) return c.formatExpr("go$append(%e, %s)", e.Args[0], strings.Join(c.translateExprSlice(e.Args[1:], sliceType.Elem()), ", ")) case "delete": return c.formatExpr(`delete %e[%s]`, e.Args[0], c.makeKey(e.Args[1], c.p.info.Types[e.Args[0]].Type.Underlying().(*types.Map).Key())) case "copy": if basic, isBasic := c.p.info.Types[e.Args[1]].Type.Underlying().(*types.Basic); isBasic && basic.Info()&types.IsString != 0 { return c.formatExpr("go$copyString(%e, %e)", e.Args[0], e.Args[1]) } return c.formatExpr("go$copySlice(%e, %e)", e.Args[0], e.Args[1]) case "print", "println": return c.formatExpr("console.log(%s)", strings.Join(c.translateExprSlice(e.Args, nil), ", ")) case "complex": return c.formatExpr("new %s(%e, %e)", c.typeName(c.p.info.Types[e].Type), e.Args[0], e.Args[1]) case "real": return c.formatExpr("%e.real", e.Args[0]) case "imag": return c.formatExpr("%e.imag", e.Args[0]) case "recover": return c.formatExpr("go$recover()") case "close": return c.formatExpr(`go$notSupported("close")`) default: panic(fmt.Sprintf("Unhandled builtin: %s\n", o.Name())) } } fun = c.translateExpr(plainFun) case *ast.SelectorExpr: sel := c.p.info.Selections[f] o := sel.Obj() externalizeExpr := func(e ast.Expr) string { t := c.p.info.Types[e].Type 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: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } methodName := o.Name() if reservedKeywords[methodName] { methodName += "$" } fun = c.translateExpr(f.X) t := sel.Recv() for _, index := range sel.Index()[:len(sel.Index())-1] { if ptr, isPtr := t.(*types.Pointer); isPtr { t = ptr.Elem() } s := t.Underlying().(*types.Struct) fun = c.formatExpr("%s.%s", fun, fieldName(s, index)) t = s.Field(index).Type() } if isJsPackage(o.Pkg()) { globalRef := func(id string) string { if fun.String() == "go$global" && len(id) > 3 && strings.EqualFold(id[:3], "go$") { return id } return fun.String() + "." + id } switch o.Name() { case "Get": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s", globalRef(id)) } return c.formatExpr("%s[go$externalize(%e, Go$String)]", fun, 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[go$externalize(%e, Go$String)] = %s", fun, e.Args[0], externalizeExpr(e.Args[1])) case "Delete": return c.formatExpr("delete %s[go$externalize(%e, Go$String)]", fun, e.Args[0]) case "Length": return c.formatExpr("go$parseInt(%s.length)", fun) case "Index": return c.formatExpr("%s[%e]", fun, e.Args[0]) case "SetIndex": return c.formatExpr("%s[%e] = %s", fun, 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, fun, 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[go$externalize(%e, Go$String)].apply(%s, %s))", objVar, fun, objVar, e.Args[0], objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s[go$externalize(%e, Go$String)](%s)", fun, e.Args[0], externalizeArgs(e.Args[1:])) case "Invoke": if e.Ellipsis.IsValid() { return c.formatExpr("%s.apply(undefined, %s)", fun, externalizeExpr(e.Args[0])) } return c.formatExpr("%s(%s)", fun, externalizeArgs(e.Args)) case "New": if e.Ellipsis.IsValid() { return c.formatExpr("new (go$global.Function.prototype.bind.apply(%s, [undefined].concat(%s)))", fun, externalizeExpr(e.Args[0])) } return c.formatExpr("new (%s)(%s)", fun, externalizeArgs(e.Args)) case "Bool": return c.internalize(fun, types.Typ[types.Bool]) case "Str": return c.internalize(fun, types.Typ[types.String]) case "Int": return c.internalize(fun, types.Typ[types.Int]) case "Int64": return c.internalize(fun, types.Typ[types.Int64]) case "Uint64": return c.internalize(fun, types.Typ[types.Uint64]) case "Float": return c.internalize(fun, types.Typ[types.Float64]) case "Interface": return c.internalize(fun, types.NewInterface(nil, nil)) case "Unsafe": return fun case "IsUndefined": return c.formatParenExpr("%s === undefined", fun) case "IsNull": return c.formatParenExpr("%s === null", fun) default: panic("Invalid js package object: " + o.Name()) } } methodsRecvType := o.Type().(*types.Signature).Recv().Type() _, pointerExpected := methodsRecvType.(*types.Pointer) _, isPointer := t.Underlying().(*types.Pointer) _, isStruct := t.Underlying().(*types.Struct) _, isArray := t.Underlying().(*types.Array) if pointerExpected && !isPointer && !isStruct && !isArray { vVar := c.newVariable("v") fun = c.formatExpr("(new %s(function() { return %s; }, function(%s) { %s = %s; })).%s", c.typeName(methodsRecvType), fun, vVar, fun, vVar, methodName) break } if isWrapped(t) { fun = c.formatExpr("(new %s(%s)).%s", c.typeName(t), fun, methodName) break } fun = c.formatExpr("%s.%s", fun, methodName) case types.PackageObj: if isJsPackage(o.Pkg()) && o.Name() == "InternalObject" { return c.translateExpr(e.Args[0]) } fun = c.translateExpr(f) case types.FieldVal: fields, jsTag := c.translateSelection(sel) if jsTag != "" { sig := sel.Type().(*types.Signature) return c.internalize(c.formatExpr("%e.%s.%s(%s)", f.X, strings.Join(fields, "."), jsTag, externalizeArgs(e.Args)), sig.Results().At(0).Type()) } fun = c.formatExpr("%e.%s", f.X, strings.Join(fields, ".")) case types.MethodExpr: fun = c.translateExpr(f) default: panic("") } default: fun = c.translateExpr(plainFun) } sig := c.p.info.Types[plainFun].Type.Underlying().(*types.Signature) if len(e.Args) == 1 { if tuple, isTuple := c.p.info.Types[e.Args[0]].Type.(*types.Tuple); isTuple { tupleVar := c.newVariable("_tuple") args := make([]ast.Expr, tuple.Len()) for i := range args { args[i] = c.newIdent(c.formatExpr("%s[%d]", tupleVar, i).String(), tuple.At(i).Type()) } return c.formatExpr("(%s = %e, %s(%s))", tupleVar, e.Args[0], fun, strings.Join(c.translateArgs(sig, args, false), ", ")) } } return c.formatExpr("%s(%s)", fun, strings.Join(c.translateArgs(sig, e.Args, e.Ellipsis.IsValid()), ", ")) case *ast.StarExpr: 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.info.Types[c2.Fun].Type, 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.go$get()", e.X) case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.p.info.Types[e.Type].Type check := "%1e !== null && " + c.typeCheck("%1e.constructor", t) valueSuffix := "" if _, isInterface := t.Underlying().(*types.Interface); !isInterface { valueSuffix = ".go$val" } if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("("+check+" ? [%1e%2s, true] : [%3s, false])", e.X, valueSuffix, c.zeroValue(c.p.info.Types[e.Type].Type)) } return c.formatExpr("("+check+" ? %1e%2s : go$typeAssertionFailed(%1e, %3s))", e.X, valueSuffix, c.typeName(t)) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := c.p.info.Uses[e].(type) { case *types.PkgName: return c.formatExpr("%s", c.p.pkgVars[o.Pkg().Path()]) 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: return c.formatExpr("%s", c.zeroValue(c.p.info.Types[e].Type)) default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case *This: this := "this" if c.flattened { this = "go$this" } if isWrapped(c.p.info.Types[e].Type) { this += ".go$val" } return c.formatExpr(this) case nil: return c.formatExpr("") default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }