func writeFloat(w *bytes.Buffer, ev exact.Value, k types.BasicKind) { v, _ := exact.Int64Val(exact.Num(ev)) w.WriteString(strconv.FormatInt(v, 10)) v, _ = exact.Int64Val(exact.Denom(ev)) if v != 1 { w.WriteByte('/') w.WriteString(strconv.FormatInt(v, 10)) } w.WriteByte('.') if k == types.Float32 { w.WriteByte('F') } }
func (p *exporter) value(x exact.Value) { if trace { p.tracef("value { ") defer p.tracef("} ") } switch kind := x.Kind(); kind { case exact.Bool: tag := falseTag if exact.BoolVal(x) { tag = trueTag } p.int(tag) case exact.Int: if i, ok := exact.Int64Val(x); ok { p.int(int64Tag) p.int64(i) return } p.int(floatTag) p.float(x) case exact.Float: p.int(fractionTag) p.fraction(x) case exact.Complex: p.int(complexTag) p.fraction(exact.Real(x)) p.fraction(exact.Imag(x)) case exact.String: p.int(stringTag) p.string(exact.StringVal(x)) default: panic(fmt.Sprintf("unexpected value kind %d", kind)) } }
// IntVal is a utility function returns an int64 constant value from an exact.Value, split into high and low int32. func IntVal(eVal exact.Value, posStr string) (high, low int32) { iVal, isExact := exact.Int64Val(eVal) if !isExact { LogWarning(posStr, "inexact", fmt.Errorf("constant value %d cannot be accurately represented in int64", iVal)) } return int32(iVal >> 32), int32(iVal & 0xFFFFFFFF) }
// Conversion type-checks the conversion T(x). // The result is in x. func (check *Checker) conversion(x *operand, T Type) { constArg := x.mode == constant var ok bool switch { case constArg && isConstType(T): // constant conversion switch t := T.Underlying().(*Basic); { case representableConst(x.val, check.conf, t.kind, &x.val): ok = true case x.isInteger() && isString(t): codepoint := int64(-1) if i, ok := exact.Int64Val(x.val); ok { codepoint = i } // If codepoint < 0 the absolute value is too large (or unknown) for // conversion. This is the same as converting any other out-of-range // value - let string(codepoint) do the work. x.val = exact.MakeString(string(codepoint)) ok = true } case x.convertibleTo(check.conf, T): // non-constant conversion x.mode = value ok = true } if !ok { check.errorf(x.pos(), "cannot convert %s to %s", x, T) x.mode = invalid return } // The conversion argument types are final. For untyped values the // conversion provides the type, per the spec: "A constant may be // given a type explicitly by a constant declaration or conversion,...". final := x.typ if isUntyped(x.typ) { final = T // - For conversions to interfaces, use the argument's default type. // - For conversions of untyped constants to non-constant types, also // use the default type (e.g., []byte("foo") should report string // not []byte as type for the constant "foo"). // - Keep untyped nil for untyped nil arguments. if isInterface(T) || constArg && !isConstType(T) { final = defaultType(x.typ) } check.updateExprType(x.expr, final, true) } x.typ = T }
// Int64 returns the numeric value of this literal truncated to fit // a signed 64-bit integer. // func (l *Literal) Int64() int64 { switch x := l.Value; x.Kind() { case exact.Int: if i, ok := exact.Int64Val(x); ok { return i } return 0 case exact.Float: f, _ := exact.Float64Val(x) return int64(f) } panic(fmt.Sprintf("unexpected literal value: %T", l.Value)) }
// Int64 returns the numeric value of this constant truncated to fit // a signed 64-bit integer. // func (c *Const) Int64() int64 { switch x := c.Value; x.Kind() { case exact.Int: if i, ok := exact.Int64Val(x); ok { return i } return 0 case exact.Float: f, _ := exact.Float64Val(x) return int64(f) } panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) }
func doOp(x exact.Value, op token.Token, y exact.Value) (z exact.Value) { defer panicHandler(&z) if x == nil { return exact.UnaryOp(op, y, -1) } switch op { case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: return exact.MakeBool(exact.Compare(x, op, y)) case token.SHL, token.SHR: s, _ := exact.Int64Val(y) return exact.Shift(x, op, uint(s)) default: return exact.BinaryOp(x, op, y) } }
func ext۰reflect۰rtype۰InOut(a *analysis, cgn *cgnode, out bool) { // If we have access to the callsite, // and the argument is an int constant, // return only that parameter. index := -1 if site := cgn.callersite; site != nil { if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { v, _ := exact.Int64Val(c.Value) index = int(v) } } a.addConstraint(&rtypeInOutConstraint{ cgn: cgn, t: a.funcParams(cgn.obj), result: a.funcResults(cgn.obj), out: out, i: index, }) }
func (check *Checker) arrayLength(e ast.Expr) int64 { var x operand check.expr(&x, e) if x.mode != constant { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", &x) } return 0 } if !x.isInteger() { check.errorf(x.pos(), "array length %s must be integer", &x) return 0 } n, ok := exact.Int64Val(x.val) if !ok || n < 0 { check.errorf(x.pos(), "invalid array length %s", &x) return 0 } return n }
// checkLongShift checks if shift or shift-assign operations shift by more than // the length of the underlying variable. func checkLongShift(f *File, node ast.Node, x, y ast.Expr) { v := f.pkg.types[y].Value if v == nil { return } amt, ok := exact.Int64Val(v) if !ok { return } t := f.pkg.types[x].Type if t == nil { return } b, ok := t.Underlying().(*types.Basic) if !ok { return } var size int64 var msg string switch b.Kind() { case types.Uint8, types.Int8: size = 8 case types.Uint16, types.Int16: size = 16 case types.Uint32, types.Int32: size = 32 case types.Uint64, types.Int64: size = 64 case types.Int, types.Uint, types.Uintptr: // These types may be as small as 32 bits, but no smaller. size = 32 msg = "might be " default: return } if amt >= size { ident := f.gofmt(x) f.Badf(node.Pos(), "%s %stoo small for shift of %d", ident, msg, amt) } }
func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) { // If we have access to the callsite, // and the channel argument is a constant (as is usual), // only generate the requested direction. var dir reflect.ChanDir // unknown if site := cgn.callersite; site != nil { if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { v, _ := exact.Int64Val(c.Value) if 0 <= v && v <= int64(reflect.BothDir) { dir = reflect.ChanDir(v) } } } params := a.funcParams(cgn.obj) a.addConstraint(&reflectChanOfConstraint{ cgn: cgn, t: params + 1, result: a.funcResults(cgn.obj), dirs: dirMap[dir], }) }
func evalAction(n ast.Node) exact.Value { switch e := n.(type) { case *ast.BasicLit: return val(e.Value) case *ast.BinaryExpr: x := evalAction(e.X) if x == nil { return nil } y := evalAction(e.Y) if y == nil { return nil } switch e.Op { case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: return exact.MakeBool(exact.Compare(x, e.Op, y)) case token.SHL, token.SHR: s, _ := exact.Int64Val(y) return exact.Shift(x, e.Op, uint(s)) default: return exact.BinaryOp(x, e.Op, y) } case *ast.UnaryExpr: return exact.UnaryOp(e.Op, evalAction(e.X), -1) case *ast.CallExpr: fmt.Printf("Can't handle call (%s) yet at pos %d\n", e.Fun, e.Pos()) return nil case *ast.Ident: fmt.Printf("Can't handle Ident %s here at pos %d\n", e.Name, e.Pos()) return nil case *ast.ParenExpr: return evalAction(e.X) default: fmt.Println("Can't handle") fmt.Printf("n: %s, e: %s\n", n, e) return nil } }
// index checks an index expression for validity. // If max >= 0, it is the upper bound for index. // If index is valid and the result i >= 0, then i is the constant value of index. func (check *Checker) index(index ast.Expr, max int64) (i int64, valid bool) { var x operand check.expr(&x, index) if x.mode == invalid { return } // an untyped constant must be representable as Int check.convertUntyped(&x, Typ[Int]) if x.mode == invalid { return } // the index must be of integer type if !isInteger(x.typ) { check.invalidArg(x.pos(), "index %s must be integer", &x) return } // a constant index i must be in bounds if x.mode == constant { if exact.Sign(x.val) < 0 { check.invalidArg(x.pos(), "index %s must not be negative", &x) return } i, valid = exact.Int64Val(x.val) if !valid || max >= 0 && i >= max { check.errorf(x.pos(), "index %s is out of bounds", &x) return i, false } // 0 <= i [ && i < max ] return i, true } return -1, true }
func (c *compiler) NewConstValue(v exact.Value, typ types.Type) *LLVMValue { switch { case v.Kind() == exact.Unknown: // TODO nil literals should be represented more appropriately once the exact-package supports it. llvmtyp := c.types.ToLLVM(typ) return c.NewValue(llvm.ConstNull(llvmtyp), typ) case isString(typ): if isUntyped(typ) { typ = types.Typ[types.String] } llvmtyp := c.types.ToLLVM(typ) strval := exact.StringVal(v) strlen := len(strval) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) var ptr llvm.Value if strlen > 0 { init := llvm.ConstString(strval, false) ptr = llvm.AddGlobal(c.module.Module, init.Type(), "") ptr.SetInitializer(init) ptr = llvm.ConstBitCast(ptr, i8ptr) } else { ptr = llvm.ConstNull(i8ptr) } len_ := llvm.ConstInt(c.types.inttype, uint64(strlen), false) llvmvalue := llvm.Undef(llvmtyp) llvmvalue = llvm.ConstInsertValue(llvmvalue, ptr, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, len_, []uint32{1}) return c.NewValue(llvmvalue, typ) case isInteger(typ): if isUntyped(typ) { typ = types.Typ[types.Int] } llvmtyp := c.types.ToLLVM(typ) var llvmvalue llvm.Value if isUnsigned(typ) { v, _ := exact.Uint64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, v, false) } else { v, _ := exact.Int64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, uint64(v), true) } return c.NewValue(llvmvalue, typ) case isBoolean(typ): if isUntyped(typ) { typ = types.Typ[types.Bool] } var llvmvalue llvm.Value if exact.BoolVal(v) { llvmvalue = llvm.ConstAllOnes(llvm.Int1Type()) } else { llvmvalue = llvm.ConstNull(llvm.Int1Type()) } return c.NewValue(llvmvalue, typ) case isFloat(typ): if isUntyped(typ) { typ = types.Typ[types.Float64] } llvmtyp := c.types.ToLLVM(typ) floatval, _ := exact.Float64Val(v) llvmvalue := llvm.ConstFloat(llvmtyp, floatval) return c.NewValue(llvmvalue, typ) case typ == types.Typ[types.UnsafePointer]: llvmtyp := c.types.ToLLVM(typ) v, _ := exact.Uint64Val(v) llvmvalue := llvm.ConstInt(llvmtyp, v, false) return c.NewValue(llvmvalue, typ) case isComplex(typ): if isUntyped(typ) { typ = types.Typ[types.Complex128] } llvmtyp := c.types.ToLLVM(typ) floattyp := llvmtyp.StructElementTypes()[0] llvmvalue := llvm.ConstNull(llvmtyp) realv := exact.Real(v) imagv := exact.Imag(v) realfloatval, _ := exact.Float64Val(realv) imagfloatval, _ := exact.Float64Val(imagv) llvmre := llvm.ConstFloat(floattyp, realfloatval) llvmim := llvm.ConstFloat(floattyp, imagfloatval) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmre, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmim, []uint32{1}) return c.NewValue(llvmvalue, typ) } // Special case for string -> [](byte|rune) if u, ok := typ.Underlying().(*types.Slice); ok && isInteger(u.Elem()) { if v.Kind() == exact.String { strval := c.NewConstValue(v, types.Typ[types.String]) return strval.Convert(typ).(*LLVMValue) } } panic(fmt.Sprintf("unhandled: t=%s(%T), v=%v(%T)", c.types.TypeString(typ), typ, v, v)) }
func Write(pkg *types.Package, out io.Writer, sizes types.Sizes) { fmt.Fprintf(out, "package %s\n", pkg.Name()) e := &exporter{pkg: pkg, imports: make(map[*types.Package]bool), out: out} for _, imp := range pkg.Imports() { e.addImport(imp) } for _, name := range pkg.Scope().Names() { obj := pkg.Scope().Lookup(name) _, isTypeName := obj.(*types.TypeName) if obj.Exported() || isTypeName { e.toExport = append(e.toExport, obj) } } for i := 0; i < len(e.toExport); i++ { switch o := e.toExport[i].(type) { case *types.TypeName: fmt.Fprintf(out, "type %s %s\n", e.makeName(o), e.makeType(o.Type().Underlying())) if _, isInterface := o.Type().Underlying().(*types.Interface); !isInterface { writeMethods := func(t types.Type) { methods := types.NewMethodSet(t) for i := 0; i < methods.Len(); i++ { m := methods.At(i) if len(m.Index()) > 1 { continue // method of embedded field } out.Write([]byte("func (? " + e.makeType(m.Recv()) + ") " + e.makeName(m.Obj()) + e.makeSignature(m.Type()) + "\n")) } } writeMethods(o.Type()) writeMethods(types.NewPointer(o.Type())) } case *types.Func: out.Write([]byte("func " + e.makeName(o) + e.makeSignature(o.Type()) + "\n")) case *types.Const: optType := "" basic, isBasic := o.Type().(*types.Basic) if !isBasic || basic.Info()&types.IsUntyped == 0 { optType = " " + e.makeType(o.Type()) } basic = o.Type().Underlying().(*types.Basic) var val string switch { case basic.Info()&types.IsBoolean != 0: val = strconv.FormatBool(exact.BoolVal(o.Val())) case basic.Info()&types.IsInteger != 0: if basic.Kind() == types.Uint64 { d, _ := exact.Uint64Val(o.Val()) val = fmt.Sprintf("%#x", d) break } d, _ := exact.Int64Val(o.Val()) if basic.Kind() == types.UntypedRune { switch { case d < 0 || d > unicode.MaxRune: val = fmt.Sprintf("('\\x00' + %d)", d) case d > 0xffff: val = fmt.Sprintf("'\\U%08x'", d) default: val = fmt.Sprintf("'\\u%04x'", d) } break } val = fmt.Sprintf("%#x", d) case basic.Info()&types.IsFloat != 0: f, _ := exact.Float64Val(o.Val()) val = strconv.FormatFloat(f, 'b', -1, 64) case basic.Info()&types.IsComplex != 0: r, _ := exact.Float64Val(exact.Real(o.Val())) i, _ := exact.Float64Val(exact.Imag(o.Val())) val = fmt.Sprintf("(%s+%si)", strconv.FormatFloat(r, 'b', -1, 64), strconv.FormatFloat(i, 'b', -1, 64)) case basic.Info()&types.IsString != 0: val = fmt.Sprintf("%#v", exact.StringVal(o.Val())) default: panic("Unhandled constant type: " + basic.String()) } out.Write([]byte("const " + e.makeName(o) + optType + " = " + val + "\n")) case *types.Var: out.Write([]byte("var " + e.makeName(o) + " " + e.makeType(o.Type()) + "\n")) default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } } fmt.Fprintf(out, "$$\n") }
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)) } }
func (c *funcContext) formatExprInternal(format string, a []interface{}, parens bool) *expression { processFormat := func(f func(uint8, uint8, int)) { n := 0 for i := 0; i < len(format); i++ { b := format[i] if b == '%' { i++ k := format[i] if k >= '0' && k <= '9' { n = int(k - '0' - 1) i++ k = format[i] } f(0, k, n) n++ continue } f(b, 0, 0) } } counts := make([]int, len(a)) processFormat(func(b, k uint8, n int) { switch k { case 'e', 'f', 'h', 'l', 'r', 'i': counts[n]++ } }) out := bytes.NewBuffer(nil) vars := make([]string, len(a)) hasAssignments := false for i, e := range a { if counts[i] <= 1 { continue } if _, isIdent := e.(*ast.Ident); isIdent { continue } if val := c.p.info.Types[e.(ast.Expr)].Value; val != nil { continue } if !hasAssignments { hasAssignments = true out.WriteByte('(') parens = false } v := c.newVariable("x") out.WriteString(v + " = " + c.translateExpr(e.(ast.Expr)).String() + ", ") vars[i] = v } processFormat(func(b, k uint8, n int) { writeExpr := func(suffix string) { if vars[n] != "" { out.WriteString(vars[n] + suffix) return } out.WriteString(c.translateExpr(a[n].(ast.Expr)).StringWithParens() + suffix) } switch k { case 0: out.WriteByte(b) case 's': if e, ok := a[n].(*expression); ok { out.WriteString(e.StringWithParens()) return } out.WriteString(a[n].(string)) case 'd': out.WriteString(strconv.Itoa(a[n].(int))) case 't': out.WriteString(a[n].(token.Token).String()) case 'e': e := a[n].(ast.Expr) if val := c.p.info.Types[e].Value; val != nil { out.WriteString(c.translateExpr(e).String()) return } writeExpr("") case 'f': e := a[n].(ast.Expr) if val := c.p.info.Types[e].Value; val != nil { d, _ := exact.Int64Val(val) out.WriteString(strconv.FormatInt(d, 10)) return } if is64Bit(c.p.info.Types[e].Type.Underlying().(*types.Basic)) { out.WriteString("$flatten64(") writeExpr("") out.WriteString(")") return } writeExpr("") case 'h': e := a[n].(ast.Expr) if val := c.p.info.Types[e].Value; val != nil { d, _ := exact.Uint64Val(val) if c.p.info.Types[e].Type.Underlying().(*types.Basic).Kind() == types.Int64 { out.WriteString(strconv.FormatInt(int64(d)>>32, 10)) return } out.WriteString(strconv.FormatUint(d>>32, 10)) return } writeExpr(".$high") case 'l': if val := c.p.info.Types[a[n].(ast.Expr)].Value; val != nil { d, _ := exact.Uint64Val(val) out.WriteString(strconv.FormatUint(d&(1<<32-1), 10)) return } writeExpr(".$low") case 'r': if val := c.p.info.Types[a[n].(ast.Expr)].Value; val != nil { r, _ := exact.Float64Val(exact.Real(val)) out.WriteString(strconv.FormatFloat(r, 'g', -1, 64)) return } writeExpr(".$real") case 'i': if val := c.p.info.Types[a[n].(ast.Expr)].Value; val != nil { i, _ := exact.Float64Val(exact.Imag(val)) out.WriteString(strconv.FormatFloat(i, 'g', -1, 64)) return } writeExpr(".$imag") case '%': out.WriteRune('%') default: panic(fmt.Sprintf("formatExpr: %%%c%d", k, n)) } }) if hasAssignments { out.WriteByte(')') } return &expression{str: out.String(), parens: parens} }
func (cdd *CDD) varDecl(w *bytes.Buffer, typ types.Type, global bool, name string, val ast.Expr) (acds []*CDD) { dim, acds := cdd.Type(w, typ) w.WriteByte(' ') w.WriteString(dimFuncPtr(name, dim)) constInit := true // true if C declaration can init value if global { cdd.copyDecl(w, ";\n") // Global variables may need declaration if val != nil { if i, ok := val.(*ast.Ident); !ok || i.Name != "nil" { constInit = cdd.exprValue(val) != nil } } } if constInit { w.WriteString(" = ") if val != nil { cdd.Expr(w, val, typ) } else { zeroVal(w, typ) } } w.WriteString(";\n") cdd.copyDef(w) if !constInit { w.Reset() // Runtime initialisation assign := false switch t := underlying(typ).(type) { case *types.Slice: switch vt := val.(type) { case *ast.CompositeLit: aname := "array" + cdd.gtc.uniqueId() var n int64 for _, elem := range vt.Elts { switch e := elem.(type) { case *ast.KeyValueExpr: val := cdd.exprValue(e.Key) if val == nil { panic("slice: composite literal with non-constant key") } m, ok := exact.Int64Val(val) if !ok { panic("slice: can't convert " + val.String() + " to int64") } m++ if m > n { n = m } default: n++ } } at := types.NewArray(t.Elem(), n) o := types.NewVar(vt.Lbrace, cdd.gtc.pkg, aname, at) cdd.gtc.pkg.Scope().Insert(o) acd := cdd.gtc.newCDD(o, VarDecl, cdd.il) av := *vt cdd.gtc.ti.Types[&av] = types.TypeAndValue{Type: at} // BUG: thread-unsafe acd.varDecl(new(bytes.Buffer), o.Type(), cdd.gtc.isGlobal(o), aname, &av) cdd.InitNext = acd acds = append(acds, acd) w.WriteByte('\t') w.WriteString(name) w.WriteString(" = ASLICE(") w.WriteString(strconv.FormatInt(at.Len(), 10)) w.WriteString(", ") w.WriteString(aname) w.WriteString(");\n") default: assign = true } case *types.Array: w.WriteByte('\t') w.WriteString("ACPY(") w.WriteString(name) w.WriteString(", ") switch val.(type) { case *ast.CompositeLit: w.WriteString("((") dim, _ := cdd.Type(w, t.Elem()) dim = append([]string{"[]"}, dim...) w.WriteString("(" + dimFuncPtr("", dim) + "))") cdd.Expr(w, val, typ) default: cdd.Expr(w, val, typ) } w.WriteString("));\n") case *types.Pointer: u, ok := val.(*ast.UnaryExpr) if !ok { assign = true break } c, ok := u.X.(*ast.CompositeLit) if !ok { assign = true break } cname := "cl" + cdd.gtc.uniqueId() ct := cdd.exprType(c) o := types.NewVar(c.Lbrace, cdd.gtc.pkg, cname, ct) cdd.gtc.pkg.Scope().Insert(o) acd := cdd.gtc.newCDD(o, VarDecl, cdd.il) acd.varDecl(new(bytes.Buffer), o.Type(), cdd.gtc.isGlobal(o), cname, c) cdd.InitNext = acd acds = append(acds, acd) w.WriteByte('\t') w.WriteString(name) w.WriteString(" = &") w.WriteString(cname) w.WriteString(";\n") default: assign = true } if assign { // Ordinary assignment gos to the init() function cdd.init = true w.WriteByte('\t') w.WriteString(name) w.WriteString(" = ") cdd.Expr(w, val, typ) w.WriteString(";\n") } cdd.copyInit(w) } return }
// representableConst reports whether x can be represented as // value of the given basic type kind and for the configuration // provided (only needed for int/uint sizes). // // If rounded != nil, *rounded is set to the rounded value of x for // representable floating-point values; it is left alone otherwise. // It is ok to provide the addressof the first argument for rounded. func representableConst(x exact.Value, conf *Config, as BasicKind, rounded *exact.Value) bool { switch x.Kind() { case exact.Unknown: return true case exact.Bool: return as == Bool || as == UntypedBool case exact.Int: if x, ok := exact.Int64Val(x); ok { switch as { case Int: var s = uint(conf.sizeof(Typ[as])) * 8 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1 case Int8: const s = 8 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int16: const s = 16 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int32: const s = 32 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int64: return true case Uint, Uintptr: if s := uint(conf.sizeof(Typ[as])) * 8; s < 64 { return 0 <= x && x <= int64(1)<<s-1 } return 0 <= x case Uint8: const s = 8 return 0 <= x && x <= 1<<s-1 case Uint16: const s = 16 return 0 <= x && x <= 1<<s-1 case Uint32: const s = 32 return 0 <= x && x <= 1<<s-1 case Uint64: return 0 <= x case Float32, Float64, Complex64, Complex128, UntypedInt, UntypedFloat, UntypedComplex: return true } } n := exact.BitLen(x) switch as { case Uint, Uintptr: var s = uint(conf.sizeof(Typ[as])) * 8 return exact.Sign(x) >= 0 && n <= int(s) case Uint64: return exact.Sign(x) >= 0 && n <= 64 case Float32, Complex64: if rounded == nil { return fitsFloat32(x) } r := roundFloat32(x) if r != nil { *rounded = r return true } case Float64, Complex128: if rounded == nil { return fitsFloat64(x) } r := roundFloat64(x) if r != nil { *rounded = r return true } case UntypedInt, UntypedFloat, UntypedComplex: return true } case exact.Float: switch as { case Float32, Complex64: if rounded == nil { return fitsFloat32(x) } r := roundFloat32(x) if r != nil { *rounded = r return true } case Float64, Complex128: if rounded == nil { return fitsFloat64(x) } r := roundFloat64(x) if r != nil { *rounded = r return true } case UntypedFloat, UntypedComplex: return true } case exact.Complex: switch as { case Complex64: if rounded == nil { return fitsFloat32(exact.Real(x)) && fitsFloat32(exact.Imag(x)) } re := roundFloat32(exact.Real(x)) im := roundFloat32(exact.Imag(x)) if re != nil && im != nil { *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) return true } case Complex128: if rounded == nil { return fitsFloat64(exact.Real(x)) && fitsFloat64(exact.Imag(x)) } re := roundFloat64(exact.Real(x)) im := roundFloat64(exact.Imag(x)) if re != nil && im != nil { *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) return true } case UntypedComplex: return true } case exact.String: return as == String || as == UntypedString default: unreachable() } return false }
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)) } }
// typInternal contains the core of type checking of types. // Must only be called by typ. // func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type { switch e := e.(type) { case *ast.BadExpr: // ignore - error reported before case *ast.Ident: var x operand check.ident(&x, e, def, cycleOk) switch x.mode { case typexpr: return x.typ case invalid: // ignore - error reported before case novalue: check.errorf(x.pos(), "%s used as type", &x) default: check.errorf(x.pos(), "%s is not a type", &x) } case *ast.SelectorExpr: var x operand check.selector(&x, e) switch x.mode { case typexpr: return x.typ case invalid: // ignore - error reported before case novalue: check.errorf(x.pos(), "%s used as type", &x) default: check.errorf(x.pos(), "%s is not a type", &x) } case *ast.ParenExpr: return check.typ(e.X, def, cycleOk) case *ast.ArrayType: if e.Len != nil { var x operand check.expr(&x, e.Len) if x.mode != constant { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", &x) } break } if !x.isInteger() { check.errorf(x.pos(), "array length %s must be integer", &x) break } n, ok := exact.Int64Val(x.val) if !ok || n < 0 { check.errorf(x.pos(), "invalid array length %s", &x) break } typ := new(Array) if def != nil { def.underlying = typ } typ.len = n typ.elem = check.typ(e.Elt, nil, cycleOk) return typ } else { typ := new(Slice) if def != nil { def.underlying = typ } typ.elem = check.typ(e.Elt, nil, true) return typ } case *ast.StructType: typ := new(Struct) if def != nil { def.underlying = typ } typ.fields, typ.tags = check.collectFields(e.Fields, cycleOk) return typ case *ast.StarExpr: typ := new(Pointer) if def != nil { def.underlying = typ } typ.base = check.typ(e.X, nil, true) return typ case *ast.FuncType: return check.funcType(nil, e, def) case *ast.InterfaceType: return check.interfaceType(e, def, cycleOk) case *ast.MapType: typ := new(Map) if def != nil { def.underlying = typ } typ.key = check.typ(e.Key, nil, true) typ.elem = check.typ(e.Value, nil, true) // spec: "The comparison operators == and != must be fully defined // for operands of the key type; thus the key type must not be a // function, map, or slice." // // Delay this check because it requires fully setup types; // it is safe to continue in any case (was issue 6667). check.delay(func() { if !isComparable(typ.key) { check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key) } }) return typ case *ast.ChanType: typ := new(Chan) if def != nil { def.underlying = typ } typ.dir = e.Dir typ.elem = check.typ(e.Value, nil, true) return typ default: check.errorf(e.Pos(), "%s is not a type", e) } return Typ[Invalid] }
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)) } }