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_treeset.h" 17 18#include "ecmascript/containers/containers_errors.h" 19#include "ecmascript/interpreter/interpreter.h" 20#include "ecmascript/js_api/js_api_tree_set.h" 21#include "ecmascript/js_api/js_api_tree_set_iterator.h" 22#include "ecmascript/js_function.h" 23#include "ecmascript/tagged_tree.h" 24 25namespace panda::ecmascript::containers { 26JSTaggedValue ContainersTreeSet::TreeSetConstructor(EcmaRuntimeCallInfo *argv) 27{ 28 ASSERT(argv); 29 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Constructor); 30 JSThread *thread = argv->GetThread(); 31 [[maybe_unused]] EcmaHandleScope handleScope(thread); 32 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 33 34 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 35 if (newTarget->IsUndefined()) { 36 JSTaggedValue error = 37 ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR, 38 "The TreeSet's constructor cannot be directly invoked"); 39 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 40 } 41 // new TreeSet 42 JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 43 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget); 44 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 45 46 // Set set’s internal slot with a new empty List. 47 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(obj); 48 JSTaggedValue internal = TaggedTreeSet::Create(thread); 49 set->SetTreeSet(thread, internal); 50 51 // If comparefn was supplied, let compare be comparefn; else let compare be hole. 52 JSHandle<JSTaggedValue> compareFn(GetCallArg(argv, 0)); 53 if (compareFn->IsUndefined() || compareFn->IsNull()) { 54 return set.GetTaggedValue(); 55 } 56 if (!compareFn->IsCallable()) { 57 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, compareFn.GetTaggedValue()); 58 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 59 CString errorMsg = 60 "The type of \"comparefn\" must be callable. Received value is: " + ConvertToString(*result); 61 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); 62 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 63 } 64 65 TaggedTreeSet::Cast(internal.GetTaggedObject())->SetCompare(thread, compareFn.GetTaggedValue()); 66 return set.GetTaggedValue(); 67} 68 69JSTaggedValue ContainersTreeSet::Add(EcmaRuntimeCallInfo *argv) 70{ 71 ASSERT(argv); 72 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Add); 73 JSThread *thread = argv->GetThread(); 74 [[maybe_unused]] EcmaHandleScope handleScope(thread); 75 // get and check this set 76 JSHandle<JSTaggedValue> self = GetThis(argv); 77 if (!self->IsJSAPITreeSet()) { 78 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 79 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 80 } else { 81 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 82 "The add method cannot be bound"); 83 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 84 } 85 } 86 87 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 88 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 89 JSAPITreeSet::Add(thread, set, value); 90 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 91 return JSTaggedValue::True(); 92} 93 94JSTaggedValue ContainersTreeSet::Remove(EcmaRuntimeCallInfo *argv) 95{ 96 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Remove); 97 JSThread *thread = argv->GetThread(); 98 [[maybe_unused]] EcmaHandleScope handleScope(thread); 99 // get and check this set 100 JSHandle<JSTaggedValue> self = GetThis(argv); 101 if (!self->IsJSAPITreeSet()) { 102 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 103 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 104 } else { 105 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 106 "The remove method cannot be bound"); 107 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 108 } 109 } 110 111 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 112 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 113 return GetTaggedBoolean(JSAPITreeSet::Delete(thread, set, key)); 114} 115 116JSTaggedValue ContainersTreeSet::Has(EcmaRuntimeCallInfo *argv) 117{ 118 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Has); 119 JSThread *thread = argv->GetThread(); 120 [[maybe_unused]] EcmaHandleScope handleScope(thread); 121 // get and check this set 122 JSHandle<JSTaggedValue> self(GetThis(argv)); 123 if (!self->IsJSAPITreeSet()) { 124 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 125 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 126 } else { 127 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 128 "The has method cannot be bound"); 129 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 130 } 131 } 132 133 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 134 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 135 136 bool flag = JSAPITreeSet::Has(thread, JSHandle<JSAPITreeSet>::Cast(set), key); 137 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 138 return GetTaggedBoolean(flag); 139} 140 141JSTaggedValue ContainersTreeSet::GetFirstValue(EcmaRuntimeCallInfo *argv) 142{ 143 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetFirstValue); 144 JSThread *thread = argv->GetThread(); 145 [[maybe_unused]] EcmaHandleScope handleScope(thread); 146 // get and check this set 147 JSHandle<JSTaggedValue> self(GetThis(argv)); 148 if (!self->IsJSAPITreeSet()) { 149 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 150 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 151 } else { 152 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 153 "The getFirstValue method cannot be bound"); 154 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 155 } 156 } 157 158 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 159 return TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())->GetFirstKey(); 160} 161 162JSTaggedValue ContainersTreeSet::GetLastValue(EcmaRuntimeCallInfo *argv) 163{ 164 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetLastValue); 165 JSThread *thread = argv->GetThread(); 166 [[maybe_unused]] EcmaHandleScope handleScope(thread); 167 // get and check this set 168 JSHandle<JSTaggedValue> self(GetThis(argv)); 169 if (!self->IsJSAPITreeSet()) { 170 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 171 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 172 } else { 173 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 174 "The getLastValue method cannot be bound"); 175 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 176 } 177 } 178 179 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 180 return TaggedTreeSet::Cast(set->GetTreeSet().GetTaggedObject())->GetLastKey(); 181} 182 183JSTaggedValue ContainersTreeSet::Clear(EcmaRuntimeCallInfo *argv) 184{ 185 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Clear); 186 JSThread *thread = argv->GetThread(); 187 [[maybe_unused]] EcmaHandleScope handleScope(thread); 188 // get and check this set 189 JSHandle<JSTaggedValue> self(GetThis(argv)); 190 if (!self->IsJSAPITreeSet()) { 191 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 192 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 193 } else { 194 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 195 "The clear method cannot be bound"); 196 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 197 } 198 } 199 200 JSAPITreeSet::Clear(thread, JSHandle<JSAPITreeSet>::Cast(self)); 201 return JSTaggedValue::Undefined(); 202} 203 204JSTaggedValue ContainersTreeSet::GetLowerValue(EcmaRuntimeCallInfo *argv) 205{ 206 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetLowerValue); 207 JSThread *thread = argv->GetThread(); 208 [[maybe_unused]] EcmaHandleScope handleScope(thread); 209 // get and check this set 210 JSHandle<JSTaggedValue> self(GetThis(argv)); 211 if (!self->IsJSAPITreeSet()) { 212 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 213 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 214 } else { 215 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 216 "The getLowerValue method cannot be bound"); 217 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 218 } 219 } 220 221 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 222 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 223 JSHandle<TaggedTreeSet> tset(thread, set->GetTreeSet()); 224 return TaggedTreeSet::GetLowerKey(thread, tset, key); 225} 226 227JSTaggedValue ContainersTreeSet::GetHigherValue(EcmaRuntimeCallInfo *argv) 228{ 229 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetHigherValue); 230 JSThread *thread = argv->GetThread(); 231 [[maybe_unused]] EcmaHandleScope handleScope(thread); 232 // get and check this set 233 JSHandle<JSTaggedValue> self(GetThis(argv)); 234 if (!self->IsJSAPITreeSet()) { 235 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 236 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 237 } else { 238 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 239 "The getHigherValue method cannot be bound"); 240 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 241 } 242 } 243 244 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 245 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 246 JSHandle<TaggedTreeSet> tset(thread, set->GetTreeSet()); 247 return TaggedTreeSet::GetHigherKey(thread, tset, key); 248} 249 250JSTaggedValue ContainersTreeSet::PopFirst(EcmaRuntimeCallInfo *argv) 251{ 252 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, PopFirst); 253 JSThread *thread = argv->GetThread(); 254 [[maybe_unused]] EcmaHandleScope handleScope(thread); 255 // get and check this set 256 JSHandle<JSTaggedValue> self(GetThis(argv)); 257 if (!self->IsJSAPITreeSet()) { 258 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 259 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 260 } else { 261 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 262 "The popFirst method cannot be bound"); 263 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 264 } 265 } 266 267 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 268 return JSAPITreeSet::PopFirst(thread, set); 269} 270 271JSTaggedValue ContainersTreeSet::PopLast(EcmaRuntimeCallInfo *argv) 272{ 273 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, PopLast); 274 JSThread *thread = argv->GetThread(); 275 [[maybe_unused]] EcmaHandleScope handleScope(thread); 276 // get and check this set 277 JSHandle<JSTaggedValue> self(GetThis(argv)); 278 if (!self->IsJSAPITreeSet()) { 279 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 280 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 281 } else { 282 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 283 "The popLast method cannot be bound"); 284 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 285 } 286 } 287 288 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 289 return JSAPITreeSet::PopLast(thread, set); 290} 291 292JSTaggedValue ContainersTreeSet::IsEmpty(EcmaRuntimeCallInfo *argv) 293{ 294 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, IsEmpty); 295 JSThread *thread = argv->GetThread(); 296 [[maybe_unused]] EcmaHandleScope handleScope(thread); 297 // get and check this set 298 JSHandle<JSTaggedValue> self = GetThis(argv); 299 if (!self->IsJSAPITreeSet()) { 300 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 301 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 302 } else { 303 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 304 "The isEmpty method cannot be bound"); 305 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 306 } 307 } 308 JSHandle<JSAPITreeSet> set = JSHandle<JSAPITreeSet>::Cast(self); 309 return GetTaggedBoolean(set->GetSize() == 0); 310} 311 312JSTaggedValue ContainersTreeSet::Values(EcmaRuntimeCallInfo *argv) 313{ 314 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Values); 315 JSThread *thread = argv->GetThread(); 316 [[maybe_unused]] EcmaHandleScope handleScope(thread); 317 JSHandle<JSTaggedValue> self = GetThis(argv); 318 JSHandle<JSTaggedValue> iter = JSAPITreeSetIterator::CreateTreeSetIterator(thread, self, IterationKind::KEY); 319 return iter.GetTaggedValue(); 320} 321 322JSTaggedValue ContainersTreeSet::Entries(EcmaRuntimeCallInfo *argv) 323{ 324 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, Entries); 325 JSThread *thread = argv->GetThread(); 326 [[maybe_unused]] EcmaHandleScope handleScope(thread); 327 JSHandle<JSTaggedValue> self = GetThis(argv); 328 JSHandle<JSTaggedValue> iter = 329 JSAPITreeSetIterator::CreateTreeSetIterator(thread, self, IterationKind::KEY_AND_VALUE); 330 return iter.GetTaggedValue(); 331} 332 333JSTaggedValue ContainersTreeSet::ForEach(EcmaRuntimeCallInfo *argv) 334{ 335 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, ForEach); 336 JSThread *thread = argv->GetThread(); 337 [[maybe_unused]] EcmaHandleScope handleScope(thread); 338 // get and check TreeSet object 339 JSHandle<JSTaggedValue> self = GetThis(argv); 340 if (!self->IsJSAPITreeSet()) { 341 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 342 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 343 } else { 344 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 345 "The forEach method cannot be bound"); 346 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 347 } 348 } 349 // get and check callback function 350 JSHandle<JSTaggedValue> func(GetCallArg(argv, 0)); 351 if (!func->IsCallable()) { 352 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, func.GetTaggedValue()); 353 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 354 CString errorMsg = 355 "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); 356 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); 357 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 358 } 359 // If thisArg was supplied, let T be thisArg; else let T be undefined. 360 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 1); 361 JSHandle<JSAPITreeSet> tset = JSHandle<JSAPITreeSet>::Cast(self); 362 JSMutableHandle<TaggedTreeSet> iteratedSet(thread, tset->GetTreeSet()); 363 uint32_t elements = iteratedSet->NumberOfElements(); 364 JSHandle<TaggedArray> entries = TaggedTreeSet::GetArrayFromSet(thread, iteratedSet); 365 uint32_t index = 0; 366 size_t length = entries->GetLength(); 367 const uint32_t argsLength = 3; 368 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 369 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined()); 370 while (index < elements) { 371 int entriesIndex = entries->Get(index).GetInt(); 372 key.Update(iteratedSet->GetKey(entriesIndex)); 373 // Let funcResult be Call(callbackfn, T, «e, e, S»). 374 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength); 375 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 376 info->SetCallArg(key.GetTaggedValue(), key.GetTaggedValue(), self.GetTaggedValue()); 377 JSTaggedValue ret = JSFunction::Call(info); 378 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret); 379 // check entries should be update, size will be update by set add and remove. 380 if (tset->GetSize() != static_cast<int>(length)) { 381 iteratedSet.Update(tset->GetTreeSet()); 382 entries = TaggedTreeSet::GetArrayFromSet(thread, iteratedSet); 383 elements = iteratedSet->NumberOfElements(); 384 length = entries->GetLength(); 385 } 386 index++; 387 } 388 return JSTaggedValue::Undefined(); 389} 390 391JSTaggedValue ContainersTreeSet::GetLength(EcmaRuntimeCallInfo *argv) 392{ 393 ASSERT(argv); 394 BUILTINS_API_TRACE(argv->GetThread(), TreeSet, GetLength); 395 JSThread *thread = argv->GetThread(); 396 [[maybe_unused]] EcmaHandleScope handleScope(thread); 397 // get and check this set 398 JSHandle<JSTaggedValue> self(GetThis(argv)); 399 if (!self->IsJSAPITreeSet()) { 400 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPITreeSet()) { 401 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 402 } else { 403 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 404 "The getLength method cannot be bound"); 405 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 406 } 407 } 408 409 int count = JSHandle<JSAPITreeSet>::Cast(self)->GetSize(); 410 return JSTaggedValue(count); 411} 412} // namespace panda::ecmascript::containers 413