cuelang.org/go@v0.13.0/cue/interpreter/wasm/call.go (about) 1 // Copyright 2023 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 wasm 16 17 import ( 18 "fmt" 19 20 "cuelang.org/go/cue" 21 "cuelang.org/go/internal/pkg" 22 "github.com/tetratelabs/wazero/api" 23 ) 24 25 func encBool(b bool) uint64 { 26 if b { 27 return api.EncodeU32(1) 28 } 29 return api.EncodeU32(0) 30 } 31 32 // encNumber returns the Wasm/System V ABI representation of the number 33 // wrapped into val, which must conform to the type of typ. 34 func encNumber(typ cue.Value, val cue.Value) (r uint64) { 35 ctx := val.Context() 36 37 _int32 := ctx.CompileString("int32") 38 if _int32.Subsume(typ) == nil { 39 i, _ := val.Int64() 40 return api.EncodeI32(int32(i)) 41 } 42 43 _int64 := ctx.CompileString("int64") 44 if _int64.Subsume(typ) == nil { 45 i, _ := val.Int64() 46 return api.EncodeI64(i) 47 } 48 49 _uint32 := ctx.CompileString("uint32") 50 if _uint32.Subsume(typ) == nil { 51 i, _ := val.Uint64() 52 return api.EncodeU32(uint32(i)) 53 } 54 55 _uint64 := ctx.CompileString("uint64") 56 if _uint64.Subsume(typ) == nil { 57 i, _ := val.Uint64() 58 return i 59 } 60 61 _float32 := ctx.CompileString("float32") 62 if _float32.Subsume(typ) == nil { 63 f, _ := val.Float64() 64 return api.EncodeF32(float32(f)) 65 } 66 67 _float64 := ctx.CompileString("float64") 68 if _float64.Subsume(typ) == nil { 69 f, _ := val.Float64() 70 return api.EncodeF64(f) 71 } 72 73 panic("encNumber: unsupported argument type") 74 } 75 76 func decBool(v uint64) bool { 77 return api.DecodeU32(v) == 1 78 } 79 80 // decNumber decodes the Wasm/System V ABI encoding of the 81 // val number of type typ into a Go value. 82 func decNumber(typ cue.Value, val uint64) (r any) { 83 ctx := typ.Context() 84 85 _int32 := ctx.CompileString("int32") 86 if _int32.Subsume(typ) == nil { 87 return api.DecodeI32(val) 88 } 89 90 _uint32 := ctx.CompileString("uint32") 91 if _uint32.Subsume(typ) == nil { 92 return api.DecodeU32(val) 93 } 94 95 _int64 := ctx.CompileString("int64") 96 if _int64.Subsume(typ) == nil { 97 return int64(val) 98 } 99 100 _uint64 := ctx.CompileString("uint64") 101 if _uint64.Subsume(typ) == nil { 102 return val 103 } 104 105 _float32 := ctx.CompileString("float32") 106 if _float32.Subsume(typ) == nil { 107 return api.DecodeF32(val) 108 } 109 110 _float64 := ctx.CompileString("float64") 111 if _float64.Subsume(typ) == nil { 112 return api.DecodeF64(val) 113 } 114 115 panic(fmt.Sprintf("unsupported argument type %v (kind %v)", typ, typ.IncompleteKind())) 116 } 117 118 func encBytes(i *instance, b []byte) *memory { 119 m, _ := i.Alloc(uint32(len(b))) 120 m.WriteAt(b, 0) 121 return m 122 } 123 124 // cABIFunc implements the Wasm/System V ABI translation. The named 125 // function, which must be loadable by the instance, and must be of 126 // the specified sig type, will be called by the runtime after its 127 // arguments will be converted according to the ABI. The result of the 128 // call will be then also be converted back into a Go value and handed 129 // to the runtime. 130 func cABIFunc(i *instance, name string, sig []cue.Value) func(*pkg.CallCtxt) { 131 // Compute the layout of all encountered structs (arguments 132 // and result) such that we will have it available at the time 133 // of an actual call. 134 argsTyp, resTyp := splitLast(sig) 135 argLayouts := make([]*structLayout, 0, len(argsTyp)) 136 var retLayout *structLayout 137 for _, typ := range argsTyp { 138 switch typ.IncompleteKind() { 139 case cue.StructKind: 140 argLayouts = append(argLayouts, structLayoutVal(typ)) 141 default: 142 argLayouts = append(argLayouts, nil) 143 } 144 } 145 if resTyp.IncompleteKind() == cue.StructKind { 146 retLayout = structLayoutVal(resTyp) 147 } 148 149 fn, _ := i.load(name) 150 return func(c *pkg.CallCtxt) { 151 argsTyp, resTyp := splitLast(sig) 152 args := make([]uint64, 0, len(argsTyp)) 153 for k, typ := range argsTyp { 154 switch typ.IncompleteKind() { 155 case cue.BoolKind: 156 args = append(args, encBool(c.Bool(k))) 157 case cue.IntKind, cue.FloatKind, cue.NumberKind: 158 args = append(args, encNumber(typ, c.Value(k))) 159 case cue.StructKind: 160 ms := encodeStruct(i, c.Value(k), argLayouts[k]) 161 defer i.FreeAll(ms) 162 163 args = append(args, uint64(ms[0].ptr)) 164 default: 165 panic(fmt.Sprintf("unsupported argument type %v (kind %v)", typ, typ.IncompleteKind())) 166 } 167 } 168 169 var retMem *memory 170 if resTyp.IncompleteKind() == cue.StructKind { 171 retMem, _ = i.Alloc(uint32(retLayout.size)) 172 // TODO: add support for structs containing pointers. 173 defer i.Free(retMem) 174 args = append(args, uint64(retMem.ptr)) 175 } 176 177 if c.Do() { 178 res, err := fn.Call(i.ctx, args...) 179 if err != nil { 180 c.Err = err 181 return 182 } 183 switch resTyp.IncompleteKind() { 184 case cue.BoolKind: 185 c.Ret = decBool(res[0]) 186 case cue.IntKind, cue.FloatKind, cue.NumberKind: 187 c.Ret = decNumber(resTyp, res[0]) 188 case cue.StructKind: 189 c.Ret = decodeStruct(retMem.Bytes(), retLayout) 190 default: 191 panic(fmt.Sprintf("unsupported result type %v (kind %v)", resTyp, resTyp.IncompleteKind())) 192 } 193 } 194 } 195 }