1/* 2 * Copyright (c) 2022 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/containers/containers_queue.h" 17 18#include "ecmascript/containers/containers_errors.h" 19#include "ecmascript/interpreter/interpreter.h" 20#include "ecmascript/js_api/js_api_queue.h" 21#include "ecmascript/js_function.h" 22 23namespace panda::ecmascript::containers { 24JSTaggedValue ContainersQueue::QueueConstructor(EcmaRuntimeCallInfo *argv) 25{ 26 ASSERT(argv); 27 BUILTINS_API_TRACE(argv->GetThread(), Queue, Constructor); 28 JSThread *thread = argv->GetThread(); 29 [[maybe_unused]] EcmaHandleScope handleScope(thread); 30 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 31 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 32 if (newTarget->IsUndefined()) { 33 JSTaggedValue error = 34 ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR, 35 "The Queue's constructor cannot be directly invoked"); 36 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 37 } 38 JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 39 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget); 40 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 41 JSHandle<TaggedArray> newTaggedArray = factory->NewTaggedArray(JSAPIQueue::DEFAULT_CAPACITY_LENGTH); 42 obj->SetElements(thread, newTaggedArray); 43 44 return obj.GetTaggedValue(); 45} 46 47JSTaggedValue ContainersQueue::Add(EcmaRuntimeCallInfo *argv) 48{ 49 ASSERT(argv); 50 BUILTINS_API_TRACE(argv->GetThread(), Queue, Add); 51 JSThread *thread = argv->GetThread(); 52 [[maybe_unused]] EcmaHandleScope handleScope(thread); 53 JSHandle<JSTaggedValue> self = GetThis(argv); 54 55 if (!self->IsJSAPIQueue()) { 56 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) { 57 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 58 } else { 59 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 60 "The add method cannot be bound"); 61 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 62 } 63 } 64 65 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 66 JSAPIQueue::Add(thread, JSHandle<JSAPIQueue>::Cast(self), value); 67 return JSTaggedValue::True(); 68} 69 70JSTaggedValue ContainersQueue::GetFirst(EcmaRuntimeCallInfo *argv) 71{ 72 ASSERT(argv); 73 BUILTINS_API_TRACE(argv->GetThread(), Queue, Add); 74 JSThread *thread = argv->GetThread(); 75 [[maybe_unused]] EcmaHandleScope handleScope(thread); 76 JSHandle<JSTaggedValue> self = GetThis(argv); 77 78 if (!self->IsJSAPIQueue()) { 79 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) { 80 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 81 } else { 82 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 83 "The getFirst method cannot be bound"); 84 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 85 } 86 } 87 88 JSTaggedValue value = JSAPIQueue::GetFirst(thread, JSHandle<JSAPIQueue>::Cast(self)); 89 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); 90 return value; 91} 92 93JSTaggedValue ContainersQueue::Pop(EcmaRuntimeCallInfo *argv) 94{ 95 ASSERT(argv); 96 BUILTINS_API_TRACE(argv->GetThread(), Queue, Add); 97 JSThread *thread = argv->GetThread(); 98 [[maybe_unused]] EcmaHandleScope handleScope(thread); 99 JSHandle<JSTaggedValue> self = GetThis(argv); 100 101 if (!self->IsJSAPIQueue()) { 102 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) { 103 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 104 } else { 105 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 106 "The pop method cannot be bound"); 107 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 108 } 109 } 110 111 JSTaggedValue value = JSAPIQueue::Pop(thread, JSHandle<JSAPIQueue>::Cast(self)); 112 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); 113 return value; 114} 115 116JSTaggedValue ContainersQueue::ForEach(EcmaRuntimeCallInfo *argv) 117{ 118 ASSERT(argv); 119 BUILTINS_API_TRACE(argv->GetThread(), Queue, ForEach); 120 JSThread *thread = argv->GetThread(); 121 [[maybe_unused]] EcmaHandleScope handleScope(thread); 122 123 // Let O be ToObject(this value). 124 JSHandle<JSTaggedValue> thisHandle = GetThis(argv); // JSAPIQueue 125 if (!thisHandle->IsJSAPIQueue()) { 126 if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIQueue()) { 127 thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget()); 128 } else { 129 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 130 "The forEach method cannot be bound"); 131 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 132 } 133 } 134 135 JSHandle<JSAPIQueue> queue = JSHandle<JSAPIQueue>::Cast(thisHandle); 136 // Let len be ToLength(Get(O, "length")). 137 uint32_t len = JSAPIQueue::GetArrayLength(thread, queue); 138 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 139 140 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0); 141 // If IsCallable(callbackfn) is false, throw a TypeError exception. 142 if (!callbackFnHandle->IsCallable()) { 143 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); 144 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 145 CString errorMsg = 146 "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); 147 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); 148 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 149 } 150 // If thisArg was supplied, let T be thisArg; else let T be undefined. 151 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1); 152 153 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined()); 154 uint32_t index = 0; 155 uint32_t k = 0; 156 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 157 while (k < len) { 158 JSHandle<JSTaggedValue> kValue = 159 JSHandle<JSTaggedValue>(thread, queue->Get(thread, index)); 160 index = queue->GetNextPosition(index); 161 key.Update(JSTaggedValue(k)); 162 const uint32_t argsLength = 3; 163 EcmaRuntimeCallInfo *info = 164 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); 165 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 166 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisHandle.GetTaggedValue()); 167 JSTaggedValue funcResult = JSFunction::Call(info); 168 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); 169 k++; 170 } 171 return JSTaggedValue::Undefined(); 172} 173 174JSTaggedValue ContainersQueue::GetIteratorObj(EcmaRuntimeCallInfo *argv) 175{ 176 ASSERT(argv); 177 BUILTINS_API_TRACE(argv->GetThread(), Queue, GetIteratorObj); 178 JSThread *thread = argv->GetThread(); 179 [[maybe_unused]] EcmaHandleScope handleScope(thread); 180 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 181 JSHandle<JSTaggedValue> self = GetThis(argv); 182 if (!self->IsJSAPIQueue()) { 183 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) { 184 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 185 } else { 186 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 187 "The Symbol.iterator method cannot be bound"); 188 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 189 } 190 } 191 JSHandle<JSAPIQueueIterator> iter(factory->NewJSAPIQueueIterator(JSHandle<JSAPIQueue>::Cast(self))); 192 return iter.GetTaggedValue(); 193} 194 195JSTaggedValue ContainersQueue::GetSize(EcmaRuntimeCallInfo *argv) 196{ 197 ASSERT(argv); 198 BUILTINS_API_TRACE(argv->GetThread(), Queue, GetSize); 199 JSThread *thread = argv->GetThread(); 200 [[maybe_unused]] EcmaHandleScope handleScope(thread); 201 JSHandle<JSTaggedValue> self = GetThis(argv); 202 203 if (!self->IsJSAPIQueue()) { 204 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIQueue()) { 205 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 206 } else { 207 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 208 "The getLength method cannot be bound"); 209 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 210 } 211 } 212 213 uint32_t length = JSHandle<JSAPIQueue>::Cast(self)->GetSize(); 214 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 215 216 return JSTaggedValue(length); 217} 218} // namespace panda::ecmascript::containers 219