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 allows users to write their own functions and make 16 // them available to CUE via Wasm modules. 17 // 18 // To enable Wasm support, pass the result of [New] to 19 // [cuelang.org/go/cue/cuecontext.New]. Wasm is enabled by default in 20 // the command line tool. 21 // 22 // Wasm is an experimental feature and the interface described in this 23 // document may change in the future. 24 // 25 // # Using Wasm modules in CUE 26 // 27 // To utilize Wasm modules, CUE files need to declare their intent by 28 // specifying a package attribute: 29 // 30 // @extern("wasm") 31 // package p 32 // 33 // Individual functions can then be imported from Wasm modules using 34 // a field attribute: 35 // 36 // add: _ @extern("foo.wasm", abi=c, sig="func(int64, int64): int64") 37 // mul: _ @extern("foo.wasm", abi=c, sig="func(float64, float64): float64") 38 // not: _ @extern("foo.wasm", abi=c, sig="func(bool): bool") 39 // 40 // The first attribute argument specifies the file name of the Wasm 41 // module, which must reside in the same directory as the CUE file 42 // which uses it. The abi indicates the abstract binary interface (ABI) 43 // used by the function (see below) while sig indicates the type 44 // signature of the function. The grammar for sig is: 45 // 46 // list := expr [ { "," expr } ] 47 // func := "func" "(" [ list ] ")" ":" expr 48 // 49 // Where each expr is a valid CUE identifier or selector. 50 // 51 // The specific ABI used may restrict the allowable signatures further. 52 // 53 // By default, the named Wasm module is searched for a function with 54 // the same name as the CUE field that is associated with the attribute. 55 // If you want to import a function under a different name, you can 56 // specify this in the attribute using an optional name parameter, for 57 // example: 58 // 59 // isPrime: _ @extern("bar.wasm", abi=c, name=is_prime, sig="func(uint64): bool") 60 // 61 // # Runtime requirements for Wasm modules 62 // 63 // CUE runs Wasm code in a secure sandbox, which restricts access to 64 // external resources. Therefore, any Wasm code intended for execution 65 // in CUE must be self-contained and cannot have external dependencies. 66 // 67 // All code exported for use by CUE must be free of observable side 68 // effects. The result of a function call must depend only on its 69 // arguments, and no other implicit state. If a function uses global 70 // state, it must do so only in a way that is undetectable from the 71 // outside. For example, a function that caches results to speed up 72 // its future invocations (memoization) is permitted, but a function 73 // that returns a random number is not. 74 // 75 // The CUE runtime may run different function invocations in different 76 // Wasm runtime instances, so Wasm code must not depend on the existence 77 // of shared state between different function invocations. 78 // 79 // Wasm code must always terminate and return a result. 80 // 81 // Failure to provide the above guarantees will break the internal 82 // logic of CUE and will cause the CUE evaluation to be undefined. 83 // 84 // The CUE runtime may try to detect violations of the above rules, 85 // but it cannot provide any guarantees that violations will be detected. 86 // It is the responsability of the programmer to comply to the above 87 // requirements. 88 // 89 // # ABI requirements for Wasm modules 90 // 91 // Currently only the [System V ABI] (also known as the C ABI) is 92 // supported. Furthermore, only scalar data types and structs containing 93 // either scalar types or other structs can be exchanged between CUE 94 // and Wasm. Scalar means booleans, sized integers, and sized floats. 95 // The sig field in the attribute refers to these data types by their 96 // CUE names, such as bool, uint16, float64. 97 // 98 // Additionally the Wasm module must export two functions with the 99 // following C type signature: 100 // 101 // void* allocate(int n); 102 // void deallocate(void *ptr, int n); 103 // 104 // Allocate returns a Wasm pointer to a buffer of size n. Deallocate 105 // takes a Wasm pointer and the size of the buffer it points to and 106 // frees it. 107 // 108 // # How to compile Rust for use in CUE 109 // 110 // To compile Rust code into a Wasm module usable by CUE, make sure 111 // you have either the wasm32-unknown-unknown or wasm32-wasi targets 112 // installed: 113 // 114 // rustup target add wasm32-wasi 115 // 116 // Note that even with wasm32-wasi, you should assume a [no_std] 117 // environment. Even though CUE can load [WASI] modules, the loaded 118 // modules do not currently have access to a WASI environment. This 119 // might change in the future. 120 // 121 // Compile your Rust crate using a cdynlib crate type as your [cargo target] 122 // targeting the installed Wasm target and make sure the functions you 123 // are exporting are using the C ABI, like so: 124 // 125 // #[no_mangle] 126 // pub extern "C" fn mul(a: f64, b: f64) -> f64 { 127 // a * b 128 // } 129 // 130 // The following Rust functions can be used to implement allocate and 131 // deallocate described above: 132 // 133 // #[cfg_attr(all(target_arch = "wasm32"), export_name = "allocate")] 134 // #[no_mangle] 135 // pub extern "C" fn _allocate(size: u32) -> *mut u8 { 136 // allocate(size as usize) 137 // } 138 // 139 // fn allocate(size: usize) -> *mut u8 { 140 // let vec: Vec<MaybeUninit<u8>> = Vec::with_capacity(size); 141 // 142 // Box::into_raw(vec.into_boxed_slice()) as *mut u8 143 // } 144 // 145 // #[cfg_attr(all(target_arch = "wasm32"), export_name = "deallocate")] 146 // #[no_mangle] 147 // pub unsafe extern "C" fn _deallocate(ptr: u32, size: u32) { 148 // deallocate(ptr as *mut u8, size as usize); 149 // } 150 // 151 // unsafe fn deallocate(ptr: *mut u8, size: usize) { 152 // let _ = Vec::from_raw_parts(ptr, 0, size); 153 // } 154 // 155 // [System V ABI]: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md 156 // [no_std]: https://docs.rust-embedded.org/book/intro/no-std.html 157 // [WASI]: https://wasi.dev 158 // [cargo target]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html 159 package wasm 160