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