1/* 2 * Copyright (c) 2021 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_iterator.h" 17 18#include "ecmascript/interpreter/interpreter.h" 19#include "ecmascript/js_async_from_sync_iterator.h" 20#include "ecmascript/object_fast_operator-inl.h" 21 22namespace panda::ecmascript { 23JSTaggedValue JSIterator::IteratorCloseAndReturn(JSThread *thread, const JSHandle<JSTaggedValue> &iter) 24{ 25 ASSERT(thread->HasPendingException()); 26 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 27 JSTaggedValue exception = thread->GetException(); 28 JSHandle<JSTaggedValue> record = JSHandle<JSTaggedValue>(factory->NewCompletionRecord(CompletionRecordType::THROW, 29 JSHandle<JSTaggedValue>(thread, exception))); 30 JSHandle<JSTaggedValue> result = JSIterator::IteratorClose(thread, iter, record); 31 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 32 if (result->IsCompletionRecord()) { 33 return CompletionRecord::Cast(result->GetTaggedObject())->GetValue(); 34 } 35 return result.GetTaggedValue(); 36} 37 38JSHandle<JSTaggedValue> JSIterator::GetIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj) 39{ 40 // 1.ReturnIfAbrupt(obj). 41 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); 42 // 2.If method was not passed, then 43 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 44 JSHandle<JSTaggedValue> iteratorSymbol = env->GetIteratorSymbol(); 45 JSHandle<JSTaggedValue> func = JSObject::GetMethod(thread, obj, iteratorSymbol); 46 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); 47 48 return GetIterator(thread, obj, func); 49} 50 51JSHandle<JSTaggedValue> JSIterator::GetIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 52 const JSHandle<JSTaggedValue> &method) 53{ 54 // 1.ReturnIfAbrupt(obj). 55 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); 56 // 3.Let iterator be Call(method,obj). 57 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 58 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, method, obj, undefined, 0); 59 JSTaggedValue ret = JSFunction::Call(info); 60 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 61 JSHandle<JSTaggedValue> iter(thread, ret); 62 // 5.If Type(iterator) is not Object, throw a TypeError exception 63 if (!iter->IsECMAObject()) { 64 THROW_TYPE_ERROR_AND_RETURN(thread, "JSIterator::GetIterator: iter is not object", undefined); 65 } 66 return iter; 67} 68 69JSHandle<JSTaggedValue> JSIterator::GetAsyncIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj) 70{ 71 // 3.If method is not present, then 72 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 73 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 74 JSHandle<JSTaggedValue> asynciteratorSymbol = env->GetAsyncIteratorSymbol(); 75 // i. Set method to ? GetMethod(obj, @@asyncIterator). 76 // ii. If method is undefined, then 77 JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, obj, asynciteratorSymbol); 78 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); 79 if (method->IsUndefined()) { 80 JSHandle<JSTaggedValue> iteratorSymbol = env->GetIteratorSymbol(); 81 JSHandle<JSTaggedValue> func = JSObject::GetMethod(thread, obj, iteratorSymbol); 82 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 83 JSHandle<JSTaggedValue> syncIterator = GetIterator(thread, obj, func); 84 JSHandle<JSTaggedValue> nextStr = thread->GlobalConstants()->GetHandledNextString(); 85 JSHandle<JSTaggedValue> nextMethod = JSTaggedValue::GetProperty(thread, syncIterator, nextStr).GetValue(); 86 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 87 JSHandle<AsyncIteratorRecord> syncIteratorRecord = 88 factory->NewAsyncIteratorRecord(syncIterator, nextMethod, false); 89 JSHandle<JSTaggedValue> asyncIterator = 90 JSAsyncFromSyncIterator::CreateAsyncFromSyncIterator(thread, syncIteratorRecord); 91 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 92 return asyncIterator; 93 } 94 95 // 4.Let iterator be Call(method,obj). 96 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 97 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, method, obj, undefined, 0); 98 JSTaggedValue ret = JSFunction::Call(info); 99 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 100 JSHandle<JSTaggedValue> iterator(thread, ret); 101 // 5.If Type(iterator) is not Object, throw a TypeError exception 102 if (!iterator->IsECMAObject()) { 103 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not object", undefined); 104 } 105 return iterator; 106} 107 108 109// 7.4.2 110JSHandle<JSTaggedValue> JSIterator::IteratorNext(JSThread *thread, const JSHandle<JSTaggedValue> &iter) 111{ 112 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 113 114 // 1.If value was not passed, then Let result be Invoke(iterator, "next", « »). 115 JSHandle<JSTaggedValue> key(globalConst->GetHandledNextString()); 116 JSHandle<JSTaggedValue> next(JSObject::GetMethod(thread, iter, key)); 117 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 118 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined(); 119 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iter, undefined, 0); 120 JSTaggedValue ret = JSFunction::Call(info); 121 // 3.ReturnIfAbrupt(result) 122 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined); 123 // 4.If Type(result) is not Object, throw a TypeError exception. 124 if (!ret.IsECMAObject()) { 125 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Object", undefined); 126 } 127 JSHandle<JSTaggedValue> result(thread, ret); 128 return result; 129} 130 131JSHandle<JSTaggedValue> JSIterator::IteratorNext(JSThread *thread, const JSHandle<JSTaggedValue> &iter, 132 const JSHandle<JSTaggedValue> &value) 133{ 134 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 135 // 2.Let result be Invoke(iterator, "next", «value»). 136 JSHandle<JSTaggedValue> key(globalConst->GetHandledNextString()); 137 JSHandle<JSTaggedValue> next(JSObject::GetMethod(thread, iter, key)); 138 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 139 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined(); 140 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iter, undefined, 1); 141 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined); 142 info->SetCallArg(value.GetTaggedValue()); 143 JSTaggedValue ret = JSFunction::Call(info); 144 // 3.ReturnIfAbrupt(result) 145 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined); 146 // 4.If Type(result) is not Object, throw a TypeError exception. 147 if (!ret.IsECMAObject()) { 148 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Object", undefined); 149 } 150 JSHandle<JSTaggedValue> result(thread, ret); 151 return result; 152} 153 154JSHandle<JSTaggedValue> JSIterator::IteratorNext(JSThread *thread, const JSHandle<AsyncIteratorRecord> &iter, 155 const JSHandle<JSTaggedValue> &value) 156{ 157 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 158 // 2.Let result be Invoke(iterator, "next", «value»). 159 JSHandle<JSTaggedValue> iterator(thread, iter->GetIterator()); 160 JSHandle<JSTaggedValue> next(thread, iter->GetNextMethod()); 161 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined(); 162 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iterator, undefined, 1); 163 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined); 164 info->SetCallArg(value.GetTaggedValue()); 165 JSTaggedValue ret = JSFunction::Call(info); 166 // 3.ReturnIfAbrupt(result) 167 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined); 168 // 4.If Type(result) is not Object, throw a TypeError exception. 169 if (!ret.IsECMAObject()) { 170 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Object", undefined); 171 } 172 JSHandle<JSTaggedValue> result(thread, ret); 173 return result; 174} 175 176JSHandle<JSTaggedValue> JSIterator::IteratorNext(JSThread *thread, const JSHandle<AsyncIteratorRecord> &iter) 177{ 178 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 179 // 2.Let result be Invoke(iterator, "next", «value»). 180 JSHandle<JSTaggedValue> iterator(thread, iter->GetIterator()); 181 JSHandle<JSTaggedValue> next(thread, iter->GetNextMethod()); 182 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined(); 183 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iterator, undefined, 0); 184 JSTaggedValue ret = JSFunction::Call(info); 185 // 3.ReturnIfAbrupt(result) 186 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined); 187 // 4.If Type(result) is not Object, throw a TypeError exception. 188 if (!ret.IsECMAObject()) { 189 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Object", undefined); 190 } 191 JSHandle<JSTaggedValue> result(thread, ret); 192 return result; 193} 194// 7.4.3 195bool JSIterator::IteratorComplete(JSThread *thread, const JSHandle<JSTaggedValue> &iterResult) 196{ 197 ASSERT_PRINT(iterResult->IsECMAObject(), "iterResult must be JSObject"); 198 // Return ToBoolean(Get(iterResult, "done")). 199 JSHandle<JSTaggedValue> doneStr = thread->GlobalConstants()->GetHandledDoneString(); 200 JSHandle<JSTaggedValue> done = JSTaggedValue::GetProperty(thread, iterResult, doneStr).GetValue(); 201 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); 202 return done->ToBoolean(); 203} 204// 7.4.4 205JSHandle<JSTaggedValue> JSIterator::IteratorValue(JSThread *thread, const JSHandle<JSTaggedValue> &iterResult) 206{ 207 ASSERT_PRINT(iterResult->IsECMAObject(), "iterResult must be JSObject"); 208 // Return Get(iterResult, "value"). 209 JSHandle<JSTaggedValue> valueStr = thread->GlobalConstants()->GetHandledValueString(); 210 JSHandle<JSTaggedValue> value = JSTaggedValue::GetProperty(thread, iterResult, valueStr).GetValue(); 211 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 212 return value; 213} 214// 7.4.5 215JSHandle<JSTaggedValue> JSIterator::IteratorStep(JSThread *thread, const JSHandle<JSTaggedValue> &iter) 216{ 217 // 1.Let result be IteratorNext(iterator). 218 JSHandle<JSTaggedValue> result = IteratorNext(thread, iter); 219 // 2.ReturnIfAbrupt(result). 220 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); 221 // 3.Let done be IteratorComplete(result). 222 bool done = IteratorComplete(thread, result); 223 // 4.ReturnIfAbrupt(done). 224 JSHandle<JSTaggedValue> doneHandle(thread, JSTaggedValue(done)); 225 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, doneHandle); 226 // 5.If done is true, return false. 227 if (done) { 228 JSHandle<JSTaggedValue> falseHandle(thread, JSTaggedValue::False()); 229 return falseHandle; 230 } 231 return result; 232} 233// 7.4.6 234JSHandle<JSTaggedValue> JSIterator::IteratorClose(JSThread *thread, const JSHandle<JSTaggedValue> &iter, 235 const JSHandle<JSTaggedValue> &completion) 236{ 237 // 1.Assert: Type(iterator) is Object. 238 ASSERT_PRINT(iter->IsECMAObject(), "iter must be JSObject"); 239 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 240 JSHandle<JSTaggedValue> exceptionOnThread; 241 if (thread->HasPendingException()) { 242 exceptionOnThread = JSHandle<JSTaggedValue>(thread, thread->GetException()); 243 thread->ClearException(); 244 } 245 JSTaggedValue returnStr = globalConst->GetReturnString(); 246 // 3.Let return be GetMethod(iterator, "return"). 247 JSTaggedValue func = ObjectFastOperator::FastGetPropertyByName(thread, iter.GetTaggedValue(), returnStr); 248 // 4.ReturnIfAbrupt(return). 249 JSHandle<JSTaggedValue> returnFunc(thread, func); 250 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, returnFunc); 251 252 // 5.If return is undefined, return Completion(completion). 253 if (returnFunc->IsUndefined() || returnFunc->IsNull()) { 254 if (!exceptionOnThread.IsEmpty()) { 255 thread->SetException(exceptionOnThread.GetTaggedValue()); 256 } 257 return completion; 258 } 259 260 if (!returnFunc->IsCallable()) { 261 THROW_TYPE_ERROR_AND_RETURN(thread, "return function is not Callable", returnFunc); 262 } 263 // 6.Let innerResult be Call(return, iterator, « »). 264 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined(); 265 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, returnFunc, iter, undefined, 0); 266 JSTaggedValue ret = JSFunction::Call(info); 267 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 268 if (!exceptionOnThread.IsEmpty()) { 269 thread->SetException(exceptionOnThread.GetTaggedValue()); 270 } 271 JSHandle<JSTaggedValue> innerResult(thread, ret); 272 JSHandle<CompletionRecord> completionRecord(thread, globalConst->GetUndefined()); 273 if (completion->IsCompletionRecord()) { 274 completionRecord = JSHandle<CompletionRecord>::Cast(completion); 275 } 276 // 7.If completion.[[type]] is throw, return Completion(completion). 277 if (!completionRecord.GetTaggedValue().IsUndefined() && completionRecord->IsThrow()) { 278 if (!exceptionOnThread.IsEmpty()) { 279 thread->SetException(exceptionOnThread.GetTaggedValue()); 280 } 281 return completion; 282 } 283 // 8.If innerResult.[[type]] is throw, return Completion(innerResult). 284 if (thread->HasPendingException()) { 285 return innerResult; 286 } 287 // 9.If Type(innerResult.[[value]]) is not Object, throw a TypeError exception. 288 if (!innerResult->IsECMAObject()) { 289 THROW_TYPE_ERROR_AND_RETURN(thread, "Is not object", undefined); 290 } 291 if (!exceptionOnThread.IsEmpty()) { 292 thread->SetException(exceptionOnThread.GetTaggedValue()); 293 } 294 return completion; 295} 296// 7.4.7 297JSHandle<JSObject> JSIterator::CreateIterResultObject(JSThread *thread, const JSHandle<JSTaggedValue> &value, bool done) 298{ 299 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 300 auto globalConst = thread->GlobalConstants(); 301 // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%). 302 JSHandle<JSHClass> klass = JSHandle<JSHClass>::Cast(globalConst->GetHandledIteratorResultClass()); 303 JSHandle<JSObject> obj = factory->NewJSObject(klass); 304 305 // 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value). 306 // 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done). 307 obj->SetPropertyInlinedProps(thread, VALUE_INLINE_PROPERTY_INDEX, value.GetTaggedValue()); 308 obj->SetPropertyInlinedProps(thread, DONE_INLINE_PROPERTY_INDEX, JSTaggedValue(done)); 309 // 5. Return obj. 310 return obj; 311} 312} // namespace panda::ecmascript 313