1/* 2 * Copyright (c) 2022-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/containers/containers_deque.h" 17 18#include "ecmascript/containers/containers_errors.h" 19#include "ecmascript/interpreter/interpreter.h" 20#include "ecmascript/js_api/js_api_deque.h" 21#include "ecmascript/js_function.h" 22 23namespace panda::ecmascript::containers { 24JSTaggedValue ContainersDeque::DequeConstructor(EcmaRuntimeCallInfo *argv) 25{ 26 ASSERT(argv != nullptr); 27 BUILTINS_API_TRACE(argv->GetThread(), Deque, 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 Deque'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> newElements = factory->NewTaggedArray(JSAPIDeque::DEFAULT_CAPACITY_LENGTH); 42 obj->SetElements(thread, newElements); 43 return obj.GetTaggedValue(); 44} 45 46JSTaggedValue ContainersDeque::InsertFront(EcmaRuntimeCallInfo *argv) 47{ 48 ASSERT(argv != nullptr); 49 BUILTINS_API_TRACE(argv->GetThread(), Deque, InsertFront); 50 JSThread *thread = argv->GetThread(); 51 [[maybe_unused]] EcmaHandleScope handleScope(thread); 52 JSHandle<JSTaggedValue> self = GetThis(argv); 53 54 if (!self->IsJSAPIDeque()) { 55 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) { 56 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 57 } else { 58 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 59 "The insertFront method cannot be bound"); 60 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 61 } 62 } 63 64 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 65 JSAPIDeque::InsertFront(thread, JSHandle<JSAPIDeque>::Cast(self), value); 66 67 return JSTaggedValue::True(); 68} 69 70 71JSTaggedValue ContainersDeque::InsertEnd(EcmaRuntimeCallInfo *argv) 72{ 73 ASSERT(argv != nullptr); 74 BUILTINS_API_TRACE(argv->GetThread(), Deque, InsertEnd); 75 JSThread *thread = argv->GetThread(); 76 [[maybe_unused]] EcmaHandleScope handleScope(thread); 77 JSHandle<JSTaggedValue> self = GetThis(argv); 78 79 if (!self->IsJSAPIDeque()) { 80 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) { 81 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 82 } else { 83 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 84 "The insertEnd method cannot be bound"); 85 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 86 } 87 } 88 89 JSHandle<JSTaggedValue> value(GetCallArg(argv, 0)); 90 JSAPIDeque::InsertEnd(thread, JSHandle<JSAPIDeque>::Cast(self), value); 91 92 return JSTaggedValue::True(); 93} 94 95JSTaggedValue ContainersDeque::GetFirst(EcmaRuntimeCallInfo *argv) 96{ 97 ASSERT(argv != nullptr); 98 BUILTINS_API_TRACE(argv->GetThread(), Deque, GetFirst); 99 JSThread *thread = argv->GetThread(); 100 [[maybe_unused]] EcmaHandleScope handleScope(thread); 101 JSHandle<JSTaggedValue> self = GetThis(argv); 102 103 if (!self->IsJSAPIDeque()) { 104 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) { 105 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 106 } else { 107 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 108 "The getFirst method cannot be bound"); 109 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 110 } 111 } 112 113 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self); 114 JSTaggedValue firstElement = deque->GetFront(); 115 return firstElement; 116} 117 118JSTaggedValue ContainersDeque::GetLast(EcmaRuntimeCallInfo *argv) 119{ 120 ASSERT(argv != nullptr); 121 BUILTINS_API_TRACE(argv->GetThread(), Deque, GetLast); 122 JSThread *thread = argv->GetThread(); 123 [[maybe_unused]] EcmaHandleScope handleScope(thread); 124 JSHandle<JSTaggedValue> self = GetThis(argv); 125 126 if (!self->IsJSAPIDeque()) { 127 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) { 128 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 129 } else { 130 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 131 "The getLast method cannot be bound"); 132 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 133 } 134 } 135 136 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self); 137 JSTaggedValue lastElement = deque->GetTail(); 138 return lastElement; 139} 140 141JSTaggedValue ContainersDeque::Has(EcmaRuntimeCallInfo *argv) 142{ 143 ASSERT(argv != nullptr); 144 BUILTINS_API_TRACE(argv->GetThread(), Deque, Has); 145 JSThread *thread = argv->GetThread(); 146 [[maybe_unused]] EcmaHandleScope handleScope(thread); 147 JSHandle<JSTaggedValue> self = GetThis(argv); 148 149 if (!self->IsJSAPIDeque()) { 150 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) { 151 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 152 } else { 153 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 154 "The has method cannot be bound"); 155 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 156 } 157 } 158 159 JSHandle<JSTaggedValue> value(GetCallArg(argv, 0)); 160 161 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self); 162 bool isHas = deque->Has(value.GetTaggedValue()); 163 return GetTaggedBoolean(isHas); 164} 165 166JSTaggedValue ContainersDeque::PopFirst(EcmaRuntimeCallInfo *argv) 167{ 168 ASSERT(argv != nullptr); 169 BUILTINS_API_TRACE(argv->GetThread(), Deque, PopFirst); 170 JSThread *thread = argv->GetThread(); 171 [[maybe_unused]] EcmaHandleScope handleScope(thread); 172 JSHandle<JSTaggedValue> self = GetThis(argv); 173 174 if (!self->IsJSAPIDeque()) { 175 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) { 176 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 177 } else { 178 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 179 "The popFirst method cannot be bound"); 180 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 181 } 182 } 183 184 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self); 185 JSTaggedValue firstElement = deque->PopFirst(); 186 return firstElement; 187} 188 189JSTaggedValue ContainersDeque::PopLast(EcmaRuntimeCallInfo *argv) 190{ 191 ASSERT(argv != nullptr); 192 BUILTINS_API_TRACE(argv->GetThread(), Deque, PopLast); 193 JSThread *thread = argv->GetThread(); 194 [[maybe_unused]] EcmaHandleScope handleScope(thread); 195 JSHandle<JSTaggedValue> self = GetThis(argv); 196 197 if (!self->IsJSAPIDeque()) { 198 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) { 199 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 200 } else { 201 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 202 "The popLast method cannot be bound"); 203 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 204 } 205 } 206 207 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self); 208 JSTaggedValue lastElement = deque->PopLast(); 209 return lastElement; 210} 211 212JSTaggedValue ContainersDeque::ForEach(EcmaRuntimeCallInfo *argv) 213{ 214 ASSERT(argv != nullptr); 215 BUILTINS_API_TRACE(argv->GetThread(), Deque, ForEach); 216 JSThread *thread = argv->GetThread(); 217 [[maybe_unused]] EcmaHandleScope handleScope(thread); 218 219 JSHandle<JSTaggedValue> thisHandle = GetThis(argv); 220 if (!thisHandle->IsJSAPIDeque()) { 221 if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIDeque()) { 222 thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget()); 223 } else { 224 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 225 "The forEach method cannot be bound"); 226 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 227 } 228 } 229 230 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0); 231 if (!callbackFnHandle->IsCallable()) { 232 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle); 233 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 234 CString errorMsg = 235 "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); 236 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); 237 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 238 } 239 240 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(thisHandle); 241 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1); 242 243 uint32_t first = deque->GetFirst(); 244 uint32_t last = deque->GetLast(); 245 246 JSHandle<TaggedArray> elements(thread, deque->GetElements()); 247 uint32_t capacity = elements->GetLength(); 248 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 249 uint32_t index = 0; 250 while (first != last) { 251 JSTaggedValue kValue = deque->Get(index); 252 EcmaRuntimeCallInfo *info = 253 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, 3); // 3:three args 254 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 255 info->SetCallArg(kValue, JSTaggedValue(index), thisHandle.GetTaggedValue()); 256 JSTaggedValue funcResult = JSFunction::Call(info); 257 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); 258 ASSERT(capacity != 0); 259 first = (first + 1) % capacity; 260 index = index + 1; 261 } 262 263 return JSTaggedValue::Undefined(); 264} 265 266JSTaggedValue ContainersDeque::GetIteratorObj(EcmaRuntimeCallInfo *argv) 267{ 268 ASSERT(argv != nullptr); 269 BUILTINS_API_TRACE(argv->GetThread(), Deque, GetIteratorObj); 270 JSThread *thread = argv->GetThread(); 271 [[maybe_unused]] EcmaHandleScope handleScope(thread); 272 273 JSHandle<JSTaggedValue> self = GetThis(argv); 274 275 if (!self->IsJSAPIDeque()) { 276 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) { 277 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 278 } else { 279 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 280 "The Symbol.iterator method cannot be bound"); 281 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 282 } 283 } 284 285 JSTaggedValue values = JSAPIDeque::GetIteratorObj(thread, JSHandle<JSAPIDeque>::Cast(self)); 286 287 return values; 288} 289 290JSTaggedValue ContainersDeque::GetSize(EcmaRuntimeCallInfo *argv) 291{ 292 ASSERT(argv != nullptr); 293 BUILTINS_API_TRACE(argv->GetThread(), Deque, GetSize); 294 JSThread *thread = argv->GetThread(); 295 JSHandle<JSTaggedValue> self = GetThis(argv); 296 297 if (!self->IsJSAPIDeque()) { 298 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIDeque()) { 299 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 300 } else { 301 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 302 "The getLength method cannot be bound"); 303 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 304 } 305 } 306 307 JSHandle<JSAPIDeque> deque = JSHandle<JSAPIDeque>::Cast(self); 308 uint32_t length = deque->GetSize(); 309 310 return JSTaggedValue(length); 311} 312} // namespace panda::ecmascript::containers 313