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
107
108
109
110
111
112
use crate::{
js_string,
vm::{GeneratorResumeKind, Opcode},
};
use super::{ByteCompiler, Literal, Operand};
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::IteratorReturn);
// `iterator` didn't have a `return` method, is already done or is not on the iterator stack.
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_with_varying_operand(Opcode::ThrowNewTypeError, error_msg);
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.is_async_generator());
self.emit(Opcode::Jump, &[Operand::U32(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.is_async() {
// 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::CreateIteratorResult, &[Operand::Bool(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) {
// Stack: value
self.emit_opcode(Opcode::AsyncGeneratorYield);
// Stack: resume_kind, received
let non_return_resume = self.jump_if_not_resume_kind(GeneratorResumeKind::Return);
// Stack: resume_kind(Return), received
self.emit_opcode(Opcode::Pop);
// Stack: received
self.emit_opcode(Opcode::Await);
// Stack: resume_kind, received
let non_normal_resume = self.jump_if_not_resume_kind(GeneratorResumeKind::Normal);
// Stack: resume_kind(Normal), received
self.emit_opcode(Opcode::Pop);
// Stack: received
self.emit_resume_kind(GeneratorResumeKind::Return);
// Stack: resume_kind(Return) received
self.patch_jump(non_normal_resume);
// Stack: resume_kind, received
self.patch_jump(non_return_resume);
// Stack: resume_kind, received
}
}