1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/js_generator_object.h"
17 #include "ecmascript/generator_helper.h"
18 #include "ecmascript/js_iterator.h"
19 #include "ecmascript/js_object-inl.h"
20 #include "ecmascript/js_tagged_value-inl.h"
21
22 namespace panda::ecmascript {
GeneratorValidate(JSThread * thread,const JSHandle<JSTaggedValue> & obj)23 JSGeneratorState JSGeneratorObject::GeneratorValidate(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
24 {
25 // 1.Perform ? RequireInternalSlot(generator, [[GeneratorState]]).
26 // 2.Assert: generator also has a [[GeneratorContext]] internal slot.
27 if (!obj->IsECMAObject()) {
28 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not object",
29 JSGeneratorState::UNDEFINED);
30 }
31 JSHandle<JSObject> toObj = JSTaggedValue::ToObject(thread, obj);
32 if (!toObj->IsGeneratorObject()) {
33 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not generator object", JSGeneratorState::UNDEFINED);
34 }
35
36 // 3.Let state be generator.[[GeneratorState]].
37 JSHandle<JSGeneratorObject> generator(thread, JSGeneratorObject::Cast(*(toObj)));
38 JSGeneratorState state = generator->GetGeneratorState();
39 // 4.If state is executing, throw a TypeError exception.
40 if (state == JSGeneratorState::EXECUTING) {
41 THROW_TYPE_ERROR_AND_RETURN(thread, "State is executing", JSGeneratorState::UNDEFINED);
42 }
43 // 5.Return state.
44 return state;
45 }
46
GeneratorResume(JSThread * thread,const JSHandle<JSGeneratorObject> & generator,JSTaggedValue value)47 JSHandle<JSObject> JSGeneratorObject::GeneratorResume(JSThread *thread, const JSHandle<JSGeneratorObject> &generator,
48 JSTaggedValue value)
49 {
50 // 1.Let state be ? GeneratorValidate(generator).
51 JSHandle<JSTaggedValue> gen(thread, generator.GetTaggedValue());
52 JSGeneratorState state = GeneratorValidate(thread, gen);
53 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
54
55 // 2.If state is completed, return CreateIterResultObject(undefined, true).
56 if (state == JSGeneratorState::COMPLETED) {
57 JSHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
58 return JSIterator::CreateIterResultObject(thread, valueHandle, true);
59 }
60
61 // 3.Assert: state is either suspendedStart or suspendedYield.
62 ASSERT_PRINT(state == JSGeneratorState::SUSPENDED_START ||
63 state == JSGeneratorState::SUSPENDED_YIELD,
64 "state is neither suspendedStart nor suspendedYield");
65
66 // 4.Let genContext be generator.[[GeneratorContext]].
67 JSHandle<GeneratorContext> genContext(thread, generator->GetGeneratorContext());
68
69 // 5.Let methodContext be the running execution context.
70 // 6.Suspend methodContext.
71
72 // 7.Set generator.[[GeneratorState]] to executing.
73 generator->SetGeneratorState(JSGeneratorState::EXECUTING);
74
75 // 8.Push genContext onto the execution context stack; genContext is now the running execution context.
76 // 9.Resume the suspended evaluation of genContext using NormalCompletion(value) as the result of the operation
77 // that suspended it. Let result be the value returned by the resumed computation.
78 // 10.Assert: When we return here, genContext has already been removed from the execution context stack and
79 // methodContext is the currently running execution context.
80 // 11.Return Completion(result).
81 JSHandle<JSObject> result = GeneratorHelper::Next(thread, genContext, value);
82 return result;
83 }
84
GeneratorResumeAbrupt(JSThread * thread,const JSHandle<JSGeneratorObject> & generator,const JSHandle<CompletionRecord> & abruptCompletion)85 JSHandle<JSObject> JSGeneratorObject::GeneratorResumeAbrupt(JSThread *thread,
86 const JSHandle<JSGeneratorObject> &generator,
87 const JSHandle<CompletionRecord> &abruptCompletion)
88 {
89 // 1.Let state be ? GeneratorValidate(generator).
90 JSHandle<JSTaggedValue> gen(thread, generator.GetTaggedValue());
91 JSGeneratorState state = GeneratorValidate(thread, gen);
92 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
93
94 // 2.If state is suspendedStart, then
95 // a.Set generator.[[GeneratorState]] to completed.
96 // b.Once a generator enters the completed state it never leaves it and its associated execution context is
97 // never resumed. Any execution state associated with generator can be discarded at this point.
98 // c.Set state to completed.
99 if (state == JSGeneratorState::SUSPENDED_START) {
100 state = JSGeneratorState::COMPLETED;
101 generator->SetGeneratorState(state);
102 }
103
104 // 3.If state is completed, then
105 // a.If abruptCompletion.[[Type]] is return, then
106 // i.Return CreateIterResultObject(abruptCompletion.[[Value]], true).
107 // b.Return Completion(abruptCompletion).
108 if (state == JSGeneratorState::COMPLETED) {
109 JSHandle<JSTaggedValue> valueHandle(thread, abruptCompletion->GetValue());
110 JSHandle<JSObject> result = JSIterator::CreateIterResultObject(thread, valueHandle, true);
111 if (abruptCompletion->GetType() == CompletionRecordType::RETURN) {
112 return result;
113 }
114 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, valueHandle.GetTaggedValue(), result);
115 }
116
117 // 4.Assert: state is suspendedYield.
118 ASSERT_PRINT(state == JSGeneratorState::SUSPENDED_YIELD, "state is not suspendedYield");
119
120 // 5.Let genContext be generator.[[GeneratorContext]].
121 JSHandle<GeneratorContext> genContext(thread, generator->GetGeneratorContext());
122
123 // 6.Let methodContext be the running execution context.
124 // 7.Suspend methodContext.
125
126 // 8.Set generator.[[GeneratorState]] to executing.
127 generator->SetGeneratorState(JSGeneratorState::EXECUTING);
128
129 // 9.Push genContext onto the execution context stack; genContext is now the running execution context.
130 // 10.Resume the suspended evaluation of genContext using abruptCompletion as the result of the operation that
131 // suspended it. Let result be the completion record returned by the resumed computation.
132 // 11.Assert: When we return here, genContext has already been removed from the execution context stack and
133 // methodContext is the currently running execution context.
134 // 12.Return Completion(result).
135 JSHandle<JSObject> result;
136 if (abruptCompletion->GetType() == CompletionRecordType::RETURN) {
137 result = GeneratorHelper::Return(thread, genContext, abruptCompletion->GetValue());
138 } else {
139 result = GeneratorHelper::Throw(thread, genContext, abruptCompletion->GetValue());
140 }
141 return result;
142 }
143 } // namespace panda::ecmascript
144