github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/pkg/internal/context.go (about) 1 // Copyright 2020 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 internal 16 17 import ( 18 "io" 19 "math/big" 20 21 "github.com/cockroachdb/apd/v2" 22 "github.com/joomcode/cue/cue" 23 "github.com/joomcode/cue/cue/registry" 24 "github.com/joomcode/cue/cue/token" 25 "github.com/joomcode/cue/internal/core/adt" 26 "github.com/joomcode/cue/internal/core/runtime" 27 "github.com/joomcode/cue/internal/value" 28 ) 29 30 // CallCtxt is passed to builtin implementations that need to use a cue.Value. This is an internal type. Its interface may change. 31 type CallCtxt struct { 32 ctx *adt.OpContext 33 builtin *Builtin 34 Err interface{} 35 Ret interface{} 36 37 args []adt.Value 38 } 39 40 func (c *CallCtxt) Runtime() *runtime.Runtime { 41 return (c.ctx.Runtime).(*runtime.Runtime) 42 } 43 44 func (c *CallCtxt) Registry() *registry.Registry { 45 return (c.ctx.Runtime).(*runtime.Runtime).Registry 46 } 47 48 func (c *CallCtxt) Pos() token.Pos { 49 return c.ctx.Pos() 50 } 51 52 func (c *CallCtxt) Name() string { 53 return c.builtin.name(c.ctx) 54 } 55 56 // Do returns whether the call should be done. 57 func (c *CallCtxt) Do() bool { 58 return c.Err == nil 59 } 60 61 func (c *CallCtxt) Value(i int) cue.Value { 62 v := value.Make(c.ctx, c.args[i]) 63 // TODO: remove default 64 // v, _ = v.Default() 65 if !v.IsConcrete() { 66 c.errcf(adt.IncompleteError, "non-concrete argument %d", i) 67 } 68 return v 69 } 70 71 func (c *CallCtxt) Struct(i int) *cue.Struct { 72 v := value.Make(c.ctx, c.args[i]) 73 s, err := v.Struct() 74 if err != nil { 75 c.invalidArgType(c.args[i], i, "struct", err) 76 return nil 77 } 78 return s 79 } 80 81 func (c *CallCtxt) Int(i int) int { return int(c.intValue(i, 64, "int64")) } 82 func (c *CallCtxt) Int8(i int) int8 { return int8(c.intValue(i, 8, "int8")) } 83 func (c *CallCtxt) Int16(i int) int16 { return int16(c.intValue(i, 16, "int16")) } 84 func (c *CallCtxt) Int32(i int) int32 { return int32(c.intValue(i, 32, "int32")) } 85 func (c *CallCtxt) Rune(i int) rune { return rune(c.intValue(i, 32, "rune")) } 86 func (c *CallCtxt) Int64(i int) int64 { return int64(c.intValue(i, 64, "int64")) } 87 88 func (c *CallCtxt) intValue(i, bits int, typ string) int64 { 89 arg := c.args[i] 90 x := value.Make(c.ctx, arg) 91 n, err := x.Int(nil) 92 if err != nil { 93 c.invalidArgType(arg, i, typ, err) 94 return 0 95 } 96 if n.BitLen() > bits { 97 c.errf(err, "int %s overflows %s in argument %d in call to %s", 98 n, typ, i, c.Name()) 99 } 100 res, _ := x.Int64() 101 return res 102 } 103 104 func (c *CallCtxt) Uint(i int) uint { return uint(c.uintValue(i, 64, "uint64")) } 105 func (c *CallCtxt) Uint8(i int) uint8 { return uint8(c.uintValue(i, 8, "uint8")) } 106 func (c *CallCtxt) Byte(i int) uint8 { return byte(c.uintValue(i, 8, "byte")) } 107 func (c *CallCtxt) Uint16(i int) uint16 { return uint16(c.uintValue(i, 16, "uint16")) } 108 func (c *CallCtxt) Uint32(i int) uint32 { return uint32(c.uintValue(i, 32, "uint32")) } 109 func (c *CallCtxt) Uint64(i int) uint64 { return uint64(c.uintValue(i, 64, "uint64")) } 110 111 func (c *CallCtxt) uintValue(i, bits int, typ string) uint64 { 112 x := value.Make(c.ctx, c.args[i]) 113 n, err := x.Int(nil) 114 if err != nil || n.Sign() < 0 { 115 c.invalidArgType(c.args[i], i, typ, err) 116 return 0 117 } 118 if n.BitLen() > bits { 119 c.errf(err, "int %s overflows %s in argument %d in call to %s", 120 n, typ, i, c.Name()) 121 } 122 res, _ := x.Uint64() 123 return res 124 } 125 126 func (c *CallCtxt) Decimal(i int) *apd.Decimal { 127 x := value.Make(c.ctx, c.args[i]) 128 if _, err := x.MantExp(nil); err != nil { 129 c.invalidArgType(c.args[i], i, "Decimal", err) 130 return nil 131 } 132 return &c.args[i].(*adt.Num).X 133 } 134 135 func (c *CallCtxt) Float64(i int) float64 { 136 x := value.Make(c.ctx, c.args[i]) 137 res, err := x.Float64() 138 if err != nil { 139 c.invalidArgType(c.args[i], i, "float64", err) 140 return 0 141 } 142 return res 143 } 144 145 func (c *CallCtxt) BigInt(i int) *big.Int { 146 x := value.Make(c.ctx, c.args[i]) 147 n, err := x.Int(nil) 148 if err != nil { 149 c.invalidArgType(c.args[i], i, "int", err) 150 return nil 151 } 152 return n 153 } 154 155 var ten = big.NewInt(10) 156 157 func (c *CallCtxt) BigFloat(i int) *big.Float { 158 x := value.Make(c.ctx, c.args[i]) 159 var mant big.Int 160 exp, err := x.MantExp(&mant) 161 if err != nil { 162 c.invalidArgType(c.args[i], i, "float", err) 163 return nil 164 } 165 f := &big.Float{} 166 f.SetInt(&mant) 167 if exp != 0 { 168 var g big.Float 169 e := big.NewInt(int64(exp)) 170 f.Mul(f, g.SetInt(e.Exp(ten, e, nil))) 171 } 172 return f 173 } 174 175 func (c *CallCtxt) String(i int) string { 176 // TODO: use Evaluate instead. 177 x := value.Make(c.ctx, c.args[i]) 178 v, err := x.String() 179 if err != nil { 180 c.invalidArgType(c.args[i], i, "string", err) 181 return "" 182 } 183 return v 184 } 185 186 func (c *CallCtxt) Bytes(i int) []byte { 187 x := value.Make(c.ctx, c.args[i]) 188 v, err := x.Bytes() 189 if err != nil { 190 c.invalidArgType(c.args[i], i, "bytes", err) 191 return nil 192 } 193 return v 194 } 195 196 func (c *CallCtxt) Reader(i int) io.Reader { 197 x := value.Make(c.ctx, c.args[i]) 198 // TODO: optimize for string and bytes cases 199 r, err := x.Reader() 200 if err != nil { 201 c.invalidArgType(c.args[i], i, "bytes|string", err) 202 return nil 203 } 204 return r 205 } 206 207 func (c *CallCtxt) Bool(i int) bool { 208 x := value.Make(c.ctx, c.args[i]) 209 b, err := x.Bool() 210 if err != nil { 211 c.invalidArgType(c.args[i], i, "bool", err) 212 return false 213 } 214 return b 215 } 216 217 func (c *CallCtxt) List(i int) (a []cue.Value) { 218 arg := c.args[i] 219 x := value.Make(c.ctx, arg) 220 v, err := x.List() 221 if err != nil { 222 c.invalidArgType(c.args[i], i, "list", err) 223 return a 224 } 225 for v.Next() { 226 a = append(a, v.Value()) 227 } 228 return a 229 } 230 231 func (c *CallCtxt) Iter(i int) (a cue.Iterator) { 232 arg := c.args[i] 233 x := value.Make(c.ctx, arg) 234 v, err := x.List() 235 if err != nil { 236 c.invalidArgType(c.args[i], i, "list", err) 237 } 238 return v 239 } 240 241 func (c *CallCtxt) getList(i int) *adt.Vertex { 242 x := c.args[i] 243 switch v, ok := x.(*adt.Vertex); { 244 case ok && v.IsList(): 245 v.Finalize(c.ctx) 246 return v 247 248 case v != nil: 249 x = v.Value() 250 } 251 if x.Kind()&adt.ListKind == 0 { 252 var err error 253 if b, ok := x.(*adt.Bottom); ok { 254 err = &callError{b} 255 } 256 c.invalidArgType(c.args[i], i, "list", err) 257 } else { 258 err := c.ctx.NewErrf("non-concrete list for argument %d", i) 259 err.Code = adt.IncompleteError 260 c.Err = &callError{err} 261 } 262 return nil 263 } 264 265 func (c *CallCtxt) DecimalList(i int) (a []*apd.Decimal) { 266 v := c.getList(i) 267 if v == nil { 268 return nil 269 } 270 271 for j, w := range v.Elems() { 272 w.Finalize(c.ctx) // defensive 273 switch x := adt.Unwrap(adt.Default(w.Value())).(type) { 274 case *adt.Num: 275 a = append(a, &x.X) 276 277 case *adt.Bottom: 278 if x.IsIncomplete() { 279 c.Err = x 280 return nil 281 } 282 283 default: 284 if k := w.Kind(); k&adt.NumKind == 0 { 285 err := c.ctx.NewErrf( 286 "invalid list element %d in argument %d to call: cannot use value %s (%s) as number", 287 j, i, w, k) 288 c.Err = &callError{err} 289 return a 290 } 291 292 err := c.ctx.NewErrf( 293 "non-concrete value %s for element %d of number list argument %d", 294 w, j, i) 295 err.Code = adt.IncompleteError 296 c.Err = &callError{err} 297 return nil 298 } 299 } 300 return a 301 } 302 303 func (c *CallCtxt) StringList(i int) (a []string) { 304 v := c.getList(i) 305 if v == nil { 306 return nil 307 } 308 309 for j, w := range v.Elems() { 310 w.Finalize(c.ctx) // defensive 311 switch x := adt.Unwrap(adt.Default(w.Value())).(type) { 312 case *adt.String: 313 a = append(a, x.Str) 314 315 case *adt.Bottom: 316 if x.IsIncomplete() { 317 c.Err = x 318 return nil 319 } 320 321 default: 322 if k := w.Kind(); k&adt.StringKind == 0 { 323 err := c.ctx.NewErrf( 324 "invalid list element %d in argument %d to call: cannot use value %s (%s) as string", 325 j, i, w, k) 326 c.Err = &callError{err} 327 return a 328 } 329 330 err := c.ctx.NewErrf( 331 "non-concrete value %s for element %d of string list argument %d", 332 w, j, i) 333 err.Code = adt.IncompleteError 334 c.Err = &callError{err} 335 return nil 336 } 337 } 338 return a 339 }