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 "containers_hashset.h" 17 18#include "ecmascript/containers/containers_errors.h" 19#include "ecmascript/interpreter/interpreter.h" 20#include "ecmascript/js_api/js_api_hashset.h" 21#include "ecmascript/js_api/js_api_hashset_iterator.h" 22#include "ecmascript/js_function.h" 23 24namespace panda::ecmascript::containers { 25JSTaggedValue ContainersHashSet::HashSetConstructor(EcmaRuntimeCallInfo *argv) 26{ 27 ASSERT(argv != nullptr); 28 JSThread *thread = argv->GetThread(); 29 BUILTINS_API_TRACE(thread, HashSet, Constructor); 30 [[maybe_unused]] EcmaHandleScope handleScope(thread); 31 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 32 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 33 if (newTarget->IsUndefined()) { 34 JSTaggedValue error = 35 ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR, 36 "The HashSet's constructor cannot be directly invoked"); 37 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 38 } 39 40 JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 41 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget); 42 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 43 44 JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(obj); 45 JSTaggedValue hashSetArray = TaggedHashArray::Create(thread); 46 hashSet->SetTable(thread, hashSetArray); 47 hashSet->SetSize(0); 48 49 return hashSet.GetTaggedValue(); 50} 51 52JSTaggedValue ContainersHashSet::GetIteratorObj(EcmaRuntimeCallInfo *argv) 53{ 54 ASSERT(argv != nullptr); 55 JSThread *thread = argv->GetThread(); 56 BUILTINS_API_TRACE(thread, HashSet, GetIteratorObj); 57 [[maybe_unused]] EcmaHandleScope handleScope(thread); 58 JSHandle<JSTaggedValue> self = GetThis(argv); 59 if (!self->IsJSAPIHashSet()) { 60 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) { 61 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 62 } else { 63 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 64 "The Symbol.iterator method cannot be bound"); 65 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 66 } 67 } 68 JSHandle<JSTaggedValue> iter = JSAPIHashSetIterator::CreateHashSetIterator(thread, self, IterationKind::VALUE); 69 return iter.GetTaggedValue(); 70} 71 72JSTaggedValue ContainersHashSet::Values(EcmaRuntimeCallInfo *argv) 73{ 74 ASSERT(argv != nullptr); 75 JSThread *thread = argv->GetThread(); 76 BUILTINS_API_TRACE(thread, HashSet, Values); 77 [[maybe_unused]] EcmaHandleScope handleScope(thread); 78 JSHandle<JSTaggedValue> self = GetThis(argv); 79 if (!self->IsJSAPIHashSet()) { 80 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) { 81 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 82 } else { 83 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 84 "The values method cannot be bound"); 85 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 86 } 87 } 88 JSHandle<JSTaggedValue> iter = JSAPIHashSetIterator::CreateHashSetIterator(thread, self, IterationKind::VALUE); 89 return iter.GetTaggedValue(); 90} 91 92JSTaggedValue ContainersHashSet::Entries(EcmaRuntimeCallInfo *argv) 93{ 94 ASSERT(argv != nullptr); 95 JSThread *thread = argv->GetThread(); 96 BUILTINS_API_TRACE(thread, HashSet, Entries); 97 [[maybe_unused]] EcmaHandleScope handleScope(thread); 98 JSHandle<JSTaggedValue> self = GetThis(argv); 99 if (!self->IsJSAPIHashSet()) { 100 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) { 101 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 102 } else { 103 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 104 "The entries method cannot be bound"); 105 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 106 } 107 } 108 JSHandle<JSTaggedValue> iter = 109 JSAPIHashSetIterator::CreateHashSetIterator(thread, self, IterationKind::KEY_AND_VALUE); 110 return iter.GetTaggedValue(); 111} 112 113JSTaggedValue ContainersHashSet::Add(EcmaRuntimeCallInfo *argv) 114{ 115 ASSERT(argv != nullptr); 116 JSThread *thread = argv->GetThread(); 117 BUILTINS_API_TRACE(thread, HashSet, Add); 118 [[maybe_unused]] EcmaHandleScope handleScope(thread); 119 JSHandle<JSTaggedValue> self = GetThis(argv); 120 121 if (!self->IsJSAPIHashSet()) { 122 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) { 123 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 124 } else { 125 JSTaggedValue error = ContainerError::BusinessError(thread, BIND_ERROR, 126 "The add method cannot be bound"); 127 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 128 } 129 } 130 131 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 132 JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(self); 133 JSAPIHashSet::Add(thread, hashSet, value); 134 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 135 return JSTaggedValue::True(); 136} 137 138JSTaggedValue ContainersHashSet::Remove(EcmaRuntimeCallInfo *argv) 139{ 140 ASSERT(argv != nullptr); 141 JSThread *thread = argv->GetThread(); 142 BUILTINS_API_TRACE(thread, HashSet, Remove); 143 [[maybe_unused]] EcmaHandleScope handleScope(thread); 144 JSHandle<JSTaggedValue> self = GetThis(argv); 145 146 if (!self->IsJSAPIHashSet()) { 147 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) { 148 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 149 } else { 150 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 151 "The remove method cannot be bound"); 152 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 153 } 154 } 155 156 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 157 JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(self); 158 return JSAPIHashSet::Remove(thread, hashSet, key.GetTaggedValue()); 159} 160 161JSTaggedValue ContainersHashSet::Has(EcmaRuntimeCallInfo *argv) 162{ 163 ASSERT(argv != nullptr); 164 JSThread *thread = argv->GetThread(); 165 BUILTINS_API_TRACE(thread, HashSet, Has); 166 [[maybe_unused]] EcmaHandleScope handleScope(thread); 167 JSHandle<JSTaggedValue> self = GetThis(argv); 168 169 if (!self->IsJSAPIHashSet()) { 170 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) { 171 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 172 } else { 173 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 174 "The has method cannot be bound"); 175 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 176 } 177 } 178 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 179 JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::Cast(self); 180 return jsHashSet->Has(thread, value.GetTaggedValue()); 181} 182 183JSTaggedValue ContainersHashSet::Clear(EcmaRuntimeCallInfo *argv) 184{ 185 ASSERT(argv != nullptr); 186 JSThread *thread = argv->GetThread(); 187 BUILTINS_API_TRACE(thread, HashSet, Clear); 188 [[maybe_unused]] EcmaHandleScope handleScope(thread); 189 JSHandle<JSTaggedValue> self = GetThis(argv); 190 191 if (!self->IsJSAPIHashSet()) { 192 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) { 193 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 194 } else { 195 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 196 "The clear method cannot be bound"); 197 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 198 } 199 } 200 JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::Cast(self); 201 jsHashSet->Clear(thread); 202 return JSTaggedValue::Undefined(); 203} 204 205JSTaggedValue ContainersHashSet::GetLength(EcmaRuntimeCallInfo *argv) 206{ 207 ASSERT(argv != nullptr); 208 JSThread *thread = argv->GetThread(); 209 BUILTINS_API_TRACE(thread, HashSet, GetLength); 210 [[maybe_unused]] EcmaHandleScope handleScope(thread); 211 JSHandle<JSTaggedValue> self = GetThis(argv); 212 213 if (!self->IsJSAPIHashSet()) { 214 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) { 215 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 216 } else { 217 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 218 "The getLength method cannot be bound"); 219 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 220 } 221 } 222 223 JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::Cast(self); 224 return jsHashSet->GetLength(); 225} 226 227JSTaggedValue ContainersHashSet::IsEmpty(EcmaRuntimeCallInfo *argv) 228{ 229 ASSERT(argv != nullptr); 230 JSThread *thread = argv->GetThread(); 231 BUILTINS_API_TRACE(thread, HashSet, IsEmpty); 232 [[maybe_unused]] EcmaHandleScope handleScope(thread); 233 JSHandle<JSTaggedValue> self = GetThis(argv); 234 235 if (!self->IsJSAPIHashSet()) { 236 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIHashSet()) { 237 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 238 } else { 239 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 240 "The isEmpty method cannot be bound"); 241 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 242 } 243 } 244 JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::Cast(self); 245 return jsHashSet->IsEmpty(); 246} 247 248JSTaggedValue ContainersHashSet::ForEach(EcmaRuntimeCallInfo *argv) 249{ 250 ASSERT(argv != nullptr); 251 JSThread *thread = argv->GetThread(); 252 BUILTINS_API_TRACE(thread, HashSet, ForEach); 253 [[maybe_unused]] EcmaHandleScope handleScope(thread); 254 JSHandle<JSTaggedValue> thisHandle = GetThis(argv); 255 if (!thisHandle->IsJSAPIHashSet()) { 256 if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIHashSet()) { 257 thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget()); 258 } else { 259 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 260 "The forEach method cannot be bound"); 261 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 262 } 263 } 264 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0); 265 if (!callbackFnHandle->IsCallable()) { 266 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle); 267 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 268 CString errorMsg = 269 "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); 270 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); 271 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 272 } 273 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1); 274 JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(thisHandle); 275 JSHandle<TaggedHashArray> table(thread, hashSet->GetTable()); 276 uint32_t len = table->GetLength(); 277 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 278 JSMutableHandle<TaggedQueue> queue(thread, factory->NewTaggedQueue(0)); 279 JSMutableHandle<TaggedNode> node(thread, JSTaggedValue::Undefined()); 280 JSMutableHandle<JSTaggedValue> currentKey(thread, JSTaggedValue::Undefined()); 281 uint32_t index = 0; 282 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 283 while (index < len) { 284 node.Update(TaggedHashArray::GetCurrentNode(thread, queue, table, index)); 285 if (!node.GetTaggedValue().IsHole()) { 286 currentKey.Update(node->GetKey()); 287 EcmaRuntimeCallInfo *info = 288 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, 289 thisArgHandle, undefined, 3); // 3: three args 290 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 291 info->SetCallArg(currentKey.GetTaggedValue(), currentKey.GetTaggedValue(), thisHandle.GetTaggedValue()); 292 JSTaggedValue funcResult = JSFunction::Call(info); 293 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); 294 } 295 } 296 return JSTaggedValue::Undefined(); 297} 298} // namespace panda::ecmascript::containers 299