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, value, 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 vector_items<T>(data: &Val, store: &mut StoreContextMut<'_, T>) -> Option<Vec<Val>> {
102 let vector = data.unwrap_anyref()?.unwrap_struct(&*store).ok()?;
103 let Val::I32(length) = vector.field(&mut *store, 0).ok()? else {
104 return None;
105 };
106 let items = vector.field(&mut *store, 1).ok()?.unwrap_anyref()?.as_array(&*store).ok()??;
107 let items: Vec<Val> = items.elems(&mut *store).ok()?.take(length as usize).collect();
108 Some(items)
109}
110
111pub fn items_of<T>(mut first: Val, mut rest: Val, store: &mut StoreContextMut<'_, T>) -> Result<Vec<Val>> {
114 if let Some(items) = vector_items(&first, store) {
115 return Ok(items);
116 }
117 let mut items = vec![];
118 loop {
119 if first.unwrap_anyref().is_some() {
120 items.push(first);
121 }
122 match fields(&rest, store) {
123 Some((kind, data, value)) if matches!((kind & KIND_MASK) as u8, LIST | BLOCK) => {
124 (first, rest) = (data, value)
125 }
126 Some((kind, ..)) if (kind & KIND_MASK) as u8 == EMPTY => return Ok(items),
127 Some(_) => return Ok([items, vec![rest]].concat()),
128 None => return Ok(items),
129 }
130 }
131}
132
133pub fn parts_of<T>(node: &Val, store: &mut StoreContextMut<'_, T>) -> Result<Vec<Val>> {
135 match fields(node, store) {
136 Some((kind, data, value)) if matches!((kind & KIND_MASK) as u8, LIST | BLOCK) => items_of(data, value, store),
137 Some((kind, ..)) if (kind & KIND_MASK) as u8 != EMPTY => Ok(vec![*node]),
138 _ => Ok(vec![]),
139 }
140}
141
142fn object_of<T>(items: Vec<Val>, store: &mut StoreContextMut<'_, T>, memory: Memory) -> Result<Value> {
144 let mut object = Map::new();
145 for item in items {
146 match fields(&item, store) {
147 Some((kind, key, value)) if (kind & KIND_MASK) as u8 == KEY => {
148 let (key, value) = entry_of(&key, &value, store, memory)?;
149 object.insert(key, value);
150 }
151 _ => {
152 object.insert(object.len().to_string(), json_of(&item, store, memory)?);
153 }
154 }
155 }
156 Ok(Value::Object(object))
157}
158
159fn entry_of<T>(key: &Val, value: &Val, store: &mut StoreContextMut<'_, T>, memory: Memory) -> Result<(String, Value)> {
160 let name = match json_of(key, store, memory)? {
161 Value::String(name) => name,
162 other => other.to_string(),
163 };
164 Ok((name, json_of(value, store, memory)?))
165}
166
167fn plain_integer<T>(payload: &Val, store: &mut StoreContextMut<'_, T>) -> Option<i64> {
169 match as_struct(payload, store)?.field(&mut *store, 0).ok()? {
170 Val::I64(integer) => Some(integer),
171 _ => None,
172 }
173}
174
175fn integer_of<T>(payload: &Val, store: &mut StoreContextMut<'_, T>) -> Result<f64> {
177 let payload = as_struct(payload, store).ok_or_else(|| wasmtime::Error::msg("an Int without payload"))?;
178 Ok(match payload.field(&mut *store, 0)? {
179 Val::I64(integer) => integer as f64,
180 numerator @ Val::AnyRef(_) => {
181 integer_of(&numerator, store)? / integer_of(&payload.field(&mut *store, 1)?, store)?
182 }
183 Val::I32(negative) => {
184 let limbs = payload.field(&mut *store, 1)?;
185 let limbs = limbs
186 .unwrap_anyref()
187 .ok_or_else(|| wasmtime::Error::msg("a BigInt without limbs"))?
188 .unwrap_array(&*store)?;
189 let magnitude = limbs
190 .elems(&mut *store)?
191 .collect::<Vec<_>>()
192 .iter()
193 .rev()
194 .fold(0.0, |high, limb| high * 2f64.powi(LIMB_BITS as i32) + f64::from(limb.unwrap_i32() as u32));
195 if negative != 0 {
196 -magnitude
197 } else {
198 magnitude
199 }
200 }
201 other => wasmtime::bail!("an unexpected Int payload {other:?}"),
202 })
203}
204
205fn text_of<T>(data: &Val, before: Val, store: &mut StoreContextMut<'_, T>, memory: Memory) -> Result<String> {
208 let mut pieces = vec![bytes_of(data, store, memory)?];
209 let mut before = before;
210 while let Some((_, data, earlier)) = fields(&before, store) {
211 pieces.push(bytes_of(&data, store, memory)?);
212 before = earlier;
213 }
214 Ok(String::from_utf8_lossy(&pieces.into_iter().rev().flatten().collect::<Vec<u8>>()).into_owned())
215}
216
217fn bytes_of<T>(string: &Val, store: &mut StoreContextMut<'_, T>, memory: Memory) -> Result<Vec<u8>> {
219 if let Some(Ok(array)) = string.unwrap_anyref().map(|reference| reference.unwrap_array(&*store)) {
220 return Ok(array.elems(&mut *store)?.map(|byte| byte.unwrap_i32() as u8).collect());
221 }
222 let Some(string) = as_struct(string, store) else {
223 return Ok(vec![]);
224 };
225 let pointer = string.field(&mut *store, 0)?.unwrap_i32() as u32 as usize;
226 let length = string.field(&mut *store, 1)?.unwrap_i32() as u32 as usize;
227 let bytes = memory
228 .data(&*store)
229 .get(pointer..pointer + length)
230 .ok_or_else(|| wasmtime::Error::msg("a text beyond memory"))?;
231 Ok(bytes.to_vec())
232}
233
234pub struct Builders {
236 functions: Vec<(&'static str, Func)>,
237 memory: Memory,
238 heap: Option<Global>,
239}
240
241impl Builders {
242 pub fn of(lookup: &mut dyn FnMut(&str) -> Option<wasmtime::Extern>) -> Result<Builders> {
243 let functions = BUILDERS.iter().filter_map(|&name| Some((name, lookup(name)?.into_func()?))).collect();
244 let memory = lookup(MEMORY)
245 .and_then(|export| export.into_memory())
246 .ok_or_else(|| wasmtime::Error::msg("the program exports no memory"))?;
247 let heap = lookup(TEXT_HEAP).and_then(|export| export.into_global());
248 Ok(Builders { functions, memory, heap })
249 }
250
251 fn call<T>(&self, name: &str, arguments: &[Val], result: Val, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
252 let (_, function) = self
253 .functions
254 .iter()
255 .find(|(known, _)| *known == name)
256 .ok_or_else(|| wasmtime::Error::msg(format!("the program exports no {name}")))?;
257 let mut results = [result];
258 function.call(&mut *store, arguments, &mut results)?;
259 Ok(results[0])
260 }
261
262 fn node<T>(&self, name: &str, arguments: &[Val], store: &mut StoreContextMut<'_, T>) -> Result<Val> {
263 self.call(name, arguments, Val::AnyRef(None), store)
264 }
265
266 fn int_operation<T>(&self, name: &str, a: i64, b: i64, store: &mut StoreContextMut<'_, T>) -> Result<i64> {
267 Ok(self.call(name, &[Val::I64(a), Val::I64(b)], Val::I64(0), store)?.unwrap_i64())
268 }
269
270 pub fn build<T>(&self, value: &Value, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
273 match value {
274 Value::Null => self.node(NEW_EMPTY, &[], store),
275 Value::Bool(truth) => self.node(NEW_INT, &[Val::I64(i64::from(*truth))], store),
276 Value::Number(number) => match number.as_i64() {
277 Some(integer) => self.integer(&integer.to_string(), store),
278 None => self.node(NEW_FLOAT, &[Val::F64(number.as_f64().unwrap_or(f64::NAN).to_bits())], store),
279 },
280 Value::String(text) => self.text(NEW_TEXT, text.as_bytes(), store),
281 Value::Array(items) => {
282 self.list(items.iter().map(|item| self.build(item, store)).collect::<Result<_>>()?, SQUARE, store)
283 }
284 Value::Object(entries) => match single_text(entries) {
285 Some((BIG_INTEGER_KEY, digits)) if is_integer(digits) => self.integer(digits, store),
286 Some((CODEPOINT_KEY, letter)) if letter.chars().count() == 1 => {
287 self.node(NEW_CODEPOINT, &[Val::I32(letter.chars().next().unwrap_or('\0') as i32)], store)
288 }
289 _ => {
290 let mut keys = vec![];
291 for (key, value) in entries {
292 let (key, value) = (self.text(NEW_SYMBOL, key.as_bytes(), store)?, self.build(value, store)?);
293 keys.push(self.node(NEW_KEY, &[key, value, Val::I64(COLON)], store)?);
294 }
295 self.list(keys, CURLY, store)
296 }
297 },
298 }
299 }
300
301 pub fn text_node<T>(&self, bytes: Option<&[u8]>, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
303 match bytes {
304 Some(bytes) => self.text(NEW_TEXT, bytes, store),
305 None => self.node(NEW_EMPTY, &[], store),
306 }
307 }
308
309 fn text<T>(&self, constructor: &str, text: &[u8], store: &mut StoreContextMut<'_, T>) -> Result<Val> {
310 let heap = self.heap.ok_or_else(|| wasmtime::Error::msg(format!("the program exports no {TEXT_HEAP}")))?;
311 let (pointer, length) = write_to_heap(&self.memory, heap, store, text)?;
312 self.node(constructor, &[Val::I32(pointer as i32), Val::I32(length as i32)], store)
313 }
314
315 fn list<T>(&self, items: Vec<Val>, bracket: i64, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
317 if items.is_empty() {
318 return self.node(NEW_EMPTY, &[], store);
319 }
320 items
321 .into_iter()
322 .rev()
323 .try_fold(Val::AnyRef(None), |rest, first| self.node(NEW_LIST, &[first, rest, Val::I64(bracket)], store))
324 }
325
326 fn integer<T>(&self, digits: &str, store: &mut StoreContextMut<'_, T>) -> Result<Val> {
328 let handle = match digits.parse::<i64>() {
329 Ok(small) if (FIXNUM_MIN..=FIXNUM_MAX).contains(&small) => small,
330 _ => {
331 let (negative, magnitude) = digits.strip_prefix('-').map_or((false, digits), |rest| (true, rest));
332 let mut handle = 0;
333 for limb in limbs_of(magnitude).iter().rev() {
334 handle = self.int_operation(SHIFT_LEFT, handle, LIMB_BITS, store)?;
335 handle = self.int_operation(ADD, handle, i64::from(*limb), store)?;
336 }
337 if negative {
338 handle = self.int_operation(SUBTRACT, 0, handle, store)?;
339 }
340 handle
341 }
342 };
343 self.node(NEW_INT, &[Val::I64(handle)], store)
344 }
345}
346
347fn single_text(entries: &Map<String, Value>) -> Option<(&str, &str)> {
348 match entries.iter().next() {
349 Some((key, Value::String(text))) if entries.len() == 1 => Some((key.as_str(), text.as_str())),
350 _ => None,
351 }
352}
353
354fn is_integer(digits: &str) -> bool {
355 let magnitude = digits.strip_prefix('-').unwrap_or(digits);
356 !magnitude.is_empty() && magnitude.bytes().all(|digit| digit.is_ascii_digit())
357}
358
359fn limbs_of(digits: &str) -> Vec<u32> {
361 let mut limbs: Vec<u32> = vec![];
362 for digit in digits.bytes() {
363 let mut carry = u64::from(digit - b'0');
364 for limb in limbs.iter_mut() {
365 let product = u64::from(*limb) * 10 + carry;
366 *limb = product as u32;
367 carry = product >> LIMB_BITS;
368 }
369 if carry > 0 {
370 limbs.push(carry as u32);
371 }
372 }
373 limbs
374}
375
376pub fn write_to_heap<T>(
379 memory: &Memory,
380 heap: Global,
381 store: &mut StoreContextMut<'_, T>,
382 bytes: &[u8],
383) -> Result<(u32, u32)> {
384 let length = bytes.len() as u32;
385 let top = heap.get(&mut *store).i32().unwrap_or(0) as u32;
386 let memory_end = (memory.size(&*store) as u32) << PAGE_BITS;
387 let pointer = if top == 0 || top + length > memory_end {
388 memory.grow(&mut *store, ((length >> PAGE_BITS) + 1) as u64)?;
389 memory_end
390 } else {
391 top
392 };
393 let written = write_at(memory, store, pointer, bytes)?;
394 heap.set(&mut *store, Val::I32((pointer + length) as i32))?;
395 Ok(written)
396}
397
398pub fn write_at<T>(
400 memory: &Memory,
401 store: &mut StoreContextMut<'_, T>,
402 pointer: u32,
403 bytes: &[u8],
404) -> Result<(u32, u32)> {
405 let length = bytes.len() as u32;
406 let pages_needed = ((pointer + length) as usize).div_ceil(1 << PAGE_BITS);
407 let current_pages = memory.size(&*store) as usize;
408 if pages_needed > current_pages {
409 memory.grow(&mut *store, (pages_needed - current_pages) as u64)?;
410 }
411 memory.write(&mut *store, pointer as usize, bytes)?;
412 Ok((pointer, length))
413}
414
415pub fn foreign_call<T>(
418 caller: &mut wasmtime::Caller<'_, T>,
419 parts: [Option<Rooted<AnyRef>>; 5],
420) -> Result<Option<Rooted<AnyRef>>> {
421 let builders = Builders::of(&mut |name| caller.get_export(name))?;
422 let memory = builders.memory;
423 let mut store = caller.as_context_mut();
424 let [runtime, module, member, call, arguments] = parts.map(Val::AnyRef);
425 let mut text = |value: &Val| match json_of(value, &mut store, memory)? {
426 Value::String(text) => Ok(text),
427 other => Ok::<_, wasmtime::Error>(other.to_string()),
428 };
429 let (runtime, member) = (text(&runtime)?, text(&member)?);
430 if !crate::foreign::is_interpreted(&runtime) {
431 wasmtime::bail!("{runtime} {member}: an executable calls python and js only; run it with warp");
432 }
433 let module = json_of(&module, &mut store, memory)?;
434 let module_name = module.as_str().map_or_else(|| module.to_string(), str::to_string);
435 let arguments = match json_of(&call, &mut store, memory)? {
436 Value::Number(called) if called.as_i64() == Some(1) => Value::Array(
437 parts_of(&arguments, &mut store)?
438 .iter()
439 .map(|argument| json_of(argument, &mut store, memory))
440 .collect::<Result<_>>()?,
441 ),
442 _ => Value::Null,
443 };
444 let mut no_callbacks = |_: usize, _: Value| Err("an executable cannot call a warp function back yet".to_string());
445 let answer = crate::foreign::call(&runtime, &module_name, module, &member, arguments, &mut no_callbacks)
446 .map_err(wasmtime::Error::msg)?;
447 Ok(builders.build(&answer, &mut store)?.unwrap_anyref().copied())
448}