1 /*
2  * Copyright (c) 2023-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/builtins/builtins_async_from_sync_iterator.h"
17 #include "ecmascript/global_env.h"
18 #include "ecmascript/js_async_from_sync_iterator.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 #include "ecmascript/js_iterator.h"
21 #include "ecmascript/js_promise.h"
22 
23 namespace panda::ecmascript::builtins {
24 
Next(EcmaRuntimeCallInfo *argv)25 JSTaggedValue BuiltinsAsyncFromSyncIterator::Next(EcmaRuntimeCallInfo *argv)
26 {
27     JSThread *thread = argv->GetThread();
28     BUILTINS_API_TRACE(thread, AsyncFromSyncIterator, Next);
29     [[maybe_unused]] EcmaHandleScope scope(thread);
30     auto vm = thread->GetEcmaVM();
31     JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
32     // 1.Let O be the this value.
33     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
34     // 2.Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot.
35     if (!thisValue->IsAsyncFromSyncIterator()) {
36         THROW_TYPE_ERROR_AND_RETURN(thread, "is not AsyncFromSyncIterator", JSTaggedValue::Exception());
37     }
38     // 3.Let promiseCapability be ! NewPromiseCapability(%Promise%).
39     JSHandle<PromiseCapability> pcap =
40         JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
41     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
42     // 4.Let syncIteratorRecord be O.[[SyncIteratorRecord]].
43     JSHandle<JSAsyncFromSyncIterator> asyncIterator(thisValue);
44     JSHandle<AsyncIteratorRecord> syncIteratorRecord(thread, asyncIterator->GetSyncIteratorRecord());
45     // 5.If value is present, then
46     // a.Let result be IteratorNext(syncIteratorRecord, value).
47     // 6.Else,
48     // a.Let result be IteratorNext(syncIteratorRecord).
49     JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
50     JSHandle<JSTaggedValue> result;
51     if (value->IsNull()) {
52         result = JSIterator::IteratorNext(thread, syncIteratorRecord);
53     } else {
54         result = JSIterator::IteratorNext(thread, syncIteratorRecord, value);
55     }
56     // 7.IfAbruptRejectPromise(result, promiseCapability).
57     RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, pcap);
58     // 8.Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
59     return JSAsyncFromSyncIterator::AsyncFromSyncIteratorContinuation(thread, result, pcap);
60 }
61 
Throw(EcmaRuntimeCallInfo *argv)62 JSTaggedValue BuiltinsAsyncFromSyncIterator::Throw(EcmaRuntimeCallInfo *argv)
63 {
64     JSThread *thread = argv->GetThread();
65     BUILTINS_API_TRACE(thread, AsyncFromSyncIterator, Throw);
66     [[maybe_unused]] EcmaHandleScope scope(thread);
67     auto vm = thread->GetEcmaVM();
68     const GlobalEnvConstants *globalConstant = thread->GlobalConstants();
69     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
70     // 1.Let O be the this value.
71     JSHandle<JSTaggedValue> input(BuiltinsBase::GetThis(argv));
72     JSHandle<JSAsyncFromSyncIterator> asyncIterator(input);
73     // 3.Let promiseCapability be ! NewPromiseCapability(%Promise%).
74     JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
75     JSHandle<PromiseCapability> pcap =
76         JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
77     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
78     // 4.Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
79     JSHandle<AsyncIteratorRecord> syncIteratorRecord(thread, asyncIterator->GetSyncIteratorRecord());
80     JSHandle<JSTaggedValue> syncIterator(thread, syncIteratorRecord->GetIterator());
81     // 5.Let return be GetMethod(syncIterator, "throw").
82     JSHandle<JSTaggedValue> throwString = globalConstant->GetHandledThrowString();
83     JSHandle<JSTaggedValue> throwResult = JSObject::GetMethod(thread, syncIterator, throwString);
84     RETURN_REJECT_PROMISE_IF_ABRUPT(thread, throwString, pcap);
85     JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, 0);
86     JSHandle<JSTaggedValue> undefinedValue = globalConstant->GetHandledUndefined();
87     // 7.If throw is undefined, then
88     if (throwResult->IsUndefined()) {
89         JSHandle<JSObject> iterResult = JSIterator::CreateIterResultObject(thread, value, true);
90         JSHandle<JSTaggedValue> reject(thread, pcap->GetReject());
91         EcmaRuntimeCallInfo *info =
92             EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefinedValue, undefinedValue, 1);
93         info->SetCallArg(iterResult.GetTaggedValue());
94         return pcap->GetPromise();
95     }
96     JSTaggedValue ret;
97     // 8.If value is present, then
98     if (value->IsNull()) {
99         EcmaRuntimeCallInfo *callInfo =
100             EcmaInterpreter::NewRuntimeCallInfo(thread, throwResult, syncIterator, undefinedValue, 0);
101         RETURN_REJECT_PROMISE_IF_ABRUPT(thread, throwResult, pcap);
102         ret = JSFunction::Call(callInfo);
103         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
104     } else {
105         EcmaRuntimeCallInfo *callInfo =
106             EcmaInterpreter::NewRuntimeCallInfo(thread, throwResult, syncIterator, undefinedValue, 1);
107         RETURN_REJECT_PROMISE_IF_ABRUPT(thread, throwResult, pcap);
108         callInfo->SetCallArg(value.GetTaggedValue());
109         ret = JSFunction::Call(callInfo);
110         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
111     }
112     JSHandle<JSTaggedValue> result(thread, ret);
113     // 11.If Type(result) is not Object, then
114     if (!result->IsECMAObject()) {
115         // a.Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
116         JSHandle<JSObject> resolutionError =
117             factory->GetJSError(ErrorType::TYPE_ERROR,
118                                 "AsyncFromSyncIteratorPrototype.throw: is not Object.", StackCheck::NO);
119         JSHandle<JSTaggedValue> reject(thread, pcap->GetReject());
120         EcmaRuntimeCallInfo *info =
121             EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefinedValue, undefinedValue, 1);
122         info->SetCallArg(resolutionError.GetTaggedValue());
123         JSFunction::Call(info);
124         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
125 
126         // b.Return promiseCapability.[[Promise]].
127         JSHandle<JSObject> promise(thread, pcap->GetPromise());
128         return promise.GetTaggedValue();
129     }
130     // 12.Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
131     return JSAsyncFromSyncIterator::AsyncFromSyncIteratorContinuation(thread, result, pcap);
132 }
133 
Return(EcmaRuntimeCallInfo *argv)134 JSTaggedValue BuiltinsAsyncFromSyncIterator::Return(EcmaRuntimeCallInfo *argv)
135 {
136     JSThread *thread = argv->GetThread();
137     BUILTINS_API_TRACE(thread, AsyncFromSyncIterator, Return);
138     [[maybe_unused]] EcmaHandleScope scope(thread);
139     auto vm = thread->GetEcmaVM();
140     const GlobalEnvConstants *globalConstant = thread->GlobalConstants();
141     ObjectFactory *factory = vm->GetFactory();
142     // 1.Let O be the this value.
143     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
144     if (!thisValue->IsAsyncFromSyncIterator()) {
145         THROW_TYPE_ERROR_AND_RETURN(thread, "is not AsyncFromSyncIterator", JSTaggedValue::Exception());
146     }
147 
148     // 3.Let promiseCapability be ! NewPromiseCapability(%Promise%).
149     JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
150     JSHandle<PromiseCapability> pcap =
151         JSPromise::NewPromiseCapability(thread, JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction()));
152     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
153     // 4.Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
154     JSHandle<JSAsyncFromSyncIterator> asyncIterator(thisValue);
155     JSHandle<AsyncIteratorRecord> syncIteratorRecord(thread, asyncIterator->GetSyncIteratorRecord());
156     JSHandle<JSTaggedValue> syncIterator(thread, syncIteratorRecord->GetIterator());
157     // 5.Let return be GetMethod(syncIterator, "return").
158     JSHandle<JSTaggedValue> returnString = globalConstant->GetHandledReturnString();
159     JSHandle<JSTaggedValue> returnResult = JSObject::GetMethod(thread, syncIterator, returnString);
160     RETURN_REJECT_PROMISE_IF_ABRUPT(thread, returnResult, pcap);
161     JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, 0);
162     JSHandle<JSTaggedValue> undefinedValue = globalConstant->GetHandledUndefined();
163     // 7.If return is undefined, then
164     if (returnResult->IsUndefined()) {
165         JSHandle<JSObject> iterResult = JSIterator::CreateIterResultObject(thread, value, true);
166         JSHandle<JSTaggedValue> its = JSHandle<JSTaggedValue>::Cast(iterResult);
167         JSHandle<JSTaggedValue> resolve(thread, pcap->GetResolve());
168         EcmaRuntimeCallInfo *info =
169             EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefinedValue, undefinedValue, 1);
170         info->SetCallArg(its.GetTaggedValue());
171         JSHandle<JSObject> promise(thread, pcap->GetPromise());
172         return promise.GetTaggedValue();
173     }
174     JSTaggedValue ret;
175     // 8.If value is present, then
176     if (value->IsNull()) {
177         EcmaRuntimeCallInfo *callInfo =
178             EcmaInterpreter::NewRuntimeCallInfo(thread, returnResult, syncIterator, undefinedValue, 0);
179         RETURN_REJECT_PROMISE_IF_ABRUPT(thread, returnResult, pcap);
180         ret = JSFunction::Call(callInfo);
181         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
182     } else {
183         EcmaRuntimeCallInfo *callInfo =
184             EcmaInterpreter::NewRuntimeCallInfo(thread, returnResult, syncIterator, undefinedValue, 1);
185         RETURN_REJECT_PROMISE_IF_ABRUPT(thread, returnResult, pcap);
186         callInfo->SetCallArg(value.GetTaggedValue());
187         ret = JSFunction::Call(callInfo);
188         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
189     }
190     JSHandle<JSTaggedValue> result(thread, ret);
191     // 11.If Type(result) is not Object, then
192     if (!result->IsECMAObject()) {
193         // a.Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
194         JSHandle<JSObject> resolutionError = factory->GetJSError(ErrorType::TYPE_ERROR,
195             "AsyncFromSyncIteratorPrototype.return: is not Object.", StackCheck::NO);
196         JSHandle<JSTaggedValue> rstErr = JSHandle<JSTaggedValue>::Cast(resolutionError);
197         JSHandle<JSTaggedValue> reject(thread, pcap->GetReject());
198         EcmaRuntimeCallInfo *info =
199             EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefinedValue, undefinedValue, 1);
200         info->SetCallArg(rstErr.GetTaggedValue());
201         JSFunction::Call(info);
202         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
203 
204         // b.Return promiseCapability.[[Promise]].
205         JSHandle<JSObject> promise(thread, pcap->GetPromise());
206         return promise.GetTaggedValue();
207     }
208     // 12.Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
209     return JSAsyncFromSyncIterator::AsyncFromSyncIteratorContinuation(thread, result, pcap);
210 }
211 }  // namespace panda::ecmascript::builtins
212