...

Source file src/cuelang.org/go/cue/interpreter/wasm/call.go

Documentation: cuelang.org/go/cue/interpreter/wasm

     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  }
   200  

View as plain text