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()).map(Any::is_nan), Ok(true));
332 assert_eq!((function() - 1.0.to_any()).map(Any::is_nan), Ok(true));
333 assert_eq!(function() ^ 6.0.to_any(), Ok(6.0.to_any()));
334 }
335
336 #[test]
339 fn function_comparison() {
340 refused(function().lt(s("z")), error::function_text());
341 refused(s("z").lt(function()), error::function_text());
342 refused(function().lt(function()), error::function_text());
343 refused(function().ge(s("z")), error::function_text());
344 let five = || 5.0.to_any();
345 let big = || BigInt::<A>::from(5i64).to_any();
346 refused(
348 function().lt([].to_array().to_any()),
349 error::function_text(),
350 );
351 for other in [five(), big(), true.to_any()] {
352 assert_eq!(function().lt(other.clone()), Ok(false.to_any()));
353 assert_eq!(other.clone().lt(function()), Ok(false.to_any()));
354 assert_eq!(function().le(other.clone()), Ok(false.to_any()));
355 assert_eq!(function().ge(other), Ok(false.to_any()));
356 }
357 }
358
359 fn boom() -> Any<A> {
361 with(&[
362 ("valueOf", throws(s("boom"))),
363 ("toString", throws(s("boom"))),
364 ])
365 }
366
367 #[test]
370 fn member_arguments_are_converted() {
371 let arr = || {
372 [1.0.to_any(), 2.0.to_any(), 3.0.to_any()]
373 .to_array()
374 .to_any()
375 };
376 let str = || s("abc");
377 let (zero, one) = (|| 0.0.to_any(), || 1.0.to_any());
378 let x = boom;
379 type Case = (&'static str, Any<A>, &'static str, Vec<Any<A>>);
380 let cases: Vec<Case> = vec![
381 ("array.at", arr(), "at", vec![x()]),
382 ("array.slice start", arr(), "slice", vec![x()]),
383 ("array.slice end", arr(), "slice", vec![zero(), x()]),
384 ("array.indexOf from", arr(), "indexOf", vec![one(), x()]),
385 (
386 "array.lastIndexOf from",
387 arr(),
388 "lastIndexOf",
389 vec![one(), x()],
390 ),
391 ("array.includes from", arr(), "includes", vec![one(), x()]),
392 ("array.join", arr(), "join", vec![x()]),
393 ("array.toSpliced start", arr(), "toSpliced", vec![x()]),
394 (
395 "array.toSpliced skip",
396 arr(),
397 "toSpliced",
398 vec![zero(), x()],
399 ),
400 ("array.with index", arr(), "with", vec![x(), one()]),
401 ("array.flat depth", arr(), "flat", vec![x()]),
402 ("string.at", str(), "at", vec![x()]),
403 ("string.charAt", str(), "charAt", vec![x()]),
404 ("string.charCodeAt", str(), "charCodeAt", vec![x()]),
405 ("string.codePointAt", str(), "codePointAt", vec![x()]),
406 ("string.concat", str(), "concat", vec![x()]),
407 ("string.endsWith needle", str(), "endsWith", vec![x()]),
408 ("string.endsWith end", str(), "endsWith", vec![s("c"), x()]),
409 ("string.startsWith needle", str(), "startsWith", vec![x()]),
410 (
411 "string.startsWith start",
412 str(),
413 "startsWith",
414 vec![s("a"), x()],
415 ),
416 ("string.includes needle", str(), "includes", vec![x()]),
417 ("string.includes pos", str(), "includes", vec![s("a"), x()]),
418 ("string.indexOf needle", str(), "indexOf", vec![x()]),
419 ("string.indexOf pos", str(), "indexOf", vec![s("a"), x()]),
420 ("string.lastIndexOf needle", str(), "lastIndexOf", vec![x()]),
421 (
422 "string.lastIndexOf pos",
423 str(),
424 "lastIndexOf",
425 vec![s("a"), x()],
426 ),
427 ("string.padEnd length", str(), "padEnd", vec![x()]),
428 (
429 "string.padEnd fill",
430 str(),
431 "padEnd",
432 vec![5.0.to_any(), x()],
433 ),
434 ("string.padStart length", str(), "padStart", vec![x()]),
435 (
436 "string.padStart fill",
437 str(),
438 "padStart",
439 vec![5.0.to_any(), x()],
440 ),
441 ("string.repeat", str(), "repeat", vec![x()]),
442 (
443 "string.replace pattern",
444 str(),
445 "replace",
446 vec![x(), s("z")],
447 ),
448 (
449 "string.replace replacement",
450 str(),
451 "replace",
452 vec![s("a"), x()],
453 ),
454 (
455 "string.replaceAll pattern",
456 str(),
457 "replaceAll",
458 vec![x(), s("z")],
459 ),
460 (
461 "string.replaceAll replacement",
462 str(),
463 "replaceAll",
464 vec![s("a"), x()],
465 ),
466 ("string.slice start", str(), "slice", vec![x()]),
467 ("string.slice end", str(), "slice", vec![zero(), x()]),
468 ("string.substring start", str(), "substring", vec![x()]),
469 (
470 "string.substring end",
471 str(),
472 "substring",
473 vec![zero(), x()],
474 ),
475 ("string.split separator", str(), "split", vec![x()]),
476 ("string.split limit", str(), "split", vec![s("b"), x()]),
477 ("number.toFixed", 1.5.to_any(), "toFixed", vec![x()]),
478 (
479 "number.toExponential",
480 1.5.to_any(),
481 "toExponential",
482 vec![x()],
483 ),
484 ("number.toPrecision", 1.5.to_any(), "toPrecision", vec![x()]),
485 ("number.toString radix", 1.5.to_any(), "toString", vec![x()]),
486 (
487 "bigint.toString radix",
488 BigInt::<A>::from(5i64).to_any(),
489 "toString",
490 vec![x()],
491 ),
492 ];
493 for (name, recv, method, args) in cases {
494 assert_eq!(call(recv, method, args), Err("boom".into()), "{name}");
495 }
496 }
497
498 fn big(n: i64) -> Any<A> {
499 BigInt::<A>::from(n).to_any()
500 }
501
502 fn value_of(v: Any<A>) -> Any<A> {
503 with(&[("valueOf", returns(v))])
504 }
505
506 fn to_string_of(v: Any<A>) -> Any<A> {
507 with(&[("toString", returns(v))])
508 }
509
510 fn call(recv: Any<A>, name: &str, args: Vec<Any<A>>) -> Result<Any<A>, Any<A>> {
512 recv.dot(name.into())
513 .end_call(&crate::vm::runtime::<A>().unwrap(), || {
514 Ok(args.to_array().to_any())
515 })
516 }
517
518 #[test]
521 fn big_int_value_of_is_numeric() {
522 type Op = fn(Any<A>, Any<A>) -> Result<Any<A>, Any<A>>;
523 let ops: [(&str, Op, i64); 11] = [
524 ("+", |x, y| x + y, 8),
525 ("-", |x, y| x - y, 4),
526 ("*", |x, y| x * y, 12),
527 ("/", |x, y| x / y, 3),
528 ("%", |x, y| x % y, 0),
529 ("**", |x, y| x.pow(y), 36),
530 ("&", |x, y| x & y, 2),
531 ("|", |x, y| x | y, 6),
532 ("^", |x, y| x ^ y, 4),
533 ("<<", |x, y| x << y, 24),
534 (">>", |x, y| x >> y, 1),
535 ];
536 for (name, op, expected) in ops {
537 assert_eq!(op(value_of(big(6)), big(2)), Ok(big(expected)), "{name}");
538 assert_eq!(op(big(6), value_of(big(2))), Ok(big(expected)), "{name}");
539 assert!(op(value_of(big(6)), 2.0.to_any()).is_err(), "{name} mixes");
540 assert!(op(2.0.to_any(), value_of(big(6))).is_err(), "{name} mixes");
541 }
542 assert!(value_of(big(6)).unsigned_right_shift(big(1)).is_err());
543 assert_eq!(value_of(big(1)).bitwise_not(), Ok(big(-2)));
544 let half = (big(1) << big(1_048_575)).unwrap();
546 let ones = (half.clone() + (half - big(1)).unwrap()).unwrap();
547 assert_eq!(ones.bitwise_not(), Err(error::bigint_too_large()));
548 assert_eq!(-value_of(big(5)), Ok(big(-5)));
549 refused(value_of(big(1)).to_number(), error::bigint_to_number());
551 assert!(value_of(big(1)).unary_plus().is_err());
552 assert!((big(1) * to_string_of(s("2"))).is_err());
554 assert_eq!(to_string_of(s("2")) * 3.0.to_any(), Ok(6.0.to_any()));
555 assert_eq!(value_of(big(1)) + s("a"), Ok(s("1a")));
557 assert_eq!(s("a") + value_of(big(-1)), Ok(s("a-1")));
558 }
559
560 #[test]
563 fn plus_without_a_hint() {
564 assert_eq!(to_string_of(s("x")) + 1.0.to_any(), Ok(s("x1")));
565 assert_eq!(1.0.to_any() + to_string_of(s("x")), Ok(s("1x")));
566 assert_eq!(to_string_of(s("2")) + 1.0.to_any(), Ok(s("21")));
567 assert_eq!(value_of(s("a")) + 1.0.to_any(), Ok(s("a1")));
568 assert_eq!(value_of(1.0.to_any()) + to_string_of(s("t")), Ok(s("1t")));
569 assert_eq!(
570 value_of(1.0.to_any()) + value_of(2.0.to_any()),
571 Ok(3.0.to_any())
572 );
573 assert_eq!(value_of(true.to_any()) + 1.0.to_any(), Ok(2.0.to_any()));
574 assert_eq!(
575 value_of(Nullish::Null.to_any()) + 1.0.to_any(),
576 Ok(1.0.to_any())
577 );
578 assert!(is_nan(
579 (value_of(Any::undefined()) + 1.0.to_any()).and_then(Any::to_number)
580 ));
581 let t = |v| with(&[("toString", throws(s(v)))]);
583 refused(t("a") + t("b"), "a");
584 refused(1.0.to_any() + t("b"), "b");
585 }
586
587 #[test]
590 fn every_primitive_result() {
591 let cases: [(Any<A>, &str, Option<f64>); 7] = [
592 (Nullish::Null.to_any(), "null", Some(0.0)),
593 (Any::undefined(), "undefined", None),
594 (true.to_any(), "true", Some(1.0)),
595 (s("7"), "7", Some(7.0)),
596 (s(""), "", Some(0.0)),
597 (2.5.to_any(), "2.5", Some(2.5)),
598 ((-0.0).to_any(), "0", Some(-0.0)),
599 ];
600 for (v, text, number) in cases {
601 assert_eq!(to_string_of(v.clone()).to_string(), Ok(text.into()));
602 assert_eq!(
603 value_of(v.clone()).to_string(),
604 Ok("[object Object]".into())
605 );
606 let n = value_of(v.clone()).to_number().unwrap();
607 match number {
608 Some(x) => assert_eq!(f64::from(n).to_bits(), x.to_bits(), "{text}"),
609 None => assert!(n.is_nan()),
610 }
611 let n = f64::from(to_string_of(v).to_number().unwrap());
613 assert_eq!(n.to_bits(), number.unwrap_or(f64::NAN).to_bits(), "{text}");
614 }
615 assert_eq!(to_string_of(big(5)).to_string(), Ok("5".into()));
616 assert_eq!(to_string_of(s("-3")).to_number(), Ok(Number::from(-3.0)));
617 assert_eq!(-value_of(s("3")), Ok((-3.0).to_any()));
618 assert!(is_nan(value_of(s("5n")).to_number()));
619 }
620
621 #[test]
624 fn hint_per_caller() {
625 let o = || {
626 with(&[
627 ("valueOf", returns(5.0.to_any())),
628 ("toString", returns(s("1"))),
629 ])
630 };
631 assert_eq!(o().to_number(), Ok(Number::from(5.0)));
633 assert_eq!(o() - 0.0.to_any(), Ok(5.0.to_any()));
634 assert_eq!(o().lt(2.0.to_any()), Ok(false.to_any()));
635 assert_eq!(o().ge(2.0.to_any()), Ok(true.to_any()));
636 assert_eq!(2.0.to_any().gt(o()), Ok(false.to_any()));
637 assert_eq!(2.0.to_any().le(o()), Ok(true.to_any()));
638 assert_eq!(o() + 0.0.to_any(), Ok(5.0.to_any()));
640 assert_eq!(o().to_string(), Ok("1".into()));
642 assert_eq!(call(s("a1b"), "indexOf", vec![o()]), Ok(1.0.to_any()));
643 assert_eq!(
644 call([o(), o()].to_array().to_any(), "join", vec![]),
645 Ok(s("1,1"))
646 );
647 assert_eq!(
648 call([1.0.to_any()].to_array().to_any(), "join", vec![o()]),
649 Ok(s("1"))
650 );
651 let hint = |h| match o().to_primitive(h).unwrap() {
653 Primitive::Number(n) => format!("n{}", f64::from(n)),
654 Primitive::String(t) => format!("s{}", std::string::String::from(t)),
655 _ => unreachable!(),
656 };
657 assert_eq!(hint(None), "n5");
658 assert_eq!(hint(Some(ToPrimitivePreferredType::Number)), "n5");
659 assert_eq!(hint(Some(ToPrimitivePreferredType::String)), "s1");
660 }
661
662 #[test]
666 fn relational_with_objects() {
667 let two = || value_of(2.0.to_any());
668 assert_eq!(value_of(1.0.to_any()).lt(two()), Ok(true.to_any()));
669 assert_eq!(two().gt(value_of(1.0.to_any())), Ok(true.to_any()));
670 assert_eq!(two().le(two()), Ok(true.to_any()));
671 assert_eq!(two().ge(two()), Ok(true.to_any()));
672 assert_eq!(two().lt(two()), Ok(false.to_any()));
673 assert_eq!(to_string_of(s("b")).lt(s("c")), Ok(true.to_any()));
676 assert_eq!(
677 to_string_of(s("10")).lt(to_string_of(s("9"))),
678 Ok(true.to_any())
679 );
680 assert_eq!(value_of(s("10")).lt(9.0.to_any()), Ok(false.to_any()));
681 assert_eq!(big(1).lt(two()), Ok(true.to_any()));
682 assert_eq!(value_of(big(3)).gt(2.0.to_any()), Ok(true.to_any()));
683 assert_eq!(value_of(big(3)).ge(value_of(big(3))), Ok(true.to_any()));
684 for nan in [value_of(Number::NAN.to_any()), value_of(s("x"))] {
686 for r in [
687 nan.clone().lt(1.0.to_any()),
688 nan.clone().gt(1.0.to_any()),
689 nan.clone().le(1.0.to_any()),
690 nan.clone().ge(1.0.to_any()),
691 1.0.to_any().lt(nan.clone()),
692 1.0.to_any().ge(nan.clone()),
693 ] {
694 assert_eq!(r, Ok(false.to_any()));
695 }
696 }
697 assert_eq!(value_of(s("x")).lt(big(1)), Ok(false.to_any()));
698 assert_eq!(value_of(s("x")).ge(big(1)), Ok(false.to_any()));
699 let t = |v| with(&[("valueOf", throws(s(v)))]);
701 refused(t("l").lt(1.0.to_any()), "l");
702 refused(1.0.to_any().lt(t("r")), "r");
703 refused(1.0.to_any().ge(t("r")), "r");
704 }
705
706 #[test]
710 fn array_elements_are_converted() {
711 let arr = |items: Vec<Any<A>>| items.to_array().to_any::<A>();
712 assert_eq!(
713 arr(vec![to_string_of(s("x")), 1.0.to_any()]).to_string(),
714 Ok("x,1".into())
715 );
716 assert_eq!(
717 arr(vec![value_of(1.0.to_any())]).to_string(),
718 Ok("[object Object]".into())
719 );
720 assert_eq!(arr(vec![to_string_of(s("x"))]) + 1.0.to_any(), Ok(s("x1")));
721 assert_eq!(-arr(vec![to_string_of(s("5"))]), Ok((-5.0).to_any()));
722 assert_eq!(
723 arr(vec![to_string_of(s("5"))]).to_number(),
724 Ok(Number::from(5.0))
725 );
726 assert!(is_nan(arr(vec![value_of(5.0.to_any())]).to_number()));
727 assert_eq!(
728 arr(vec![to_string_of(s("1"))]).lt(arr(vec![to_string_of(s("2"))])),
729 Ok(true.to_any())
730 );
731 let nested = arr(vec![arr(vec![
732 to_string_of(s("n")),
733 Nullish::Null.to_any(),
734 ])]);
735 assert_eq!(nested.to_string(), Ok("n,".into()));
736 for r in [
737 arr(vec![1.0.to_any(), boom()]).to_string().map(|_| ()),
738 arr(vec![arr(vec![boom()])]).to_number().map(|_| ()),
739 (arr(vec![boom()]) + 1.0.to_any()).map(|_| ()),
740 (1.0.to_any() - arr(vec![boom()])).map(|_| ()),
741 (-arr(vec![boom()])).map(|_| ()),
742 arr(vec![boom()]).lt(1.0.to_any()).map(|_| ()),
743 ] {
744 assert_eq!(r, Err("boom".into()));
745 }
746 refused(
748 arr(vec![to_string_of(arr(vec![]))]).to_string(),
749 error::cannot_convert_to_primitive(),
750 );
751 }
752
753 #[test]
756 fn no_conversion_where_none_is_asked() {
757 let o = boom;
758 assert!(o().to_boolean());
759 assert_eq!(!o(), Ok(false.to_any()));
760 assert_eq!(o().typeof_(), Ok(s("object")));
761 let and_condition = o();
762 assert_eq!(
763 Any::conditional(
764 and_condition.clone(),
765 || Ok(1.0.to_any()),
766 || Ok(and_condition)
767 ),
768 Ok(1.0.to_any())
769 );
770 let or_condition = o();
771 assert!(
772 Any::conditional(
773 or_condition.clone(),
774 || Ok(or_condition),
775 || Ok(1.0.to_any())
776 )
777 .is_ok()
778 );
779 assert_eq!(o().entry(s("a")), Ok(Any::undefined()));
781 let x = o();
783 assert!(x == x.clone());
784 assert!(o() != o());
785 let m = with(&[("toString", throws(s("boom")))]);
787 assert!(m.dot("toString".into()).end().is_ok());
788 }
789
790 #[test]
793 fn method_gets_no_arguments() {
794 let arity = A::static_function(
795 |_, args| Ok(Number::from(args.length() as f64).to_any()),
796 0,
797 [].to_array(),
798 None,
799 )
800 .to_any();
801 assert_eq!(
802 with(&[("valueOf", arity.clone())]).to_number(),
803 Ok(Number::from(0.0))
804 );
805 assert_eq!(with(&[("toString", arity)]).to_string(), Ok("0".into()));
806 }
807
808 #[test]
811 fn every_non_function_is_skipped() {
812 let others = [
813 Nullish::Null.to_any(),
814 Any::undefined(),
815 true.to_any(),
816 big(1),
817 [].to_array().to_any(),
818 with(&[]),
819 ];
820 for v in others {
821 let o = || with(&[("valueOf", v.clone())]);
822 assert_eq!(o().to_string(), Ok("[object Object]".into()));
823 assert!(is_nan(o().to_number()));
824 let o = || with(&[("toString", v.clone())]);
825 refused(o().to_string(), error::cannot_convert_to_primitive());
826 refused(o() + 1.0.to_any(), error::cannot_convert_to_primitive());
827 let o = || with(&[("toString", v.clone()), ("valueOf", returns(4.0.to_any()))]);
829 assert_eq!(o().to_number(), Ok(Number::from(4.0)));
830 assert_eq!(o().to_string(), Ok("4".into()));
832 }
833 }
834
835 #[test]
838 fn result_is_not_converted() {
839 for result in [texted(), with(&[("toString", returns(s("a")))])] {
840 let o = || with(&[("toString", returns(result.clone()))]);
841 refused(o().to_string(), error::cannot_convert_to_primitive());
842 refused(o().to_number(), error::cannot_convert_to_primitive());
843 let o = || {
844 with(&[
845 ("valueOf", returns(result.clone())),
846 ("toString", throws(s("t"))),
847 ])
848 };
849 refused(o().to_number(), "t");
850 refused(o().to_string(), "t");
851 }
852 let o = with(&[
853 ("valueOf", returns(with(&[]))),
854 ("toString", returns(with(&[]))),
855 ]);
856 refused(o.clone().to_number(), error::cannot_convert_to_primitive());
857 refused(o + 1.0.to_any(), error::cannot_convert_to_primitive());
858 }
859
860 #[test]
863 fn discarded_conversion_still_throws() {
864 let empty = || [].to_array().to_any::<A>();
865 refused(call(empty(), "join", vec![boom()]), "boom");
866 refused(
867 call(empty(), "join", vec![to_string_of(with(&[]))]),
868 error::cannot_convert_to_primitive(),
869 );
870 refused(
871 call(empty(), "join", vec![[boom()].to_array().to_any()]),
872 "boom",
873 );
874 assert_eq!(call(empty(), "join", vec![function()]), Ok(s("")));
875 assert_eq!(
876 call(s("a"), "split", vec![to_string_of(s("a")), 0.0.to_any()]).map(|a| a.to_string()),
877 Ok(Ok("".into()))
878 );
879 refused(call(s("a"), "split", vec![boom(), 0.0.to_any()]), "boom");
880 let t = |v| with(&[("valueOf", throws(s(v)))]);
882 refused(call(s("a"), "split", vec![t("sep"), t("limit")]), "limit");
883 }
884
885 #[test]
887 fn member_arguments_use_the_value() {
888 let abc = || s("abc");
889 let one = || value_of(1.0.to_any());
890 let text = |r: Result<Any<A>, Any<A>>| r.and_then(Any::to_string);
891 let arr = || {
892 [1.0.to_any(), 2.0.to_any(), 3.0.to_any()]
893 .to_array()
894 .to_any::<A>()
895 };
896 assert_eq!(text(call(abc(), "charAt", vec![one()])), Ok("b".into()));
897 assert_eq!(text(call(abc(), "slice", vec![one()])), Ok("bc".into()));
898 assert_eq!(
899 text(call(abc(), "repeat", vec![value_of(2.0.to_any())])),
900 Ok("abcabc".into())
901 );
902 assert_eq!(
903 text(call(
904 abc(),
905 "padStart",
906 vec![value_of(5.0.to_any()), to_string_of(s("-"))]
907 )),
908 Ok("--abc".into())
909 );
910 assert_eq!(
911 text(call(abc(), "concat", vec![to_string_of(s("d"))])),
912 Ok("abcd".into())
913 );
914 assert_eq!(
915 call(abc(), "indexOf", vec![to_string_of(s("c"))]),
916 Ok(2.0.to_any())
917 );
918 assert_eq!(
919 call(abc(), "startsWith", vec![to_string_of(s("b")), one()]),
920 Ok(true.to_any())
921 );
922 assert_eq!(
923 call(arr(), "at", vec![value_of((-1.0).to_any())]),
924 Ok(3.0.to_any())
925 );
926 assert_eq!(
927 call(arr(), "indexOf", vec![2.0.to_any(), one()]),
928 Ok(1.0.to_any())
929 );
930 assert_eq!(
931 text(call(arr(), "join", vec![to_string_of(s("+"))])),
932 Ok("1+2+3".into())
933 );
934 assert_eq!(
935 text(call(1.5.to_any(), "toFixed", vec![one()])),
936 Ok("1.5".into())
937 );
938 assert_eq!(
939 text(call(
940 255.0.to_any(),
941 "toString",
942 vec![value_of(16.0.to_any())]
943 )),
944 Ok("ff".into())
945 );
946 }
947}