-
Notifications
You must be signed in to change notification settings - Fork 283
/
builtin.go
95 lines (89 loc) · 3.43 KB
/
builtin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Copyright 2023 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package wasm
import (
"cuelang.org/go/cue"
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/pkg"
"cuelang.org/go/internal/value"
)
// generateCallThatReturnsBuiltin returns a call expression to a nullary
// builtin that returns another builtin that corresponds to the external
// Wasm function declared by the user. name is the name of the function,
// args are its declared arguments, scope is a CUE value that represents
// the structure into which to resolve the arguments and i is the
// loaded Wasm instance that contains the function.
//
// This function is implemented as a higher-order function to solve a
// bootstrapping issue. The user can specifies arbitrary types for the
// function's arguments, and these types can be arbitrary CUE types
// defined in arbitrary places. This function is called before CUE
// evaluation at a time where identifiers are not yet resolved. This
// higher-order design solves the bootstrapping issue by deferring the
// resolution of identifiers (and selectors, and expressions in general)
// until runtime. At compile-time we only generate a nullary builtin
// that we call, and being nullary, it does not need to refer to any
// unresolved identifiers, rather the identifiers (and selectors) are
// saved in the closure that executes later, at runtime.
//
// Additionally, resolving identifiers (and selectors) requires an
// OpContext, and we do not have an OpContext at compile time. With
// this higher-order design we get an appropiate OpContext when the
// runtime calls the nullary builtin hence solving the bootstrapping
// problem.
func generateCallThatReturnsBuiltin(name string, scope adt.Value, args []string, i *instance) (adt.Expr, error) {
// ensure that the function exists before trying to call it.
_, err := i.load(name)
if err != nil {
return nil, err
}
call := &adt.CallExpr{Fun: &adt.Builtin{
Result: adt.TopKind,
Name: name,
Func: func(opctx *adt.OpContext, _ []adt.Value) adt.Expr {
scope := value.Make(opctx, scope)
sig := compileStringsInScope(args, scope)
args, result := splitLast(sig)
b := &pkg.Builtin{
Name: name,
Params: params(args),
Result: result.Kind(),
Func: cABIFunc(i, name, sig),
}
return pkg.ToBuiltin(b)
},
}}
return call, nil
}
// param converts a CUE value that represents the type of a function
// argument into its pkg.Param counterpart.
func param(arg cue.Value) pkg.Param {
param := pkg.Param{
Kind: arg.IncompleteKind(),
}
if param.Kind == adt.StructKind || ((param.Kind & adt.NumberKind) != 0) {
_, v := value.ToInternal(arg)
param.Value = v
}
return param
}
// params converts a list of CUE values that represent the types of a
// function's arguments into their pkg.Param counterparts.
func params(args []cue.Value) []pkg.Param {
var params []pkg.Param
for _, a := range args {
params = append(params, param(a))
}
return params
}