github.com/solo-io/cue@v0.4.7/internal/core/convert/go.go (about) 1 // Copyright 2019 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package convert allows converting to and from Go values and Types. 16 package convert 17 18 import ( 19 "encoding" 20 "encoding/json" 21 "fmt" 22 "math/big" 23 "reflect" 24 "sort" 25 "strconv" 26 "strings" 27 28 "github.com/cockroachdb/apd/v2" 29 "golang.org/x/text/encoding/unicode" 30 31 "github.com/solo-io/cue/cue/ast" 32 "github.com/solo-io/cue/cue/ast/astutil" 33 "github.com/solo-io/cue/cue/errors" 34 "github.com/solo-io/cue/cue/parser" 35 "github.com/solo-io/cue/cue/token" 36 "github.com/solo-io/cue/internal/core/adt" 37 "github.com/solo-io/cue/internal/core/compile" 38 "github.com/solo-io/cue/internal/types" 39 ) 40 41 // This file contains functionality for converting Go to CUE. 42 // 43 // The code in this file is a prototype implementation and is far from 44 // optimized. 45 46 func GoValueToValue(ctx *adt.OpContext, x interface{}, nilIsTop bool) adt.Value { 47 v := GoValueToExpr(ctx, nilIsTop, x) 48 // TODO: return Value 49 return toValue(v) 50 } 51 52 func GoTypeToExpr(ctx *adt.OpContext, x interface{}) (adt.Expr, errors.Error) { 53 v := convertGoType(ctx, reflect.TypeOf(x)) 54 if err := ctx.Err(); err != nil { 55 return v, err.Err 56 } 57 return v, nil 58 } 59 60 func toValue(e adt.Expr) adt.Value { 61 if v, ok := e.(adt.Value); ok { 62 return v 63 } 64 obj := &adt.Vertex{} 65 obj.AddConjunct(adt.MakeRootConjunct(nil, e)) 66 return obj 67 } 68 69 func compileExpr(ctx *adt.OpContext, expr ast.Expr) adt.Value { 70 c, err := compile.Expr(nil, ctx, pkgID(), expr) 71 if err != nil { 72 return &adt.Bottom{Err: errors.Promote(err, "compile")} 73 } 74 return adt.Resolve(ctx, c) 75 } 76 77 // parseTag parses a CUE expression from a cue tag. 78 func parseTag(ctx *adt.OpContext, obj *ast.StructLit, field, tag string) ast.Expr { 79 if p := strings.Index(tag, ","); p >= 0 { 80 tag = tag[:p] 81 } 82 if tag == "" { 83 return topSentinel 84 } 85 expr, err := parser.ParseExpr("<field:>", tag) 86 if err != nil { 87 err := errors.Promote(err, "parser") 88 ctx.AddErr(errors.Wrapf(err, ctx.Pos(), 89 "invalid tag %q for field %q", tag, field)) 90 return &ast.BadExpr{} 91 } 92 return expr 93 } 94 95 // TODO: should we allow mapping names in cue tags? This only seems like a good 96 // idea if we ever want to allow mapping CUE to a different name than JSON. 97 var tagsWithNames = []string{"json", "yaml", "protobuf"} 98 99 func getName(f *reflect.StructField) string { 100 name := f.Name 101 if f.Anonymous { 102 name = "" 103 } 104 for _, s := range tagsWithNames { 105 if tag, ok := f.Tag.Lookup(s); ok { 106 if p := strings.Index(tag, ","); p >= 0 { 107 tag = tag[:p] 108 } 109 if tag != "" { 110 name = tag 111 break 112 } 113 } 114 } 115 return name 116 } 117 118 // isOptional indicates whether a field should be marked as optional. 119 func isOptional(f *reflect.StructField) bool { 120 isOptional := false 121 switch f.Type.Kind() { 122 case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Interface, reflect.Slice: 123 // Note: it may be confusing to distinguish between an empty slice and 124 // a nil slice. However, it is also surprising to not be able to specify 125 // a default value for a slice. So for now we will allow it. 126 isOptional = true 127 } 128 if tag, ok := f.Tag.Lookup("cue"); ok { 129 // TODO: only if first field is not empty. 130 isOptional = false 131 for _, f := range strings.Split(tag, ",")[1:] { 132 switch f { 133 case "opt": 134 isOptional = true 135 case "req": 136 return false 137 } 138 } 139 } else if tag, ok = f.Tag.Lookup("json"); ok { 140 isOptional = false 141 for _, f := range strings.Split(tag, ",")[1:] { 142 if f == "omitempty" { 143 return true 144 } 145 } 146 } 147 return isOptional 148 } 149 150 // isOmitEmpty means that the zero value is interpreted as undefined. 151 func isOmitEmpty(f *reflect.StructField) bool { 152 isOmitEmpty := false 153 switch f.Type.Kind() { 154 case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Interface, reflect.Slice: 155 // Note: it may be confusing to distinguish between an empty slice and 156 // a nil slice. However, it is also surprising to not be able to specify 157 // a default value for a slice. So for now we will allow it. 158 isOmitEmpty = true 159 160 default: 161 // TODO: we can also infer omit empty if a type cannot be nil if there 162 // is a constraint that unconditionally disallows the zero value. 163 } 164 tag, ok := f.Tag.Lookup("json") 165 if ok { 166 isOmitEmpty = false 167 for _, f := range strings.Split(tag, ",")[1:] { 168 if f == "omitempty" { 169 return true 170 } 171 } 172 } 173 return isOmitEmpty 174 } 175 176 // parseJSON parses JSON into a CUE value. b must be valid JSON. 177 func parseJSON(ctx *adt.OpContext, b []byte) adt.Value { 178 expr, err := parser.ParseExpr("json", b) 179 if err != nil { 180 panic(err) // cannot happen 181 } 182 return compileExpr(ctx, expr) 183 } 184 185 func isZero(v reflect.Value) bool { 186 x := v.Interface() 187 if x == nil { 188 return true 189 } 190 switch k := v.Kind(); k { 191 case reflect.Struct, reflect.Array: 192 // we never allow optional values for these types. 193 return false 194 195 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, 196 reflect.Slice: 197 // Note that for maps we preserve the distinction between a nil map and 198 // an empty map. 199 return v.IsNil() 200 201 case reflect.String: 202 return v.Len() == 0 203 204 default: 205 return x == reflect.Zero(v.Type()).Interface() 206 } 207 } 208 209 func GoValueToExpr(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Expr { 210 e := convertRec(ctx, nilIsTop, x) 211 if e == nil { 212 return ctx.AddErrf("unsupported Go type (%T)", x) 213 } 214 return e 215 } 216 217 func isNil(x reflect.Value) bool { 218 switch x.Kind() { 219 // Only check for supported types; ignore func and chan. 220 case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface: 221 return x.IsNil() 222 } 223 return false 224 } 225 226 func convertRec(ctx *adt.OpContext, nilIsTop bool, x interface{}) adt.Value { 227 if t := (&types.Value{}); types.CastValue(t, x) { 228 // TODO: panic if nto the same runtime. 229 return t.V 230 } 231 src := ctx.Source() 232 switch v := x.(type) { 233 case nil: 234 if nilIsTop { 235 ident, _ := ctx.Source().(*ast.Ident) 236 return &adt.Top{Src: ident} 237 } 238 return &adt.Null{Src: ctx.Source()} 239 240 case *ast.File: 241 x, err := compile.Files(nil, ctx, pkgID(), v) 242 if err != nil { 243 return &adt.Bottom{Err: errors.Promote(err, "compile")} 244 } 245 if len(x.Conjuncts) != 1 { 246 panic("unexpected length") 247 } 248 return x 249 250 case ast.Expr: 251 return compileExpr(ctx, v) 252 253 case *big.Int: 254 return &adt.Num{Src: src, K: adt.IntKind, X: *apd.NewWithBigInt(v, 0)} 255 256 case *big.Rat: 257 // should we represent this as a binary operation? 258 n := &adt.Num{Src: src, K: adt.IntKind} 259 _, err := apd.BaseContext.Quo(&n.X, apd.NewWithBigInt(v.Num(), 0), apd.NewWithBigInt(v.Denom(), 0)) 260 if err != nil { 261 return ctx.AddErrf("could not convert *big.Rat: %v", err) 262 } 263 if !v.IsInt() { 264 n.K = adt.FloatKind 265 } 266 return n 267 268 case *big.Float: 269 n := &adt.Num{Src: src, K: adt.FloatKind} 270 _, _, err := n.X.SetString(v.String()) 271 if err != nil { 272 return ctx.AddErr(errors.Promote(err, "invalid float")) 273 } 274 return n 275 276 case *apd.Decimal: 277 // TODO: should we allow an "int" bit to be set here? It is a bit 278 // tricky, as we would also need to pass down the result of rounding. 279 // So more likely an API must return explicitly whether a value is 280 // a float or an int after all. 281 // The code to autodetect whether something is an integer can be done 282 // with this: 283 kind := adt.FloatKind 284 var d apd.Decimal 285 res, _ := apd.BaseContext.RoundToIntegralExact(&d, v) 286 if !res.Inexact() { 287 kind = adt.IntKind 288 } 289 n := &adt.Num{Src: ctx.Source(), K: kind} 290 n.X = *v 291 return n 292 293 case json.Marshaler: 294 b, err := v.MarshalJSON() 295 if err != nil { 296 return ctx.AddErr(errors.Promote(err, "json.Marshaler")) 297 } 298 299 return parseJSON(ctx, b) 300 301 case encoding.TextMarshaler: 302 b, err := v.MarshalText() 303 if err != nil { 304 return ctx.AddErr(errors.Promote(err, "encoding.TextMarshaler")) 305 } 306 b, err = json.Marshal(string(b)) 307 if err != nil { 308 return ctx.AddErr(errors.Promote(err, "json")) 309 } 310 return parseJSON(ctx, b) 311 312 case error: 313 var errs errors.Error 314 switch x := v.(type) { 315 case errors.Error: 316 errs = x 317 default: 318 errs = ctx.Newf("%s", x.Error()) 319 } 320 return &adt.Bottom{Err: errs} 321 case bool: 322 return &adt.Bool{Src: ctx.Source(), B: v} 323 case string: 324 s, _ := unicode.UTF8.NewEncoder().String(v) 325 return &adt.String{Src: ctx.Source(), Str: s} 326 case []byte: 327 return &adt.Bytes{Src: ctx.Source(), B: v} 328 case int: 329 return toInt(ctx, int64(v)) 330 case int8: 331 return toInt(ctx, int64(v)) 332 case int16: 333 return toInt(ctx, int64(v)) 334 case int32: 335 return toInt(ctx, int64(v)) 336 case int64: 337 return toInt(ctx, int64(v)) 338 case uint: 339 return toUint(ctx, uint64(v)) 340 case uint8: 341 return toUint(ctx, uint64(v)) 342 case uint16: 343 return toUint(ctx, uint64(v)) 344 case uint32: 345 return toUint(ctx, uint64(v)) 346 case uint64: 347 return toUint(ctx, uint64(v)) 348 case uintptr: 349 return toUint(ctx, uint64(v)) 350 case float64: 351 n := &adt.Num{Src: src, K: adt.FloatKind} 352 _, _, err := n.X.SetString(fmt.Sprint(v)) 353 if err != nil { 354 return ctx.AddErr(errors.Promote(err, "invalid float")) 355 } 356 return n 357 case float32: 358 n := &adt.Num{Src: src, K: adt.FloatKind} 359 _, _, err := n.X.SetString(fmt.Sprint(v)) 360 if err != nil { 361 return ctx.AddErr(errors.Promote(err, "invalid float")) 362 } 363 return n 364 365 case reflect.Value: 366 if v.CanInterface() { 367 return convertRec(ctx, nilIsTop, v.Interface()) 368 } 369 370 default: 371 value := reflect.ValueOf(v) 372 switch value.Kind() { 373 case reflect.Bool: 374 return &adt.Bool{Src: ctx.Source(), B: value.Bool()} 375 376 case reflect.String: 377 str := value.String() 378 str, _ = unicode.UTF8.NewEncoder().String(str) 379 // TODO: here and above: allow to fail on invalid strings. 380 // if !utf8.ValidString(str) { 381 // return ctx.AddErrf("cannot convert result to string: invalid UTF-8") 382 // } 383 return &adt.String{Src: ctx.Source(), Str: str} 384 385 case reflect.Int, reflect.Int8, reflect.Int16, 386 reflect.Int32, reflect.Int64: 387 return toInt(ctx, value.Int()) 388 389 case reflect.Uint, reflect.Uint8, reflect.Uint16, 390 reflect.Uint32, reflect.Uint64, reflect.Uintptr: 391 return toUint(ctx, value.Uint()) 392 393 case reflect.Float32, reflect.Float64: 394 return convertRec(ctx, nilIsTop, value.Float()) 395 396 case reflect.Ptr: 397 if value.IsNil() { 398 if nilIsTop { 399 ident, _ := ctx.Source().(*ast.Ident) 400 return &adt.Top{Src: ident} 401 } 402 return &adt.Null{Src: ctx.Source()} 403 } 404 return convertRec(ctx, nilIsTop, value.Elem().Interface()) 405 406 case reflect.Struct: 407 obj := &adt.StructLit{Src: src} 408 v := &adt.Vertex{} 409 env := ctx.Env(0) 410 if env == nil { 411 env = &adt.Environment{} 412 } 413 v.AddStruct(obj, env, adt.CloseInfo{}) 414 v.SetValue(ctx, adt.Finalized, &adt.StructMarker{}) 415 416 t := value.Type() 417 for i := 0; i < value.NumField(); i++ { 418 sf := t.Field(i) 419 if sf.PkgPath != "" { 420 continue 421 } 422 val := value.Field(i) 423 if !nilIsTop && isNil(val) { 424 continue 425 } 426 if tag, _ := sf.Tag.Lookup("json"); tag == "-" { 427 continue 428 } 429 if isOmitEmpty(&sf) && isZero(val) { 430 continue 431 } 432 sub := convertRec(ctx, nilIsTop, val.Interface()) 433 if sub == nil { 434 // mimic behavior of encoding/json: skip fields of unsupported types 435 continue 436 } 437 if _, ok := sub.(*adt.Bottom); ok { 438 return sub 439 } 440 441 // leave errors like we do during normal evaluation or do we 442 // want to return the error? 443 name := getName(&sf) 444 if name == "-" { 445 continue 446 } 447 if sf.Anonymous && name == "" { 448 arc, ok := sub.(*adt.Vertex) 449 if ok { 450 v.Arcs = append(v.Arcs, arc.Arcs...) 451 } 452 continue 453 } 454 455 f := ctx.StringLabel(name) 456 obj.Decls = append(obj.Decls, &adt.Field{Label: f, Value: sub}) 457 arc, ok := sub.(*adt.Vertex) 458 if ok { 459 a := *arc 460 arc = &a 461 arc.Label = f 462 } else { 463 arc = &adt.Vertex{Label: f, BaseValue: sub} 464 arc.UpdateStatus(adt.Finalized) 465 arc.AddConjunct(adt.MakeRootConjunct(nil, sub)) 466 } 467 v.Arcs = append(v.Arcs, arc) 468 } 469 470 return v 471 472 case reflect.Map: 473 v := &adt.Vertex{BaseValue: &adt.StructMarker{}} 474 v.SetValue(ctx, adt.Finalized, &adt.StructMarker{}) 475 476 t := value.Type() 477 switch key := t.Key(); key.Kind() { 478 default: 479 if !key.Implements(textMarshaler) { 480 return ctx.AddErrf("unsupported Go type for map key (%v)", key) 481 } 482 fallthrough 483 case reflect.String, 484 reflect.Int, reflect.Int8, reflect.Int16, 485 reflect.Int32, reflect.Int64, 486 reflect.Uint, reflect.Uint8, reflect.Uint16, 487 reflect.Uint32, reflect.Uint64, reflect.Uintptr: 488 489 keys := value.MapKeys() 490 sort.Slice(keys, func(i, j int) bool { 491 return fmt.Sprint(keys[i]) < fmt.Sprint(keys[j]) 492 }) 493 for _, k := range keys { 494 val := value.MapIndex(k) 495 // if isNil(val) { 496 // continue 497 // } 498 499 sub := convertRec(ctx, nilIsTop, val.Interface()) 500 // mimic behavior of encoding/json: report error of 501 // unsupported type. 502 if sub == nil { 503 return ctx.AddErrf("unsupported Go type (%T)", val.Interface()) 504 } 505 if isBottom(sub) { 506 return sub 507 } 508 509 s := fmt.Sprint(k) 510 f := ctx.StringLabel(s) 511 arc, ok := sub.(*adt.Vertex) 512 if ok { 513 a := *arc 514 arc = &a 515 arc.Label = f 516 } else { 517 arc = &adt.Vertex{Label: f, BaseValue: sub} 518 arc.UpdateStatus(adt.Finalized) 519 arc.AddConjunct(adt.MakeRootConjunct(nil, sub)) 520 } 521 v.Arcs = append(v.Arcs, arc) 522 } 523 } 524 525 return v 526 527 case reflect.Slice, reflect.Array: 528 var values []adt.Value 529 530 for i := 0; i < value.Len(); i++ { 531 val := value.Index(i) 532 x := convertRec(ctx, nilIsTop, val.Interface()) 533 if x == nil { 534 return ctx.AddErrf("unsupported Go type (%T)", 535 val.Interface()) 536 } 537 if isBottom(x) { 538 return x 539 } 540 values = append(values, x) 541 } 542 543 return ctx.NewList(values...) 544 } 545 } 546 return nil 547 } 548 549 func toInt(ctx *adt.OpContext, x int64) adt.Value { 550 n := &adt.Num{Src: ctx.Source(), K: adt.IntKind} 551 n.X = *apd.New(x, 0) 552 return n 553 } 554 555 func toUint(ctx *adt.OpContext, x uint64) adt.Value { 556 n := &adt.Num{Src: ctx.Source(), K: adt.IntKind} 557 n.X.Coeff.SetUint64(x) 558 return n 559 } 560 561 func convertGoType(ctx *adt.OpContext, t reflect.Type) adt.Expr { 562 // TODO: this can be much more efficient. 563 // TODO: synchronize 564 return goTypeToValue(ctx, true, t) 565 } 566 567 var ( 568 jsonMarshaler = reflect.TypeOf(new(json.Marshaler)).Elem() 569 textMarshaler = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() 570 topSentinel = ast.NewIdent("_") 571 ) 572 573 // goTypeToValue converts a Go Type to a value. 574 // 575 // TODO: if this value will always be unified with a concrete type in Go, then 576 // many of the fields may be omitted. 577 func goTypeToValue(ctx *adt.OpContext, allowNullDefault bool, t reflect.Type) adt.Expr { 578 if _, t, ok := ctx.LoadType(t); ok { 579 return t 580 } 581 582 _, v := goTypeToValueRec(ctx, allowNullDefault, t) 583 if v == nil { 584 return ctx.AddErrf("unsupported Go type (%v)", t) 585 } 586 return v 587 } 588 589 func goTypeToValueRec(ctx *adt.OpContext, allowNullDefault bool, t reflect.Type) (e ast.Expr, expr adt.Expr) { 590 if src, t, ok := ctx.LoadType(t); ok { 591 return src, t 592 } 593 594 switch reflect.Zero(t).Interface().(type) { 595 case *big.Int, big.Int: 596 e = ast.NewIdent("int") 597 goto store 598 599 case *big.Float, big.Float, *big.Rat, big.Rat: 600 e = ast.NewIdent("number") 601 goto store 602 603 case *apd.Decimal, apd.Decimal: 604 e = ast.NewIdent("number") 605 goto store 606 } 607 608 // Even if this is for types that we know cast to a certain type, it can't 609 // hurt to return top, as in these cases the concrete values will be 610 // strict instances and there cannot be any tags that further constrain 611 // the values. 612 if t.Implements(jsonMarshaler) || t.Implements(textMarshaler) { 613 return topSentinel, nil 614 } 615 616 switch k := t.Kind(); k { 617 case reflect.Ptr: 618 elem := t.Elem() 619 for elem.Kind() == reflect.Ptr { 620 elem = elem.Elem() 621 } 622 e, _ = goTypeToValueRec(ctx, false, elem) 623 if allowNullDefault { 624 e = wrapOrNull(e) 625 } 626 627 case reflect.Interface: 628 switch t.Name() { 629 case "error": 630 // This is really null | _|_. There is no error if the error is null. 631 e = ast.NewNull() 632 default: 633 e = topSentinel // `_` 634 } 635 636 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 637 reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 638 e = compile.LookupRange(t.Kind().String()).Source().(ast.Expr) 639 640 case reflect.Uint, reflect.Uintptr: 641 e = compile.LookupRange("uint64").Source().(ast.Expr) 642 643 case reflect.Int: 644 e = compile.LookupRange("int64").Source().(ast.Expr) 645 646 case reflect.String: 647 e = ast.NewIdent("__string") 648 649 case reflect.Bool: 650 e = ast.NewIdent("__bool") 651 652 case reflect.Float32, reflect.Float64: 653 e = ast.NewIdent("__number") 654 655 case reflect.Struct: 656 obj := &ast.StructLit{} 657 658 // TODO: dirty trick: set this to a temporary Vertex and then update the 659 // arcs and conjuncts of this vertex below. This will allow circular 660 // references. Maybe have a special kind of "hardlink" reference. 661 ctx.StoreType(t, obj, nil) 662 663 for i := 0; i < t.NumField(); i++ { 664 f := t.Field(i) 665 if f.PkgPath != "" { 666 continue 667 } 668 _, ok := f.Tag.Lookup("cue") 669 elem, _ := goTypeToValueRec(ctx, !ok, f.Type) 670 if isBad(elem) { 671 continue // Ignore fields for unsupported types 672 } 673 674 // leave errors like we do during normal evaluation or do we 675 // want to return the error? 676 name := getName(&f) 677 if name == "-" { 678 continue 679 } 680 681 if tag, ok := f.Tag.Lookup("cue"); ok { 682 v := parseTag(ctx, obj, name, tag) 683 if isBad(v) { 684 return v, nil 685 } 686 elem = ast.NewBinExpr(token.AND, elem, v) 687 } 688 // TODO: if an identifier starts with __ (or otherwise is not a 689 // valid CUE name), make it a string and create a map to a new 690 // name for references. 691 692 // The GO JSON decoder always allows a value to be undefined. 693 d := &ast.Field{Label: ast.NewIdent(name), Value: elem} 694 if isOptional(&f) { 695 d.Optional = token.Blank.Pos() 696 } 697 obj.Elts = append(obj.Elts, d) 698 } 699 700 // TODO: should we validate references here? Can be done using 701 // astutil.ToFile and astutil.Resolve. 702 703 e = obj 704 705 case reflect.Array, reflect.Slice: 706 if t.Elem().Kind() == reflect.Uint8 { 707 e = ast.NewIdent("__bytes") 708 } else { 709 elem, _ := goTypeToValueRec(ctx, allowNullDefault, t.Elem()) 710 if elem == nil { 711 b := ctx.AddErrf("unsupported Go type (%v)", t.Elem()) 712 return &ast.BadExpr{}, b 713 } 714 715 if t.Kind() == reflect.Array { 716 e = ast.NewBinExpr(token.MUL, 717 ast.NewLit(token.INT, strconv.Itoa(t.Len())), 718 ast.NewList(elem)) 719 } else { 720 e = ast.NewList(&ast.Ellipsis{Type: elem}) 721 } 722 } 723 if k == reflect.Slice { 724 e = wrapOrNull(e) 725 } 726 727 case reflect.Map: 728 switch key := t.Key(); key.Kind() { 729 case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, 730 reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, 731 reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 732 default: 733 b := ctx.AddErrf("unsupported Go type for map key (%v)", key) 734 return &ast.BadExpr{}, b 735 } 736 737 v, x := goTypeToValueRec(ctx, allowNullDefault, t.Elem()) 738 if v == nil { 739 b := ctx.AddErrf("unsupported Go type (%v)", t.Elem()) 740 return &ast.BadExpr{}, b 741 } 742 if isBad(v) { 743 return v, x 744 } 745 746 e = ast.NewStruct(&ast.Field{ 747 Label: ast.NewList(ast.NewIdent("__string")), 748 Value: v, 749 }) 750 751 e = wrapOrNull(e) 752 } 753 754 store: 755 // TODO: store error if not nil? 756 if e != nil { 757 f := &ast.File{Decls: []ast.Decl{&ast.EmbedDecl{Expr: e}}} 758 astutil.Resolve(f, func(_ token.Pos, msg string, args ...interface{}) { 759 ctx.AddErrf(msg, args...) 760 }) 761 var x adt.Expr 762 c, err := compile.Expr(nil, ctx, pkgID(), e) 763 if err != nil { 764 b := &adt.Bottom{Err: err} 765 ctx.AddBottom(b) 766 x = b 767 } else { 768 x = c.Expr() 769 } 770 ctx.StoreType(t, e, x) 771 return e, x 772 } 773 return e, nil 774 } 775 776 func isBottom(x adt.Node) bool { 777 if x == nil { 778 return true 779 } 780 b, _ := x.(*adt.Bottom) 781 return b != nil 782 } 783 784 func isBad(x ast.Expr) bool { 785 if x == nil { 786 return true 787 } 788 if bad, _ := x.(*ast.BadExpr); bad != nil { 789 return true 790 } 791 return false 792 } 793 794 func wrapOrNull(e ast.Expr) ast.Expr { 795 switch x := e.(type) { 796 case *ast.BasicLit: 797 if x.Kind == token.NULL { 798 return x 799 } 800 case *ast.BadExpr: 801 return e 802 } 803 return makeNullable(e, true) 804 } 805 806 func makeNullable(e ast.Expr, nullIsDefault bool) ast.Expr { 807 var null ast.Expr = ast.NewNull() 808 if nullIsDefault { 809 null = &ast.UnaryExpr{Op: token.MUL, X: null} 810 } 811 return ast.NewBinExpr(token.OR, null, e) 812 } 813 814 // pkgID returns a package path that can never resolve to an existing package. 815 func pkgID() string { 816 return "_" 817 }