Test padding processing for zip

This commit is contained in:
André Bargull 2025-07-03 08:38:35 +02:00 committed by Philip Chimento
parent 84fdbadcb4
commit 64aa8b8f9c
4 changed files with 590 additions and 0 deletions

View File

@ -0,0 +1,144 @@
// Copyright (C) 2025 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.zip
description: >
Abrupt completion for GetIterator in "padding" option iteration.
info: |
Iterator.zip ( iterables [ , options ] )
...
14. If mode is "longest", then
...
b. Else,
i. Let paddingIter be Completion(GetIterator(paddingOption, sync)).
ii. IfAbruptCloseIterators(paddingIter, iters).
...
IfAbruptCloseIterators ( value, iteratorRecords )
1. Assert: value is a Completion Record.
2. If value is an abrupt completion, return ? IteratorCloseAll(iteratorRecords, value).
3. Else, set value to value.[[Value]].
IteratorCloseAll ( iters, completion )
1. For each element iter of iters, in reverse List order, do
a. Set completion to Completion(IteratorClose(iter, completion)).
2. Return ? completion.
IteratorClose ( iteratorRecord, completion )
1. Assert: iteratorRecord.[[Iterator]] is an Object.
2. Let iterator be iteratorRecord.[[Iterator]].
3. Let innerResult be Completion(GetMethod(iterator, "return")).
4. If innerResult is a normal completion, then
a. Let return be innerResult.[[Value]].
b. If return is undefined, return ? completion.
c. Set innerResult to Completion(Call(return, iterator)).
5. If completion is a throw completion, return ? completion.
...
includes: [compareArray.js]
features: [joint-iteration]
---*/
var log = [];
var first = {
next() {
log.push("unexpected call to next method");
},
return() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, first);
assert.sameValue(arguments.length, 0);
// NB: Log after above asserts, because failures aren't propagated.
log.push("first return");
// This exception is ignored.
throw new Test262Error();
}
};
var second = {
next() {
log.push("unexpected call to next method");
},
return() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, second);
assert.sameValue(arguments.length, 0);
// NB: Log after above asserts, because failures aren't propagated.
log.push("second return");
// This exception is ignored.
throw new Test262Error();
}
};
var third = {
next() {
log.push("unexpected call to next method");
},
get return() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, third);
assert.sameValue(arguments.length, 0);
// NB: Log after above asserts, because failures aren't propagated.
log.push("third return");
// This exception is ignored.
throw new Test262Error();
}
};
function ExpectedError() {}
// Padding iterator throws from |Symbol.iterator|.
var padding = {
[Symbol.iterator]() {
throw new ExpectedError();
},
next() {
log.push("unexpected call to next method");
},
return() {
log.push("unexpected call to return method");
},
};
assert.throws(ExpectedError, function() {
Iterator.zip([first, second, third], {mode: "longest", padding});
});
assert.compareArray(log, [
"third return",
"second return",
"first return",
]);
// Clear log
log.length = 0;
// Padding iterator throws from |next|.
var padding = {
[Symbol.iterator]() {
return this;
},
next() {
throw new ExpectedError();
},
return() {
log.push("unexpected call to return method");
},
};
assert.throws(ExpectedError, function() {
Iterator.zip([first, second, third], {mode: "longest", padding});
});
assert.compareArray(log, [
"third return",
"second return",
"first return",
]);

View File

