1#[path = "gen.runtime.rs"]
26pub(super) mod compiled;
27
28use crate::vm::{Any, IVm, Object, error};
29
30pub type Module<A> = fn(Any<A>) -> Result<Any<A>, Any<A>>;
35
36pub fn exports<A: IVm>(module: Module<A>) -> Result<Object<A>, Any<A>> {
42 Object::try_from(module(compiled::runtime::<A>()?)?).map_err(|_| error::not_an_export_object())
43}
44
45#[cfg(test)]
46mod tests {
47 use super::compiled::runtime;
48 use crate::vm::test::function::{returns, throws};
49 use crate::{
50 naive::Naive,
51 vm::{Any, BigInt, ToAny, ToArray, ToObject, error, unstable::static_read},
52 };
53
54 type A = Naive;
55
56 fn object(_runtime: Any<A>) -> Result<Any<A>, Any<A>> {
59 Ok([("a".into(), 1.0.to_any())].to_object().to_any())
60 }
61 fn throwing(_runtime: Any<A>) -> Result<Any<A>, Any<A>> {
62 Err("boom".into())
63 }
64 fn number(_runtime: Any<A>) -> Result<Any<A>, Any<A>> {
65 Ok(42.0.to_any())
66 }
67
68 #[test]
71 fn exports() {
72 use crate::vm::exports;
73 let object = exports::<A>(object).unwrap();
74 assert_eq!(object.own_property(&"a".into()), Some(1.0.to_any()));
75 assert_eq!(exports::<A>(throwing).unwrap_err(), "boom".into());
76 assert_eq!(
77 exports::<A>(number).unwrap_err(),
78 error::not_an_export_object()
79 );
80 }
81
82 fn at(value: &Any<A>, key: &str) -> Any<A> {
84 Any::dot(value.clone(), key.into()).end().unwrap()
85 }
86
87 fn static_at(rt: &Any<A>, id: &str) -> Any<A> {
89 at(rt, "static")
90 .call([id.into()].to_array().to_any())
91 .unwrap()
92 }
93
94 #[test]
104 fn value() {
105 let rt: Any<A> = runtime::<A>().unwrap();
106 let is = static_at(&rt, "Object.is");
107 assert_eq!(
108 is.clone().to_string(),
109 Ok("function is() { [native code] }".into())
110 );
111 assert_eq!(
112 Any::dot(is.clone(), "length".into()).end(),
113 Ok(2.0.to_any())
114 );
115 let call = |a: Any<A>, b: Any<A>| is.clone().call([a, b].to_array().to_any());
116 assert_eq!(call((-0.0).to_any(), 0.0.to_any()), Ok(false.to_any()));
117 assert_eq!(
118 call(f64::NAN.to_any(), f64::NAN.to_any()),
119 Ok(true.to_any())
120 );
121 assert_eq!(call("a".into(), "a".into()), Ok(true.to_any()));
122 let again = static_at(&rt, "Object.is");
123 assert_eq!(is, again);
124 assert!(is.same_value(&again));
125 let is_nan = static_at(&rt, "Number.isNaN");
126 assert_eq!(
127 is_nan.clone().to_string(),
128 Ok("function isNaN() { [native code] }".into())
129 );
130 assert_ne!(is, is_nan);
131 let entries = static_at(&rt, "Object.entries");
132 assert_eq!(
133 entries.clone().to_string(),
134 Ok("function entries() { [native code] }".into())
135 );
136 assert_eq!(
137 Any::dot(entries.clone(), "length".into()).end(),
138 Ok(1.0.to_any())
139 );
140 let pairs = entries
141 .clone()
142 .call(["ab".into()].to_array().to_any())
143 .unwrap();
144 assert_eq!(pairs.to_json(), Ok(r#"[["0","a"],["1","b"]]"#.into()));
145 assert!(entries.call([].to_array().to_any()).is_err());
146 for id in ["Object.keys", "Math.max", "Object", "is", "toString", ""] {
147 assert!(static_at(&rt, id).is_nullish(), "{id}");
148 }
149 let dot = at(&rt, "dot");
150 let dispatch = |receiver: Any<A>, key: &str| {
151 dot.clone()
152 .call([receiver, key.into()].to_array().to_any())
153 .unwrap()
154 };
155 assert!(dispatch(1.0.to_any(), "startsWith").is_nullish());
156 assert!(dispatch("abc".into(), "slice").is_nullish());
157 let f = returns(7.0.to_any());
158 let own: Any<A> = [("startsWith".into(), f.clone())].to_object().to_any();
159 assert_eq!(dispatch(own, "startsWith"), f);
160 let on_abc = dispatch("abc".into(), "startsWith");
161 assert_ne!(
162 on_abc.clone().to_string(),
163 Ok("function startsWith() { [native code] }".into())
164 );
165 assert_eq!(
166 on_abc.call(["bc".into(), 1.0.to_any()].to_array().to_any()),
167 Ok(true.to_any())
168 );
169 }
170
171 #[test]
176 fn static_read_by_path() {
177 let rt: Any<A> = runtime::<A>().unwrap();
178 let is = static_read(&rt, "Object.is").unwrap();
179 assert_eq!(is, static_at(&rt, "Object.is"));
180 assert_eq!(static_read(&rt, "Object.is").unwrap(), is);
181 assert_eq!(
182 static_read(&rt, "Object.entries").unwrap(),
183 static_at(&rt, "Object.entries")
184 );
185 for path in ["Object.keys", "Math.max", "Object", "Math"] {
186 assert_eq!(static_read(&rt, path), Err(error::no_static_function(path)));
187 }
188 }
189
190 #[test]
197 fn member_call_through_the_value() {
198 let rt: Any<A> = runtime::<A>().unwrap();
199 let key: Any<A> = "startsWith".into();
200 assert_eq!(
201 1.0.to_any::<A>()
202 .dot(key.clone())
203 .option_call(&rt, || panic!("the arguments of a skipped call"))
204 .end(),
205 Ok(Any::undefined())
206 );
207 let abc: Any<A> = "abc".into();
208 let args = || Ok(["ab".into()].to_array().to_any());
209 assert_eq!(
210 abc.clone().dot(key.clone()).end_call(&rt, args),
211 Ok(true.to_any())
212 );
213 assert_eq!(
214 abc.clone().dot(key.clone()).option_call(&rt, args).end(),
215 Ok(true.to_any())
216 );
217 assert_eq!(
218 abc.clone()
219 .dot(key.clone())
220 .end_call(&rt, || Ok(["bc".into(), 1.0.to_any()].to_array().to_any())),
221 Ok(true.to_any())
222 );
223 assert!(
224 abc.clone()
225 .dot(key.clone())
226 .end_call(&rt, || Ok(["a".into(), BigInt::<A>::from(0i64).to_any()]
227 .to_array()
228 .to_any()))
229 .is_err()
230 );
231 let boom = throws("A".into());
232 let search: Any<A> = [("toString".into(), boom)].to_object().to_any();
233 assert_eq!(
234 abc.dot(key)
235 .end_call(&rt, || Ok([search, BigInt::<A>::from(0i64).to_any()]
236 .to_array()
237 .to_any())),
238 Err("A".into())
239 );
240 }
241}