// Uint64 returns the numeric value of this constant truncated to fit // an unsigned 64-bit integer. // func (c *Const) Uint64() uint64 { switch x := c.Value; x.Kind() { case exact.Int: if u, ok := exact.Uint64Val(x); ok { return u } return 0 case exact.Float: f, _ := exact.Float64Val(x) return uint64(f) } panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) }
// Uint64 returns the numeric value of this literal truncated to fit // an unsigned 64-bit integer. // func (l *Literal) Uint64() uint64 { switch x := l.Value; x.Kind() { case exact.Int: if u, ok := exact.Uint64Val(x); ok { return u } return 0 case exact.Float: f, _ := exact.Float64Val(x) return uint64(f) } panic(fmt.Sprintf("unexpected literal value: %T", l.Value)) }
func writeInt(w *bytes.Buffer, ev exact.Value, k types.BasicKind) { if k == types.Uintptr { u, _ := exact.Uint64Val(ev) w.WriteString("0x") w.WriteString(strconv.FormatUint(u, 16)) return } w.WriteString(ev.String()) switch k { case types.Int32: w.WriteByte('L') case types.Uint32: w.WriteString("UL") case types.Int64: w.WriteString("LL") case types.Uint64: w.WriteString("ULL") } }
func (check *Checker) shift(x, y *operand, op token.Token) { untypedx := isUntyped(x.typ) // The lhs must be of integer type or be representable // as an integer; otherwise the shift has no chance. if !isInteger(x.typ) && (!untypedx || !representableConst(x.val, nil, UntypedInt, nil)) { check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } // spec: "The right operand in a shift expression must have unsigned // integer type or be an untyped constant that can be converted to // unsigned integer type." switch { case isInteger(y.typ) && isUnsigned(y.typ): // nothing to do case isUntyped(y.typ): check.convertUntyped(y, Typ[UntypedInt]) if y.mode == invalid { x.mode = invalid return } default: check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) x.mode = invalid return } if x.mode == constant { if y.mode == constant { // rhs must be within reasonable bounds const stupidShift = 1023 - 1 + 52 // so we can express smallestFloat64 s, ok := exact.Uint64Val(y.val) if !ok || s > stupidShift { check.invalidOp(y.pos(), "stupid shift count %s", y) x.mode = invalid return } // The lhs is representable as an integer but may not be an integer // (e.g., 2.0, an untyped float) - this can only happen for untyped // non-integer numeric constants. Correct the type so that the shift // result is of integer type. if !isInteger(x.typ) { x.typ = Typ[UntypedInt] } x.val = exact.Shift(x.val, op, uint(s)) return } // non-constant shift with constant lhs if untypedx { // spec: "If the left operand of a non-constant shift // expression is an untyped constant, the type of the // constant is what it would be if the shift expression // were replaced by its left operand alone.". // // Delay operand checking until we know the final type: // The lhs expression must be in the untyped map, mark // the entry as lhs shift operand. info, found := check.untyped[x.expr] assert(found) info.isLhs = true check.untyped[x.expr] = info // keep x's type x.mode = value return } } // constant rhs must be >= 0 if y.mode == constant && exact.Sign(y.val) < 0 { check.invalidOp(y.pos(), "shift count %s must not be negative", y) } // non-constant shift - lhs must be an integer if !isInteger(x.typ) { check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } x.mode = value }
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 (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 (c *PkgContext) translateExpr(expr ast.Expr) string { exprType := c.info.Types[expr] if value, valueFound := c.info.Values[expr]; valueFound { basic := types.Typ[types.String] if value.Kind() != exact.String { // workaround for bug in go/types basic = exprType.Underlying().(*types.Basic) } switch { case basic.Info()&types.IsBoolean != 0: return strconv.FormatBool(exact.BoolVal(value)) case basic.Info()&types.IsInteger != 0: if is64Bit(basic) { d, _ := exact.Uint64Val(value) return fmt.Sprintf("new %s(%d, %d)", c.typeName(exprType), d>>32, d&(1<<32-1)) } d, _ := exact.Int64Val(value) return strconv.FormatInt(d, 10) case basic.Info()&types.IsFloat != 0: f, _ := exact.Float64Val(value) return strconv.FormatFloat(f, 'g', -1, 64) case basic.Info()&types.IsComplex != 0: r, _ := exact.Float64Val(exact.Real(value)) i, _ := exact.Float64Val(exact.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } return fmt.Sprintf("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case basic.Info()&types.IsString != 0: buffer := bytes.NewBuffer(nil) for _, r := range []byte(exact.StringVal(value)) { switch r { case '\b': buffer.WriteString(`\b`) case '\f': buffer.WriteString(`\f`) case '\n': buffer.WriteString(`\n`) case '\r': buffer.WriteString(`\r`) case '\t': buffer.WriteString(`\t`) case '\v': buffer.WriteString(`\v`) case '"': buffer.WriteString(`\"`) case '\\': buffer.WriteString(`\\`) default: if r < 0x20 || r > 0x7E { fmt.Fprintf(buffer, `\x%02X`, r) continue } buffer.WriteByte(r) } } return `"` + buffer.String() + `"` default: panic("Unhandled constant type: " + basic.String()) } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { elements := make([]string, 0) i := 0 zero := c.zeroValue(elementType) for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, _ := exact.Int64Val(c.info.Values[kve.Key]) i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateExprToType(element, elementType) i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) != 0 { zero := c.zeroValue(t.Elem()) for len(elements) < int(t.Len()) { elements = append(elements, zero) } return createListComposite(t.Elem(), elements) } return fmt.Sprintf("Go$makeArray(%s, %d, function() { return %s; })", toArrayType(t.Elem()), t.Len(), c.zeroValue(t.Elem())) case *types.Slice: return fmt.Sprintf("new %s(%s)", c.typeName(exprType), createListComposite(t.Elem(), collectIndexedElements(t.Elem()))) case *types.Map: elements := make([]string, len(e.Elts)*2) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) elements[i*2] = c.translateExprToType(kve.Key, t.Key()) elements[i*2+1] = c.translateExprToType(kve.Value, t.Elem()) } return fmt.Sprintf("new %s([%s])", c.typeName(exprType), strings.Join(elements, ", ")) case *types.Struct: elements := make([]string, t.NumFields()) isKeyValue := true if len(e.Elts) != 0 { _, isKeyValue = e.Elts[0].(*ast.KeyValueExpr) } if !isKeyValue { for i, element := range e.Elts { elements[i] = c.translateExprToType(element, t.Field(i).Type()) } } if isKeyValue { for i := range elements { elements[i] = c.zeroValue(t.Field(i).Type()) } for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) for j := range elements { if kve.Key.(*ast.Ident).Name == t.Field(j).Name() { elements[j] = c.translateExprToType(kve.Value, t.Field(j).Type()) break } } } } if named, isNamed := exprType.(*types.Named); isNamed { return fmt.Sprintf("new %s(%s)", c.objectName(named.Obj()), strings.Join(elements, ", ")) } structVar := c.newVariable("_struct") c.translateTypeSpec(&ast.TypeSpec{ Name: c.newIdent(structVar, t), Type: e.Type, }) return fmt.Sprintf("new %s(%s)", structVar, strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: return strings.TrimSpace(string(c.CatchOutput(func() { c.newScope(func() { params := c.translateParams(e.Type) closurePrefix := "(" closureSuffix := ")" if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") closurePrefix = "(function(" + list + ") { return " closureSuffix = "; })(" + list + ")" } c.Printf("%sfunction(%s) {", closurePrefix, strings.Join(params, ", ")) c.Indent(func() { c.translateFunctionBody(e.Body.List, exprType.(*types.Signature)) }) c.Printf("}%s", closureSuffix) }) }))) case *ast.UnaryExpr: switch e.Op { case token.AND: switch c.info.Types[e.X].Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) default: if _, isComposite := e.X.(*ast.CompositeLit); isComposite { return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.translateExpr(e.X), c.typeName(c.info.Types[e])) } closurePrefix := "" closureSuffix := "" if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") closurePrefix = "(function(" + list + ") { return " closureSuffix = "; })(" + list + ")" } vVar := c.newVariable("v") return fmt.Sprintf("%snew %s(function() { return %s; }, function(%s) { %s; })%s", closurePrefix, c.typeName(exprType), c.translateExpr(e.X), vVar, c.translateAssign(e.X, vVar), closureSuffix) } case token.ARROW: return "undefined" } t := c.info.Types[e.X] basic := t.Underlying().(*types.Basic) op := e.Op.String() switch e.Op { case token.ADD: return c.translateExpr(e.X) case token.SUB: if is64Bit(basic) { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(-%s.high, -%s.low))", x, c.translateExpr(e.X), c.typeName(t), x, x) } if basic.Info()&types.IsComplex != 0 { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(-%s.real, -%s.imag))", x, c.translateExpr(e.X), c.typeName(t), x, x) } case token.XOR: if is64Bit(basic) { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(~%s.high, ~%s.low >>> 0))", x, c.translateExpr(e.X), c.typeName(t), x, x) } op = "~" } return fixNumber(fmt.Sprintf("%s%s", op, c.translateExpr(e.X)), basic) case *ast.BinaryExpr: if e.Op == token.NEQ { return fmt.Sprintf("!(%s)", c.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } t := c.info.Types[e.X] t2 := c.info.Types[e.Y] _, isInterface := t2.Underlying().(*types.Interface) if isInterface { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && basic.Info()&types.IsNumeric != 0 { if is64Bit(basic) { var expr string switch e.Op { case token.MUL: return fmt.Sprintf("Go$mul64(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.QUO: return fmt.Sprintf("Go$div64(%s, %s, false)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.REM: return fmt.Sprintf("Go$div64(%s, %s, true)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.SHL: return fmt.Sprintf("Go$shiftLeft64(%s, %s)", c.translateExpr(e.X), c.flatten64(e.Y)) case token.SHR: return fmt.Sprintf("Go$shiftRight%s(%s, %s)", toJavaScriptType(basic), c.translateExpr(e.X), c.flatten64(e.Y)) case token.EQL: expr = "x.high === y.high && x.low === y.low" case token.LSS: expr = "x.high < y.high || (x.high === y.high && x.low < y.low)" case token.LEQ: expr = "x.high < y.high || (x.high === y.high && x.low <= y.low)" case token.GTR: expr = "x.high > y.high || (x.high === y.high && x.low > y.low)" case token.GEQ: expr = "x.high > y.high || (x.high === y.high && x.low >= y.low)" case token.ADD, token.SUB: expr = fmt.Sprintf("new %s(x.high %s y.high, x.low %s y.low)", c.typeName(t), e.Op, e.Op) case token.AND, token.OR, token.XOR: expr = fmt.Sprintf("new %s(x.high %s y.high, (x.low %s y.low) >>> 0)", c.typeName(t), e.Op, e.Op) case token.AND_NOT: expr = fmt.Sprintf("new %s(x.high &~ y.high, (x.low &~ y.low) >>> 0)", c.typeName(t)) default: panic(e.Op) } x := c.newVariable("x") y := c.newVariable("y") expr = strings.Replace(expr, "x.", x+".", -1) expr = strings.Replace(expr, "y.", y+".", -1) return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr) } if basic.Info()&types.IsComplex != 0 { var expr string switch e.Op { case token.EQL: expr = "x.real === y.real && x.imag === y.imag" case token.ADD, token.SUB: expr = fmt.Sprintf("new %s(x.real %s y.real, x.imag %s y.imag)", c.typeName(t), e.Op, e.Op) case token.MUL: expr = fmt.Sprintf("new %s(x.real * y.real - x.imag * y.imag, x.real * y.imag + x.imag * y.real)", c.typeName(t)) case token.QUO: return fmt.Sprintf("Go$divComplex(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) default: panic(e.Op) } x := c.newVariable("x") y := c.newVariable("y") expr = strings.Replace(expr, "x.", x+".", -1) expr = strings.Replace(expr, "y.", y+".", -1) return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr) } switch e.Op { case token.EQL: return fmt.Sprintf("%s === %s", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.LSS, token.LEQ, token.GTR, token.GEQ: return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)) case token.ADD, token.SUB: return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic) case token.MUL: if basic.Kind() == types.Int32 { x := c.newVariable("x") y := c.newVariable("y") return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >> 0) + (%s << 16 >>> 16) * %s) >> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y) } if basic.Kind() == types.Uint32 || basic.Kind() == types.Uintptr { x := c.newVariable("x") y := c.newVariable("y") return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >>> 0) + (%s << 16 >>> 16) * %s) >>> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y) } return fixNumber(fmt.Sprintf("%s * %s", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) case token.QUO: value := fmt.Sprintf("%s / %s", c.translateExpr(e.X), c.translateExpr(e.Y)) if basic.Info()&types.IsInteger != 0 { value = "(Go$obj = " + value + `, (Go$obj === Go$obj && Go$obj !== 1/0 && Go$obj !== -1/0) ? Go$obj : Go$throwRuntimeError("integer divide by zero"))` } switch basic.Kind() { case types.Int, types.Uint: return "(" + value + " >> 0)" // cut off decimals default: return fixNumber(value, basic) } case token.REM: return fmt.Sprintf(`(Go$obj = %s %% %s, Go$obj === Go$obj ? Go$obj : Go$throwRuntimeError("integer divide by zero"))`, c.translateExpr(e.X), c.translateExpr(e.Y)) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && basic.Info()&types.IsUnsigned != 0 { op = ">>>" } if c.info.Values[e.Y] != nil { return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), op, c.translateExpr(e.Y)), basic) } if e.Op == token.SHR && basic.Info()&types.IsUnsigned == 0 { return fixNumber(fmt.Sprintf("(%s >> Go$min(%s, 31))", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) } y := c.newVariable("y") return fixNumber(fmt.Sprintf("(%s = %s, %s < 32 ? (%s %s %s) : 0)", y, c.translateExprToType(e.Y, types.Typ[types.Uint]), y, c.translateExpr(e.X), op, y), basic) case token.AND, token.OR, token.XOR: return fixNumber(fmt.Sprintf("(%s %s %s)", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic) case token.AND_NOT: return fixNumber(fmt.Sprintf("(%s &~ %s)", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) default: panic(e.Op) } } switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ, token.LAND, token.LOR: return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)) case token.EQL: switch u := t.Underlying().(type) { case *types.Struct: x := c.newVariable("x") y := c.newVariable("y") var conds []string for i := 0; i < u.NumFields(); i++ { field := u.Field(i) if field.Name() == "_" { continue } conds = append(conds, c.translateExpr(&ast.BinaryExpr{ X: c.newIdent(x+"."+field.Name(), field.Type()), Op: token.EQL, Y: c.newIdent(y+"."+field.Name(), field.Type()), })) } if len(conds) == 0 { conds = []string{"true"} } return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), strings.Join(conds, " && ")) case *types.Interface: return fmt.Sprintf("Go$interfaceIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) case *types.Array: return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) case *types.Pointer: xUnary, xIsUnary := e.X.(*ast.UnaryExpr) yUnary, yIsUnary := e.Y.(*ast.UnaryExpr) if xIsUnary && xUnary.Op == token.AND && yIsUnary && yUnary.Op == token.AND { xIndex, xIsIndex := xUnary.X.(*ast.IndexExpr) yIndex, yIsIndex := yUnary.X.(*ast.IndexExpr) if xIsIndex && yIsIndex { return fmt.Sprintf("Go$sliceIsEqual(%s, %s, %s, %s)", c.translateExpr(xIndex.X), c.flatten64(xIndex.Index), c.translateExpr(yIndex.X), c.flatten64(yIndex.Index)) } } switch u.Elem().Underlying().(type) { case *types.Struct, *types.Interface: return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t) case *types.Array: return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) default: return fmt.Sprintf("Go$pointerIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) } default: return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t) } default: panic(e.Op) } case *ast.ParenExpr: return fmt.Sprintf("(%s)", c.translateExpr(e.X)) case *ast.IndexExpr: xType := c.info.Types[e.X] if ptr, isPointer := xType.(*types.Pointer); isPointer { xType = ptr.Elem() } switch t := xType.Underlying().(type) { case *types.Array: return fmt.Sprintf("%s[%s]", c.translateExpr(e.X), c.flatten64(e.Index)) case *types.Slice: sliceVar := c.newVariable("_slice") indexVar := c.newVariable("_index") return fmt.Sprintf(`(%s = %s, %s = %s, (%s >= 0 && %s < %s.length) ? %s.array[%s.offset + %s] : Go$throwRuntimeError("index out of range"))`, sliceVar, c.translateExpr(e.X), indexVar, c.flatten64(e.Index), indexVar, indexVar, sliceVar, sliceVar, sliceVar, indexVar) case *types.Map: key := c.makeKey(e.Index, t.Key()) if _, isTuple := exprType.(*types.Tuple); isTuple { return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? [Go$obj.v, true] : [%s, false])`, c.translateExpr(e.X), key, c.zeroValue(t.Elem())) } return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? Go$obj.v : %s)`, c.translateExpr(e.X), key, c.zeroValue(t.Elem())) case *types.Basic: return fmt.Sprintf("%s.charCodeAt(%s)", c.translateExpr(e.X), c.flatten64(e.Index)) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: b, isBasic := c.info.Types[e.X].(*types.Basic) isString := isBasic && b.Info()&types.IsString != 0 slice := c.translateExprToType(e.X, exprType) if e.High == nil { if e.Low == nil { return slice } if isString { return fmt.Sprintf("%s.substring(%s)", slice, c.flatten64(e.Low)) } return fmt.Sprintf("Go$subslice(%s, %s)", slice, c.flatten64(e.Low)) } low := "0" if e.Low != nil { low = c.flatten64(e.Low) } if isString { return fmt.Sprintf("%s.substring(%s, %s)", slice, low, c.flatten64(e.High)) } if e.Max != nil { return fmt.Sprintf("Go$subslice(%s, %s, %s, %s)", slice, low, c.flatten64(e.High), c.flatten64(e.Max)) } return fmt.Sprintf("Go$subslice(%s, %s, %s)", slice, low, c.flatten64(e.High)) case *ast.SelectorExpr: sel := c.info.Selections[e] parameterName := func(v *types.Var) string { if v.Anonymous() || v.Name() == "" { return c.newVariable("param") } return c.newVariable(v.Name()) } makeParametersList := func() []string { params := sel.Obj().Type().(*types.Signature).Params() names := make([]string, params.Len()) for i := 0; i < params.Len(); i++ { names[i] = parameterName(params.At(i)) } return names } switch sel.Kind() { case types.FieldVal: return c.translateExpr(e.X) + "." + translateSelection(sel) case types.MethodVal: parameters := makeParametersList() recv := c.newVariable("_recv") return fmt.Sprintf("(%s = %s, function(%s) { return %s.%s(%s); })", recv, c.translateExpr(e.X), strings.Join(parameters, ", "), recv, e.Sel.Name, strings.Join(parameters, ", ")) case types.MethodExpr: recv := "recv" if isWrapped(sel.Recv()) { recv = fmt.Sprintf("(new %s(recv))", c.typeName(sel.Recv())) } parameters := makeParametersList() return fmt.Sprintf("(function(%s) { return %s.%s(%s); })", strings.Join(append([]string{"recv"}, parameters...), ", "), recv, sel.Obj().(*types.Func).Name(), strings.Join(parameters, ", ")) case types.PackageObj: return fmt.Sprintf("%s.%s", c.translateExpr(e.X), e.Sel.Name) } panic("") case *ast.CallExpr: plainFun := e.Fun for { if p, isParen := plainFun.(*ast.ParenExpr); isParen { plainFun = p.X continue } break } switch f := plainFun.(type) { case *ast.Ident: switch o := c.info.Objects[f].(type) { case *types.Builtin: switch o.Name() { case "new": t := c.info.Types[e].(*types.Pointer) if types.IsIdentical(t.Elem().Underlying(), types.Typ[types.Uintptr]) { return "new Uint8Array(8)" } switch t.Elem().Underlying().(type) { case *types.Struct, *types.Array: return c.zeroValue(t.Elem()) default: return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.zeroValue(t.Elem()), c.typeName(t)) } case "make": switch t2 := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Slice: if len(e.Args) == 3 { return fmt.Sprintf("Go$subslice(new %s(Go$makeArray(%s, %s, function() { return %s; })), 0, %s)", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[2], types.Typ[types.Int]), c.zeroValue(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int])) } return fmt.Sprintf("new %s(Go$makeArray(%s, %s, function() { return %s; }))", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int]), c.zeroValue(t2.Elem())) default: args := []string{"undefined"} for _, arg := range e.Args[1:] { args = append(args, c.translateExpr(arg)) } return fmt.Sprintf("new %s(%s)", c.typeName(c.info.Types[e.Args[0]]), strings.Join(args, ", ")) } case "len": arg := c.translateExpr(e.Args[0]) switch argType := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Basic, *types.Slice: return arg + ".length" case *types.Pointer: return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Map: return fmt.Sprintf("(Go$obj = %s, Go$obj !== null ? Go$keys(Go$obj).length : 0)", arg) case *types.Chan: return "0" // length of array is constant default: panic(fmt.Sprintf("Unhandled len type: %T\n", argType)) } case "cap": arg := c.translateExpr(e.Args[0]) switch argType := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Slice: return arg + ".capacity" case *types.Pointer: return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Chan: return "0" // capacity of array is constant default: panic(fmt.Sprintf("Unhandled cap type: %T\n", argType)) } case "panic": return fmt.Sprintf("throw new Go$Panic(%s)", c.translateExprToType(e.Args[0], types.NewInterface(nil, nil))) case "append": if e.Ellipsis.IsValid() { return fmt.Sprintf("Go$append(%s, %s)", c.translateExpr(e.Args[0]), c.translateExprToType(e.Args[1], exprType)) } sliceType := exprType.Underlying().(*types.Slice) toAppend := createListComposite(sliceType.Elem(), c.translateExprSlice(e.Args[1:], sliceType.Elem())) return fmt.Sprintf("Go$append(%s, new %s(%s))", c.translateExpr(e.Args[0]), c.typeName(exprType), toAppend) case "delete": return fmt.Sprintf(`delete (%s || Go$Map.Go$nil)[%s]`, c.translateExpr(e.Args[0]), c.makeKey(e.Args[1], c.info.Types[e.Args[0]].Underlying().(*types.Map).Key())) case "copy": return fmt.Sprintf("Go$copy(%s, %s)", c.translateExprToType(e.Args[0], types.NewSlice(types.Typ[types.Byte])), c.translateExprToType(e.Args[1], types.NewSlice(types.Typ[types.Byte]))) case "print", "println": return fmt.Sprintf("console.log(%s)", strings.Join(c.translateExprSlice(e.Args, nil), ", ")) case "complex": return fmt.Sprintf("new %s(%s, %s)", c.typeName(c.info.Types[e]), c.translateExpr(e.Args[0]), c.translateExpr(e.Args[1])) case "real": return c.translateExpr(e.Args[0]) + ".real" case "imag": return c.translateExpr(e.Args[0]) + ".imag" case "recover": return "Go$recover()" case "close": return `Go$throwRuntimeError("not supported by GopherJS: close")` default: panic(fmt.Sprintf("Unhandled builtin: %s\n", o.Name())) } case *types.TypeName: // conversion if basic, isBasic := o.Type().Underlying().(*types.Basic); isBasic && !types.IsIdentical(c.info.Types[e.Args[0]], types.Typ[types.UnsafePointer]) { return fixNumber(c.translateExprToType(e.Args[0], o.Type()), basic) } return c.translateExprToType(e.Args[0], o.Type()) } case *ast.FuncType: // conversion return c.translateExprToType(e.Args[0], c.info.Types[plainFun]) } funType := c.info.Types[plainFun] sig, isSig := funType.Underlying().(*types.Signature) if !isSig { // conversion if c.pkg.Path() == "reflect" { if call, isCall := e.Args[0].(*ast.CallExpr); isCall && types.IsIdentical(c.info.Types[call.Fun], types.Typ[types.UnsafePointer]) { if named, isNamed := funType.(*types.Pointer).Elem().(*types.Named); isNamed { return c.translateExpr(call.Args[0]) + "." + named.Obj().Name() // unsafe conversion } } } return c.translateExprToType(e.Args[0], funType) } var fun string switch f := plainFun.(type) { case *ast.SelectorExpr: sel := c.info.Selections[f] switch sel.Kind() { case types.MethodVal: methodsRecvType := sel.Obj().(*types.Func).Type().(*types.Signature).Recv().Type() _, pointerExpected := methodsRecvType.(*types.Pointer) _, isPointer := sel.Recv().Underlying().(*types.Pointer) _, isStruct := sel.Recv().Underlying().(*types.Struct) _, isArray := sel.Recv().Underlying().(*types.Array) if pointerExpected && !isPointer && !isStruct && !isArray { target := c.translateExpr(f.X) vVar := c.newVariable("v") fun = fmt.Sprintf("(new %s(function() { return %s; }, function(%s) { %s = %s; })).%s", c.typeName(methodsRecvType), target, vVar, target, vVar, f.Sel.Name) break } if isWrapped(sel.Recv()) { fun = fmt.Sprintf("(new %s(%s)).%s", c.typeName(sel.Recv()), c.translateExpr(f.X), f.Sel.Name) break } fun = fmt.Sprintf("%s.%s", c.translateExpr(f.X), f.Sel.Name) case types.FieldVal, types.MethodExpr, types.PackageObj: fun = c.translateExpr(f) default: panic("") } default: fun = c.translateExpr(plainFun) } if len(e.Args) == 1 { if tuple, isTuple := c.info.Types[e.Args[0]].(*types.Tuple); isTuple { args := make([]ast.Expr, tuple.Len()) for i := range args { args[i] = c.newIdent(fmt.Sprintf("Go$tuple[%d]", i), tuple.At(i).Type()) } return fmt.Sprintf("(Go$tuple = %s, %s(%s))", c.translateExpr(e.Args[0]), fun, c.translateArgs(sig, args, false)) } } return fmt.Sprintf("%s(%s)", fun, c.translateArgs(sig, e.Args, e.Ellipsis.IsValid())) case *ast.StarExpr: if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 { if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.IsIdentical(c.info.Types[c2.Fun], types.Typ[types.UnsafePointer]) { if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND { return c.translateExpr(unary.X) // unsafe conversion } } } switch exprType.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } return c.translateExpr(e.X) + ".Go$get()" case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.info.Types[e.Type] check := "Go$obj !== null && " + c.typeCheck("Go$obj.constructor", t) value := "Go$obj" if _, isInterface := t.Underlying().(*types.Interface); !isInterface { value += ".Go$val" } if _, isTuple := exprType.(*types.Tuple); isTuple { return fmt.Sprintf("(Go$obj = %s, %s ? [%s, true] : [%s, false])", c.translateExpr(e.X), check, value, c.zeroValue(c.info.Types[e.Type])) } return fmt.Sprintf("(Go$obj = %s, %s ? %s : Go$typeAssertionFailed(Go$obj))", c.translateExpr(e.X), check, value) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := c.info.Objects[e].(type) { case *types.PkgName: return c.pkgVars[o.Pkg().Path()] case *types.Var, *types.Const: return c.objectName(o) case *types.Func: return c.objectName(o) case *types.TypeName: return c.typeName(o.Type()) case *types.Nil: return c.zeroValue(c.info.Types[e]) case nil: return e.Name default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case nil: return "" default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
func (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)) } }
func (c *compiler) VisitCompositeLit(lit *ast.CompositeLit) (v *LLVMValue) { typ := c.types.expr[lit].Type var valuemap map[interface{}]Value var valuelist []Value if ptr, ok := typ.(*types.Pointer); ok { typ = ptr.Elem() defer func() { v = v.pointer }() } var isstruct, isarray, isslice, ismap bool switch typ.Underlying().(type) { case *types.Struct: isstruct = true case *types.Array: isarray = true case *types.Slice: isslice = true case *types.Map: ismap = true default: panic(fmt.Errorf("Unhandled type: %s", typ)) } if lit.Elts != nil { for i, elt := range lit.Elts { if kv, iskv := elt.(*ast.KeyValueExpr); iskv { if valuemap == nil { valuemap = make(map[interface{}]Value) } var key interface{} var elttyp types.Type switch { case isstruct: name := kv.Key.(*ast.Ident).Name key = name typ := typ.Underlying().(*types.Struct) elttyp = typ.Field(fieldIndex(typ, name)).Type case isarray: key = c.types.expr[kv.Key].Value typ := typ.Underlying().(*types.Array) elttyp = typ.Elem() case isslice: key = c.types.expr[kv.Key].Value typ := typ.Underlying().(*types.Slice) elttyp = typ.Elem() case ismap: key = c.VisitExpr(kv.Key) typ := typ.Underlying().(*types.Map) elttyp = typ.Elem() default: panic("unreachable") } c.convertUntyped(kv.Value, elttyp) valuemap[key] = c.VisitExpr(kv.Value) } else { switch { case isstruct: typ := typ.Underlying().(*types.Struct) c.convertUntyped(elt, typ.Field(i).Type) case isarray: typ := typ.Underlying().(*types.Array) c.convertUntyped(elt, typ.Elem()) case isslice: typ := typ.Underlying().(*types.Slice) c.convertUntyped(elt, typ.Elem()) } value := c.VisitExpr(elt) valuelist = append(valuelist, value) } } } // For array/slice types, convert key:value to contiguous // values initialiser. switch typ.Underlying().(type) { case *types.Array, *types.Slice: if len(valuemap) > 0 { var maxkey uint64 for key, _ := range valuemap { key, _ := exact.Uint64Val(key.(exact.Value)) if key > maxkey { maxkey = key } } valuelist = make([]Value, maxkey+1) for key, value := range valuemap { key, _ := exact.Uint64Val(key.(exact.Value)) valuelist[key] = value } } } origtyp := typ switch typ := typ.Underlying().(type) { case *types.Array: elttype := typ.Elem() llvmelttype := c.types.ToLLVM(elttype) llvmvalues := make([]llvm.Value, typ.Len()) for i := range llvmvalues { var value Value if i < len(valuelist) { value = valuelist[i] } if value == nil { llvmvalues[i] = llvm.ConstNull(llvmelttype) } else if value.LLVMValue().IsConstant() { llvmvalues[i] = value.Convert(elttype).LLVMValue() } else { llvmvalues[i] = llvm.Undef(llvmelttype) } } array := llvm.ConstArray(llvmelttype, llvmvalues) for i, value := range valuelist { if llvmvalues[i].IsUndef() { value := value.Convert(elttype).LLVMValue() array = c.builder.CreateInsertValue(array, value, i, "") } } return c.NewValue(array, origtyp) case *types.Slice: ptr := c.createTypeMalloc(c.types.ToLLVM(typ)) eltType := c.types.ToLLVM(typ.Elem()) arrayType := llvm.ArrayType(eltType, len(valuelist)) valuesPtr := c.createMalloc(llvm.SizeOf(arrayType)) valuesPtr = c.builder.CreateIntToPtr(valuesPtr, llvm.PointerType(eltType, 0), "") //valuesPtr = c.builder.CreateBitCast(valuesPtr, llvm.PointerType(valuesPtr.Type(), 0), "") length := llvm.ConstInt(c.types.inttype, uint64(len(valuelist)), false) c.builder.CreateStore(valuesPtr, c.builder.CreateStructGEP(ptr, 0, "")) // data c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 1, "")) // len c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 2, "")) // cap null := llvm.ConstNull(c.types.ToLLVM(typ.Elem())) for i, value := range valuelist { index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false) valuePtr := c.builder.CreateGEP(valuesPtr, []llvm.Value{index}, "") if value == nil { c.builder.CreateStore(null, valuePtr) } else { c.builder.CreateStore(value.Convert(typ.Elem()).LLVMValue(), valuePtr) } } m := c.NewValue(ptr, types.NewPointer(origtyp)) return m.makePointee() case *types.Struct: values := valuelist llvmtyp := c.types.ToLLVM(typ) ptr := c.createTypeMalloc(llvmtyp) if valuemap != nil { for key, value := range valuemap { index := fieldIndex(typ, key.(string)) for len(values) <= index { values = append(values, nil) } values[index] = value } } for i, value := range values { if value != nil { elttype := typ.Field(i).Type llvm_value := value.Convert(elttype).LLVMValue() ptr := c.builder.CreateStructGEP(ptr, i, "") c.builder.CreateStore(llvm_value, ptr) } } m := c.NewValue(ptr, types.NewPointer(origtyp)) return m.makePointee() case *types.Map: value := llvm.ConstNull(c.types.ToLLVM(typ)) // TODO initialise map return c.NewValue(value, origtyp) } panic(fmt.Sprint("Unhandled type kind: ", typ)) }
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") }