@ -0,0 +1,124 @@
// Copyright (C) 2025 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.zip
description: >
Abrupt completion for IteratorClose in "padding" option iteration.
info: |
Iterator.zip ( iterables [ , options ] )
...
14. If mode is "longest", then
...
b. Else,
i. Let paddingIter be Completion(GetIterator(paddingOption, sync)).
...
v. If usingIterator is true, then
1. Let completion be Completion(IteratorClose(paddingIter, NormalCompletion(unused))).
2. IfAbruptCloseIterators(completion, iters).
...
IfAbruptCloseIterators ( value, iteratorRecords )
1. Assert: value is a Completion Record.
2. If value is an abrupt completion, return ? IteratorCloseAll(iteratorRecords, value).
3. Else, set value to value.[[Value]].
IteratorCloseAll ( iters, completion )
1. For each element iter of iters, in reverse List order, do
a. Set completion to Completion(IteratorClose(iter, completion)).
2. Return ? completion.
IteratorClose ( iteratorRecord, completion )
1. Assert: iteratorRecord.[[Iterator]] is an Object.
2. Let iterator be iteratorRecord.[[Iterator]].
3. Let innerResult be Completion(GetMethod(iterator, "return")).
4. If innerResult is a normal completion, then
a. Let return be innerResult.[[Value]].
b. If return is undefined, return ? completion.
c. Set innerResult to Completion(Call(return, iterator)).
5. If completion is a throw completion, return ? completion.
...
includes: [compareArray.js]
features: [joint-iteration]
---*/
var log = [];
var first = {
next() {
log.push("unexpected call to next method");
},
return() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, first);
assert.sameValue(arguments.length, 0);
// NB: Log after above asserts, because failures aren't propagated.
log.push("first return");
// This exception is ignored.
throw new Test262Error();
}
};
var second = {
next() {
log.push("unexpected call to next method");
},
return() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, second);
assert.sameValue(arguments.length, 0);
// NB: Log after above asserts, because failures aren't propagated.
log.push("second return");
// This exception is ignored.
throw new Test262Error();
}
};
var third = {
next() {
log.push("unexpected call to next method");
},
get return() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, third);
assert.sameValue(arguments.length, 0);
// NB: Log after above asserts, because failures aren't propagated.
log.push("third return");
// This exception is ignored.
throw new Test262Error();
}
};
function ExpectedError() {}
// Padding iterator throws from |Symbol.iterator|.
var padding = {
[Symbol.iterator]() {
return this;
},
next() {
return {done: false};
},
return() {
log.push("padding return");
throw new ExpectedError();
},
};
assert.throws(ExpectedError, function() {
Iterator.zip([first, second, third], {mode: "longest", padding});
});
assert.compareArray(log, [
"padding return",
"third return",
"second return",
"first return",
]);

View File

@ -0,0 +1,187 @@
// Copyright (C) 2025 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.zip
description: >
Abrupt completion for IteratorStepValue in "padding" option iteration.
info: |
Iterator.zip ( iterables [ , options ] )
...
14. If mode is "longest", then
...
b. Else,
i. Let paddingIter be Completion(GetIterator(paddingOption, sync)).
...
iv. Perform the following steps iterCount times:
1. If usingIterator is true, then
a. Set next to Completion(IteratorStepValue(paddingIter)).
b. IfAbruptCloseIterators(next, iters).
...
IfAbruptCloseIterators ( value, iteratorRecords )
1. Assert: value is a Completion Record.
2. If value is an abrupt completion, return ? IteratorCloseAll(iteratorRecords, value).
3. Else, set value to value.[[Value]].
IteratorCloseAll ( iters, completion )
1. For each element iter of iters, in reverse List order, do
a. Set completion to Completion(IteratorClose(iter, completion)).
2. Return ? completion.
IteratorClose ( iteratorRecord, completion )
1. Assert: iteratorRecord.[[Iterator]] is an Object.
2. Let iterator be iteratorRecord.[[Iterator]].
3. Let innerResult be Completion(GetMethod(iterator, "return")).
4. If innerResult is a normal completion, then
a. Let return be innerResult.[[Value]].
b. If return is undefined, return ? completion.
c. Set innerResult to Completion(Call(return, iterator)).
5. If completion is a throw completion, return ? completion.
...
includes: [compareArray.js]
features: [joint-iteration]
---*/
var log = [];
var first = {
next() {
log.push("unexpected call to next method");
},
return() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, first);
assert.sameValue(arguments.length, 0);
// NB: Log after above asserts, because failures aren't propagated.
log.push("first return");
// This exception is ignored.
throw new Test262Error();
}
};
var second = {
next() {
log.push("unexpected call to next method");
},
return() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, second);
assert.sameValue(arguments.length, 0);
// NB: Log after above asserts, because failures aren't propagated.
log.push("second return");
// This exception is ignored.
throw new Test262Error();
}
};
var third = {
next() {
log.push("unexpected call to next method");
},
get return() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, third);
assert.sameValue(arguments.length, 0);
// NB: Log after above asserts, because failures aren't propagated.
log.push("third return");
// This exception is ignored.
throw new Test262Error();
}
};
function ExpectedError() {}
// Padding iterator throws from |next|.
var padding = {
[Symbol.iterator]() {
return this;
},
next() {
throw new ExpectedError();
},
return() {
log.push("unexpected call to return method");
},
};
assert.throws(ExpectedError, function() {
Iterator.zip([first, second, third], {mode: "longest", padding});
});
assert.compareArray(log, [
"third return",
"second return",
"first return",
]);
// Clear log
log.length = 0;
// Padding iterator throws from |done|.
var padding = {
[Symbol.iterator]() {
return this;
},
next() {
return {
get done() {
throw new ExpectedError();
},
get value() {
log.push("unexpected access to value");
},
};
},
return() {
log.push("unexpected call to return method");
},
};
assert.throws(ExpectedError, function() {
Iterator.zip([first, second, third], {mode: "longest", padding});
});
assert.compareArray(log, [
"third return",
"second return",
"first return",
]);
// Clear log
log.length = 0;
// Padding iterator throws from |value|.
var padding = {
[Symbol.iterator]() {
return this;
},
next() {
return {
done: false,
get value() {
throw new ExpectedError();
},
};
},
return() {
log.push("unexpected call to return method");
},
};
assert.throws(ExpectedError, function() {
Iterator.zip([first, second, third], {mode: "longest", padding});
});
assert.compareArray(log, [
"third return",
"second return",
"first return",
]);

