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