use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use crate::runtime::{Int, Table};
use crate::vm::machine::VM;
use crate::vm::value::{Value, VmTable};
const MAX_CONVERT_DEPTH: usize = 64;
fn to_runtime(value: &Value, depth: usize) -> Result<crate::runtime::Value, String> {
if depth > MAX_CONVERT_DEPTH {
return Err("value nests too deeply to pass to a native function".to_string());
}
Ok(match value {
Value::Nil => crate::runtime::Value::Nil,
Value::Bool(b) => crate::runtime::Value::Bool(*b),
Value::Int(i) => crate::runtime::Value::Integer(Int::Small(*i)),
Value::BigInt(i) => crate::runtime::Value::Integer(Int::from_bigint((**i).clone())),
Value::Float(f) => crate::runtime::Value::Number(*f),
Value::String(s) => crate::runtime::Value::String(s.to_string()),
Value::Table(t) => {
let source = t.borrow();
let mut table = Table {
array: Vec::with_capacity(source.array.len()),
fields: HashMap::with_capacity(source.fields.len()),
const_fields: HashSet::new(),
frozen: false,
metatable: None,
};
for item in &source.array {
table.array.push(to_runtime(item, depth + 1)?);
}
for (key, item) in &source.fields {
table
.fields
.insert(key.to_string(), to_runtime(item, depth + 1)?);
}
crate::runtime::Value::Table(Rc::new(RefCell::new(table)))
}
Value::Closure(_) | Value::Native(..) => {
return Err("cannot pass a function to a native primitive".to_string());
}
Value::Buffer(_) => {
return Err("cannot pass a buffer to a native primitive".to_string());
}
})
}
fn from_runtime(value: &crate::runtime::Value, depth: usize) -> Result<Value, String> {
if depth > MAX_CONVERT_DEPTH {
return Err("value returned by a native function nests too deeply".to_string());
}
Ok(match value {
crate::runtime::Value::Nil => Value::Nil,
crate::runtime::Value::Bool(b) => Value::Bool(*b),
crate::runtime::Value::Number(n) => Value::Float(*n),
crate::runtime::Value::Integer(i) => match i {
Int::Small(v) => Value::Int(*v),
Int::Big(b) => Value::from_bigint((**b).clone()),
},
crate::runtime::Value::String(s) => Value::str(s),
crate::runtime::Value::Table(t) => {
let source = t.borrow();
let mut table = VmTable::with_capacity(source.array.len(), source.fields.len());
for item in &source.array {
table.array.push(from_runtime(item, depth + 1)?);
}
for (key, item) in &source.fields {
table
.fields
.insert(Rc::from(key.as_str()), from_runtime(item, depth + 1)?);
}
Value::Table(Rc::new(RefCell::new(table)))
}
crate::runtime::Value::Varargs(values) => {
match values.first() {
Some(first) => from_runtime(first, depth + 1)?,
None => Value::Nil,
}
}
crate::runtime::Value::Function(_) => {
return Err("native primitive returned a function, which cannot cross".to_string());
}
})
}
pub(crate) fn call_primitive(
primitive: crate::runtime::Native,
args: &[Value],
) -> Result<Vec<Value>, String> {
let converted = args
.iter()
.map(|arg| to_runtime(arg, 0))
.collect::<Result<Vec<_>, _>>()?;
let results = primitive(converted)?;
let mut out = Vec::with_capacity(results.len());
for result in &results {
if let crate::runtime::Value::Varargs(values) = result {
for value in values {
out.push(from_runtime(value, 0)?);
}
} else {
out.push(from_runtime(result, 0)?);
}
}
Ok(out)
}
macro_rules! bridged {
($wrapper:ident, $slice:path) => {
fn $wrapper<const I: usize>(_vm: &mut VM, args: &[Value]) -> Result<Vec<Value>, String> {
call_primitive($slice[I].1, args)
}
};
}
bridged!(core_primitive, crate::runtime::PRIMITIVES);
bridged!(string_primitive, crate::string_lib::NATIVES);
bridged!(crypto_primitive, crate::crypto_lib::NATIVES);
bridged!(http_primitive, crate::http_lib::NATIVES);
bridged!(fs_primitive, crate::fs_lib::NATIVES);
bridged!(io_primitive, crate::io_lib::NATIVES);
bridged!(sql_primitive, crate::sql_lib::NATIVES);
macro_rules! register {
($vm:expr, $wrapper:ident, $slice:path, [$($index:literal),* $(,)?]) => {{
const _: () = assert!(
$slice.len() == [$($index),*].len(),
concat!(stringify!($slice), " changed; update the index list"),
);
$({
let (name, _) = $slice[$index];
$vm.globals
.insert(
Rc::from(name),
Value::Native($crate::native_def!($slice[$index].0, $wrapper::<$index>)),
);
})*
}};
}
pub fn register_bridged_natives(vm: &mut VM) {
register!(
vm,
core_primitive,
crate::runtime::PRIMITIVES,
[
0, 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
]
);
register!(
vm,
string_primitive,
crate::string_lib::NATIVES,
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
);
register!(
vm,
crypto_primitive,
crate::crypto_lib::NATIVES,
[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
]
);
register!(
vm,
http_primitive,
crate::http_lib::NATIVES,
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
);
register!(
vm,
fs_primitive,
crate::fs_lib::NATIVES,
[0, 1, 2, 3, 4, 5, 6, 7, 8]
);
register!(
vm,
io_primitive,
crate::io_lib::NATIVES,
[0, 1, 2, 3, 4, 5, 6]
);
register!(vm, sql_primitive, crate::sql_lib::NATIVES, [0, 1, 2, 3, 4]);
}