cuelang.org/go@v0.10.1/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 u := api.DecodeU32(v) 78 if u == 1 { 79 return true 80 } 81 return false 82 } 83 84 // decNumber decodes the the Wasm/System V ABI encoding of the 85 // val number of type typ into a Go value. 86 func decNumber(typ cue.Value, val uint64) (r any) { 87 ctx := typ.Context() 88 89 _int32 := ctx.CompileString("int32") 90 if _int32.Subsume(typ) == nil { 91 return api.DecodeI32(val) 92 } 93 94 _uint32 := ctx.CompileString("uint32") 95 if _uint32.Subsume(typ) == nil { 96 return api.DecodeU32(val) 97 } 98 99 _int64 := ctx.CompileString("int64") 100 if _int64.Subsume(typ) == nil { 101 return int64(val) 102 } 103 104 _uint64 := ctx.CompileString("uint64") 105 if _uint64.Subsume(typ) == nil { 106 return val 107 } 108 109 _float32 := ctx.CompileString("float32") 110 if _float32.Subsume(typ) == nil { 111 return api.DecodeF32(val) 112 } 113 114 _float64 := ctx.CompileString("float64") 115 if _float64.Subsume(typ) == nil { 116 return api.DecodeF64(val) 117 } 118 119 panic(fmt.Sprintf("unsupported argument type %v (kind %v)", typ, typ.IncompleteKind())) 120 } 121 122 func encBytes(i *instance, b []byte) *memory { 123 m, _ := i.Alloc(uint32(len(b))) 124 m.WriteAt(b, 0) 125 return m 126 } 127 128 // cABIFunc implements the Wasm/System V ABI translation. The named 129 // function, which must be loadable by the instance, and must be of 130 // the specified sig type, will be called by the runtime after its 131 // arguments will be converted according to the ABI. The result of the 132 // call will be then also be converted back into a Go value and handed 133 // to the runtime. 134 func cABIFunc(i *instance, name string, sig []cue.Value) func(*pkg.CallCtxt) { 135 // Compute the layout of all encountered structs (arguments 136 // and result) such that we will have it available at the time 137 // of an actual call. 138 argsTyp, resTyp := splitLast(sig) 139 argLayouts := make([]*structLayout, 0, len(argsTyp)) 140 var retLayout *structLayout 141 for _, typ := range argsTyp { 142 switch typ.IncompleteKind() { 143 case cue.StructKind: 144 argLayouts = append(argLayouts, structLayoutVal(typ)) 145 default: 146 argLayouts = append(argLayouts, nil) 147 } 148 } 149 if resTyp.IncompleteKind() == cue.StructKind { 150 retLayout = structLayoutVal(resTyp) 151 } 152 153 fn, _ := i.load(name) 154 return func(c *pkg.CallCtxt) { 155 argsTyp, resTyp := splitLast(sig) 156 args := make([]uint64, 0, len(argsTyp)) 157 for k, typ := range argsTyp { 158 switch typ.IncompleteKind() { 159 case cue.BoolKind: 160 args = append(args, encBool(c.Bool(k))) 161 case cue.IntKind, cue.FloatKind, cue.NumberKind: 162 args = append(args, encNumber(typ, c.Value(k))) 163 case cue.StructKind: 164 ms := encodeStruct(i, c.Value(k), argLayouts[k]) 165 defer i.FreeAll(ms) 166 167 args = append(args, uint64(ms[0].ptr)) 168 default: 169 panic(fmt.Sprintf("unsupported argument type %v (kind %v)", typ, typ.IncompleteKind())) 170 } 171 } 172 173 var retMem *memory 174 if resTyp.IncompleteKind() == cue.StructKind { 175 retMem, _ = i.Alloc(uint32(retLayout.size)) 176 // TODO: add support for structs containing pointers. 177 defer i.Free(retMem) 178 args = append(args, uint64(retMem.ptr)) 179 } 180 181 if c.Do() { 182 res, err := fn.Call(i.ctx, args...) 183 if err != nil { 184 c.Err = err 185 return 186 } 187 switch resTyp.IncompleteKind() { 188 case cue.BoolKind: 189 c.Ret = decBool(res[0]) 190 case cue.IntKind, cue.FloatKind, cue.NumberKind: 191 c.Ret = decNumber(resTyp, res[0]) 192 case cue.StructKind: 193 c.Ret = decodeStruct(retMem.Bytes(), retLayout) 194 default: 195 panic(fmt.Sprintf("unsupported result type %v (kind %v)", resTyp, resTyp.IncompleteKind())) 196 } 197 } 198 } 199 }