Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 41 additions & 13 deletions core/engine/src/builtins/iterable/iterator_prototype.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
Context, JsArgs, JsObject, JsResult, JsSymbol, JsValue,
builtins::{
IntrinsicObject,
IntrinsicObject, Number,
array::Array,
builder::BuiltInBuilder,
iterable::{
Expand Down Expand Up @@ -304,7 +304,9 @@ impl Iterator {
let limit = args.get_or_undefined(0);
let num_limit = if_abrupt_close_iterator!(limit.to_number(context), iterated, context);

// 6. If numLimit is NaN, throw a RangeError exception.
// 6. If numLimit is NaN, then
// a. Let error be ThrowCompletion(a newly created RangeError object).
// b. Return ? IteratorClose(iterated, error).
if num_limit.is_nan() {
return iterated.close(
Err(js_error!(
Expand All @@ -314,10 +316,22 @@ impl Iterator {
);
}

// 7. Let integerLimit be ! ToIntegerOrInfinity(numLimit).
// 7. If numLimit is finite and numLimit > 𝔽(2**53 - 1), then
// a. Let error be ThrowCompletion(a newly created RangeError object).
// b. Return ? IteratorClose(iterated, error).
if num_limit.is_finite() && num_limit > Number::MAX_SAFE_INTEGER {
return iterated.close(
Err(js_error!(
RangeError: "Iterator.prototype.take: limit cannot be greater than 2^53 - 1"
)),
context,
);
}

// 8. Let integerLimit be ! ToIntegerOrInfinity(numLimit).
let integer_limit = IntegerOrInfinity::from(num_limit);

// 8. If integerLimit < 0, then
// 9. If integerLimit < 0, then
let integer_limit = match integer_limit {
IntegerOrInfinity::Integer(n) if n >= 0 => Some(n as u64),
IntegerOrInfinity::PositiveInfinity => None,
Expand All @@ -333,14 +347,14 @@ impl Iterator {
}
};

// 9. Set iterated to ? GetIteratorDirect(O).
// 10. Set iterated to ? GetIteratorDirect(O).
let iterated = get_iterator_direct(iterated.iterator(), context)?;

// 10-12 are deferred to `IteratorHelper::create` and `Take::new`.
// 11-13 are deferred to `IteratorHelper::create` and `Take::new`.
let result =
IteratorHelper::create(iterator_helper::Take::new(iterated, integer_limit), context);

// 13. Return result.
// 14. Return result.
Ok(result.into())
}

Expand All @@ -365,7 +379,9 @@ impl Iterator {
let limit = args.get_or_undefined(0);
let num_limit = if_abrupt_close_iterator!(limit.to_number(context), iterated, context);

// 6. If numLimit is NaN, throw a RangeError exception.
// 6. If numLimit is NaN, then
// a. Let error be ThrowCompletion(a newly created RangeError object).
// b. Return ? IteratorClose(iterated, error).
if num_limit.is_nan() {
return iterated.close(
Err(js_error!(
Expand All @@ -375,10 +391,22 @@ impl Iterator {
);
}

// 7. Let integerLimit be ! ToIntegerOrInfinity(numLimit).
// 7. If numLimit is finite and numLimit > 𝔽(2**53 - 1), then
// a. Let error be ThrowCompletion(a newly created RangeError object).
// b. Return ? IteratorClose(iterated, error).
if num_limit.is_finite() && num_limit > Number::MAX_SAFE_INTEGER {
return iterated.close(
Err(js_error!(
RangeError: "Iterator.prototype.drop: limit cannot be greater than 2^53 - 1"
)),
context,
);
}

// 8. Let integerLimit be ! ToIntegerOrInfinity(numLimit).
let integer_limit = IntegerOrInfinity::from(num_limit);

// 8. If integerLimit < 0, then
// 9. If integerLimit < 0, then
let integer_limit = match integer_limit {
IntegerOrInfinity::Integer(n) if n >= 0 => Some(n as u64),
IntegerOrInfinity::PositiveInfinity => None,
Expand All @@ -393,14 +421,14 @@ impl Iterator {
);
}
};
// 9. Set iterated to ? GetIteratorDirect(O).
// 10. Set iterated to ? GetIteratorDirect(O).
let iterated = get_iterator_direct(iterated.iterator(), context)?;

// 10-12 are deferred to `IteratorHelper::create` and `Drop::new`.
// 11-13 are deferred to `IteratorHelper::create` and `Drop::new`.
let result =
IteratorHelper::create(iterator_helper::Drop::new(iterated, integer_limit), context);

// 13. Return result.
// 14. Return result.
Ok(result.into())
}

Expand Down
75 changes: 75 additions & 0 deletions core/engine/src/builtins/iterable/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,45 @@ fn iterator_take_nan_throws() {
)]);
}

#[test]
fn iterator_take_limit_above_max_safe_integer_throws() {
run_test_actions([TestAction::assert_native_error(
"Iterator.from([1]).take(Number.MAX_SAFE_INTEGER + 1)",
JsNativeErrorKind::Range,
"Iterator.prototype.take: limit cannot be greater than 2^53 - 1",
)]);
}

#[test]
fn iterator_take_limit_max_safe_integer_or_infinity() {
run_test_actions([
TestAction::assert_eq(
"Iterator.from([1,2,3]).take(Number.MAX_SAFE_INTEGER).toArray().join(',')",
js_str!("1,2,3"),
),
TestAction::assert_eq(
"Iterator.from([1,2,3]).take(Infinity).toArray().join(',')",
js_str!("1,2,3"),
),
]);
}

#[test]
fn iterator_take_limit_above_max_safe_integer_closes_underlying() {
run_test_actions([
TestAction::run(
"let closed = false;
let it = {
__proto__: Iterator.prototype,
get next() { throw new Error('next should not be read'); },
return() { closed = true; return {}; }
};
try { it.take(Number.MAX_SAFE_INTEGER + 1); } catch (e) { if (!(e instanceof RangeError)) throw e; }",
),
TestAction::assert("closed"),
]);
}

#[test]
fn iterator_take_more_than_length() {
run_test_actions([TestAction::assert_eq(
Expand All @@ -174,6 +213,42 @@ fn iterator_drop_more_than_length() {
)]);
}

#[test]
fn iterator_drop_limit_above_max_safe_integer_throws() {
run_test_actions([TestAction::assert_native_error(
"Iterator.from([1]).drop(Number.MAX_SAFE_INTEGER + 1)",
JsNativeErrorKind::Range,
"Iterator.prototype.drop: limit cannot be greater than 2^53 - 1",
)]);
}

#[test]
fn iterator_drop_limit_max_safe_integer_or_infinity() {
run_test_actions([
TestAction::assert_eq(
"Iterator.from([1,2,3]).drop(Number.MAX_SAFE_INTEGER).toArray().length",
0,
),
TestAction::assert_eq("Iterator.from([1,2,3]).drop(Infinity).toArray().length", 0),
]);
}

#[test]
fn iterator_drop_limit_above_max_safe_integer_closes_underlying() {
run_test_actions([
TestAction::run(
"let closed = false;
let it = {
__proto__: Iterator.prototype,
get next() { throw new Error('next should not be read'); },
return() { closed = true; return {}; }
};
try { it.drop(Number.MAX_SAFE_INTEGER + 1); } catch (e) { if (!(e instanceof RangeError)) throw e; }",
),
TestAction::assert("closed"),
]);
}

#[test]
fn iterator_drop_zero() {
run_test_actions([TestAction::assert_eq(
Expand Down
Loading