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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use {
crate::{external_error, RuntimeResult, Value, ValueMap, Vm},
downcast_rs::impl_downcast,
std::{
fmt,
hash::{Hash, Hasher},
sync::Arc,
},
};
pub use downcast_rs::Downcast;
pub trait ExternalValue: fmt::Debug + fmt::Display + Send + Sync + Downcast {
fn value_type(&self) -> String;
}
impl_downcast!(ExternalValue);
pub struct Args {
pub register: u8,
pub count: u8,
}
#[allow(clippy::type_complexity)]
pub struct ExternalFunction {
pub function: Arc<dyn Fn(&mut Vm, &Args) -> RuntimeResult + Send + Sync + 'static>,
pub is_instance_function: bool,
}
impl ExternalFunction {
pub fn new(
function: impl Fn(&mut Vm, &Args) -> RuntimeResult + Send + Sync + 'static,
is_instance_function: bool,
) -> Self {
Self {
function: Arc::new(function),
is_instance_function,
}
}
}
impl Clone for ExternalFunction {
fn clone(&self) -> Self {
Self {
function: self.function.clone(),
is_instance_function: self.is_instance_function,
}
}
}
impl fmt::Debug for ExternalFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let raw = Arc::into_raw(self.function.clone());
write!(
f,
"external {}function: {:?}",
if self.is_instance_function {
"instance "
} else {
""
},
raw
)
}
}
impl Hash for ExternalFunction {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_usize(Arc::as_ptr(&self.function) as *const () as usize);
}
}
pub fn visit_external_value<T>(
map: &ValueMap,
mut f: impl FnMut(&mut T) -> RuntimeResult,
) -> RuntimeResult
where
T: ExternalValue,
{
match map.data().get(&Value::ExternalDataId) {
Some(Value::ExternalValue(maybe_external)) => {
let mut value = maybe_external.as_ref().write();
match value.downcast_mut::<T>() {
Some(external) => f(external),
None => external_error!(
"Invalid type for external value, found '{}'",
value.value_type(),
),
}
}
_ => external_error!("External value not found"),
}
}
pub fn is_external_instance<T>(map: &ValueMap) -> bool
where
T: ExternalValue,
{
match map.data().get(&Value::ExternalDataId) {
Some(Value::ExternalValue(maybe_external)) => maybe_external.as_ref().read().is::<T>(),
_ => false,
}
}
#[macro_export]
macro_rules! get_external_instance {
($args: ident,
$external_name: expr,
$fn_name: expr,
$external_type: ident,
$match_name: ident,
$body: block) => {{
match &$args {
[Value::Map(instance), ..] => {
$crate::visit_external_value(instance, |$match_name: &mut $external_type| $body)
}
_ => $crate::external_error!(
"{0}.{1}: Expected {0} instance as first argument",
$external_name,
$fn_name,
),
}
}};
}