mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 05:47:34 +00:00
LibJS: Implement %IteratorHelperPrototype%.return
This commit is contained in:
parent
57e7112a20
commit
0e63d04a35
6 changed files with 263 additions and 6 deletions
|
@ -44,16 +44,28 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorHelperPrototype::return_)
|
|||
{
|
||||
// 1. Let O be this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[UnderlyingIterator]]).
|
||||
auto iterator = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: O has a [[GeneratorState]] slot.
|
||||
// 4. If O.[[GeneratorState]] is suspendedStart, then
|
||||
// a. Set O.[[GeneratorState]] to completed.
|
||||
// b. NOTE: Once a generator enters the completed state it never leaves it and its associated execution context is never resumed. Any execution state associated with O can be discarded at this point.
|
||||
// c. Perform ? IteratorClose(O.[[UnderlyingIterator]], NormalCompletion(unused)).
|
||||
// d. Return CreateIterResultObject(undefined, true).
|
||||
if (iterator->generator_state() == GeneratorObject::GeneratorState::SuspendedStart) {
|
||||
// a. Set O.[[GeneratorState]] to completed.
|
||||
iterator->set_generator_state(GeneratorObject::GeneratorState::Completed);
|
||||
|
||||
// b. NOTE: Once a generator enters the completed state it never leaves it and its associated execution context is never resumed. Any execution state associated with O can be discarded at this point.
|
||||
|
||||
// c. Perform ? IteratorClose(O.[[UnderlyingIterator]], NormalCompletion(unused)).
|
||||
TRY(iterator_close(vm, iterator->underlying_iterator(), normal_completion({})));
|
||||
|
||||
// d. Return CreateIterResultObject(undefined, true).
|
||||
return create_iterator_result_object(vm, js_undefined(), true);
|
||||
}
|
||||
|
||||
// 5. Let C be Completion { [[Type]]: return, [[Value]]: undefined, [[Target]]: empty }.
|
||||
Completion completion { Completion::Type::Return, js_undefined(), {} };
|
||||
|
||||
// 6. Return ? GeneratorResumeAbrupt(O, C, "Iterator Helper").
|
||||
|
||||
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, "IteratorHelper.prototype.return"sv);
|
||||
return TRY(iterator->resume_abrupt(vm, move(completion), "Iterator Helper"sv));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,6 +49,28 @@ describe("errors", () => {
|
|||
iterator.next();
|
||||
}).toThrow(TestError);
|
||||
});
|
||||
|
||||
test("iterator's return method throws", () => {
|
||||
function TestError() {}
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
throw new TestError();
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
const iterator = new TestIterator().drop(1);
|
||||
iterator.return();
|
||||
}).toThrow(TestError);
|
||||
});
|
||||
});
|
||||
|
||||
describe("normal behavior", () => {
|
||||
|
@ -115,4 +137,31 @@ describe("normal behavior", () => {
|
|||
expect(value.value).toBeUndefined();
|
||||
expect(value.done).toBeTrue();
|
||||
});
|
||||
|
||||
test("return is forwarded to the underlying iterator's return method", () => {
|
||||
let returnCount = 0;
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
++returnCount;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TestIterator().drop(1);
|
||||
expect(returnCount).toBe(0);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,6 +40,28 @@ describe("errors", () => {
|
|||
}).toThrow(TestError);
|
||||
});
|
||||
|
||||
test("iterator's return method throws", () => {
|
||||
function TestError() {}
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
throw new TestError();
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
const iterator = new TestIterator().filter(() => true);
|
||||
iterator.return();
|
||||
}).toThrow(TestError);
|
||||
});
|
||||
|
||||
test("predicate function throws", () => {
|
||||
function TestError() {}
|
||||
|
||||
|
@ -153,4 +175,31 @@ describe("normal behavior", () => {
|
|||
expect(firstFilterCount).toBe(4);
|
||||
expect(secondFilterCount).toBe(2);
|
||||
});
|
||||
|
||||
test("return is forwarded to the underlying iterator's return method", () => {
|
||||
let returnCount = 0;
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
++returnCount;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TestIterator().filter(() => true);
|
||||
expect(returnCount).toBe(0);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,6 +40,28 @@ describe("errors", () => {
|
|||
}).toThrow(TestError);
|
||||
});
|
||||
|
||||
test("iterator's return method throws", () => {
|
||||
function TestError() {}
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
throw new TestError();
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
const iterator = new TestIterator().flatMap(() => 0);
|
||||
iterator.return();
|
||||
}).toThrow(TestError);
|
||||
});
|
||||
|
||||
test("mapper function throws", () => {
|
||||
function TestError() {}
|
||||
|
||||
|
@ -138,4 +160,31 @@ describe("normal behavior", () => {
|
|||
expect(value.value).toBeUndefined();
|
||||
expect(value.done).toBeTrue();
|
||||
});
|
||||
|
||||
test("return is forwarded to the underlying iterator's return method", () => {
|
||||
let returnCount = 0;
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
++returnCount;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TestIterator().flatMap(() => 0);
|
||||
expect(returnCount).toBe(0);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,6 +40,28 @@ describe("errors", () => {
|
|||
}).toThrow(TestError);
|
||||
});
|
||||
|
||||
test("iterator's return method throws", () => {
|
||||
function TestError() {}
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
throw new TestError();
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
const iterator = new TestIterator().map(() => 0);
|
||||
iterator.return();
|
||||
}).toThrow(TestError);
|
||||
});
|
||||
|
||||
test("mapper function throws", () => {
|
||||
function TestError() {}
|
||||
|
||||
|
@ -141,4 +163,31 @@ describe("normal behavior", () => {
|
|||
expect(value.value).toBeUndefined();
|
||||
expect(value.done).toBeTrue();
|
||||
});
|
||||
|
||||
test("return is forwarded to the underlying iterator's return method", () => {
|
||||
let returnCount = 0;
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
++returnCount;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TestIterator().map(() => 0);
|
||||
expect(returnCount).toBe(0);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -49,6 +49,28 @@ describe("errors", () => {
|
|||
iterator.next();
|
||||
}).toThrow(TestError);
|
||||
});
|
||||
|
||||
test("iterator's return method throws", () => {
|
||||
function TestError() {}
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
throw new TestError();
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
const iterator = new TestIterator().take(1);
|
||||
iterator.return();
|
||||
}).toThrow(TestError);
|
||||
});
|
||||
});
|
||||
|
||||
describe("normal behavior", () => {
|
||||
|
@ -120,4 +142,31 @@ describe("normal behavior", () => {
|
|||
expect(value.value).toBeUndefined();
|
||||
expect(value.done).toBeTrue();
|
||||
});
|
||||
|
||||
test("return is forwarded to the underlying iterator's return method", () => {
|
||||
let returnCount = 0;
|
||||
|
||||
class TestIterator extends Iterator {
|
||||
next() {
|
||||
return {
|
||||
done: false,
|
||||
value: 1,
|
||||
};
|
||||
}
|
||||
|
||||
return() {
|
||||
++returnCount;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TestIterator().take(1);
|
||||
expect(returnCount).toBe(0);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
|
||||
iterator.return();
|
||||
expect(returnCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue