// IntVal is a utility function returns an int64 constant value from an exact.Value, split into high and low int32. func (comp *Compilation) IntVal(eVal exact.Value, posStr string) (high, low int32) { iVal, isExact := exact.Int64Val(eVal) if !isExact { comp.LogWarning(posStr, "inexact", fmt.Errorf("constant value %d cannot be accurately represented in int64", iVal)) } return int32(iVal >> 32), int32(iVal & 0xFFFFFFFF) }
func (p *exporter) value(x exact.Value) { if trace { p.tracef("value { ") defer p.tracef("} ") } switch kind := x.Kind(); kind { case exact.Bool: tag := falseTag if exact.BoolVal(x) { tag = trueTag } p.int(tag) case exact.Int: if i, ok := exact.Int64Val(x); ok { p.int(int64Tag) p.int64(i) return } p.int(floatTag) p.float(x) case exact.Float: p.int(fractionTag) p.fraction(x) case exact.Complex: p.int(complexTag) p.fraction(exact.Real(x)) p.fraction(exact.Imag(x)) case exact.String: p.int(stringTag) p.string(exact.StringVal(x)) default: panic(fmt.Sprintf("unexpected value kind %d", kind)) } }
// Conversion type-checks the conversion T(x). // The result is in x. func (check *Checker) conversion(x *operand, T Type) { constArg := x.mode == constant var ok bool switch { case constArg && isConstType(T): // constant conversion switch t := T.Underlying().(*Basic); { case representableConst(x.val, check.conf, t.kind, &x.val): ok = true case x.isInteger() && isString(t): codepoint := int64(-1) if i, ok := exact.Int64Val(x.val); ok { codepoint = i } // If codepoint < 0 the absolute value is too large (or unknown) for // conversion. This is the same as converting any other out-of-range // value - let string(codepoint) do the work. x.val = exact.MakeString(string(codepoint)) ok = true } case x.convertibleTo(check.conf, T): // non-constant conversion x.mode = value ok = true } if !ok { check.errorf(x.pos(), "cannot convert %s to %s", x, T) x.mode = invalid return } // The conversion argument types are final. For untyped values the // conversion provides the type, per the spec: "A constant may be // given a type explicitly by a constant declaration or conversion,...". final := x.typ if isUntyped(x.typ) { final = T // - For conversions to interfaces, use the argument's default type. // - For conversions of untyped constants to non-constant types, also // use the default type (e.g., []byte("foo") should report string // not []byte as type for the constant "foo"). // - Keep untyped nil for untyped nil arguments. if IsInterface(T) || constArg && !isConstType(T) { final = defaultType(x.typ) } check.updateExprType(x.expr, final, true) } x.typ = T }
// Int64 returns the numeric value of this constant truncated to fit // a signed 64-bit integer. // func (c *Const) Int64() int64 { switch x := c.Value; x.Kind() { case exact.Int: if i, ok := exact.Int64Val(x); ok { return i } return 0 case exact.Float: f, _ := exact.Float64Val(x) return int64(f) } panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) }
func (check *Checker) arrayLength(e ast.Expr) int64 { var x operand check.expr(&x, e) if x.mode != constant { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", &x) } return 0 } if !x.isInteger() { check.errorf(x.pos(), "array length %s must be integer", &x) return 0 } n, ok := exact.Int64Val(x.val) if !ok || n < 0 { check.errorf(x.pos(), "invalid array length %s", &x) return 0 } return n }
// checkLongShift checks if shift or shift-assign operations shift by more than // the length of the underlying variable. func checkLongShift(f *File, node ast.Node, x, y ast.Expr) { v := f.pkg.types[y].Value if v == nil { return } amt, ok := exact.Int64Val(v) if !ok { return } t := f.pkg.types[x].Type if t == nil { return } b, ok := t.Underlying().(*types.Basic) if !ok { return } var size int64 var msg string switch b.Kind() { case types.Uint8, types.Int8: size = 8 case types.Uint16, types.Int16: size = 16 case types.Uint32, types.Int32: size = 32 case types.Uint64, types.Int64: size = 64 case types.Int, types.Uint, types.Uintptr: // These types may be as small as 32 bits, but no smaller. size = 32 msg = "might be " default: return } if amt >= size { ident := f.gofmt(x) f.Badf(node.Pos(), "%s %stoo small for shift of %d", ident, msg, amt) } }
// index checks an index expression for validity. // If max >= 0, it is the upper bound for index. // If index is valid and the result i >= 0, then i is the constant value of index. func (check *Checker) index(index ast.Expr, max int64) (i int64, valid bool) { var x operand check.expr(&x, index) if x.mode == invalid { return } // an untyped constant must be representable as Int check.convertUntyped(&x, Typ[Int]) if x.mode == invalid { return } // the index must be of integer type if !isInteger(x.typ) { check.invalidArg(x.pos(), "index %s must be integer", &x) return } // a constant index i must be in bounds if x.mode == constant { if exact.Sign(x.val) < 0 { check.invalidArg(x.pos(), "index %s must not be negative", &x) return } i, valid = exact.Int64Val(x.val) if !valid || max >= 0 && i >= max { check.errorf(x.pos(), "index %s is out of bounds", &x) return i, false } // 0 <= i [ && i < max ] return i, true } return -1, true }
// genDecl processes one declaration clause. func (f *File) genDecl(node ast.Node) bool { decl, ok := node.(*ast.GenDecl) if !ok || decl.Tok != token.CONST { // We only care about const declarations. return true } // The name of the type of the constants we are declaring. // Can change if this is a multi-element declaration. typ := "" // Loop over the elements of the declaration. Each element is a ValueSpec: // a list of names possibly followed by a type, possibly followed by values. // If the type and value are both missing, we carry down the type (and value, // but the "go/types" package takes care of that). for _, spec := range decl.Specs { vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST. if vspec.Type == nil && len(vspec.Values) > 0 { // "X = 1". With no type but a value, the constant is untyped. // Skip this vspec and reset the remembered type. typ = "" continue } if vspec.Type != nil { // "X T". We have a type. Remember it. ident, ok := vspec.Type.(*ast.Ident) if !ok { continue } typ = ident.Name } if typ != f.typeName { // This is not the type we're looking for. continue } // We now have a list of names (from one line of source code) all being // declared with the desired type. // Grab their names and actual values and store them in f.values. for _, name := range vspec.Names { if name.Name == "_" { continue } // This dance lets the type checker find the values for us. It's a // bit tricky: look up the object declared by the name, find its // types.Const, and extract its value. obj, ok := f.pkg.defs[name] if !ok { log.Fatalf("no value for constant %s", name) } info := obj.Type().Underlying().(*types.Basic).Info() if info&types.IsInteger == 0 { log.Fatalf("can't handle non-integer constant type %s", typ) } value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST. if value.Kind() != exact.Int { log.Fatalf("can't happen: constant is not an integer %s", name) } i64, isInt := exact.Int64Val(value) u64, isUint := exact.Uint64Val(value) if !isInt && !isUint { log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String()) } if !isInt { u64 = uint64(i64) } v := Value{ name: name.Name, value: u64, signed: info&types.IsUnsigned == 0, str: value.String(), } f.values = append(f.values, v) } } return false }
func (c *funcContext) translateExpr(expr ast.Expr) *expression { exprType := c.p.TypeOf(expr) if value := c.p.Types[expr].Value; value != nil { basic := exprType.Underlying().(*types.Basic) switch { case isBoolean(basic): return c.formatExpr("%s", strconv.FormatBool(exact.BoolVal(value))) case isInteger(basic): if is64Bit(basic) { if basic.Kind() == types.Int64 { d, ok := exact.Int64Val(value) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatInt(d>>32, 10), strconv.FormatUint(uint64(d)&(1<<32-1), 10)) } d, ok := exact.Uint64Val(value) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } d, ok := exact.Int64Val(value) if !ok { panic("could not get exact int") } return c.formatExpr("%s", strconv.FormatInt(d, 10)) case isFloat(basic): f, _ := exact.Float64Val(value) return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case isComplex(basic): r, _ := exact.Float64Val(exact.Real(value)) i, _ := exact.Float64Val(exact.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case isString(basic): return c.formatExpr("%s", encodeString(exact.StringVal(value))) default: panic("Unhandled constant type: " + basic.String()) } } var obj types.Object switch e := expr.(type) { case *ast.SelectorExpr: obj = c.p.Uses[e.Sel] case *ast.Ident: obj = c.p.Defs[e] if obj == nil { obj = c.p.Uses[e] } } if obj != nil && typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Global": return c.formatExpr("$global") case "Module": return c.formatExpr("$module") case "Undefined": return c.formatExpr("undefined") } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { var elements []string i := 0 zero := c.translateExpr(c.zeroValue(elementType)).String() for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, ok := exact.Int64Val(c.p.Types[kve.Key].Value) if !ok { panic("could not get exact int") } i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateImplicitConversionWithCloning(element, elementType).String() i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) == 0 { return c.formatExpr("%s.zero()", c.typeName(t)) } zero := c.translateExpr(c.zeroValue(t.Elem())).String() for len(elements) < int(t.Len()) { elements = append(elements, zero) } return c.formatExpr(`$toNativeArray(%s, [%s])`, typeKind(t.Elem()), strings.Join(elements, ", ")) case *types.Slice: return c.formatExpr("new %s([%s])", c.typeName(exprType), strings.Join(collectIndexedElements(t.Elem()), ", ")) case *types.Map: entries := make([]string, len(e.Elts)) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) entries[i] = fmt.Sprintf("{ k: %s, v: %s }", c.translateImplicitConversionWithCloning(kve.Key, t.Key()), c.translateImplicitConversionWithCloning(kve.Value, t.Elem())) } return c.formatExpr("$makeMap(%s.keyFor, [%s])", c.typeName(t.Key()), strings.Join(entries, ", ")) case *types.Struct: elements := make([]string, t.NumFields()) isKeyValue := true if len(e.Elts) != 0 { _, isKeyValue = e.Elts[0].(*ast.KeyValueExpr) } if !isKeyValue { for i, element := range e.Elts { elements[i] = c.translateImplicitConversionWithCloning(element, t.Field(i).Type()).String() } } if isKeyValue { for i := range elements { elements[i] = c.translateExpr(c.zeroValue(t.Field(i).Type())).String() } for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) for j := range elements { if kve.Key.(*ast.Ident).Name == t.Field(j).Name() { elements[j] = c.translateImplicitConversionWithCloning(kve.Value, t.Field(j).Type()).String() break } } } } return c.formatExpr("new %s.ptr(%s)", c.typeName(exprType), strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: _, fun := translateFunction(e.Type, nil, e.Body, c, exprType.(*types.Signature), c.p.FuncLitInfos[e], "") if len(c.p.escapingVars) != 0 { names := make([]string, 0, len(c.p.escapingVars)) for obj := range c.p.escapingVars { names = append(names, c.p.objectNames[obj]) } sort.Strings(names) list := strings.Join(names, ", ") return c.formatExpr("(function(%s) { return %s; })(%s)", list, fun, list) } return c.formatExpr("(%s)", fun) case *ast.UnaryExpr: t := c.p.TypeOf(e.X) switch e.Op { case token.AND: if typesutil.IsJsObject(exprType) { return c.formatExpr("%e.object", e.X) } switch t.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } switch x := astutil.RemoveParens(e.X).(type) { case *ast.CompositeLit: return c.formatExpr("$newDataPointer(%e, %s)", x, c.typeName(c.p.TypeOf(e))) case *ast.Ident: obj := c.p.Uses[x].(*types.Var) if c.p.escapingVars[obj] { return c.formatExpr("(%1s.$ptr || (%1s.$ptr = new %2s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %1s)))", c.p.objectNames[obj], c.typeName(exprType)) } return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) case *ast.SelectorExpr: sel, ok := c.p.Selections[x] if !ok { // qualified identifier obj := c.p.Uses[x.Sel].(*types.Var) return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) } newSel := &ast.SelectorExpr{X: c.newIdent("this.$target", c.p.TypeOf(x.X)), Sel: x.Sel} c.setType(newSel, exprType) c.p.Selections[newSel] = sel return c.formatExpr("(%1e.$ptr_%2s || (%1e.$ptr_%2s = new %3s(function() { return %4e; }, function($v) { %5s }, %1e)))", x.X, x.Sel.Name, c.typeName(exprType), newSel, c.translateAssign(newSel, c.newIdent("$v", exprType), false)) case *ast.IndexExpr: if _, ok := c.p.TypeOf(x.X).Underlying().(*types.Slice); ok { return c.formatExpr("$indexPtr(%1e.$array, %1e.$offset + %2e, %3s)", x.X, x.Index, c.typeName(exprType)) } return c.formatExpr("$indexPtr(%e, %e, %s)", x.X, x.Index, c.typeName(exprType)) case *ast.StarExpr: return c.translateExpr(x.X) default: panic(fmt.Sprintf("Unhandled: %T\n", x)) } case token.ARROW: call := &ast.CallExpr{ Fun: c.newIdent("$recv", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", t)), types.NewTuple(types.NewVar(0, nil, "", exprType), types.NewVar(0, nil, "", types.Typ[types.Bool])), false)), Args: []ast.Expr{e.X}, } c.Blocking[call] = true if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("%e", call) } return c.formatExpr("%e[0]", call) } basic := t.Underlying().(*types.Basic) switch e.Op { case token.ADD: return c.translateExpr(e.X) case token.SUB: switch { case is64Bit(basic): return c.formatExpr("new %1s(-%2h, -%2l)", c.typeName(t), e.X) case isComplex(basic): return c.formatExpr("new %1s(-%2r, -%2i)", c.typeName(t), e.X) case isUnsigned(basic): return c.fixNumber(c.formatExpr("-%e", e.X), basic) default: return c.formatExpr("-%e", e.X) } case token.XOR: if is64Bit(basic) { return c.formatExpr("new %1s(~%2h, ~%2l >>> 0)", c.typeName(t), e.X) } return c.fixNumber(c.formatExpr("~%e", e.X), basic) case token.NOT: return c.formatExpr("!%e", e.X) default: panic(e.Op) } case *ast.BinaryExpr: if e.Op == token.NEQ { return c.formatExpr("!(%s)", c.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } t := c.p.TypeOf(e.X) t2 := c.p.TypeOf(e.Y) _, isInterface := t2.Underlying().(*types.Interface) if isInterface || types.Identical(t, types.Typ[types.UntypedNil]) { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && isNumeric(basic) { if is64Bit(basic) { switch e.Op { case token.MUL: return c.formatExpr("$mul64(%e, %e)", e.X, e.Y) case token.QUO: return c.formatExpr("$div64(%e, %e, false)", e.X, e.Y) case token.REM: return c.formatExpr("$div64(%e, %e, true)", e.X, e.Y) case token.SHL: return c.formatExpr("$shiftLeft64(%e, %f)", e.X, e.Y) case token.SHR: return c.formatExpr("$shiftRight%s(%e, %f)", toJavaScriptType(basic), e.X, e.Y) case token.EQL: return c.formatExpr("(%1h === %2h && %1l === %2l)", e.X, e.Y) case token.LSS: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l < %2l))", e.X, e.Y) case token.LEQ: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l <= %2l))", e.X, e.Y) case token.GTR: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l > %2l))", e.X, e.Y) case token.GEQ: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l >= %2l))", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1h %4t %2h, %1l %4t %2l)", e.X, e.Y, c.typeName(t), e.Op) case token.AND, token.OR, token.XOR: return c.formatExpr("new %3s(%1h %4t %2h, (%1l %4t %2l) >>> 0)", e.X, e.Y, c.typeName(t), e.Op) case token.AND_NOT: return c.formatExpr("new %3s(%1h & ~%2h, (%1l & ~%2l) >>> 0)", e.X, e.Y, c.typeName(t)) default: panic(e.Op) } } if isComplex(basic) { switch e.Op { case token.EQL: return c.formatExpr("(%1r === %2r && %1i === %2i)", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1r %4t %2r, %1i %4t %2i)", e.X, e.Y, c.typeName(t), e.Op) case token.MUL: return c.formatExpr("new %3s(%1r * %2r - %1i * %2i, %1r * %2i + %1i * %2r)", e.X, e.Y, c.typeName(t)) case token.QUO: return c.formatExpr("$divComplex(%e, %e)", e.X, e.Y) default: panic(e.Op) } } switch e.Op { case token.EQL: return c.formatParenExpr("%e === %e", e.X, e.Y) case token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.ADD, token.SUB: return c.fixNumber(c.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) case token.MUL: switch basic.Kind() { case types.Int32: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >> 0) + (%1e << 16 >>> 16) * %2e) >> 0", e.X, e.Y) case types.Uint32, types.Uintptr: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >>> 0) + (%1e << 16 >>> 16) * %2e) >>> 0", e.X, e.Y) } return c.fixNumber(c.formatExpr("%e * %e", e.X, e.Y), basic) case token.QUO: if isInteger(basic) { // cut off decimals shift := ">>" if isUnsigned(basic) { shift = ">>>" } return c.formatExpr(`(%1s = %2e / %3e, (%1s === %1s && %1s !== 1/0 && %1s !== -1/0) ? %1s %4s 0 : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_q"), e.X, e.Y, shift) } if basic.Kind() == types.Float32 { return c.fixNumber(c.formatExpr("%e / %e", e.X, e.Y), basic) } return c.formatExpr("%e / %e", e.X, e.Y) case token.REM: return c.formatExpr(`(%1s = %2e %% %3e, %1s === %1s ? %1s : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_r"), e.X, e.Y) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && isUnsigned(basic) { op = ">>>" } if c.p.Types[e.Y].Value != nil { return c.fixNumber(c.formatExpr("%e %s %e", e.X, op, e.Y), basic) } if e.Op == token.SHR && !isUnsigned(basic) { return c.fixNumber(c.formatParenExpr("%e >> $min(%e, 31)", e.X, e.Y), basic) } y := c.newVariable("y") return c.fixNumber(c.formatExpr("(%s = %s, %s < 32 ? (%e %s %s) : 0)", y, c.translateImplicitConversion(e.Y, types.Typ[types.Uint]), y, e.X, op, y), basic) case token.AND, token.OR: if isUnsigned(basic) { return c.formatParenExpr("(%e %t %e) >>> 0", e.X, e.Op, e.Y) } return c.formatParenExpr("%e %t %e", e.X, e.Op, e.Y) case token.AND_NOT: return c.fixNumber(c.formatParenExpr("%e & ~%e", e.X, e.Y), basic) case token.XOR: return c.fixNumber(c.formatParenExpr("%e ^ %e", e.X, e.Y), basic) default: panic(e.Op) } } switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.LAND: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (!(%s)) { %s = false; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e && %e", e.X, e.Y) case token.LOR: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (%s) { %s = true; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e || %e", e.X, e.Y) case token.EQL: switch u := t.Underlying().(type) { case *types.Array, *types.Struct: return c.formatExpr("$equal(%e, %e, %s)", e.X, e.Y, c.typeName(t)) case *types.Interface: return c.formatExpr("$interfaceIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Pointer: if _, ok := u.Elem().Underlying().(*types.Array); ok { return c.formatExpr("$equal(%s, %s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t), c.typeName(u.Elem())) } case *types.Basic: if isBoolean(u) { if b, ok := analysis.BoolValue(e.X, c.p.Info.Info); ok && b { return c.translateExpr(e.Y) } if b, ok := analysis.BoolValue(e.Y, c.p.Info.Info); ok && b { return c.translateExpr(e.X) } } } return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) default: panic(e.Op) } case *ast.ParenExpr: return c.formatParenExpr("%e", e.X) case *ast.IndexExpr: switch t := c.p.TypeOf(e.X).Underlying().(type) { case *types.Array, *types.Pointer: pattern := rangeCheck("%1e[%2f]", c.p.Types[e.Index].Value != nil, true) if _, ok := t.(*types.Pointer); ok { // check pointer for nix (attribute getter causes a panic) pattern = `(%1e.nilCheck, ` + pattern + `)` } return c.formatExpr(pattern, e.X, e.Index) case *types.Slice: return c.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f]", c.p.Types[e.Index].Value != nil, false), e.X, e.Index) case *types.Map: if typesutil.IsJsObject(c.p.TypeOf(e.Index)) { c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: e.Index.Pos(), Msg: "cannot use js.Object as map key"}) } key := fmt.Sprintf("%s.keyFor(%s)", c.typeName(t.Key()), c.translateImplicitConversion(e.Index, t.Key())) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? [%1s.v, true] : [%4e, false])`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) } return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? %1s.v : %4e)`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) case *types.Basic: return c.formatExpr("%e.charCodeAt(%f)", e.X, e.Index) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: if b, isBasic := c.p.TypeOf(e.X).Underlying().(*types.Basic); isBasic && isString(b) { switch { case e.Low == nil && e.High == nil: return c.translateExpr(e.X) case e.Low == nil: return c.formatExpr("%e.substring(0, %f)", e.X, e.High) case e.High == nil: return c.formatExpr("%e.substring(%f)", e.X, e.Low) default: return c.formatExpr("%e.substring(%f, %f)", e.X, e.Low, e.High) } } slice := c.translateConversionToSlice(e.X, exprType) switch { case e.Low == nil && e.High == nil: return c.formatExpr("%s", slice) case e.Low == nil: if e.Max != nil { return c.formatExpr("$subslice(%s, 0, %f, %f)", slice, e.High, e.Max) } return c.formatExpr("$subslice(%s, 0, %f)", slice, e.High) case e.High == nil: return c.formatExpr("$subslice(%s, %f)", slice, e.Low) default: if e.Max != nil { return c.formatExpr("$subslice(%s, %f, %f, %f)", slice, e.Low, e.High, e.Max) } return c.formatExpr("$subslice(%s, %f, %f)", slice, e.Low, e.High) } case *ast.SelectorExpr: sel, ok := c.p.Selections[e] if !ok { // qualified identifier return c.formatExpr("%s", c.objectName(obj)) } switch sel.Kind() { case types.FieldVal: fields, jsTag := c.translateSelection(sel, e.Pos()) if jsTag != "" { if _, ok := sel.Type().(*types.Signature); ok { return c.formatExpr("$internalize(%1e.%2s.%3s, %4s, %1e.%2s)", e.X, strings.Join(fields, "."), jsTag, c.typeName(sel.Type())) } return c.internalize(c.formatExpr("%e.%s.%s", e.X, strings.Join(fields, "."), jsTag), sel.Type()) } return c.formatExpr("%e.%s", e.X, strings.Join(fields, ".")) case types.MethodVal: recv := c.makeReceiver(e.X, sel) return c.formatExpr(`$methodVal(%s, "%s")`, recv, sel.Obj().(*types.Func).Name()) case types.MethodExpr: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } return c.formatExpr(`$methodExpr(%s, "%s")`, c.typeName(sel.Recv()), sel.Obj().(*types.Func).Name()) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } case *ast.CallExpr: plainFun := astutil.RemoveParens(e.Fun) if astutil.IsTypeExpr(plainFun, c.p.Info.Info) { return c.formatExpr("%s", c.translateConversion(e.Args[0], c.p.TypeOf(plainFun))) } sig := c.p.TypeOf(plainFun).Underlying().(*types.Signature) switch f := plainFun.(type) { case *ast.Ident: obj := c.p.Uses[f] if o, ok := obj.(*types.Builtin); ok { return c.translateBuiltin(o.Name(), sig, e.Args, e.Ellipsis.IsValid()) } if typesutil.IsJsPackage(obj.Pkg()) && obj.Name() == "InternalObject" { return c.translateExpr(e.Args[0]) } return c.translateCall(e, sig, c.translateExpr(f)) case *ast.SelectorExpr: sel, ok := c.p.Selections[f] if !ok { // qualified identifier obj := c.p.Uses[f.Sel] if typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Debugger": return c.formatExpr("debugger") case "InternalObject": return c.translateExpr(e.Args[0]) case "MakeFunc": return c.formatExpr("(function() { return $externalize(%e(this, new ($sliceType($jsObjectPtr))($global.Array.prototype.slice.call(arguments, []))), $emptyInterface); })", e.Args[0]) } } return c.translateCall(e, sig, c.translateExpr(f)) } externalizeExpr := func(e ast.Expr) string { t := c.p.TypeOf(e) if types.Identical(t, types.Typ[types.UntypedNil]) { return "null" } return c.externalize(c.translateExpr(e).String(), t) } externalizeArgs := func(args []ast.Expr) string { s := make([]string, len(args)) for i, arg := range args { s[i] = externalizeExpr(arg) } return strings.Join(s, ", ") } switch sel.Kind() { case types.MethodVal: recv := c.makeReceiver(f.X, sel) if typesutil.IsJsPackage(sel.Obj().Pkg()) { globalRef := func(id string) string { if recv.String() == "$global" && id[0] == '$' { return id } return recv.String() + "." + id } switch sel.Obj().Name() { case "Get": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s", globalRef(id)) } return c.formatExpr("%s[$externalize(%e, $String)]", recv, e.Args[0]) case "Set": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s = %s", globalRef(id), externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Delete": return c.formatExpr("delete %s[$externalize(%e, $String)]", recv, e.Args[0]) case "Length": return c.formatExpr("$parseInt(%s.length)", recv) case "Index": return c.formatExpr("%s[%e]", recv, e.Args[0]) case "SetIndex": return c.formatExpr("%s[%e] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Call": if id, ok := c.identifierConstant(e.Args[0]); ok { if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s.%s.apply(%s, %s))", objVar, recv, objVar, id, objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s(%s)", globalRef(id), externalizeArgs(e.Args[1:])) } if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s[$externalize(%e, $String)].apply(%s, %s))", objVar, recv, objVar, e.Args[0], objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)](%s)", recv, e.Args[0], externalizeArgs(e.Args[1:])) case "Invoke": if e.Ellipsis.IsValid() { return c.formatExpr("%s.apply(undefined, %s)", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("%s(%s)", recv, externalizeArgs(e.Args)) case "New": if e.Ellipsis.IsValid() { return c.formatExpr("new ($global.Function.prototype.bind.apply(%s, [undefined].concat(%s)))", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("new (%s)(%s)", recv, externalizeArgs(e.Args)) case "Bool": return c.internalize(recv, types.Typ[types.Bool]) case "String": return c.internalize(recv, types.Typ[types.String]) case "Int": return c.internalize(recv, types.Typ[types.Int]) case "Int64": return c.internalize(recv, types.Typ[types.Int64]) case "Uint64": return c.internalize(recv, types.Typ[types.Uint64]) case "Float": return c.internalize(recv, types.Typ[types.Float64]) case "Interface": return c.internalize(recv, types.NewInterface(nil, nil)) case "Unsafe": return recv default: panic("Invalid js package object: " + sel.Obj().Name()) } } methodName := sel.Obj().Name() if reservedKeywords[methodName] { methodName += "$" } return c.translateCall(e, sig, c.formatExpr("%s.%s", recv, methodName)) case types.FieldVal: fields, jsTag := c.translateSelection(sel, f.Pos()) if jsTag != "" { call := c.formatExpr("%e.%s.%s(%s)", f.X, strings.Join(fields, "."), jsTag, externalizeArgs(e.Args)) switch sig.Results().Len() { case 0: return call case 1: return c.internalize(call, sig.Results().At(0).Type()) default: c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: f.Pos(), Msg: "field with js tag can not have func type with multiple results"}) } } return c.translateCall(e, sig, c.formatExpr("%e.%s", f.X, strings.Join(fields, "."))) case types.MethodExpr: return c.translateCall(e, sig, c.translateExpr(f)) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } default: return c.translateCall(e, sig, c.translateExpr(plainFun)) } case *ast.StarExpr: if typesutil.IsJsObject(c.p.TypeOf(e.X)) { return c.formatExpr("new $jsObjectPtr(%e)", e.X) } if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 { if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.Identical(c.p.TypeOf(c2.Fun), types.Typ[types.UnsafePointer]) { if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND { return c.translateExpr(unary.X) // unsafe conversion } } } switch exprType.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } return c.formatExpr("%e.$get()", e.X) case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.p.TypeOf(e.Type) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("$assertType(%e, %s, true)", e.X, c.typeName(t)) } return c.formatExpr("$assertType(%e, %s)", e.X, c.typeName(t)) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := obj.(type) { case *types.Var, *types.Const: return c.formatExpr("%s", c.objectName(o)) case *types.Func: return c.formatExpr("%s", c.objectName(o)) case *types.TypeName: return c.formatExpr("%s", c.typeName(o.Type())) case *types.Nil: if typesutil.IsJsObject(exprType) { return c.formatExpr("null") } switch t := exprType.Underlying().(type) { case *types.Basic: if t.Kind() != types.UnsafePointer { panic("unexpected basic type") } return c.formatExpr("0") case *types.Slice, *types.Pointer, *types.Chan: return c.formatExpr("%s.nil", c.typeName(exprType)) case *types.Map: return c.formatExpr("false") case *types.Interface: return c.formatExpr("$ifaceNil") case *types.Signature: return c.formatExpr("$throwNilPointerError") default: panic(fmt.Sprintf("unexpected type: %T", t)) } default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case *this: if isWrapped(c.p.TypeOf(e)) { return c.formatExpr("this.$val") } return c.formatExpr("this") case nil: return c.formatExpr("") default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
func (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.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.Types[e].Value; val != nil { out.WriteString(c.translateExpr(e).String()) return } writeExpr("") case 'f': e := a[n].(ast.Expr) if val := c.p.Types[e].Value; val != nil { d, _ := exact.Int64Val(val) out.WriteString(strconv.FormatInt(d, 10)) return } if is64Bit(c.p.TypeOf(e).Underlying().(*types.Basic)) { out.WriteString("$flatten64(") writeExpr("") out.WriteString(")") return } writeExpr("") case 'h': e := a[n].(ast.Expr) if val := c.p.Types[e].Value; val != nil { d, _ := exact.Uint64Val(val) if c.p.TypeOf(e).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.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.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.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 (s *Sonar) Visit(n ast.Node) ast.Visitor { // TODO: detect "x&mask==0", emit sonar(x, x&^mask) switch nn := n.(type) { case *ast.BinaryExpr: break case *ast.GenDecl: if nn.Tok != token.VAR { return nil // constants and types are not interesting } return s case *ast.SelectorExpr: return nil case *ast.SwitchStmt: if nn.Tag == nil || nn.Body == nil { return s // recurse } // Replace: // switch a := foo(); bar(a) { // case x: ... // case y: ... // } // with: // switch { // default: // a := foo() // __tmp := bar(a) // switch { // case __tmp == x: ... // case __tmp == y: ... // } // } // The == comparisons will be instrumented later when we recurse. sw := new(ast.SwitchStmt) *sw = *nn var stmts []ast.Stmt if sw.Init != nil { stmts = append(stmts, sw.Init) sw.Init = nil } const tmpvar = "__go_fuzz_tmp" tmp := &ast.Ident{Name: tmpvar} typ := s.info.Types[sw.Tag] s.info.Types[tmp] = typ stmts = append(stmts, &ast.AssignStmt{Lhs: []ast.Expr{tmp}, Tok: token.DEFINE, Rhs: []ast.Expr{sw.Tag}}) stmts = append(stmts, &ast.AssignStmt{Lhs: []ast.Expr{&ast.Ident{Name: "_"}}, Tok: token.ASSIGN, Rhs: []ast.Expr{tmp}}) sw.Tag = nil stmts = append(stmts, sw) for _, cas1 := range sw.Body.List { cas := cas1.(*ast.CaseClause) for i, expr := range cas.List { tmp := &ast.Ident{Name: tmpvar, NamePos: expr.Pos()} s.info.Types[tmp] = typ cas.List[i] = &ast.BinaryExpr{X: tmp, Op: token.EQL, Y: expr} } } nn.Tag = nil nn.Init = nil nn.Body = &ast.BlockStmt{List: []ast.Stmt{&ast.CaseClause{Body: stmts}}} return s // recurse case *ast.ForStmt: // For condition is usually uninteresting, but produces lots of samples. // So we skip it if it looks boring. if nn.Init != nil { ast.Walk(s, nn.Init) } if nn.Post != nil { ast.Walk(s, nn.Post) } ast.Walk(s, nn.Body) if nn.Cond != nil { // Look for the following pattern: // for foo := ...; foo ? ...; ... { ... } boring := false if nn.Init != nil { if init, ok1 := nn.Init.(*ast.AssignStmt); ok1 && init.Tok == token.DEFINE && len(init.Lhs) == 1 { if id, ok2 := init.Lhs[0].(*ast.Ident); ok2 { if bex, ok3 := nn.Cond.(*ast.BinaryExpr); ok3 { if x, ok4 := bex.X.(*ast.Ident); ok4 && x.Name == id.Name { boring = true } if x, ok4 := bex.Y.(*ast.Ident); ok4 && x.Name == id.Name { boring = true } } } } } if !boring { ast.Walk(s, nn.Cond) } } return nil default: return s // recurse } // TODO: handle map index expressions (especially useful for strings). // E.g. when code matches a read in identifier against a set of known identifiers. // For the record, it looks as follows. However, it is tricky to distinguish // from slice/array index and map assignments... //. . . . . . . *ast.IndexExpr { //. . . . . . . . X: *ast.Ident { //. . . . . . . . . Name: "m" //. . . . . . . . } //. . . . . . . . Index: *ast.Ident { //. . . . . . . . . Name: "s" //. . . . . . . . } //. . . . . . . } // TODO: transform expressions so that lhs expression contains a variable // and rhs contains all constant operands. For example, for (real code from vp8 codec): // cf := (b[0]>>4)&7 == 5 // we would like to transform it to: // b[0] & (7<<4) == 5<<4 // and then to: // b[0] == 5<<4 | b & ^(7<<4) // and emit: // Sonar(b[0], 5<<4 | b & ^(7<<4), SonarEQL) // This will allow the fuzzer to figure out what bytes it needs to replace // with what bytes in order to crack this condition. // Similarly, for: // x/3 == 100 // we would like to emit: // Sonar(x, 100*3, SonarEQL) // TODO: intercept strings.Index/HasPrefix and similar functions. nn := n.(*ast.BinaryExpr) var flags uint8 switch nn.Op { case token.EQL: flags = SonarEQL break case token.NEQ: flags = SonarNEQ break case token.LSS: flags = SonarLSS break case token.GTR: flags = SonarGTR break case token.LEQ: flags = SonarLEQ break case token.GEQ: flags = SonarGEQ break default: return s // recurse } // Replace: // x != y // with: // func() bool { v1 := x; v2 := y; go-fuzz-dep.Sonar(v1, v2, flags); return v1 != v2 }() == true v1 := nn.X v2 := nn.Y ast.Walk(s, v1) ast.Walk(s, v2) if isCap(v1) || isCap(v2) { // Haven't seen useful cases yet. return s } if isLen(v1) || isLen(v2) { // TODO: we could pass both length value and the len argument. // For example, if the code is: // name := ... // obtained from input // if len(name) > 5 { ... } // If we would have the name value at runtime, we will know // what part of the input to alter to affect len result. flags |= SonarLength } checkType := func(tv types.TypeAndValue) bool { // Comparisons of pointers, maps, chans and bool are not interesting. if _, ok := tv.Type.(*types.Pointer); ok { return false } if _, ok := tv.Type.(*types.Map); ok { return false } if _, ok := tv.Type.(*types.Chan); ok { return false } s := tv.Type.Underlying().String() if s == "bool" || s == "ideal bool" || s == "error" || s == "untyped nil" || s == "unsafe.Pointer" { return false } return true } if !checkType(s.info.Types[v1]) || !checkType(s.info.Types[v2]) { return nil } var tv types.TypeAndValue if isConstExpr(s.info, v1) { flags |= SonarConst1 } else { tv = s.info.Types[v1] } if isConstExpr(s.info, v2) { flags |= SonarConst2 } else { tv = s.info.Types[v2] } if flags&SonarConst1 != 0 && flags&SonarConst2 != 0 { return nil } id := int(flags) | sonarSeq<<8 startPos := s.fset.Position(nn.Pos()) endPos := s.fset.Position(nn.End()) *s.blocks = append(*s.blocks, CoverBlock{sonarSeq, s.name, startPos.Line, startPos.Column, endPos.Line, endPos.Column, int(flags)}) sonarSeq++ block := &ast.BlockStmt{} typstr := tv.Type.String() if strings.HasPrefix(typstr, s.pkg+".") { typstr = typstr[len(s.pkg)+1:] } conv := func(name string, v ast.Expr) ast.Expr { // Convert const to the type of the other expr. isConst := isConstExpr(s.info, v) badConst := false if isConst { c := s.info.Types[v].Value if c.Kind() == exact.Int { if v, ok := exact.Int64Val(c); !ok || int64(int(v)) != v { // Such const can't be used outside of its current context, // because it will be converted to int and that will fail. badConst = true } } } if badConst || isWeirdShift(s.info, v) { v = &ast.CallExpr{ Fun: &ast.Ident{Name: typstr}, Args: []ast.Expr{v}, } s.info.Types[v] = tv } if !isConst { // Assign to a temp to avoid double side-effects. tmp := ast.NewIdent(name) block.List = append(block.List, &ast.AssignStmt{Tok: token.DEFINE, Lhs: []ast.Expr{tmp}, Rhs: []ast.Expr{v}}) v = tmp s.info.Types[v] = tv } return v } v1 = conv("v1", v1) v2 = conv("v2", v2) block.List = append(block.List, &ast.ExprStmt{ X: &ast.CallExpr{ Fun: &ast.SelectorExpr{X: &ast.Ident{Name: fuzzdepPkg}, Sel: &ast.Ident{Name: "Sonar"}}, Args: []ast.Expr{v1, v2, &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(id)}}, }, }, &ast.ReturnStmt{Results: []ast.Expr{&ast.BinaryExpr{Op: nn.Op, X: v1, Y: v2}}}, ) nn.X = &ast.CallExpr{ Fun: &ast.FuncLit{ Type: &ast.FuncType{Results: &ast.FieldList{List: []*ast.Field{{Type: &ast.Ident{Name: "bool"}}}}}, Body: block, }, } nn.Y = &ast.BasicLit{Kind: token.INT, Value: "true"} nn.Op = token.EQL return nil }
// representableConst reports whether x can be represented as // value of the given basic type kind and for the configuration // provided (only needed for int/uint sizes). // // If rounded != nil, *rounded is set to the rounded value of x for // representable floating-point values; it is left alone otherwise. // It is ok to provide the addressof the first argument for rounded. func representableConst(x exact.Value, conf *Config, as BasicKind, rounded *exact.Value) bool { switch x.Kind() { case exact.Unknown: return true case exact.Bool: return as == Bool || as == UntypedBool case exact.Int: if x, ok := exact.Int64Val(x); ok { switch as { case Int: var s = uint(conf.sizeof(Typ[as])) * 8 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1 case Int8: const s = 8 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int16: const s = 16 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int32: const s = 32 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int64: return true case Uint, Uintptr: if s := uint(conf.sizeof(Typ[as])) * 8; s < 64 { return 0 <= x && x <= int64(1)<<s-1 } return 0 <= x case Uint8: const s = 8 return 0 <= x && x <= 1<<s-1 case Uint16: const s = 16 return 0 <= x && x <= 1<<s-1 case Uint32: const s = 32 return 0 <= x && x <= 1<<s-1 case Uint64: return 0 <= x case Float32, Float64, Complex64, Complex128, UntypedInt, UntypedFloat, UntypedComplex: return true } } n := exact.BitLen(x) switch as { case Uint, Uintptr: var s = uint(conf.sizeof(Typ[as])) * 8 return exact.Sign(x) >= 0 && n <= int(s) case Uint64: return exact.Sign(x) >= 0 && n <= 64 case Float32, Complex64: if rounded == nil { return fitsFloat32(x) } r := roundFloat32(x) if r != nil { *rounded = r return true } case Float64, Complex128: if rounded == nil { return fitsFloat64(x) } r := roundFloat64(x) if r != nil { *rounded = r return true } case UntypedInt, UntypedFloat, UntypedComplex: return true } case exact.Float: switch as { case Float32, Complex64: if rounded == nil { return fitsFloat32(x) } r := roundFloat32(x) if r != nil { *rounded = r return true } case Float64, Complex128: if rounded == nil { return fitsFloat64(x) } r := roundFloat64(x) if r != nil { *rounded = r return true } case UntypedFloat, UntypedComplex: return true } case exact.Complex: switch as { case Complex64: if rounded == nil { return fitsFloat32(exact.Real(x)) && fitsFloat32(exact.Imag(x)) } re := roundFloat32(exact.Real(x)) im := roundFloat32(exact.Imag(x)) if re != nil && im != nil { *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) return true } case Complex128: if rounded == nil { return fitsFloat64(exact.Real(x)) && fitsFloat64(exact.Imag(x)) } re := roundFloat64(exact.Real(x)) im := roundFloat64(exact.Imag(x)) if re != nil && im != nil { *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) return true } case UntypedComplex: return true } case exact.String: return as == String || as == UntypedString default: unreachable() } return false }
// newValueFromConst converts a constant value to an LLVM value. func (fr *frame) newValueFromConst(v exact.Value, typ types.Type) *govalue { switch { case v == nil: llvmtyp := fr.types.ToLLVM(typ) return newValue(llvm.ConstNull(llvmtyp), typ) case isString(typ): if isUntyped(typ) { typ = types.Typ[types.String] } llvmtyp := fr.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(fr.module.Module, init.Type(), "") ptr.SetInitializer(init) ptr.SetLinkage(llvm.InternalLinkage) ptr = llvm.ConstBitCast(ptr, i8ptr) } else { ptr = llvm.ConstNull(i8ptr) } len_ := llvm.ConstInt(fr.types.inttype, uint64(strlen), false) llvmvalue := llvm.Undef(llvmtyp) llvmvalue = llvm.ConstInsertValue(llvmvalue, ptr, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, len_, []uint32{1}) return newValue(llvmvalue, typ) case isInteger(typ): if isUntyped(typ) { typ = types.Typ[types.Int] } llvmtyp := fr.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 newValue(llvmvalue, typ) case isBoolean(typ): if isUntyped(typ) { typ = types.Typ[types.Bool] } return newValue(boolLLVMValue(exact.BoolVal(v)), typ) case isFloat(typ): if isUntyped(typ) { typ = types.Typ[types.Float64] } llvmtyp := fr.types.ToLLVM(typ) floatval, _ := exact.Float64Val(v) llvmvalue := llvm.ConstFloat(llvmtyp, floatval) return newValue(llvmvalue, typ) case typ == types.Typ[types.UnsafePointer]: llvmtyp := fr.types.ToLLVM(typ) v, _ := exact.Uint64Val(v) llvmvalue := llvm.ConstInt(fr.types.inttype, v, false) llvmvalue = llvm.ConstIntToPtr(llvmvalue, llvmtyp) return newValue(llvmvalue, typ) case isComplex(typ): if isUntyped(typ) { typ = types.Typ[types.Complex128] } llvmtyp := fr.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 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 := fr.newValueFromConst(v, types.Typ[types.String]) return fr.convert(strval, typ) } } panic(fmt.Sprintf("unhandled: t=%s(%T), v=%v(%T)", typ, typ, v, v)) }