View File

@ -0,0 +1,135 @@
// Copyright (C) 2025 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.zip
description: >
Perform iteration of the "padding" option.
info: |
Iterator.zip ( iterables [ , options ] )
...
14. If mode is "longest", then
...
b. Else,
i. Let paddingIter be Completion(GetIterator(paddingOption, sync)).
...
iii. Let usingIterator be true.
iv. Perform the following steps iterCount times:
1. If usingIterator is true, then
a. Set next to Completion(IteratorStepValue(paddingIter)).
...
c. If next is done, then
i. Set usingIterator to false.
d. Else,
i. Append next to padding.
2. If usingIterator is false, append undefined to padding.
v. If usingIterator is true, then
1. Let completion be Completion(IteratorClose(paddingIter, NormalCompletion(unused))).
...
...
GetIterator ( obj, kind )
...
2. Else,
a. Let method be ? GetMethod(obj, %Symbol.iterator%).
3. If method is undefined, throw a TypeError exception.
4. Return ? GetIteratorFromMethod(obj, method).
GetIteratorFromMethod ( obj, method )
1. Let iterator be ? Call(method, obj).
2. If iterator is not an Object, throw a TypeError exception.
3. Return ? GetIteratorDirect(iterator).
GetIteratorDirect ( obj )
1. Let nextMethod be ? Get(obj, "next").
2. Let iteratorRecord be the Iterator Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }.
3. Return iteratorRecord.
includes: [proxyTrapsHelper.js, compareArray.js]
features: [joint-iteration]
---*/
function makeProxyWithGetHandler(log, name, obj) {
return new Proxy(obj, allowProxyTraps({
get(target, propertyKey, receiver) {
log.push(`${name}::${String(propertyKey)}`);
return Reflect.get(target, propertyKey, receiver);
}
}));
}
// "padding" option must be an iterable.
assert.throws(TypeError, function() {
Iterator.zip([], {
mode: "longest",
padding: {},
});
});
for (var n = 0; n <= 5; ++n) {
var iterables = Array(n).fill([]);
for (var k = 0; k <= n + 2; ++k) {
var elements = Array(k).fill(0);
var elementsIter = elements.values();
var log = [];
var padding = makeProxyWithGetHandler(log, "padding", {
[Symbol.iterator]() {
log.push("call iterator");
// Called with the correct receiver and no arguments.
assert.sameValue(this, padding);
assert.sameValue(arguments.length, 0);
return this;
},
next() {
log.push("call next");
// Called with the correct receiver and no arguments.
assert.sameValue(this, padding);
assert.sameValue(arguments.length, 0);
return elementsIter.next();
},
return() {
log.push("call return");
// Called with the correct receiver and no arguments.
assert.sameValue(this, padding);
assert.sameValue(arguments.length, 0);
return {};
}
});
Iterator.zip(iterables, {mode: "longest", padding});
// Property reads and calls from GetIterator.
var expected = [
"padding::Symbol(Symbol.iterator)",
"call iterator",
"padding::next",
];
// Call the "next" method |n| times until the padding iterator is exhausted.
for (var i = 0; i < Math.min(n, k); ++i) {
expected.push("call next");
}
// If |n| is larger than |k|, then there was one final call to the "next"
// method. Otherwise the "return" method was called to close the padding
// iterator.
if (n > k) {
expected.push("call next");
} else {
expected.push(
"padding::return",
"call return"
);
}
assert.compareArray(log, expected);
}
}