1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::{js_string, vm::Opcode};
use super::{ByteCompiler, Literal};
impl ByteCompiler<'_, '_> {
/// Closes an iterator
///
/// This is equivalent to the [`IteratorClose`][iter] and [`AsyncIteratorClose`][async]
/// operations.
///
/// Iterator Stack:
/// - iterator **=>** \<empty\>
///
/// [iter]: https://tc39.es/ecma262/#sec-iteratorclose
/// [async]: https://tc39.es/ecma262/#sec-asynciteratorclose
pub(super) fn iterator_close(&mut self, async_: bool) {
self.emit_opcode(Opcode::IteratorDone);
let skip_return = self.jump_if_true();
// iterator didn't finish iterating.
self.emit_opcode(Opcode::IteratorReturn);
// `iterator` didn't have a `return` method, so we can early exit.
let early_exit = self.jump_if_false();
if async_ {
self.emit_opcode(Opcode::Await);
self.emit_opcode(Opcode::GeneratorNext);
}
self.emit_opcode(Opcode::IsObject);
let skip_throw = self.jump_if_true();
let error_msg = self.get_or_insert_literal(Literal::String(js_string!(
"inner result was not an object"
)));
self.emit(Opcode::ThrowNewTypeError, &[error_msg]);
self.patch_jump(skip_return);
self.emit_opcode(Opcode::IteratorPop);
self.patch_jump(skip_throw);
self.patch_jump(early_exit);
}
/// Closes all active iterators in the current [`CallFrame`][crate::vm::CallFrame].
pub(super) fn close_active_iterators(&mut self) {
let start = self.next_opcode_location();
self.emit_opcode(Opcode::IteratorStackEmpty);
let empty = self.jump_if_true();
self.iterator_close(self.in_async_generator);
self.emit(Opcode::Jump, &[start]);
self.patch_jump(empty);
}
/// Yields from the current generator.
///
/// This is equivalent to the [`Yield ( value )`][yield] operation from the spec.
///
/// stack:
/// - value **=>** received
///
/// [yield]: https://tc39.es/ecma262/#sec-yield
pub(super) fn r#yield(&mut self) {
// 1. Let generatorKind be GetGeneratorKind().
if self.in_async_generator {
// 2. If generatorKind is async, return ? AsyncGeneratorYield(? Await(value)).
self.emit_opcode(Opcode::Await);
self.emit_opcode(Opcode::GeneratorNext);
self.async_generator_yield();
} else {
// 3. Otherwise, return ? GeneratorYield(CreateIterResultObject(value, false)).
self.emit_opcode(Opcode::CreateIteratorResult);
self.emit_u8(u8::from(false));
self.emit_opcode(Opcode::GeneratorYield);
}
self.emit_opcode(Opcode::GeneratorNext);
}
/// Yields from the current async generator.
///
/// This is equivalent to the [`AsyncGeneratorYield ( value )`][async_yield] operation from the spec.
///
/// stack:
/// - value **=>** received
///
/// [async_yield]: https://tc39.es/ecma262/#sec-asyncgeneratoryield
pub(super) fn async_generator_yield(&mut self) {
self.emit_opcode(Opcode::AsyncGeneratorYield);
let (normal, throw, r#return) =
self.emit_opcode_with_three_operands(Opcode::GeneratorJumpOnResumeKind);
{
self.patch_jump(r#return);
self.emit_opcode(Opcode::Await);
let (normal, throw, r#return) =
self.emit_opcode_with_three_operands(Opcode::GeneratorJumpOnResumeKind);
self.patch_jump(normal);
self.emit_opcode(Opcode::GeneratorSetReturn);
self.patch_jump(throw);
self.patch_jump(r#return);
}
self.patch_jump(normal);
self.patch_jump(throw);
}
}