1use crate::vm::{Any, Array, Function, IVm, Object, Unpacked, error, primitive::Primitive};
2
3use std::result::Result;
4
5#[allow(dead_code)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ToPrimitivePreferredType {
12 Number,
13 String,
14}
15
16type MethodResult<A> = Result<Option<Primitive<A>>, Any<A>>;
19
20fn value_of<A: IVm>(_: &Array<A>) -> MethodResult<A> {
21 Ok(None)
24}
25
26fn arr_to_string<A: IVm>(a: &Array<A>) -> MethodResult<A> {
27 a.join(",".into()).map(|s| Some(Primitive::String(s)))
32}
33
34type ConversionMethod = (&'static str, Option<&'static str>);
40
41const VALUE_OF: ConversionMethod = ("valueOf", None);
42
43const TO_STRING: ConversionMethod = ("toString", Some("[object Object]"));
44
45fn ordinary_to_primitive<A: IVm, M>(
50 preferred_type: ToPrimitivePreferredType,
51 [value_of, to_string]: [M; 2],
52 mut call: impl FnMut(M) -> MethodResult<A>,
53) -> Result<Primitive<A>, Any<A>> {
54 let order = match preferred_type {
55 ToPrimitivePreferredType::Number => [value_of, to_string],
56 ToPrimitivePreferredType::String => [to_string, value_of],
57 };
58 for method in order {
59 if let Some(p) = call(method)? {
60 return Ok(p);
61 }
62 }
63 Err(error::cannot_convert_to_primitive())
64}
65
66fn obj_method<A: IVm>(o: &Object<A>, (name, stock): ConversionMethod) -> MethodResult<A> {
75 match o.own_property(&name.into()) {
76 Some(m) => match Function::try_from(m) {
77 Ok(f) => f.call(Array::default()).map(to_primitive_value),
78 Err(_) => Ok(None),
79 },
80 None => Ok(stock.map(|s| Primitive::String(s.into()))),
81 }
82}
83
84fn to_primitive_value<A: IVm>(v: Any<A>) -> Option<Primitive<A>> {
86 Primitive::try_from(Unpacked::from(v)).ok()
87}
88
89impl<A: IVm> Any<A> {
90 pub fn to_primitive(
93 self,
94 preferred_type: Option<ToPrimitivePreferredType>,
95 ) -> Result<Primitive<A>, Any<A>> {
96 let preferred_type = preferred_type.unwrap_or(ToPrimitivePreferredType::Number);
98 match Primitive::try_from(Unpacked::from(self)) {
99 Ok(p) => Ok(p),
100 Err(Unpacked::Object(o)) => {
101 ordinary_to_primitive(preferred_type, [VALUE_OF, TO_STRING], |m| obj_method(&o, m))
102 }
103 Err(Unpacked::Array(a)) => {
104 ordinary_to_primitive(preferred_type, [value_of, arr_to_string], |m| m(&a))
105 }
106 Err(Unpacked::Function(f)) => f.text().map(Primitive::String),
110 Err(_) => {
111 unreachable!("`Primitive::try_from` refuses only an object, an array or a function")
112 }
113 }
114 }
115}
116
117#[cfg(test)]
122mod tests {
123 use super::ToPrimitivePreferredType;
124 use crate::common::sized_index::SizedIndex;
125 use crate::vm::test::function::{returns, throws};
126 use crate::{
127 naive::Naive,
128 vm::{
129 Any, BigInt, IVm, Nullish, Number, ToAny, ToArray, ToObject, error,
130 primitive::Primitive,
131 },
132 };
133
134 type A = Naive;
135
136 fn s(v: &str) -> Any<A> {
137 v.into()
138 }
139
140 fn function() -> Any<A> {
141 returns(1.0.to_any())
142 }
143
144 fn with(props: &[(&str, Any<A>)]) -> Any<A> {
145 props
146 .iter()
147 .map(|(k, v)| ((*k).into(), v.clone()))
148 .collect::<Vec<_>>()
149 .to_object()
150 .to_any()
151 }
152
153 fn refused<T: core::fmt::Debug + PartialEq>(r: Result<T, Any<A>>, thrown: impl Into<Any<A>>) {
154 assert_eq!(r, Err(thrown.into()));
155 }
156
157 fn is_nan(r: Result<Number, Any<A>>) -> bool {
158 r.unwrap().is_nan()
159 }
160
161 #[test]
163 fn own_method_is_called() {
164 let o = || with(&[("toString", returns(s("b")))]);
165 assert_eq!(o().to_string(), Ok("b".into()));
166 assert_eq!(s("a") + o(), Ok(s("ab")));
167 let o = || with(&[("valueOf", returns(1.0.to_any()))]);
168 assert_eq!(o().to_number(), Ok(Number::from(1.0)));
169 assert_eq!(o() + 1.0.to_any(), Ok(2.0.to_any()));
170 assert_eq!(o().lt(2.0.to_any()), Ok(true.to_any()));
171 let big = with(&[("valueOf", returns(BigInt::<A>::from(5i64).to_any()))]);
172 assert_eq!(-big, Ok(BigInt::<A>::from(-5i64).to_any()));
173 }
174
175 #[test]
178 fn hint_picks_the_order() {
179 let o = || {
180 with(&[
181 ("valueOf", returns(1.0.to_any())),
182 ("toString", returns(s("t"))),
183 ])
184 };
185 assert_eq!(o().to_string(), Ok("t".into()));
186 assert_eq!(o().to_number(), Ok(Number::from(1.0)));
187 assert_eq!(o() + s("!"), Ok(s("1!")));
188 }
189
190 #[test]
194 fn non_function_is_skipped() {
195 for value in [s("x"), 1.0.to_any()] {
196 let o = || with(&[("valueOf", value.clone())]);
197 assert_eq!(o().to_string(), Ok("[object Object]".into()));
198 assert!(is_nan(o().to_number()));
199 assert_eq!(o() + s("!"), Ok(s("[object Object]!")));
200 let o = || with(&[("toString", value.clone())]);
201 refused(o().to_string(), error::cannot_convert_to_primitive());
202 refused(o().to_number(), error::cannot_convert_to_primitive());
203 }
204 }
205
206 #[test]
208 fn throw_propagates() {
209 let o = || {
210 with(&[
211 ("valueOf", throws(s("boom"))),
212 ("toString", returns(s("t"))),
213 ])
214 };
215 refused(o().to_number(), "boom");
216 assert_eq!(o().to_string(), Ok("t".into()));
217 }
218
219 #[test]
223 fn left_operand_first() {
224 type Op = fn(Any<A>, Any<A>) -> Result<Any<A>, Any<A>>;
225 let ops: [Op; 16] = [
226 |x, y| x + y,
227 |x, y| x - y,
228 |x, y| x * y,
229 |x, y| x / y,
230 |x, y| x % y,
231 |x, y| x.pow(y),
232 |x, y| x & y,
233 |x, y| x | y,
234 |x, y| x ^ y,
235 |x, y| x << y,
236 |x, y| x >> y,
237 |x, y| x.unsigned_right_shift(y),
238 Any::lt,
239 Any::gt,
240 Any::le,
241 Any::ge,
242 ];
243 let o = |v| with(&[("valueOf", throws(s(v)))]);
244 for op in ops {
245 refused(op(o("left"), o("right")), "left");
246 }
247 }
248
249 #[test]
252 fn non_primitive_result_moves_on() {
253 let inner = || with(&[("toString", returns(s("inner")))]);
254 let o = || with(&[("valueOf", returns(inner()))]);
255 assert!(is_nan(o().to_number()));
256 for result in [inner(), [].to_array().to_any(), function()] {
257 let o = || {
258 with(&[
259 ("valueOf", returns(result.clone())),
260 ("toString", returns(s("2"))),
261 ])
262 };
263 assert_eq!(o().to_number(), Ok(Number::from(2.0)));
264 let o = || with(&[("toString", returns(result.clone()))]);
265 refused(o().to_string(), error::cannot_convert_to_primitive());
266 }
267 }
268
269 #[test]
273 fn string_hint_never_reaches_value_of() {
274 let o = || with(&[("valueOf", function())]);
275 assert_eq!(o().to_string(), Ok("[object Object]".into()));
276 assert_eq!(o().to_number(), Ok(Number::from(1.0)));
277 }
278
279 #[test]
281 fn plain_object_is_unchanged() {
282 let o = || with(&[("a", function())]);
283 assert_eq!(o().to_string(), Ok("[object Object]".into()));
284 assert!(is_nan(o().to_number()));
285 assert_eq!((o() + s("!")), Ok(s("[object Object]!")));
286 }
287
288 fn texted() -> Any<A> {
290 A::static_function(|_, _| Ok(1.0.to_any()), 0, [].to_array(), Some("()=>1")).to_any()
291 }
292
293 #[test]
296 fn function_text() {
297 assert_eq!(texted().to_string(), Ok("()=>1".into()));
298 assert_eq!(texted() + s("!"), Ok(s("()=>1!")));
299 assert_eq!(1.0.to_any() + texted(), Ok(s("1()=>1")));
300 assert_eq!(texted() + texted(), Ok(s("()=>1()=>1")));
301 assert_eq!(texted().lt(s("z")), Ok(true.to_any()));
302 assert_eq!(s("(").lt(texted()), Ok(true.to_any()));
303 assert_eq!(
304 [texted(), 2.0.to_any()]
305 .to_array()
306 .to_any::<A>()
307 .to_string(),
308 Ok("()=>1,2".into())
309 );
310 assert!(is_nan([texted()].to_array().to_any().to_number()));
311 assert!(is_nan(texted().to_number()));
313 }
314
315 #[test]
318 fn function_without_text_is_refused() {
319 refused(function().to_string(), error::function_text());
320 refused(function() + s("!"), error::function_text());
321 refused(function() + 1.0.to_any(), error::function_text());
322 refused(1.0.to_any() + function(), error::function_text());
323 refused(function() + function(), error::function_text());
324 }
325
326 #[test]
329 fn function_number_is_nan() {
330 assert!(is_nan(function().to_number()));
331 assert_eq!((-function()).as_ref().map(Any::is_nan), Ok(true));
332 assert_eq!(
333 (function() - 1.0.to_any()).as_ref().map(Any::is_nan),
334 Ok(true)
335 );
336 assert_eq!(function() ^ 6.0.to_any(), Ok(6.0.to_any()));
337 }
338
339 #[test]
342 fn function_comparison() {
343 refused(function().lt(s("z")), error::function_text());
344 refused(s("z").lt(function()), error::function_text());
345 refused(function().lt(function()), error::function_text());
346 refused(function().ge(s("z")), error::function_text());
347 let five = || 5.0.to_any();
348 let big = || BigInt::<A>::from(5i64).to_any();
349 refused(
351 function().lt([].to_array().to_any()),
352 error::function_text(),
353 );
354 for other in [five(), big(), true.to_any()] {
355 assert_eq!(function().lt(other.clone()), Ok(false.to_any()));
356 assert_eq!(other.clone().lt(function()), Ok(false.to_any()));
357 assert_eq!(function().le(other.clone()), Ok(false.to_any()));
358 assert_eq!(function().ge(other), Ok(false.to_any()));
359 }
360 }
361
362 fn boom() -> Any<A> {
364 with(&[
365 ("valueOf", throws(s("boom"))),
366 ("toString", throws(s("boom"))),
367 ])
368 }
369
370 #[test]
373 fn member_arguments_are_converted() {
374 let arr = || {
375 [1.0.to_any(), 2.0.to_any(), 3.0.to_any()]
376 .to_array()
377 .to_any()
378 };
379 let str = || s("abc");
380 let (zero, one) = (|| 0.0.to_any(), || 1.0.to_any());
381 let x = boom;
382 type Case = (&'static str, Any<A>, &'static str, Vec<Any<A>>);
383 let cases: Vec<Case> = vec![
384 ("array.at", arr(), "at", vec![x()]),
385 ("array.slice start", arr(), "slice", vec![x()]),
386 ("array.slice end", arr(), "slice", vec![zero(), x()]),
387 ("array.indexOf from", arr(), "indexOf", vec![one(), x()]),
388 (
389 "array.lastIndexOf from",
390 arr(),
391 "lastIndexOf",
392 vec![one(), x()],
393 ),
394 ("array.includes from", arr(), "includes", vec![one(), x()]),
395 ("array.join", arr(), "join", vec![x()]),
396 ("array.toSpliced start", arr(), "toSpliced", vec![x()]),
397 (
398 "array.toSpliced skip",
399 arr(),
400 "toSpliced",
401 vec![zero(), x()],
402 ),
403 ("array.with index", arr(), "with", vec![x(), one()]),
404 ("array.flat depth", arr(), "flat", vec![x()]),
405 ("string.at", str(), "at", vec![x()]),
406 ("string.charAt", str(), "charAt", vec![x()]),
407 ("string.charCodeAt", str(), "charCodeAt", vec![x()]),
408 ("string.codePointAt", str(), "codePointAt", vec![x()]),
409 ("string.concat", str(), "concat", vec![x()]),
410 ("string.endsWith needle", str(), "endsWith", vec![x()]),
411 ("string.endsWith end", str(), "endsWith", vec![s("c"), x()]),
412 ("string.startsWith needle", str(), "startsWith", vec![x()]),
413 (
414 "string.startsWith start",
415 str(),
416 "startsWith",
417 vec![s("a"), x()],
418 ),
419 ("string.includes needle", str(), "includes", vec![x()]),
420 ("string.includes pos", str(), "includes", vec![s("a"), x()]),
421 ("string.indexOf needle", str(), "indexOf", vec![x()]),
422 ("string.indexOf pos", str(), "indexOf", vec![s("a"), x()]),
423 ("string.lastIndexOf needle", str(), "lastIndexOf", vec![x()]),
424 (
425 "string.lastIndexOf pos",
426 str(),
427 "lastIndexOf",
428 vec![s("a"), x()],
429 ),
430 ("string.padEnd length", str(), "padEnd", vec![x()]),
431 (
432 "string.padEnd fill",
433 str(),
434 "padEnd",
435 vec![5.0.to_any(), x()],
436 ),
437 ("string.padStart length", str(), "padStart", vec![x()]),
438 (
439 "string.padStart fill",
440 str(),
441 "padStart",
442 vec![5.0.to_any(), x()],
443 ),
444 ("string.repeat", str(), "repeat", vec![x()]),
445 (
446 "string.replace pattern",
447 str(),
448 "replace",
449 vec![x(), s("z")],
450 ),
451 (
452 "string.replace replacement",
453 str(),
454 "replace",
455 vec![s("a"), x()],
456 ),
457 (
458 "string.replaceAll pattern",
459 str(),
460 "replaceAll",
461 vec![x(), s("z")],
462 ),
463 (
464 "string.replaceAll replacement",
465 str(),
466 "replaceAll",
467 vec![s("a"), x()],
468 ),
469 ("string.slice start", str(), "slice", vec![x()]),
470 ("string.slice end", str(), "slice", vec![zero(), x()]),
471 ("string.substring start", str(), "substring", vec![x()]),
472 (
473 "string.substring end",
474 str(),
475 "substring",
476 vec![zero(), x()],
477 ),
478 ("string.split separator", str(), "split", vec![x()]),
479 ("string.split limit", str(), "split", vec![s("b"), x()]),
480 ("number.toFixed", 1.5.to_any(), "toFixed", vec![x()]),
481 (
482 "number.toExponential",
483 1.5.to_any(),
484 "toExponential",
485 vec![x()],
486 ),
487 ("number.toPrecision", 1.5.to_any(), "toPrecision", vec![x()]),
488 ("number.toString radix", 1.5.to_any(), "toString", vec![x()]),
489 (
490 "bigint.toString radix",
491 BigInt::<A>::from(5i64).to_any(),
492 "toString",
493 vec![x()],
494 ),
495 ];
496 for (name, recv, method, args) in cases {
497 assert_eq!(call(recv, method, args), Err("boom".into()), "{name}");
498 }
499 }
500
501 fn big(n: i64) -> Any<A> {
502 BigInt::<A>::from(n).to_any()
503 }
504
505 fn value_of(v: Any<A>) -> Any<A> {
506 with(&[("valueOf", returns(v))])
507 }
508
509 fn to_string_of(v: Any<A>) -> Any<A> {
510 with(&[("toString", returns(v))])
511 }
512
513 fn call(recv: Any<A>, name: &str, args: Vec<Any<A>>) -> Result<Any<A>, Any<A>> {
515 recv.dot(name.into())
516 .end_call(&crate::vm::runtime::<A>().unwrap(), || {
517 Ok(args.to_array().to_any())
518 })
519 }
520
521 #[test]
524 fn big_int_value_of_is_numeric() {
525 type Op = fn(Any<A>, Any<A>) -> Result<Any<A>, Any<A>>;
526 let ops: [(&str, Op, i64); 11] = [
527 ("+", |x, y| x + y, 8),
528 ("-", |x, y| x - y, 4),
529 ("*", |x, y| x * y, 12),
530 ("/", |x, y| x / y, 3),
531 ("%", |x, y| x % y, 0),
532 ("**", |x, y| x.pow(y), 36),
533 ("&", |x, y| x & y, 2),
534 ("|", |x, y| x | y, 6),
535 ("^", |x, y| x ^ y, 4),
536 ("<<", |x, y| x << y, 24),
537 (">>", |x, y| x >> y, 1),
538 ];
539 for (name, op, expected) in ops {
540 assert_eq!(op(value_of(big(6)), big(2)), Ok(big(expected)), "{name}");
541 assert_eq!(op(big(6), value_of(big(2))), Ok(big(expected)), "{name}");
542 assert!(op(value_of(big(6)), 2.0.to_any()).is_err(), "{name} mixes");
543 assert!(op(2.0.to_any(), value_of(big(6))).is_err(), "{name} mixes");
544 }
545 assert!(value_of(big(6)).unsigned_right_shift(big(1)).is_err());
546 assert_eq!(value_of(big(1)).bitwise_not(), Ok(big(-2)));
547 let half = (big(1) << big(1_048_575)).unwrap();
549 let ones = (half.clone() + (half - big(1)).unwrap()).unwrap();
550 assert_eq!(ones.bitwise_not(), Err(error::bigint_too_large()));
551 assert_eq!(-value_of(big(5)), Ok(big(-5)));
552 refused(value_of(big(1)).to_number(), error::bigint_to_number());
554 assert!(value_of(big(1)).unary_plus().is_err());
555 assert!((big(1) * to_string_of(s("2"))).is_err());
557 assert_eq!(to_string_of(s("2")) * 3.0.to_any(), Ok(6.0.to_any()));
558 assert_eq!(value_of(big(1)) + s("a"), Ok(s("1a")));
560 assert_eq!(s("a") + value_of(big(-1)), Ok(s("a-1")));
561 }
562
563 #[test]
566 fn plus_without_a_hint() {
567 assert_eq!(to_string_of(s("x")) + 1.0.to_any(), Ok(s("x1")));
568 assert_eq!(1.0.to_any() + to_string_of(s("x")), Ok(s("1x")));
569 assert_eq!(to_string_of(s("2")) + 1.0.to_any(), Ok(s("21")));
570 assert_eq!(value_of(s("a")) + 1.0.to_any(), Ok(s("a1")));
571 assert_eq!(value_of(1.0.to_any()) + to_string_of(s("t")), Ok(s("1t")));
572 assert_eq!(
573 value_of(1.0.to_any()) + value_of(2.0.to_any()),
574 Ok(3.0.to_any())
575 );
576 assert_eq!(value_of(true.to_any()) + 1.0.to_any(), Ok(2.0.to_any()));
577 assert_eq!(
578 value_of(Nullish::Null.to_any()) + 1.0.to_any(),
579 Ok(1.0.to_any())
580 );
581 assert!(is_nan(
582 (value_of(Any::undefined()) + 1.0.to_any()).and_then(Any::to_number)
583 ));
584 let t = |v| with(&[("toString", throws(s(v)))]);
586 refused(t("a") + t("b"), "a");
587 refused(1.0.to_any() + t("b"), "b");
588 }
589
590 #[test]
593 fn every_primitive_result() {
594 let cases: [(Any<A>, &str, Option<f64>); 7] = [
595 (Nullish::Null.to_any(), "null", Some(0.0)),
596 (Any::undefined(), "undefined", None),
597 (true.to_any(), "true", Some(1.0)),
598 (s("7"), "7", Some(7.0)),
599 (s(""), "", Some(0.0)),
600 (2.5.to_any(), "2.5", Some(2.5)),
601 ((-0.0).to_any(), "0", Some(-0.0)),
602 ];
603 for (v, text, number) in cases {
604 assert_eq!(to_string_of(v.clone()).to_string(), Ok(text.into()));
605 assert_eq!(
606 value_of(v.clone()).to_string(),
607 Ok("[object Object]".into())
608 );
609 let n = value_of(v.clone()).to_number().unwrap();
610 match number {
611 Some(x) => assert_eq!(f64::from(n).to_bits(), x.to_bits(), "{text}"),
612 None => assert!(n.is_nan()),
613 }
614 let n = f64::from(to_string_of(v).to_number().unwrap());
616 assert_eq!(n.to_bits(), number.unwrap_or(f64::NAN).to_bits(), "{text}");
617 }
618 assert_eq!(to_string_of(big(5)).to_string(), Ok("5".into()));
619 assert_eq!(to_string_of(s("-3")).to_number(), Ok(Number::from(-3.0)));
620 assert_eq!(-value_of(s("3")), Ok((-3.0).to_any()));
621 assert!(is_nan(value_of(s("5n")).to_number()));
622 }
623
624 #[test]
627 fn hint_per_caller() {
628 let o = || {
629 with(&[
630 ("valueOf", returns(5.0.to_any())),
631 ("toString", returns(s("1"))),
632 ])
633 };
634 assert_eq!(o().to_number(), Ok(Number::from(5.0)));
636 assert_eq!(o() - 0.0.to_any(), Ok(5.0.to_any()));
637 assert_eq!(o().lt(2.0.to_any()), Ok(false.to_any()));
638 assert_eq!(o().ge(2.0.to_any()), Ok(true.to_any()));
639 assert_eq!(2.0.to_any().gt(o()), Ok(false.to_any()));
640 assert_eq!(2.0.to_any().le(o()), Ok(true.to_any()));
641 assert_eq!(o() + 0.0.to_any(), Ok(5.0.to_any()));
643 assert_eq!(o().to_string(), Ok("1".into()));
645 assert_eq!(call(s("a1b"), "indexOf", vec![o()]), Ok(1.0.to_any()));
646 assert_eq!(
647 call([o(), o()].to_array().to_any(), "join", vec![]),
648 Ok(s("1,1"))
649 );
650 assert_eq!(
651 call([1.0.to_any()].to_array().to_any(), "join", vec![o()]),
652 Ok(s("1"))
653 );
654 let hint = |h| match o().to_primitive(h).unwrap() {
656 Primitive::Number(n) => format!("n{}", f64::from(n)),
657 Primitive::String(t) => format!("s{}", std::string::String::from(t)),
658 _ => unreachable!(),
659 };
660 assert_eq!(hint(None), "n5");
661 assert_eq!(hint(Some(ToPrimitivePreferredType::Number)), "n5");
662 assert_eq!(hint(Some(ToPrimitivePreferredType::String)), "s1");
663 }
664
665 #[test]
669 fn relational_with_objects() {
670 let two = || value_of(2.0.to_any());
671 assert_eq!(value_of(1.0.to_any()).lt(two()), Ok(true.to_any()));
672 assert_eq!(two().gt(value_of(1.0.to_any())), Ok(true.to_any()));
673 assert_eq!(two().le(two()), Ok(true.to_any()));
674 assert_eq!(two().ge(two()), Ok(true.to_any()));
675 assert_eq!(two().lt(two()), Ok(false.to_any()));
676 assert_eq!(to_string_of(s("b")).lt(s("c")), Ok(true.to_any()));
679 assert_eq!(
680 to_string_of(s("10")).lt(to_string_of(s("9"))),
681 Ok(true.to_any())
682 );
683 assert_eq!(value_of(s("10")).lt(9.0.to_any()), Ok(false.to_any()));
684 assert_eq!(big(1).lt(two()), Ok(true.to_any()));
685 assert_eq!(value_of(big(3)).gt(2.0.to_any()), Ok(true.to_any()));
686 assert_eq!(value_of(big(3)).ge(value_of(big(3))), Ok(true.to_any()));
687 for nan in [value_of(Number::NAN.to_any()), value_of(s("x"))] {
689 for r in [
690 nan.clone().lt(1.0.to_any()),
691 nan.clone().gt(1.0.to_any()),
692 nan.clone().le(1.0.to_any()),
693 nan.clone().ge(1.0.to_any()),
694 1.0.to_any().lt(nan.clone()),
695 1.0.to_any().ge(nan.clone()),
696 ] {
697 assert_eq!(r, Ok(false.to_any()));
698 }
699 }
700 assert_eq!(value_of(s("x")).lt(big(1)), Ok(false.to_any()));
701 assert_eq!(value_of(s("x")).ge(big(1)), Ok(false.to_any()));
702 let t = |v| with(&[("valueOf", throws(s(v)))]);
704 refused(t("l").lt(1.0.to_any()), "l");
705 refused(1.0.to_any().lt(t("r")), "r");
706 refused(1.0.to_any().ge(t("r")), "r");
707 }
708
709 #[test]
713 fn array_elements_are_converted() {
714 let arr = |items: Vec<Any<A>>| items.to_array().to_any::<A>();
715 assert_eq!(
716 arr(vec![to_string_of(s("x")), 1.0.to_any()]).to_string(),
717 Ok("x,1".into())
718 );
719 assert_eq!(
720 arr(vec![value_of(1.0.to_any())]).to_string(),
721 Ok("[object Object]".into())
722 );
723 assert_eq!(arr(vec![to_string_of(s("x"))]) + 1.0.to_any(), Ok(s("x1")));
724 assert_eq!(-arr(vec![to_string_of(s("5"))]), Ok((-5.0).to_any()));
725 assert_eq!(
726 arr(vec![to_string_of(s("5"))]).to_number(),
727 Ok(Number::from(5.0))
728 );
729 assert!(is_nan(arr(vec![value_of(5.0.to_any())]).to_number()));
730 assert_eq!(
731 arr(vec![to_string_of(s("1"))]).lt(arr(vec![to_string_of(s("2"))])),
732 Ok(true.to_any())
733 );
734 let nested = arr(vec![arr(vec![
735 to_string_of(s("n")),
736 Nullish::Null.to_any(),
737 ])]);
738 assert_eq!(nested.to_string(), Ok("n,".into()));
739 for r in [
740 arr(vec![1.0.to_any(), boom()]).to_string().map(|_| ()),
741 arr(vec![arr(vec![boom()])]).to_number().map(|_| ()),
742 (arr(vec![boom()]) + 1.0.to_any()).map(|_| ()),
743 (1.0.to_any() - arr(vec![boom()])).map(|_| ()),
744 (-arr(vec![boom()])).map(|_| ()),
745 arr(vec![boom()]).lt(1.0.to_any()).map(|_| ()),
746 ] {
747 assert_eq!(r, Err("boom".into()));
748 }
749 refused(
751 arr(vec![to_string_of(arr(vec![]))]).to_string(),
752 error::cannot_convert_to_primitive(),
753 );
754 }
755
756 #[test]
759 fn no_conversion_where_none_is_asked() {
760 let o = boom;
761 assert!(o().to_boolean());
762 assert_eq!(!o(), Ok(false.to_any()));
763 assert_eq!(o().typeof_(), Ok(s("object")));
764 let and_condition = o();
765 assert_eq!(
766 Any::conditional(
767 and_condition.clone(),
768 || Ok(1.0.to_any()),
769 || Ok(and_condition)
770 ),
771 Ok(1.0.to_any())
772 );
773 let or_condition = o();
774 assert!(
775 Any::conditional(
776 or_condition.clone(),
777 || Ok(or_condition),
778 || Ok(1.0.to_any())
779 )
780 .is_ok()
781 );
782 assert_eq!(o().entry(s("a")), Ok(Any::undefined()));
784 let x = o();
786 assert!(x == x.clone());
787 assert!(o() != o());
788 let m = with(&[("toString", throws(s("boom")))]);
790 assert!(m.dot("toString".into()).end().is_ok());
791 }
792
793 #[test]
796 fn method_gets_no_arguments() {
797 let arity = A::static_function(
798 |_, args| Ok(Number::from(args.length() as f64).to_any()),
799 0,
800 [].to_array(),
801 None,
802 )
803 .to_any();
804 assert_eq!(
805 with(&[("valueOf", arity.clone())]).to_number(),
806 Ok(Number::from(0.0))
807 );
808 assert_eq!(with(&[("toString", arity)]).to_string(), Ok("0".into()));
809 }
810
811 #[test]
814 fn every_non_function_is_skipped() {
815 let others = [
816 Nullish::Null.to_any(),
817 Any::undefined(),
818 true.to_any(),
819 big(1),
820 [].to_array().to_any(),
821 with(&[]),
822 ];
823 for v in others {
824 let o = || with(&[("valueOf", v.clone())]);
825 assert_eq!(o().to_string(), Ok("[object Object]".into()));
826 assert!(is_nan(o().to_number()));
827 let o = || with(&[("toString", v.clone())]);
828 refused(o().to_string(), error::cannot_convert_to_primitive());
829 refused(o() + 1.0.to_any(), error::cannot_convert_to_primitive());
830 let o = || with(&[("toString", v.clone()), ("valueOf", returns(4.0.to_any()))]);
832 assert_eq!(o().to_number(), Ok(Number::from(4.0)));
833 assert_eq!(o().to_string(), Ok("4".into()));
835 }
836 }
837
838 #[test]
841 fn result_is_not_converted() {
842 for result in [texted(), with(&[("toString", returns(s("a")))])] {
843 let o = || with(&[("toString", returns(result.clone()))]);
844 refused(o().to_string(), error::cannot_convert_to_primitive());
845 refused(o().to_number(), error::cannot_convert_to_primitive());
846 let o = || {
847 with(&[
848 ("valueOf", returns(result.clone())),
849 ("toString", throws(s("t"))),
850 ])
851 };
852 refused(o().to_number(), "t");
853 refused(o().to_string(), "t");
854 }
855 let o = with(&[
856 ("valueOf", returns(with(&[]))),
857 ("toString", returns(with(&[]))),
858 ]);
859 refused(o.clone().to_number(), error::cannot_convert_to_primitive());
860 refused(o + 1.0.to_any(), error::cannot_convert_to_primitive());
861 }
862
863 #[test]
866 fn discarded_conversion_still_throws() {
867 let empty = || [].to_array().to_any::<A>();
868 refused(call(empty(), "join", vec![boom()]), "boom");
869 refused(
870 call(empty(), "join", vec![to_string_of(with(&[]))]),
871 error::cannot_convert_to_primitive(),
872 );
873 refused(
874 call(empty(), "join", vec![[boom()].to_array().to_any()]),
875 "boom",
876 );
877 assert_eq!(call(empty(), "join", vec![function()]), Ok(s("")));
878 assert_eq!(
879 call(s("a"), "split", vec![to_string_of(s("a")), 0.0.to_any()]).map(|a| a.to_string()),
880 Ok(Ok("".into()))
881 );
882 refused(call(s("a"), "split", vec![boom(), 0.0.to_any()]), "boom");
883 let t = |v| with(&[("valueOf", throws(s(v)))]);
885 refused(call(s("a"), "split", vec![t("sep"), t("limit")]), "limit");
886 }
887
888 #[test]
890 fn member_arguments_use_the_value() {
891 let abc = || s("abc");
892 let one = || value_of(1.0.to_any());
893 let text = |r: Result<Any<A>, Any<A>>| r.and_then(Any::to_string);
894 let arr = || {
895 [1.0.to_any(), 2.0.to_any(), 3.0.to_any()]
896 .to_array()
897 .to_any::<A>()
898 };
899 assert_eq!(text(call(abc(), "charAt", vec![one()])), Ok("b".into()));
900 assert_eq!(text(call(abc(), "slice", vec![one()])), Ok("bc".into()));
901 assert_eq!(
902 text(call(abc(), "repeat", vec![value_of(2.0.to_any())])),
903 Ok("abcabc".into())
904 );
905 assert_eq!(
906 text(call(
907 abc(),
908 "padStart",
909 vec![value_of(5.0.to_any()), to_string_of(s("-"))]
910 )),
911 Ok("--abc".into())
912 );
913 assert_eq!(
914 text(call(abc(), "concat", vec![to_string_of(s("d"))])),
915 Ok("abcd".into())
916 );
917 assert_eq!(
918 call(abc(), "indexOf", vec![to_string_of(s("c"))]),
919 Ok(2.0.to_any())
920 );
921 assert_eq!(
922 call(abc(), "startsWith", vec![to_string_of(s("b")), one()]),
923 Ok(true.to_any())
924 );
925 assert_eq!(
926 call(arr(), "at", vec![value_of((-1.0).to_any())]),
927 Ok(3.0.to_any())
928 );
929 assert_eq!(
930 call(arr(), "indexOf", vec![2.0.to_any(), one()]),
931 Ok(1.0.to_any())
932 );
933 assert_eq!(
934 text(call(arr(), "join", vec![to_string_of(s("+"))])),
935 Ok("1+2+3".into())
936 );
937 assert_eq!(
938 text(call(1.5.to_any(), "toFixed", vec![one()])),
939 Ok("1.5".into())
940 );
941 assert_eq!(
942 text(call(
943 255.0.to_any(),
944 "toString",
945 vec![value_of(16.0.to_any())]
946 )),
947 Ok("ff".into())
948 );
949 }
950}