1use crate::foreign::{BIG_INTEGER_KEY, CODEPOINT_KEY};
5use serde_json::{Map, Value};
6use wasmtime::{AnyRef, AsContextMut, Func, Global, Memory, Result, Rooted, StoreContextMut, StructRef, Val};
7
8pub const KIND_MASK: i64 = 0xFF;
9pub const KIND_BITS: i64 = 8;
10pub const EMPTY: u8 = 0;
11pub const INT: u8 = 1;
12pub const FLOAT: u8 = 2;
13pub const TEXT: u8 = 3;
14pub const CODEPOINT: u8 = 4;
15pub const SYMBOL: u8 = 5;
16pub const KEY: u8 = 6;
17pub const BLOCK: u8 = 7;
18pub const LIST: u8 = 8;
19pub const ERROR: u8 = 11;
20pub const FUNCTION: u8 = 16;
21pub const BOOL_INFO: i64 = 1;
23pub const COLON: i64 = 1;
25pub const CURLY: i64 = 0;
26pub const SQUARE: i64 = 1;
27const FIELD_KIND: usize = 0;
28const FIELD_DATA: usize = 1;
29const FIELD_VALUE: usize = 2;
30const NEW_EMPTY: &str = "new_empty";
32const NEW_INT: &str = "new_int";
33const NEW_FLOAT: &str = "new_float";
34const NEW_CODEPOINT: &str = "new_codepoint";
35const NEW_TEXT: &str = "new_text";
36const NEW_SYMBOL: &str = "new_symbol";
37const NEW_KEY: &str = "new_key";
38const NEW_LIST: &str = "new_list";
39const SHIFT_LEFT: &str = "int_shift_left";
41const ADD: &str = "exact_add";
42const SUBTRACT: &str = "exact_sub";
43pub const BUILDERS: [&str; 11] =
44 [NEW_EMPTY, NEW_INT, NEW_FLOAT, NEW_CODEPOINT, NEW_TEXT, NEW_SYMBOL, NEW_KEY, NEW_LIST, SHIFT_LEFT, ADD, SUBTRACT];
45pub const TEXT_HEAP: &str = "text_heap";
47pub const MEMORY: &str = "memory";
48const PAGE_BITS: u32 = 16;
49const LIMB_BITS: i64 = 32;
50const FIXNUM_MIN: i64 = -((1 << 62) - 1);
52const FIXNUM_MAX: i64 = 1 << 62;
53
54fn fields<T>(node: &Val, store: &mut StoreContextMut<'_, T>) -> Option<(i64, Val, Val)> {
56 let node = as_struct(node, store)?;
57 let mut field = |index| node.field(&mut *store, index).ok();
58 Some((field(FIELD_KIND)?.unwrap_i64(), field(FIELD_DATA)?, field(FIELD_VALUE)?))
59}
60
61fn as_struct<T>(value: &Val, store: &StoreContextMut<'_, T>) -> Option<Rooted<StructRef>> {
62 value.unwrap_anyref()?.unwrap_struct(store).ok()
63}
64
65pub fn json_of<T>(node: &Val, store: &mut StoreContextMut<'_, T>, memory: Memory) -> Result<Value> {
67 let Some((kind, data, value)) = fields(node, store) else {
68 return Ok(Value::Null);
69 };
70 let high_bits = (kind >> KIND_BITS) & 0xFF;
71 Ok(match (kind & KIND_MASK) as u8 {
72 EMPTY => Value::Null,
73 INT if high_bits == BOOL_INFO => Value::Bool(integer_of(&data, store)? != 0.0),
74 INT => match plain_integer(&data, store) {
75 Some(integer) => Value::from(integer),
76 None => Value::from(integer_of(&data, store)?),
77 },
78 FLOAT => Value::from(
79 as_struct(&data, store).map_or(Ok(Val::F64(0)), |boxed| boxed.field(&mut *store, 0))?.unwrap_f64(),
80 ),
81 CODEPOINT => {
82 let code = data.unwrap_anyref().and_then(|reference| reference.unwrap_i31(&*store).ok());
83 Value::String(code.and_then(|code| char::from_u32(code.get_u32())).unwrap_or('\0').to_string())
84 }
85 TEXT | SYMBOL | ERROR => Value::String(text_of(&data, store, memory)?),
86 KEY => Value::Object(Map::from_iter([entry_of(&data, &value, store, memory)?])),
87 BLOCK => object_of(items_of(data, value, store)?, store, memory)?,
88 LIST if high_bits == CURLY => object_of(items_of(data, value, store)?, store, memory)?,
89 LIST => Value::Array(
90 items_of(data, value, store)?.iter().map(|item| json_of(item, store, memory)).collect::<Result<_>>()?,
91 ),
92 FUNCTION => {
93 wasmtime::bail!("an executable cannot give a warp function to a foreign call yet; run it with warp")
94 }
95 other => wasmtime::bail!("a value of kind {other} cannot cross to a foreign call from an executable yet"),
96 })
97}
98
99pub fn items_of<T>(mut first: Val, mut rest: Val, store: &mut StoreContextMut<'_, T>) -> Result<Vec<Val>> {
101 let mut items = vec![];
102 loop {
103 if first.unwrap_anyref().is_some() {
104 items.push(first);
105 }
106 match fields(&rest, store) {
107 Some((kind, data, value)) if matches!((kind & KIND_MASK) as u8, LIST | BLOCK) => {
108 (first, rest) = (data, value)
109 }
110 Some((kind, ..)) if (kind & KIND_MASK) as u8 == EMPTY => return Ok(items),
111 Some(_) => return Ok([items, vec![rest]].concat()),
112 None => return Ok(items),
113 }
114 }
115}
116
117pub fn parts_of<T>(node: &Val, store: &mut StoreContextMut<'_, T>) -> Result<Vec<Val>> {
119 match fields(node, store) {
120 Some((kind, data, value)) if matches!((kind & KIND_MASK) as u8, LIST | BLOCK) => items_of(data, value, store),
121 Some((kind, ..)) if (kind & KIND_MASK) as u8 != EMPTY => Ok(vec![*node]),
122 _ => Ok(vec![]),
123 }
124}
125
126fn object_of<T>(items: Vec<Val>, store: &mut StoreContextMut<'_, T>, memory: Memory) -> Result<Value> {
128 let mut object = Map::new();
129 for item in items {
130 match fields(&item, store) {
131 Some((kind, key, value)) if (kind & KIND_MASK) as u8 == KEY => {
132 let (key, value) = entry_of(&key, &value, store, memory)?;
133 object.insert(key, value);
134 }
135 _ => {
136 object.insert(object.len().to_string(), json_of(&item, store, memory)?);
137 }
138 }
139 }
140 Ok(Value::Object(object))
141}
142
143fn entry_of<T>(key: &Val, value: &Val, store: &mut StoreContextMut<'_, T>, memory: Memory) -> Result<(String, Value)> {
144 let name = match json_of(key, store, memory)? {
145 Value::String(name) => name,
146 other => other.to_string(),
147 };
148 Ok((name, json_of(value, store, memory)?))
149}
150
151fn plain_integer<T>(payload: &Val, store: &mut StoreContextMut<'_, T>) -> Option<i64> {
153 match as_struct(payload, store)?.field(&mut *store, 0).ok()? {
154 Val::I64(integer) => Some(integer),
155 _ => None,
156 }
157}
158
159fn integer_of<T>(payload: &Val, store: &mut StoreContextMut<'_, T>) -> Result<f64> {
161 let payload = as_struct(payload, store).ok_or_else(|| wasmtime::Error::msg("an Int without payload"))?;
162 Ok(match payload.field(&mut *store, 0)? {
163 Val::I64(integer) => integer as f64,
164 numerator @ Val::AnyRef(_) => {
165 integer_of(&numerator, store)? / integer_of(&payload.field(&mut *store, 1)?, store)?
166 }
167 Val::I32(negative) => {
168 let limbs = payload.field(&mut *store, 1)?;
169 let limbs = limbs
170 .unwrap_anyref()
171 .ok_or_else(|| wasmtime::Error::msg("a BigInt without limbs"))?
172 .unwrap_array(&*store)?;
173 let magnitude = limbs
174 .elems(&mut *store)?
175 .collect::<Vec<_>>()
176 .iter()
177 .rev()
178 .fold(0.0, |high, limb| high * 2f64.powi(LIMB_BITS as i32) + f64::from(limb.unwrap_i32() as u32));
179 if negative != 0 {
180 -magnitude
181 } else {
182 magnitude
183 }
184 }
185 other => wasmtime::bail!("an unexpected Int payload {other:?}"),
186 })
187}
188
189fn text_of<T>(string: &Val, store: &mut StoreContextMut<'_, T>, memory: Memory) -> Result<String> {
191 let Some(string) = as_struct(string, store) else {
192 return Ok(String::new());
193 };
194 let pointer = string.field(&mut *store, 0)?.unwrap_i32() as u32 as usize;
195 let length = string.field(&mut *store, 1)?.unwrap_i32() as u32 as usize;
196 let bytes = memory
197 .data(&*store)
198 .get(pointer..pointer + length)
199 .ok_or_else(|| wasmtime::Error::msg("a text beyond memory"))?;
200 Ok(String::from_utf8_lossy(bytes).into_owned())
201}
202
203pub struct Builders {
205 functions: Vec<(&'static str, Func)>,
206 memory: Memory,
207 heap: Option<Global>,
208}
209
210impl Builders {
211 pub fn of(lookup: &mut dyn FnMut(&str) -> Option<wasmtime::Extern>) -> Result<Builders> {
212 let functions = BUILDERS.iter().filter_map(|&name| Some((name, lookup(name)?.into_func()?))).collect();
213 let memory = lookup(MEMORY)
214 .and_then(|export| export.into_memory())
215 .ok_or_else(|| wasmtime::Error::msg("the program exports no memory"))?;
216 let heap = lookup(TEXT_HEAP).and_then(|export| export.into_global());
217 Ok(Builders { functions, memory, heap })
218 }
219
220 fn call<T>(&self, name: &str, arguments: &[Val], result: Val, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
221 let (_, function) = self
222 .functions
223 .iter()
224 .find(|(known, _)| *known == name)
225 .ok_or_else(|| wasmtime::Error::msg(format!("the program exports no {name}")))?;
226 let mut results = [result];
227 function.call(&mut *store, arguments, &mut results)?;
228 Ok(results[0])
229 }
230
231 fn node<T>(&self, name: &str, arguments: &[Val], store: &mut StoreContextMut<'_, T>) -> Result<Val> {
232 self.call(name, arguments, Val::AnyRef(None), store)
233 }
234
235 fn int_operation<T>(&self, name: &str, a: i64, b: i64, store: &mut StoreContextMut<'_, T>) -> Result<i64> {
236 Ok(self.call(name, &[Val::I64(a), Val::I64(b)], Val::I64(0), store)?.unwrap_i64())
237 }
238
239 pub fn build<T>(&self, value: &Value, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
242 match value {
243 Value::Null => self.node(NEW_EMPTY, &[], store),
244 Value::Bool(truth) => self.node(NEW_INT, &[Val::I64(i64::from(*truth))], store),
245 Value::Number(number) => match number.as_i64() {
246 Some(integer) => self.integer(&integer.to_string(), store),
247 None => self.node(NEW_FLOAT, &[Val::F64(number.as_f64().unwrap_or(f64::NAN).to_bits())], store),
248 },
249 Value::String(text) => self.text(NEW_TEXT, text, store),
250 Value::Array(items) => {
251 self.list(items.iter().map(|item| self.build(item, store)).collect::<Result<_>>()?, SQUARE, store)
252 }
253 Value::Object(entries) => match single_text(entries) {
254 Some((BIG_INTEGER_KEY, digits)) if is_integer(digits) => self.integer(digits, store),
255 Some((CODEPOINT_KEY, letter)) if letter.chars().count() == 1 => {
256 self.node(NEW_CODEPOINT, &[Val::I32(letter.chars().next().unwrap_or('\0') as i32)], store)
257 }
258 _ => {
259 let mut keys = vec![];
260 for (key, value) in entries {
261 let (key, value) = (self.text(NEW_SYMBOL, key, store)?, self.build(value, store)?);
262 keys.push(self.node(NEW_KEY, &[key, value, Val::I64(COLON)], store)?);
263 }
264 self.list(keys, CURLY, store)
265 }
266 },
267 }
268 }
269
270 fn text<T>(&self, constructor: &str, text: &str, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
271 let heap = self.heap.ok_or_else(|| wasmtime::Error::msg(format!("the program exports no {TEXT_HEAP}")))?;
272 let (pointer, length) = write_to_heap(&self.memory, heap, store, text.as_bytes())?;
273 self.node(constructor, &[Val::I32(pointer as i32), Val::I32(length as i32)], store)
274 }
275
276 fn list<T>(&self, items: Vec<Val>, bracket: i64, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
278 if items.is_empty() {
279 return self.node(NEW_EMPTY, &[], store);
280 }
281 items
282 .into_iter()
283 .rev()
284 .try_fold(Val::AnyRef(None), |rest, first| self.node(NEW_LIST, &[first, rest, Val::I64(bracket)], store))
285 }
286
287 fn integer<T>(&self, digits: &str, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
289 let handle = match digits.parse::<i64>() {
290 Ok(small) if (FIXNUM_MIN..=FIXNUM_MAX).contains(&small) => small,
291 _ => {
292 let (negative, magnitude) = digits.strip_prefix('-').map_or((false, digits), |rest| (true, rest));
293 let mut handle = 0;
294 for limb in limbs_of(magnitude).iter().rev() {
295 handle = self.int_operation(SHIFT_LEFT, handle, LIMB_BITS, store)?;
296 handle = self.int_operation(ADD, handle, i64::from(*limb), store)?;
297 }
298 if negative {
299 handle = self.int_operation(SUBTRACT, 0, handle, store)?;
300 }
301 handle
302 }
303 };
304 self.node(NEW_INT, &[Val::I64(handle)], store)
305 }
306}
307
308fn single_text(entries: &Map<String, Value>) -> Option<(&str, &str)> {
309 match entries.iter().next() {
310 Some((key, Value::String(text))) if entries.len() == 1 => Some((key.as_str(), text.as_str())),
311 _ => None,
312 }
313}
314
315fn is_integer(digits: &str) -> bool {
316 let magnitude = digits.strip_prefix('-').unwrap_or(digits);
317 !magnitude.is_empty() && magnitude.bytes().all(|digit| digit.is_ascii_digit())
318}
319
320fn limbs_of(digits: &str) -> Vec<u32> {
322 let mut limbs: Vec<u32> = vec![];
323 for digit in digits.bytes() {
324 let mut carry = u64::from(digit - b'0');
325 for limb in limbs.iter_mut() {
326 let product = u64::from(*limb) * 10 + carry;
327 *limb = product as u32;
328 carry = product >> LIMB_BITS;
329 }
330 if carry > 0 {
331 limbs.push(carry as u32);
332 }
333 }
334 limbs
335}
336
337pub fn write_to_heap<T>(
340 memory: &Memory,
341 heap: Global,
342 store: &mut StoreContextMut<'_, T>,
343 bytes: &[u8],
344) -> Result<(u32, u32)> {
345 let length = bytes.len() as u32;
346 let top = heap.get(&mut *store).i32().unwrap_or(0) as u32;
347 let memory_end = (memory.size(&*store) as u32) << PAGE_BITS;
348 let pointer = if top == 0 || top + length > memory_end {
349 memory.grow(&mut *store, ((length >> PAGE_BITS) + 1) as u64)?;
350 memory_end
351 } else {
352 top
353 };
354 let written = write_at(memory, store, pointer, bytes)?;
355 heap.set(&mut *store, Val::I32((pointer + length) as i32))?;
356 Ok(written)
357}
358
359pub fn write_at<T>(
361 memory: &Memory,
362 store: &mut StoreContextMut<'_, T>,
363 pointer: u32,
364 bytes: &[u8],
365) -> Result<(u32, u32)> {
366 let length = bytes.len() as u32;
367 let pages_needed = ((pointer + length) as usize).div_ceil(1 << PAGE_BITS);
368 let current_pages = memory.size(&*store) as usize;
369 if pages_needed > current_pages {
370 memory.grow(&mut *store, (pages_needed - current_pages) as u64)?;
371 }
372 memory.write(&mut *store, pointer as usize, bytes)?;
373 Ok((pointer, length))
374}
375
376pub fn foreign_call<T>(
379 caller: &mut wasmtime::Caller<'_, T>,
380 parts: [Option<Rooted<AnyRef>>; 5],
381) -> Result<Option<Rooted<AnyRef>>> {
382 let builders = Builders::of(&mut |name| caller.get_export(name))?;
383 let memory = builders.memory;
384 let mut store = caller.as_context_mut();
385 let [runtime, module, member, call, arguments] = parts.map(Val::AnyRef);
386 let mut text = |value: &Val| match json_of(value, &mut store, memory)? {
387 Value::String(text) => Ok(text),
388 other => Ok::<_, wasmtime::Error>(other.to_string()),
389 };
390 let (runtime, member) = (text(&runtime)?, text(&member)?);
391 if !crate::foreign::is_interpreted(&runtime) {
392 wasmtime::bail!("{runtime} {member}: an executable calls python and js only; run it with warp");
393 }
394 let module = json_of(&module, &mut store, memory)?;
395 let module_name = module.as_str().map_or_else(|| module.to_string(), str::to_string);
396 let arguments = match json_of(&call, &mut store, memory)? {
397 Value::Number(called) if called.as_i64() == Some(1) => Value::Array(
398 parts_of(&arguments, &mut store)?
399 .iter()
400 .map(|argument| json_of(argument, &mut store, memory))
401 .collect::<Result<_>>()?,
402 ),
403 _ => Value::Null,
404 };
405 let mut no_callbacks = |_: usize, _: Value| Err("an executable cannot call a warp function back yet".to_string());
406 let answer = crate::foreign::call(&runtime, &module_name, module, &member, arguments, &mut no_callbacks)
407 .map_err(wasmtime::Error::msg)?;
408 Ok(builders.build(&answer, &mut store)?.unwrap_anyref().copied())
409}