/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "containers_hashset.h" #include "ecmascript/containers/containers_errors.h" #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/js_api/js_api_hashset.h" #include "ecmascript/js_api/js_api_hashset_iterator.h" #include "ecmascript/js_function.h" namespace panda::ecmascript::containers { JSTaggedValue ContainersHashSet::HashSetConstructor(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, Constructor); [[maybe_unused]] EcmaHandleScope handleScope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle newTarget = GetNewTarget(argv); if (newTarget->IsUndefined()) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR, "The HashSet's constructor cannot be directly invoked"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle constructor = GetConstructor(argv); JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle hashSet = JSHandle::Cast(obj); JSTaggedValue hashSetArray = TaggedHashArray::Create(thread); hashSet->SetTable(thread, hashSetArray); hashSet->SetSize(0); return hashSet.GetTaggedValue(); } JSTaggedValue ContainersHashSet::GetIteratorObj(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, GetIteratorObj); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIHashSet()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIHashSet()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The Symbol.iterator method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle iter = JSAPIHashSetIterator::CreateHashSetIterator(thread, self, IterationKind::VALUE); return iter.GetTaggedValue(); } JSTaggedValue ContainersHashSet::Values(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, Values); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIHashSet()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIHashSet()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The values method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle iter = JSAPIHashSetIterator::CreateHashSetIterator(thread, self, IterationKind::VALUE); return iter.GetTaggedValue(); } JSTaggedValue ContainersHashSet::Entries(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, Entries); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIHashSet()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIHashSet()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The entries method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle iter = JSAPIHashSetIterator::CreateHashSetIterator(thread, self, IterationKind::KEY_AND_VALUE); return iter.GetTaggedValue(); } JSTaggedValue ContainersHashSet::Add(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, Add); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIHashSet()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIHashSet()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, BIND_ERROR, "The add method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); JSHandle hashSet = JSHandle::Cast(self); JSAPIHashSet::Add(thread, hashSet, value); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue::True(); } JSTaggedValue ContainersHashSet::Remove(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, Remove); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIHashSet()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIHashSet()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The remove method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle key = GetCallArg(argv, 0); JSHandle hashSet = JSHandle::Cast(self); return JSAPIHashSet::Remove(thread, hashSet, key.GetTaggedValue()); } JSTaggedValue ContainersHashSet::Has(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, Has); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIHashSet()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIHashSet()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The has method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); JSHandle jsHashSet = JSHandle::Cast(self); return jsHashSet->Has(thread, value.GetTaggedValue()); } JSTaggedValue ContainersHashSet::Clear(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, Clear); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIHashSet()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIHashSet()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The clear method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle jsHashSet = JSHandle::Cast(self); jsHashSet->Clear(thread); return JSTaggedValue::Undefined(); } JSTaggedValue ContainersHashSet::GetLength(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, GetLength); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIHashSet()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIHashSet()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The getLength method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle jsHashSet = JSHandle::Cast(self); return jsHashSet->GetLength(); } JSTaggedValue ContainersHashSet::IsEmpty(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, IsEmpty); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIHashSet()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIHashSet()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The isEmpty method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle jsHashSet = JSHandle::Cast(self); return jsHashSet->IsEmpty(); } JSTaggedValue ContainersHashSet::ForEach(EcmaRuntimeCallInfo *argv) { ASSERT(argv != nullptr); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, HashSet, ForEach); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisHandle = GetThis(argv); if (!thisHandle->IsJSAPIHashSet()) { if (thisHandle->IsJSProxy() && JSHandle::Cast(thisHandle)->GetTarget().IsJSAPIHashSet()) { thisHandle = JSHandle(thread, JSHandle::Cast(thisHandle)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The forEach method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle thisArgHandle = GetCallArg(argv, 1); JSHandle hashSet = JSHandle::Cast(thisHandle); JSHandle table(thread, hashSet->GetTable()); uint32_t len = table->GetLength(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSMutableHandle queue(thread, factory->NewTaggedQueue(0)); JSMutableHandle node(thread, JSTaggedValue::Undefined()); JSMutableHandle currentKey(thread, JSTaggedValue::Undefined()); uint32_t index = 0; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); while (index < len) { node.Update(TaggedHashArray::GetCurrentNode(thread, queue, table, index)); if (!node.GetTaggedValue().IsHole()) { currentKey.Update(node->GetKey()); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, 3); // 3: three args RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(currentKey.GetTaggedValue(), currentKey.GetTaggedValue(), thisHandle.GetTaggedValue()); JSTaggedValue funcResult = JSFunction::Call(info); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); } } return JSTaggedValue::Undefined(); } } // namespace panda::ecmascript::containers