1pub mod abi;
22
23#[cfg(test)]
24mod test;
25
26use crate::{
27 common::sized_index::SizedIndex,
28 sign::Sign,
29 vm::{
30 Any, Array, BigInt, Function, IFunction, IVm, Nullish, Number, Object, Property,
31 StaticCode, String, ToAny, ToArray, ToObject, ToString, Unpacked,
32 },
33};
34
35#[repr(u8)]
38#[derive(Debug, PartialEq, Eq, Clone, Copy)]
39pub enum Kind {
40 Undefined = 0,
41 Null = 1,
42 Boolean = 2,
43 Number = 3,
44 String = 4,
45 BigInt = 5,
46 Array = 6,
47 Object = 7,
48 Function = 8,
49}
50
51fn trap(name: &str, reason: &str) -> ! {
53 panic!("l0.{name}: {reason}")
54}
55
56fn unpack<A: IVm>(v: &Any<A>) -> Unpacked<A> {
57 v.clone().into()
58}
59
60pub fn kind<A: IVm>(v: &Any<A>) -> Kind {
62 match unpack(v) {
63 Unpacked::Nullish(Nullish::Undefined) => Kind::Undefined,
64 Unpacked::Nullish(Nullish::Null) => Kind::Null,
65 Unpacked::Boolean(_) => Kind::Boolean,
66 Unpacked::Number(_) => Kind::Number,
67 Unpacked::String(_) => Kind::String,
68 Unpacked::BigInt(_) => Kind::BigInt,
69 Unpacked::Array(_) => Kind::Array,
70 Unpacked::Object(_) => Kind::Object,
71 Unpacked::Function(_) => Kind::Function,
72 }
73}
74
75fn as_string<A: IVm>(name: &str, v: &Any<A>) -> String<A> {
78 v.clone()
79 .try_into()
80 .unwrap_or_else(|_| trap(name, "not a string"))
81}
82
83fn as_bigint<A: IVm>(name: &str, v: &Any<A>) -> BigInt<A> {
84 v.clone()
85 .try_into()
86 .unwrap_or_else(|_| trap(name, "not a bigint"))
87}
88
89fn as_array<A: IVm>(name: &str, v: &Any<A>) -> Array<A> {
90 v.clone()
91 .try_into()
92 .unwrap_or_else(|_| trap(name, "not an array"))
93}
94
95fn as_object<A: IVm>(name: &str, v: &Any<A>) -> Object<A> {
96 v.clone()
97 .try_into()
98 .unwrap_or_else(|_| trap(name, "not an object"))
99}
100
101fn as_function<A: IVm>(name: &str, v: &Any<A>) -> Function<A> {
102 v.clone()
103 .try_into()
104 .unwrap_or_else(|_| trap(name, "not a function"))
105}
106
107fn index(name: &str, i: u32, n: u32) -> u32 {
109 if i >= n {
110 trap(name, "not an index")
111 }
112 i
113}
114
115pub fn undefined<A: IVm>() -> Any<A> {
119 Any::undefined()
120}
121
122pub fn null<A: IVm>() -> Any<A> {
124 Nullish::Null.to_any()
125}
126
127pub fn to_f64<A: IVm>(v: &Any<A>) -> f64 {
129 let n: Number = v
130 .clone()
131 .try_into()
132 .unwrap_or_else(|_| trap("to_f64", "not a number"));
133 n.into()
134}
135
136pub fn from_f64<A: IVm>(x: f64) -> Any<A> {
138 Number::from(x).to_any()
139}
140
141pub fn to_bool<A: IVm>(v: &Any<A>) -> bool {
143 v.clone()
144 .try_into()
145 .unwrap_or_else(|_| trap("to_bool", "not a boolean"))
146}
147
148pub fn from_bool<A: IVm>(b: bool) -> Any<A> {
150 b.to_any()
151}
152
153pub fn make_string<A: IVm>(units: &[u16]) -> Any<A> {
157 fits("make_string", units.len() as u64);
158 units.iter().copied().to_string().to_any()
159}
160
161pub fn read_units<A: IVm>(s: &Any<A>, start: u32, out: &mut [u16]) {
163 let s = as_string("read_units", s);
164 let range = range("read_units", start, out.len(), s.length());
165 for (o, i) in out.iter_mut().zip(range) {
166 *o = s[i];
167 }
168}
169
170pub fn make_bigint<A: IVm>(sign: i32, words: &[u64]) -> Any<A> {
175 let sign = match (sign, words.last()) {
176 (0, None) => Sign::Positive,
177 (1, Some(&w)) if w != 0 => Sign::Positive,
178 (-1, Some(&w)) if w != 0 => Sign::Negative,
179 _ => trap("make_bigint", "not canonical"),
180 };
181 BigInt::<A>::normalize_new(sign, words.iter().copied()).to_any()
182}
183
184pub fn sign<A: IVm>(b: &Any<A>) -> i32 {
187 let b = as_bigint("sign", b);
188 if b.length() == 0 {
189 return 0;
190 }
191 match b.sign() {
192 Sign::Positive => 1,
193 Sign::Negative => -1,
194 }
195}
196
197pub fn read_words<A: IVm>(b: &Any<A>, start: u32, out: &mut [u64]) {
200 let b = as_bigint("read_words", b);
201 let range = range("read_words", start, out.len(), b.length());
202 for (o, i) in out.iter_mut().zip(range) {
203 *o = b[i];
204 }
205}
206
207fn range(name: &str, start: u32, len: usize, n: u32) -> core::ops::Range<u32> {
209 match u32::try_from(len)
210 .ok()
211 .and_then(|len| start.checked_add(len))
212 {
213 Some(end) if end <= n => start..end,
214 _ => trap(name, "not a range"),
215 }
216}
217
218fn fits(name: &str, len: u64) {
223 if len > u64::from(u32::MAX) {
224 trap(name, "too long")
225 }
226}
227
228pub fn unit<A: IVm>(s: &Any<A>, i: u32) -> u16 {
230 let s = as_string("unit", s);
231 s[index("unit", i, s.length())]
232}
233
234pub fn from_unit<A: IVm>(u: u16) -> Any<A> {
236 [u].to_string().to_any()
237}
238
239pub fn concat<A: IVm>(a: &Any<A>, b: &Any<A>) -> Any<A> {
241 let a = as_string("concat", a);
242 let b = as_string("concat", b);
243 fits("concat", u64::from(a.length()) + u64::from(b.length()));
244 a.index_iter().chain(b.index_iter()).to_string().to_any()
245}
246
247pub fn substring<A: IVm>(s: &Any<A>, start: u32, end: u32) -> Any<A> {
250 let s = as_string("substring", s);
251 let end = index("substring", end, s.length() + 1);
252 let start = index("substring", start, end + 1);
253 (start..end).map(|i| s[i]).to_string().to_any()
254}
255
256pub fn join<A: IVm>(parts: &Any<A>, separator: &Any<A>) -> Any<A> {
260 let parts = as_array("join", parts);
261 let separator = as_string("join", separator);
262 let parts: Vec<String<A>> = parts.index_iter().map(|p| as_string("join", &p)).collect();
263 let len = parts.iter().map(|p| u64::from(p.length())).sum::<u64>()
264 + u64::from(separator.length()) * parts.len().saturating_sub(1) as u64;
265 fits("join", len);
266 let mut units: Vec<u16> = Vec::with_capacity(len as usize);
267 for (i, p) in parts.into_iter().enumerate() {
268 if i > 0 {
269 units.extend(separator.clone().index_iter());
270 }
271 units.extend(p.index_iter());
272 }
273 units.to_string().to_any()
274}
275
276pub struct Builder<A: IVm> {
282 len: u32,
283 items: Items<A>,
284}
285
286enum Items<A: IVm> {
287 Array(Vec<Any<A>>),
288 Object(Vec<Property<A>>),
289}
290
291impl<A: IVm> Builder<A> {
292 fn count(&self) -> usize {
293 match &self.items {
294 Items::Array(items) => items.len(),
295 Items::Object(properties) => properties.len(),
296 }
297 }
298
299 fn has_room(&self, name: &str) {
300 if self.count() >= self.len as usize {
301 trap(name, "full")
302 }
303 }
304
305 pub fn push(&mut self, v: Any<A>) {
307 self.has_room("push");
308 match &mut self.items {
309 Items::Array(items) => items.push(v),
310 Items::Object(_) => trap("push", "not an array builder"),
311 }
312 }
313
314 pub fn push_property(&mut self, key: Any<A>, v: Any<A>) {
317 self.has_room("push_property");
318 let key = as_string("push_property", &key);
319 match &mut self.items {
320 Items::Object(properties) => properties.push((key, v)),
321 Items::Array(_) => trap("push_property", "not an object builder"),
322 }
323 }
324
325 pub fn seal(self) -> Any<A> {
329 if self.count() != self.len as usize {
330 trap("seal", "not full")
331 }
332 match self.items {
333 Items::Array(items) => items.to_array().to_any(),
334 Items::Object(properties) => {
335 let o: Object<A> = properties.to_object();
336 o.own_entries().to_object().to_any()
337 }
338 }
339 }
340}
341
342pub fn begin_array<A: IVm>(len: u32) -> Builder<A> {
344 Builder {
345 len,
346 items: Items::Array(Vec::with_capacity(len as usize)),
347 }
348}
349
350pub fn begin_object<A: IVm>(len: u32) -> Builder<A> {
353 Builder {
354 len,
355 items: Items::Object(Vec::with_capacity(len as usize)),
356 }
357}
358
359pub fn length<A: IVm>(v: &Any<A>) -> u32 {
364 match unpack(v) {
365 Unpacked::String(s) => s.length(),
366 Unpacked::BigInt(b) => b.length(),
367 Unpacked::Array(a) => a.length(),
368 Unpacked::Object(o) => o.own_entries().len() as u32,
369 Unpacked::Function(f) => f.length(),
370 _ => trap(
371 "length",
372 "not a string, a bigint, an array, an object or a function",
373 ),
374 }
375}
376
377pub fn item<A: IVm>(v: &Any<A>, i: u32) -> Any<A> {
380 match unpack(v) {
381 Unpacked::Array(a) => a[index("item", i, a.length())].clone(),
382 Unpacked::Object(o) => property("item", &o, i).1,
383 _ => trap("item", "not an array or an object"),
384 }
385}
386
387pub fn key<A: IVm>(o: &Any<A>, i: u32) -> Any<A> {
389 property("key", &as_object("key", o), i).0.to_any()
390}
391
392fn property<A: IVm>(name: &str, o: &Object<A>, i: u32) -> Property<A> {
393 let mut entries = o.own_entries();
394 let i = index(name, i, entries.len() as u32);
395 entries.swap_remove(i as usize)
396}
397
398pub fn find<A: IVm>(o: &Any<A>, key: &Any<A>) -> i32 {
401 let o = as_object("find", o);
402 let key = as_string("find", key);
403 o.own_entries()
404 .iter()
405 .position(|(k, _)| *k == key)
406 .map_or(-1, |i| i as i32)
407}
408
409pub fn make_function<A: IVm>(
415 code: StaticCode<A>,
416 frame: Any<A>,
417 length: u32,
418 text: Option<&'static str>,
419) -> Any<A> {
420 let frame = as_array("make_function", &frame);
421 A::static_function(code, length, frame, text).to_any()
422}
423
424pub fn call<A: IVm>(f: &Any<A>, args: &Any<A>) -> Result<Any<A>, Any<A>> {
427 let f = as_function("call", f);
428 f.call(as_array("call", args))
429}
430
431pub fn frame<A: IVm>(self_: &A::InternalFunction, i: u32) -> Any<A> {
434 let frame = A::frame(self_);
435 frame[index("frame", i, frame.length())].clone()
436}
437
438pub fn text<A: IVm>(f: &Any<A>) -> Any<A> {
441 as_function("text", f)
442 .internal()
443 .text()
444 .map_or_else(Any::undefined, Any::from)
445}
446
447pub fn same<A: IVm>(a: &Any<A>, b: &Any<A>) -> bool {
452 match (unpack(a), unpack(b)) {
453 (Unpacked::Array(a), Unpacked::Array(b)) => a == b,
454 (Unpacked::Object(a), Unpacked::Object(b)) => a == b,
455 (Unpacked::Function(a), Unpacked::Function(b)) => a == b,
456 (
457 Unpacked::Array(_) | Unpacked::Object(_) | Unpacked::Function(_),
458 Unpacked::Array(_) | Unpacked::Object(_) | Unpacked::Function(_),
459 ) => false,
460 _ => trap("same", "not an array, an object or a function"),
461 }
462}