1#![doc = include_str!("README.md")]
2
3use crate::{
4 sign::Sign,
5 vm::{
6 Any, BigInt, Function, IVm, IteratorRecord, Number, String, ToAny, ToArray, ToObject,
7 ToString, error,
8 },
9};
10
11pub fn number_text(v: f64) -> std::string::String {
14 crate::vm::string_coercion::number_text::<crate::naive::Naive>(v)
15}
16
17pub fn string_any<A: IVm>(v: &str) -> Any<A> {
19 v.into()
20}
21
22pub fn string_key<A: IVm>(v: &str) -> String<A> {
24 v.into()
25}
26
27pub fn string_any_utf16<A: IVm>(v: &[u16]) -> Any<A> {
30 string_key_utf16::<A>(v).to_any()
31}
32
33pub fn string_key_utf16<A: IVm>(v: &[u16]) -> String<A> {
35 v.iter().copied().to_string()
36}
37
38pub fn bigint_any<A: IVm>(v: i64) -> Any<A> {
40 Into::<BigInt<A>>::into(v).to_any()
41}
42
43pub fn bigint_any_words<A: IVm>(negative: bool, words: &[u64]) -> Any<A> {
48 let sign = if negative {
49 Sign::Negative
50 } else {
51 Sign::Positive
52 };
53 BigInt::normalize_new(sign, words.iter().copied()).to_any()
54}
55
56pub fn f64_any<A: IVm>(v: u64) -> Any<A> {
62 Number::from(f64::from_bits(v)).to_any()
63}
64
65pub fn static_read<A: IVm>(runtime: &Any<A>, path: &str) -> Result<Any<A>, Any<A>> {
75 let read = runtime.clone().dot(string_any("static")).end()?;
76 let value = read.call([string_any(path)].to_array().to_any())?;
77 match Function::try_from(value) {
78 Ok(f) => Ok(f.to_any()),
79 Err(_) => Err(error::no_static_function(path)),
80 }
81}
82
83pub fn strict_eq<A: IVm>(a: Any<A>, b: Any<A>) -> Result<Any<A>, Any<A>> {
88 Ok((a == b).to_any())
89}
90
91pub fn strict_ne<A: IVm>(a: Any<A>, b: Any<A>) -> Result<Any<A>, Any<A>> {
93 Ok((a != b).to_any())
94}
95
96pub enum ArrayItem<A: IVm> {
100 Value(Any<A>),
101 Spread(IteratorRecord<A>),
102}
103
104pub fn value_item<A: IVm>(v: Any<A>) -> ArrayItem<A> {
106 ArrayItem::Value(v)
107}
108
109pub fn spread_item<A: IVm>(v: Any<A>) -> Result<ArrayItem<A>, Any<A>> {
114 v.get_iterator().map(ArrayItem::Spread)
115}
116
117fn reserve<A: IVm>(values: &mut Vec<Any<A>>, additional: usize) -> Result<(), Any<A>> {
121 values
122 .try_reserve(additional)
123 .map_err(|_| error::array_too_long())
124}
125
126pub fn spread_array<A: IVm>(
145 items: impl IntoIterator<Item = ArrayItem<A>>,
146) -> Result<Any<A>, Any<A>> {
147 let parts: Vec<_> = items.into_iter().collect();
148 let at_least: u64 = parts
149 .iter()
150 .map(|p| match p {
151 ArrayItem::Value(_) => 1,
152 ArrayItem::Spread(i) => i.size_hint().0 as u64,
153 })
154 .sum();
155 let limit = u64::from(u32::MAX);
156 if at_least > limit {
157 return Err(error::array_too_long());
158 }
159 let mut values = Vec::new();
160 reserve(&mut values, at_least as usize)?;
161 let mut push = |v: Any<A>| -> Result<(), Any<A>> {
162 if values.len() as u64 == limit {
163 return Err(error::array_too_long());
164 }
165 reserve(&mut values, 1)?;
166 values.push(v);
167 Ok(())
168 };
169 for part in parts {
170 match part {
171 ArrayItem::Value(v) => push(v)?,
172 ArrayItem::Spread(i) => i.into_iter().try_for_each(&mut push)?,
173 }
174 }
175 Ok(values.to_array().to_any())
176}
177
178pub fn spread_call<A: IVm>(
182 f: Any<A>,
183 items: impl IntoIterator<Item = ArrayItem<A>>,
184) -> Result<Any<A>, Any<A>> {
185 f.call(spread_array(items)?)
186}
187
188pub enum ObjectItem<A: IVm> {
192 Property(String<A>, Any<A>),
193 Spread(Any<A>),
194}
195
196pub fn property_item<A: IVm>(k: String<A>, v: Any<A>) -> ObjectItem<A> {
204 ObjectItem::Property(k, v)
205}
206
207pub fn spread_entries<A: IVm>(v: Any<A>) -> ObjectItem<A> {
209 ObjectItem::Spread(v)
210}
211
212pub fn spread_object<A: IVm>(items: impl IntoIterator<Item = ObjectItem<A>>) -> Any<A> {
219 let mut entries = Vec::new();
220 for item in items {
221 match item {
222 ObjectItem::Property(k, v) => entries.push((k, v)),
223 ObjectItem::Spread(v) => entries.extend(v.object_spread()),
224 }
225 }
226 entries.to_object().to_any()
227}
228
229#[cfg(test)]
230mod test {
231 use super::*;
232 use crate::{
233 common::sized_index::SizedIndex,
234 naive::Naive,
235 vm::{Array, IVm, Object, Unpacked},
236 };
237
238 #[test]
239 fn strings() {
240 let direct: Any<Naive> = "a".into();
241 assert_eq!(string_any::<Naive>("a"), direct);
242 let key: String<Naive> = "a".into();
243 assert_eq!(string_key::<Naive>("a"), key);
244 }
245
246 #[test]
247 fn bigints() {
248 assert_eq!(
249 bigint_any::<Naive>(-1),
250 Into::<BigInt<Naive>>::into(-1i64).to_any()
251 );
252 }
253
254 #[test]
255 fn bigints_of_any_size() {
256 let word = |negative, words: &[u64]| bigint_any_words::<Naive>(negative, words);
257 assert_eq!(word(false, &[1]), bigint_any(1));
259 assert_eq!(word(true, &[1 << 63]), bigint_any(i64::MIN));
260 assert_eq!(word(false, &[i64::MAX as u64]), bigint_any(i64::MAX));
261 assert_eq!(word(true, &[]), bigint_any(0));
263 assert_eq!(word(true, &[0, 0]), bigint_any(0));
264 let two_64 = word(false, &[0, 1]);
266 assert_eq!(two_64, (bigint_any::<Naive>(1) << bigint_any(64)).unwrap());
267 assert_eq!(word(true, &[0, 1, 0]), (-two_64.clone()).unwrap());
268 assert_ne!(two_64, word(false, &[1, 1]));
269 let big = word(false, &[0xC373_E0EE_4E3F_0AD2, 0x1_8EE9_0FF6]);
271 assert_eq!(
272 big.to_string().unwrap(),
273 "123456789012345678901234567890".into()
274 );
275 }
276
277 #[test]
278 fn numbers_as_text() {
279 assert_eq!(number_text(f64::NAN), "NaN");
280 assert_eq!(number_text(-0.0), "0");
281 assert_eq!(number_text(-1.5), "-1.5");
282 assert_eq!(number_text(1e21), "1e+21");
283 assert_eq!(number_text(1e-7), "1e-7");
284 assert_eq!(
286 number_text(0x6_b5a4_ff2c_1a40_u64 as f64 + 0.25),
287 "1888570120608320.2"
288 );
289 }
290
291 #[test]
292 fn numbers_by_their_bits() {
293 let bits = |v: Any<Naive>| match v.into() {
294 Unpacked::Number(x) => f64::from(x).to_bits(),
295 _ => panic!("a number"),
296 };
297 assert_eq!(bits(f64_any(0x4002_6666_6666_6666)), 2.3f64.to_bits());
298 assert_eq!(bits(f64_any(0x8000_0000_0000_0000)), (-0f64).to_bits());
299 assert_eq!(bits(f64_any(0x7ff8_0000_0000_0000)), 0x7ff8_0000_0000_0000);
300 }
301
302 #[test]
305 fn strict_equality() {
306 let one = || f64_any::<Naive>(0x3ff0_0000_0000_0000);
307 let nan = || f64_any::<Naive>(0x7ff8_0000_0000_0000);
308 assert_eq!(strict_eq(one(), one()), Ok(true.to_any()));
309 assert_eq!(strict_ne(one(), one()), Ok(false.to_any()));
310 assert_eq!(strict_eq(nan(), nan()), Ok(false.to_any()));
311 assert_eq!(strict_ne(nan(), nan()), Ok(true.to_any()));
312 assert_eq!(strict_eq(one(), string_any("1")), Ok(false.to_any()));
313 }
314
315 fn elements(r: Result<Any<Naive>, Any<Naive>>) -> Vec<Any<Naive>> {
318 Array::try_from(r.unwrap()).unwrap().into_iter().collect()
319 }
320
321 #[test]
324 fn spread_arrays() {
325 let one = || f64_any::<Naive>(0x3ff0_0000_0000_0000);
326 let two = || f64_any::<Naive>(0x4000_0000_0000_0000);
327 let inner: Any<Naive> = [two()].to_array().to_any();
328 assert_eq!(
329 elements(spread_array([
330 value_item(one()),
331 spread_item(string_any("ab")).unwrap(),
332 spread_item(inner).unwrap(),
333 ])),
334 [one(), string_any("a"), string_any("b"), two()]
335 );
336 let empty: Any<Naive> = [].to_array().to_any();
337 assert!(
338 elements(spread_array([
339 spread_item(string_any("")).unwrap(),
340 spread_item(empty).unwrap()
341 ]))
342 .is_empty()
343 );
344 assert!(spread_item(one()).is_err());
345 }
346
347 #[test]
350 fn spread_admission_precedes_later_operands() {
351 use crate::vm::Nullish;
352 use core::cell::Cell;
353
354 let reached = Cell::new(false);
355 let later = || -> Result<Any<Naive>, Any<Naive>> {
356 reached.set(true);
357 Err(string_any("later"))
358 };
359 let result: Result<Any<Naive>, Any<Naive>> =
360 (|| spread_array([spread_item(Nullish::Null.to_any())?, value_item(later()?)]))();
361 assert_eq!(result.err(), Some(error::not_iterable()));
362 assert!(!reached.get());
363 }
364
365 #[test]
369 fn spread_array_too_long() {
370 let one = || f64_any::<Naive>(0x3ff0_0000_0000_0000);
371 let block: Any<Naive> = (0..65_536)
372 .map(|_| one())
373 .collect::<Vec<_>>()
374 .to_array()
375 .to_any();
376 let too_many = (0..65_537).map(|_| spread_item(block.clone()).unwrap());
377 assert_eq!(spread_array(too_many).err(), Some(error::array_too_long()));
378 let within = (0..2).map(|_| spread_item(block.clone()).unwrap());
379 assert_eq!(elements(spread_array(within)).len(), 131_072);
380 }
381
382 #[test]
387 fn spread_array_unbacked() {
388 let one = || f64_any::<Naive>(0x3ff0_0000_0000_0000);
389 let block: Any<Naive> = (0..65_537)
390 .map(|_| one())
391 .collect::<Vec<_>>()
392 .to_array()
393 .to_any();
394 let all = u32::MAX as usize;
395 if Vec::<Any<Naive>>::new().try_reserve(all).is_ok() {
396 return;
397 }
398 let spreads = (0..65_535).map(|_| spread_item(block.clone()).unwrap());
399 assert_eq!(spread_array(spreads).err(), Some(error::array_too_long()));
400 }
401
402 #[test]
408 fn reserve_refuses_what_no_vec_can_hold() {
409 let mut values: Vec<Any<Naive>> = Vec::new();
410 assert_eq!(
411 reserve(&mut values, usize::MAX),
412 Err(error::array_too_long())
413 );
414 assert!(values.is_empty());
415 assert_eq!(reserve(&mut values, 3), Ok(()));
416 assert!(values.capacity() >= 3);
417 }
418
419 #[test]
423 fn spread_calls() {
424 use crate::vm::IVm;
425 let identity: Any<Naive> =
426 Naive::static_function(|_, args| Ok(args.to_any()), 0, [].to_array(), None).to_any();
427 assert_eq!(
428 elements(spread_call(
429 identity,
430 [spread_item(string_any("ab")).unwrap()]
431 )),
432 [string_any("a"), string_any("b")]
433 );
434 let one = || f64_any::<Naive>(0x3ff0_0000_0000_0000);
435 let bad_spread = (|| spread_call(one(), [spread_item(one())?]))();
436 assert_eq!(bad_spread.err(), Some(error::not_iterable()));
437 assert!(spread_call(one(), [spread_item(string_any("ab")).unwrap()]).is_err());
438 }
439
440 #[test]
444 fn spread_objects() {
445 use crate::vm::{Nullish, Object, ToObject};
446 let one = || f64_any::<Naive>(0x3ff0_0000_0000_0000);
447 let two = || f64_any::<Naive>(0x4000_0000_0000_0000);
448 let three = || f64_any::<Naive>(0x4008_0000_0000_0000);
449 let inner: Any<Naive> = [(string_key("b"), two()), (string_key("a"), three())]
450 .to_object()
451 .to_any();
452 let result = spread_object([
453 property_item(string_key("a"), one()),
454 spread_entries(inner),
455 spread_entries(string_any("x")),
456 spread_entries(Nullish::Null.to_any()),
457 ]);
458 let entries = Object::try_from(result).unwrap().own_entries();
459 assert_eq!(
460 entries,
461 [
462 (string_key("0"), string_any("x")),
463 (string_key("a"), three()),
464 (string_key("b"), two()),
465 ]
466 );
467 }
468
469 #[test]
473 fn computed_keys() {
474 let one = || f64_any::<Naive>(0x3ff0_0000_0000_0000);
475 let negative_zero = || f64_any::<Naive>(0x8000_0000_0000_0000);
476 let object = spread_object([
477 property_item(one().to_string().unwrap(), string_any("a")),
478 property_item(negative_zero().to_string().unwrap(), string_any("b")),
479 property_item(Any::undefined().to_string().unwrap(), string_any("c")),
480 property_item(string_any("1").to_string().unwrap(), string_any("d")),
481 ]);
482 let entries = Object::try_from(object).unwrap().own_entries();
483 assert_eq!(
484 entries,
485 [
486 (string_key("0"), string_any("b")),
487 (string_key("1"), string_any("d")),
488 (string_key("undefined"), string_any("c")),
489 ]
490 );
491 let plain = Object::<Naive>::default().to_any();
492 let item = property_item(plain.to_string().unwrap(), one());
493 let ObjectItem::Property(key, _) = item else {
494 panic!()
495 };
496 assert_eq!(key, string_key("[object Object]"));
497 }
498
499 #[test]
501 fn computed_key_conversion() {
502 use core::cell::Cell;
503
504 let with_to_string = |method: Any<Naive>| -> Any<Naive> {
505 [(string_key("toString"), method)].to_object().to_any()
506 };
507 let method =
508 Naive::static_function(|_, _| Ok(string_any("own")), 0, [].to_array(), None).to_any();
509 let item = property_item(
510 with_to_string(method).to_string().unwrap(),
511 string_any("value"),
512 );
513 let object = Object::try_from(spread_object([item])).unwrap();
514 assert_eq!(
515 object.own_entries(),
516 [(string_key("own"), string_any("value"))]
517 );
518
519 let method = Naive::static_function(
520 |_, _| Err(string_any("key conversion failed")),
521 0,
522 [].to_array(),
523 None,
524 )
525 .to_any();
526 let reached = Cell::new(false);
527 let later = || -> Result<Any<Naive>, Any<Naive>> {
528 reached.set(true);
529 Err(string_any("value failed"))
530 };
531 let item = (|| -> Result<ObjectItem<Naive>, Any<Naive>> {
532 Ok(property_item(with_to_string(method).to_string()?, later()?))
533 })();
534 assert!(matches!(
535 item,
536 Err(error) if error == string_any("key conversion failed")
537 ));
538 assert!(!reached.get());
539 assert!(with_to_string(f64_any(0)).to_string().is_err());
540
541 let item = (|| -> Result<ObjectItem<Naive>, Any<Naive>> {
542 Ok(property_item(string_any("own").to_string()?, later()?))
543 })();
544 assert!(matches!(item, Err(error) if error == string_any("value failed")));
545 assert!(reached.get());
546 }
547
548 #[test]
551 fn strings_of_code_units() {
552 let lone = string_key_utf16::<Naive>(&[0x61, 0xd800]);
553 assert_eq!(lone.length(), 2);
554 assert_eq!(lone[1], 0xd800);
555 assert_eq!(string_any_utf16::<Naive>(&[0x61, 0x62]), string_any("ab"));
556 assert_eq!(string_key_utf16::<Naive>(&[]), string_key(""));
557 }
558}