!1625 Add Containers HashMap and HashSet

Merge pull request !1625 from 刘甘霖/container_hashmap
This commit is contained in:
openharmony_ci 2022-07-20 09:02:54 +00:00 committed by Gitee
commit 1c9171e517
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
43 changed files with 5178 additions and 11 deletions

View File

@ -356,6 +356,8 @@ ecma_source = [
"ecmascript/builtins/builtins_weak_set.cpp",
"ecmascript/containers/containers_arraylist.cpp",
"ecmascript/containers/containers_deque.cpp",
"ecmascript/containers/containers_hashmap.cpp",
"ecmascript/containers/containers_hashset.cpp",
"ecmascript/containers/containers_lightweightmap.cpp",
"ecmascript/containers/containers_lightweightset.cpp",
"ecmascript/containers/containers_linked_list.cpp",
@ -407,6 +409,10 @@ ecma_source = [
"ecmascript/js_api_arraylist_iterator.cpp",
"ecmascript/js_api_deque.cpp",
"ecmascript/js_api_deque_iterator.cpp",
"ecmascript/js_api_hashmap.cpp",
"ecmascript/js_api_hashmap_iterator.cpp",
"ecmascript/js_api_hashset.cpp",
"ecmascript/js_api_hashset_iterator.cpp",
"ecmascript/js_api_lightweightmap.cpp",
"ecmascript/js_api_lightweightmap_iterator.cpp",
"ecmascript/js_api_lightweightset.cpp",
@ -501,7 +507,9 @@ ecma_source = [
"ecmascript/regexp/regexp_parser_cache.cpp",
"ecmascript/shared_mm/shared_mm.cpp",
"ecmascript/tagged_dictionary.cpp",
"ecmascript/tagged_hash_array.cpp",
"ecmascript/tagged_list.cpp",
"ecmascript/tagged_node.cpp",
"ecmascript/tagged_tree.cpp",
"ecmascript/template_string.cpp",
"ecmascript/waiter_list.cpp",

View File

@ -210,7 +210,8 @@ JSTaggedValue BuiltinsNumber::ParseInt(EcmaRuntimeCallInfo *argv)
}
JSTaggedValue result = NumberHelper::StringToDoubleWithRadix(str.begin(), str.end(), radix);
return result;
int32_t resTmp = NumberHelper::DoubleInRangeInt32(result.GetNumber());
return JSTaggedValue(resTmp);
}
// prototype

View File

@ -414,7 +414,7 @@ HWTEST_F_L0(BuiltinsNumberTest, parseInt)
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
JSTaggedValue result = BuiltinsNumber::ParseInt(ecmaRuntimeCallInfo);
ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(291)).GetRawData());
ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<int>(291)).GetRawData());
TestHelper::TearDownFrame(thread, prev);
}

View File

@ -0,0 +1,302 @@
/*
* 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_hashmap.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/js_api_hashmap.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/js_function.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_array-inl.h"
#include "ecmascript/tagged_hash_array.h"
#include "ecmascript/tagged_node.h"
#include "ecmascript/tagged_queue.h"
namespace panda::ecmascript::containers {
JSTaggedValue ContainersHashMap::HashMapConstructor(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, Constructor);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
if (newTarget->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSAPIHashMap> hashMap = JSHandle<JSAPIHashMap>::Cast(obj);
JSTaggedValue hashMapArray = TaggedHashArray::Create(thread);
hashMap->SetTable(thread, hashMapArray);
hashMap->SetSize(0);
return hashMap.GetTaggedValue();
}
JSTaggedValue ContainersHashMap::Keys(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, Keys);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> iter =
JSAPIHashMapIterator::CreateHashMapIterator(thread, self, IterationKind::KEY);
return iter.GetTaggedValue();
}
JSTaggedValue ContainersHashMap::Values(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, Values);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> iter =
JSAPIHashMapIterator::CreateHashMapIterator(thread, self, IterationKind::VALUE);
return iter.GetTaggedValue();
}
JSTaggedValue ContainersHashMap::Entries(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, Entries);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> iter =
JSAPIHashMapIterator::CreateHashMapIterator(thread, self, IterationKind::KEY_AND_VALUE);
return iter.GetTaggedValue();
}
JSTaggedValue ContainersHashMap::ForEach(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, ForEach);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
if (!thisHandle->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
JSHandle<JSAPIHashMap> hashMap = JSHandle<JSAPIHashMap>::Cast(thisHandle);
JSHandle<TaggedHashArray> table(thread, hashMap->GetTable());
uint32_t len = table->GetLength();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<TaggedQueue> queue(thread, factory->NewTaggedQueue(0));
JSMutableHandle<TaggedNode> node(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
uint32_t index = 0;
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
while (index < len) {
node.Update(TaggedHashArray::GetCurrentNode(thread, queue, table, index));
if (!node.GetTaggedValue().IsHole()) {
key.Update(node->GetKey());
value.Update(node->GetValue());
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle,
thisArgHandle, undefined, 3); // 3: three args
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(value.GetTaggedValue(), key.GetTaggedValue(), thisHandle.GetTaggedValue());
JSTaggedValue funcResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
}
}
return JSTaggedValue::Undefined();
}
JSTaggedValue ContainersHashMap::Set(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, Set);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
JSHandle<JSAPIHashMap> hashMap = JSHandle<JSAPIHashMap>::Cast(self);
JSAPIHashMap::Set(thread, hashMap, key, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return hashMap.GetTaggedValue();
}
JSTaggedValue ContainersHashMap::SetAll(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, SetAll);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> obj = GetCallArg(argv, 0);
if (!obj->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Incorrect parameters, it should be HashMap", JSTaggedValue::Exception());
}
JSHandle<JSAPIHashMap> dmap = JSHandle<JSAPIHashMap>::Cast(self);
JSHandle<JSAPIHashMap> smap = JSHandle<JSAPIHashMap>::Cast(obj);
JSAPIHashMap::SetAll(thread, dmap, smap);
return self.GetTaggedValue();
}
JSTaggedValue ContainersHashMap::Get(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, Get);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
JSHandle<JSAPIHashMap> hashMap = JSHandle<JSAPIHashMap>::Cast(self);
return hashMap->Get(thread, key.GetTaggedValue());
}
JSTaggedValue ContainersHashMap::Remove(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, Remove);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
JSHandle<JSAPIHashMap> hashMap = JSHandle<JSAPIHashMap>::Cast(self);
return JSAPIHashMap::Remove(thread, hashMap, key.GetTaggedValue());
}
JSTaggedValue ContainersHashMap::HasKey(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, HasKey);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
JSHandle<JSAPIHashMap> hashMap = JSHandle<JSAPIHashMap>::Cast(self);
return hashMap->HasKey(thread, key.GetTaggedValue());
}
JSTaggedValue ContainersHashMap::HasValue(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, HasValue);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
JSHandle<JSAPIHashMap> hashMap = JSHandle<JSAPIHashMap>::Cast(self);
return JSAPIHashMap::HasValue(thread, hashMap, value);
}
JSTaggedValue ContainersHashMap::Replace(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, Replace);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> newValue = GetCallArg(argv, 1);
JSHandle<JSAPIHashMap> jsHashMap = JSHandle<JSAPIHashMap>::Cast(self);
return jsHashMap->Replace(thread, key.GetTaggedValue(), newValue.GetTaggedValue());
}
JSTaggedValue ContainersHashMap::Clear(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, Clear);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSAPIHashMap> jsHashMap = JSHandle<JSAPIHashMap>::Cast(self);
jsHashMap->Clear(thread);
return JSTaggedValue::Undefined();
}
JSTaggedValue ContainersHashMap::GetLength(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, GetLength);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSAPIHashMap> jsHashMap = JSHandle<JSAPIHashMap>::Cast(self);
return jsHashMap->GetLength();
}
JSTaggedValue ContainersHashMap::IsEmpty(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashMap, IsEmpty);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap", JSTaggedValue::Exception());
}
JSHandle<JSAPIHashMap> jsHashMap = JSHandle<JSAPIHashMap>::Cast(self);
return jsHashMap->IsEmpty();
}
} // namespace panda::ecmascript::containers

View File

@ -0,0 +1,42 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_CONTAINERS_CONTAINERS_HASHMAP_H
#define ECMASCRIPT_CONTAINERS_CONTAINERS_HASHMAP_H
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/ecma_runtime_call_info.h"
namespace panda::ecmascript::containers {
class ContainersHashMap : public base::BuiltinsBase {
public:
static JSTaggedValue HashMapConstructor(EcmaRuntimeCallInfo *argv);
static JSTaggedValue HasKey(EcmaRuntimeCallInfo *argv);
static JSTaggedValue HasValue(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Replace(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Values(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv);
static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Set(EcmaRuntimeCallInfo *argv);
static JSTaggedValue SetAll(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Remove(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Get(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv);
static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv);
static JSTaggedValue IsEmpty(EcmaRuntimeCallInfo *argv);
};
} // namespace panda::ecmascript::containers
#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_HASHMAP_H

View File

@ -0,0 +1,218 @@
/*
* 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/ecma_vm.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/js_api_hashset.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/js_function.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_array-inl.h"
#include "ecmascript/tagged_hash_array.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<JSTaggedValue> newTarget = GetNewTarget(argv);
if (newTarget->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(obj);
JSTaggedValue hashSetArray = TaggedHashArray::Create(thread);
hashSet->SetTable(thread, hashSetArray);
hashSet->SetSize(0);
return hashSet.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<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> 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<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> 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<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(self);
JSAPIHashSet::Add(thread, hashSet, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return hashSet.GetTaggedValue();
}
JSTaggedValue ContainersHashSet::Remove(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, HashSet, Remove);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::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<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::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<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet", JSTaggedValue::Exception());
}
JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::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<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet", JSTaggedValue::Exception());
}
JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::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<JSTaggedValue> self = GetThis(argv);
if (!self->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet", JSTaggedValue::Exception());
}
JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::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<JSTaggedValue> thisHandle = GetThis(argv);
if (!thisHandle->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(thisHandle);
JSHandle<TaggedHashArray> table(thread, hashSet->GetTable());
uint32_t len = table->GetLength();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<TaggedQueue> queue(thread, factory->NewTaggedQueue(0));
JSMutableHandle<TaggedNode> node(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> currentKey(thread, JSTaggedValue::Undefined());
uint32_t index = 0;
JSHandle<JSTaggedValue> 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, 2); // 2: two args
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(currentKey.GetTaggedValue(), thisHandle.GetTaggedValue());
JSTaggedValue funcResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
}
}
return JSTaggedValue::Undefined();
}
} // namespace panda::ecmascript::containers

View File

@ -0,0 +1,37 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_CONTAINERS_CONTAINERS_HASHSET_H
#define ECMASCRIPT_CONTAINERS_CONTAINERS_HASHSET_H
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/ecma_runtime_call_info.h"
namespace panda::ecmascript::containers {
class ContainersHashSet : public base::BuiltinsBase {
public:
static JSTaggedValue HashSetConstructor(EcmaRuntimeCallInfo *argv);
static JSTaggedValue IsEmpty(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Has(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Add(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Remove(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv);
static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Values(EcmaRuntimeCallInfo *argv);
static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv);
static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv);
};
} // namespace panda::ecmascript::containers
#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_HASHSET_H

View File

@ -17,6 +17,8 @@
#include "containers_arraylist.h"
#include "containers_deque.h"
#include "containers_hashmap.h"
#include "containers_hashset.h"
#include "containers_lightweightmap.h"
#include "containers_lightweightset.h"
#include "containers_linked_list.h"
@ -34,6 +36,10 @@
#include "ecmascript/js_api_arraylist_iterator.h"
#include "ecmascript/js_api_deque.h"
#include "ecmascript/js_api_deque_iterator.h"
#include "ecmascript/js_api_hashmap.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/js_api_hashset.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/js_api_lightweightmap.h"
#include "ecmascript/js_api_lightweightmap_iterator.h"
#include "ecmascript/js_api_lightweightset.h"
@ -120,8 +126,14 @@ JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg)
res = InitializeContainer(thread, thisValue, InitializeLinkedList, "LinkedListConstructor");
break;
}
case ContainerTag::HashMap:
case ContainerTag::HashSet:
case ContainerTag::HashMap: {
res = InitializeContainer(thread, thisValue, InitializeHashMap, "HashMapConstructor");
break;
}
case ContainerTag::HashSet: {
res = InitializeContainer(thread, thisValue, InitializeHashSet, "HashSetConstructor");
break;
}
case ContainerTag::END:
break;
default:
@ -1054,4 +1066,149 @@ void ContainersPrivate::InitializeListIterator(JSThread *thread, const JSHandle<
SetStringTagSymbol(thread, env, setIteratorPrototype, "list Iterator");
globalConst->SetConstant(ConstantIndex::LIST_ITERATOR_PROTOTYPE_INDEX, setIteratorPrototype.GetTaggedValue());
}
JSHandle<JSTaggedValue> ContainersPrivate::InitializeHashMap(JSThread *thread)
{
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// HashMap.prototype
JSHandle<JSObject> hashMapFuncPrototype = factory->NewEmptyJSObject();
JSHandle<JSTaggedValue> hashMapFuncPrototypeValue(hashMapFuncPrototype);
// HashMap.prototype_or_dynclass
JSHandle<JSHClass> hashMapInstanceDynclass =
factory->NewEcmaDynClass(JSAPIHashMap::SIZE, JSType::JS_API_HASH_MAP, hashMapFuncPrototypeValue);
// HashMap() = new Function()
JSHandle<JSTaggedValue> hashMapFunction(NewContainerConstructor(
thread, hashMapFuncPrototype, ContainersHashMap::HashMapConstructor, "HashMap", FuncLength::ZERO));
JSHandle<JSFunction>::Cast(hashMapFunction)->SetFunctionPrototype(thread, hashMapInstanceDynclass.GetTaggedValue());
// "constructor" property on the prototype
JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(hashMapFuncPrototype), constructorKey, hashMapFunction);
// HashMap.prototype.set()
SetFrozenFunction(thread, hashMapFuncPrototype, "set", ContainersHashMap::Set, FuncLength::TWO);
// HashMap.prototype.setall()
SetFrozenFunction(thread, hashMapFuncPrototype, "setAll", ContainersHashMap::SetAll, FuncLength::ONE);
// HashMap.prototype.isEmpty()
SetFrozenFunction(thread, hashMapFuncPrototype, "isEmpty", ContainersHashMap::IsEmpty, FuncLength::ZERO);
// HashMap.prototype.remove()
SetFrozenFunction(thread, hashMapFuncPrototype, "remove", ContainersHashMap::Remove, FuncLength::ONE);
// HashMap.prototype.clear()
SetFrozenFunction(thread, hashMapFuncPrototype, "clear", ContainersHashMap::Clear, FuncLength::ZERO);
// HashMap.prototype.get()
SetFrozenFunction(thread, hashMapFuncPrototype, "get", ContainersHashMap::Get, FuncLength::ONE);
// HashMap.prototype.forEach()
SetFrozenFunction(thread, hashMapFuncPrototype, "forEach", ContainersHashMap::ForEach, FuncLength::TWO);
// HashMap.prototype.hasKey()
SetFrozenFunction(thread, hashMapFuncPrototype, "hasKey", ContainersHashMap::HasKey, FuncLength::ONE);
// HashMap.prototype.hasValue()
SetFrozenFunction(thread, hashMapFuncPrototype, "hasValue", ContainersHashMap::HasValue, FuncLength::ONE);
// HashMap.prototype.replace()
SetFrozenFunction(thread, hashMapFuncPrototype, "replace", ContainersHashMap::Replace, FuncLength::TWO);
// HashMap.prototype.keys()
SetFrozenFunction(thread, hashMapFuncPrototype, "keys", ContainersHashMap::Keys, FuncLength::ZERO);
// HashMap.prototype.Values()
SetFrozenFunction(thread, hashMapFuncPrototype, "values", ContainersHashMap::Values, FuncLength::ZERO);
// HashMap.prototype.keys()
SetFrozenFunction(thread, hashMapFuncPrototype, "entries", ContainersHashMap::Entries, FuncLength::ZERO);
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
// @@ToStringTag
SetStringTagSymbol(thread, env, hashMapFuncPrototype, "HashMap");
// %HashMapPrototype% [ @@iterator ]
JSHandle<JSTaggedValue> iteratorSymbol = env->GetIteratorSymbol();
JSHandle<JSTaggedValue> entries(factory->NewFromASCII("entries"));
JSHandle<JSTaggedValue> entriesFunc =
JSObject::GetMethod(thread, JSHandle<JSTaggedValue>::Cast(hashMapFuncPrototype), entries);
PropertyDescriptor descriptor(thread, entriesFunc, false, false, false);
JSObject::DefineOwnProperty(thread, hashMapFuncPrototype, iteratorSymbol, descriptor);
JSHandle<JSTaggedValue> lengthGetter =
CreateGetter(thread, ContainersHashMap::GetLength, "length", FuncLength::ZERO);
JSHandle<JSTaggedValue> lengthKey(thread, globalConst->GetLengthString());
SetGetter(thread, hashMapFuncPrototype, lengthKey, lengthGetter);
InitializeHashMapIterator(thread);
return hashMapFunction;
}
void ContainersPrivate::InitializeHashMapIterator(JSThread *thread)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSHClass> iteratorFuncDynclass =
JSHandle<JSHClass>::Cast(globalConst->GetHandledJSAPIIteratorFuncDynClass());
// HashMapIterator.prototype
JSHandle<JSObject> hashMapIteratorPrototype(factory->NewJSObject(iteratorFuncDynclass));
// HashMapIterator.prototype.next()
SetFrozenFunction(thread, hashMapIteratorPrototype, "next", JSAPIHashMapIterator::Next, FuncLength::ZERO);
SetStringTagSymbol(thread, env, hashMapIteratorPrototype, "HashMap Iterator");
globalConst->SetConstant(ConstantIndex::HASHMAP_ITERATOR_PROTOTYPE_INDEX,
hashMapIteratorPrototype.GetTaggedValue());
}
JSHandle<JSTaggedValue> ContainersPrivate::InitializeHashSet(JSThread *thread)
{
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// HashSet.prototype
JSHandle<JSObject> hashSetFuncPrototype = factory->NewEmptyJSObject();
JSHandle<JSTaggedValue> hashSetFuncPrototypeValue(hashSetFuncPrototype);
// HashSet.prototype_or_dynclass
JSHandle<JSHClass> hashSetInstanceDynclass =
factory->NewEcmaDynClass(JSAPIHashSet::SIZE, JSType::JS_API_HASH_SET, hashSetFuncPrototypeValue);
// HashSet() = new Function()
JSHandle<JSTaggedValue> hashSetFunction(NewContainerConstructor(
thread, hashSetFuncPrototype, ContainersHashSet::HashSetConstructor, "HashSet", FuncLength::ZERO));
JSHandle<JSFunction>::Cast(hashSetFunction)->SetFunctionPrototype(thread, hashSetInstanceDynclass.GetTaggedValue());
// "constructor" property on the prototype
JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(hashSetFuncPrototype), constructorKey, hashSetFunction);
SetFrozenFunction(thread, hashSetFuncPrototype, "isEmpty", ContainersHashSet::IsEmpty, FuncLength::ZERO);
SetFrozenFunction(thread, hashSetFuncPrototype, "has", ContainersHashSet::Has, FuncLength::ONE);
SetFrozenFunction(thread, hashSetFuncPrototype, "add", ContainersHashSet::Add, FuncLength::ONE);
SetFrozenFunction(thread, hashSetFuncPrototype, "has", ContainersHashSet::Has, FuncLength::ONE);
SetFrozenFunction(thread, hashSetFuncPrototype, "remove", ContainersHashSet::Remove, FuncLength::ONE);
SetFrozenFunction(thread, hashSetFuncPrototype, "clear", ContainersHashSet::Clear, FuncLength::ZERO);
SetFrozenFunction(thread, hashSetFuncPrototype, "values", ContainersHashSet::Values, FuncLength::ZERO);
SetFrozenFunction(thread, hashSetFuncPrototype, "entries", ContainersHashSet::Entries, FuncLength::ZERO);
SetFrozenFunction(thread, hashSetFuncPrototype, "forEach", ContainersHashSet::ForEach, FuncLength::TWO);
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
// @@ToStringTag
SetStringTagSymbol(thread, env, hashSetFuncPrototype, "HashSet");
// %HashSetPrototype% [ @@iterator ]
JSHandle<JSTaggedValue> iteratorSymbol = env->GetIteratorSymbol();
JSHandle<JSTaggedValue> values(thread, globalConst->GetValuesString());
JSHandle<JSTaggedValue> valuesFunc =
JSObject::GetMethod(thread, JSHandle<JSTaggedValue>::Cast(hashSetFuncPrototype), values);
PropertyDescriptor descriptor(thread, valuesFunc, false, false, false);
JSObject::DefineOwnProperty(thread, hashSetFuncPrototype, iteratorSymbol, descriptor);
JSHandle<JSTaggedValue> lengthGetter =
CreateGetter(thread, ContainersHashSet::GetLength, "length", FuncLength::ZERO);
JSHandle<JSTaggedValue> lengthKey(thread, globalConst->GetLengthString());
SetGetter(thread, hashSetFuncPrototype, lengthKey, lengthGetter);
InitializeHashSetIterator(thread);
return hashSetFunction;
}
void ContainersPrivate::InitializeHashSetIterator(JSThread *thread)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSHClass> iteratorFuncDynclass =
JSHandle<JSHClass>::Cast(globalConst->GetHandledJSAPIIteratorFuncDynClass());
// HashSetIterator.prototype
JSHandle<JSObject> hashSetIteratorPrototype(factory->NewJSObject(iteratorFuncDynclass));
// HashSetIterator.prototype.next()
SetFrozenFunction(thread, hashSetIteratorPrototype, "next", JSAPIHashSetIterator::Next, FuncLength::ZERO);
SetStringTagSymbol(thread, env, hashSetIteratorPrototype, "HashSet Iterator");
globalConst->SetConstant(ConstantIndex::HASHSET_ITERATOR_PROTOTYPE_INDEX,
hashSetIteratorPrototype.GetTaggedValue());
}
} // namespace panda::ecmascript::containers

View File

@ -70,6 +70,10 @@ private:
static JSHandle<JSTaggedValue> InitializeArrayList(JSThread *thread);
static void InitializeArrayListIterator(JSThread *thread, const JSHandle<GlobalEnv> &env,
GlobalEnvConstants *globalConst);
static JSHandle<JSTaggedValue> InitializeHashMap(JSThread *thread);
static void InitializeHashMapIterator(JSThread *thread);
static JSHandle<JSTaggedValue> InitializeHashSet(JSThread *thread);
static void InitializeHashSetIterator(JSThread *thread);
static JSHandle<JSTaggedValue> InitializeLightWeightMap(JSThread *thread);
static void InitializeLightWeightMapIterator(JSThread *thread);
static JSHandle<JSTaggedValue> InitializeLightWeightSet(JSThread *thread);

View File

@ -23,6 +23,8 @@ host_unittest_action("ContainersTest") {
sources = [
# test file
"containers_deque_test.cpp",
"containers_hashmap_test.cpp",
"containers_hashset_test.cpp",
"containers_lightweightmap_test.cpp",
"containers_lightweightset_test.cpp",
"containers_linked_list_test.cpp",

View File

@ -0,0 +1,948 @@
/*
* 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 "ecmascript/containers/containers_private.h"
#include "ecmascript/containers/containers_hashmap.h"
#include "ecmascript/ecma_runtime_call_info.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/js_api_hashmap.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda::ecmascript;
using namespace panda::ecmascript::containers;
namespace panda::test {
class ContainersHashMapTest : public testing::Test {
public:
static void SetUpTestCase()
{
GTEST_LOG_(INFO) << "SetUpTestCase";
}
static void TearDownTestCase()
{
GTEST_LOG_(INFO) << "TearDownCase";
}
void SetUp() override
{
TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
}
void TearDown() override
{
TestHelper::DestroyEcmaVMWithScope(instance, scope);
}
EcmaVM *instance {nullptr};
EcmaHandleScope *scope {nullptr};
JSThread *thread {nullptr};
class TestClass : public base::BuiltinsBase {
public:
static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv)
{
JSThread *thread = argv->GetThread();
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> key = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> map = GetCallArg(argv, 2); // 2 means the secode arg
if (!map->IsUndefined()) {
if (value->IsNumber()) {
JSHandle<JSTaggedValue> newValue(thread, JSTaggedValue(value->GetInt() * 2)); // 2 means mul by 2
JSAPIHashMap::Set(thread, JSHandle<JSAPIHashMap>::Cast(map), key, newValue);
}
}
JSHandle<JSAPIHashMap> jsHashMap(GetThis(argv));
JSAPIHashMap::Set(thread, jsHashMap, key, value);
return JSTaggedValue::Undefined();
}
};
protected:
JSTaggedValue InitializeHashMapConstructor()
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> globalObject = env->GetJSGlobalObject();
JSHandle<JSTaggedValue> key(factory->NewFromASCII("ArkPrivate"));
JSHandle<JSTaggedValue> value =
JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(globalObject), key).GetValue();
auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
objCallInfo->SetFunction(JSTaggedValue::Undefined());
objCallInfo->SetThis(value.GetTaggedValue());
objCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(ContainerTag::HashMap)));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
JSTaggedValue result = ContainersPrivate::Load(objCallInfo);
TestHelper::TearDownFrame(thread, prev);
return result;
}
JSHandle<JSAPIHashMap> CreateJSAPIHashMap()
{
JSHandle<JSFunction> newTarget(thread, InitializeHashMapConstructor());
auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
objCallInfo->SetFunction(newTarget.GetTaggedValue());
objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
objCallInfo->SetThis(JSTaggedValue::Undefined());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
JSTaggedValue result = ContainersHashMap::HashMapConstructor(objCallInfo);
TestHelper::TearDownFrame(thread, prev);
JSHandle<JSAPIHashMap> map(thread, result);
return map;
}
};
// new HashMap()
HWTEST_F_L0(ContainersHashMapTest, HashMapConstructor)
{
InitializeHashMapConstructor();
JSHandle<JSFunction> newTarget(thread, InitializeHashMapConstructor());
auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
objCallInfo->SetFunction(newTarget.GetTaggedValue());
objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
objCallInfo->SetThis(JSTaggedValue::Undefined());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
JSTaggedValue result = ContainersHashMap::HashMapConstructor(objCallInfo);
TestHelper::TearDownFrame(thread, prev);
ASSERT_TRUE(result.IsJSAPIHashMap());
JSHandle<JSAPIHashMap> mapHandle(thread, result);
JSTaggedValue resultProto = JSTaggedValue::GetPrototype(thread, JSHandle<JSTaggedValue>(mapHandle));
JSTaggedValue funcProto = newTarget->GetFunctionPrototype();
ASSERT_EQ(resultProto, funcProto);
int size = mapHandle->GetSize();
ASSERT_EQ(size, 0);
}
// hashmap.set(key, value), hashmap.get(key)
HWTEST_F_L0(ContainersHashMapTest, SetAndGet)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashMap> tMap = CreateJSAPIHashMap();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
callInfo->SetCallArg(1, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue(i));
}
}
// hashmap.hasKey(key), hashmap.hasValue(value)
HWTEST_F_L0(ContainersHashMapTest, HasKeyAndHasValue)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashMap> tMap = CreateJSAPIHashMap();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
callInfo->SetCallArg(1, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
// test hasKey
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::HasKey(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::True());
}
// test hasValue
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::HasValue(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::True());
}
}
// test add string
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
callInfo->SetCallArg(1, value.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), NODE_NUMBERS + i + 1);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
// test hasKey
{
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::HasKey(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::True());
}
// test hasValue
{
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, value.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::HasValue(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::True());
}
}
}
// hashmap.keys(), hashmap.values(), hashmap.entries()
HWTEST_F_L0(ContainersHashMapTest, KeysAndValuesAndEntries)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashMap> tMap = CreateJSAPIHashMap();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
callInfo->SetCallArg(1, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result2 = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result2.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result2.GetTaggedObject())->GetSize(), i + 1);
}
// test keys
auto callInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo1->SetFunction(JSTaggedValue::Undefined());
callInfo1->SetThis(tMap.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo1);
JSHandle<JSTaggedValue> iterKeys(thread, ContainersHashMap::Keys(callInfo1));
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(iterKeys->IsJSAPIHashMapIterator());
JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(iterKeys.GetTaggedValue());
[[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo);
result.Update(JSAPIHashMapIterator::Next(callInfo));
TestHelper::TearDownFrame(thread, prev1);
JSHandle<JSTaggedValue> iterKey = JSIterator::IteratorValue(thread, result);
JSTaggedValue keyFlag = tMap->HasKey(thread, iterKey.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), keyFlag);
}
// test values
callInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo1->SetFunction(JSTaggedValue::Undefined());
callInfo1->SetThis(tMap.GetTaggedValue());
prev = TestHelper::SetupFrame(thread, callInfo1);
JSHandle<JSTaggedValue> iterValues(thread, ContainersHashMap::Values(callInfo1));
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(iterValues->IsJSAPIHashMapIterator());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(iterValues.GetTaggedValue());
[[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, callInfo);
result.Update(JSAPIHashMapIterator::Next(callInfo));
TestHelper::TearDownFrame(thread, prev2);
JSHandle<JSTaggedValue> iterValue = JSIterator::IteratorValue(thread, result);
JSTaggedValue valueFlag = JSAPIHashMap::HasValue(thread, tMap, iterValue);
EXPECT_EQ(JSTaggedValue::True(), valueFlag);
}
// test add string
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
callInfo->SetCallArg(1, value.GetTaggedValue());
[[maybe_unused]] auto prev3 = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result1 = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev3);
EXPECT_TRUE(result1.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result1.GetTaggedObject())->GetSize(), NODE_NUMBERS + i + 1);
}
EXPECT_EQ(tMap->GetSize(), NODE_NUMBERS * 2);
// test keys after add
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
JSTaggedValue keyFlag = tMap->HasKey(thread, key.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), keyFlag);
}
// test values after add
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
JSTaggedValue valueFlag = JSAPIHashMap::HasValue(thread, tMap, value);
EXPECT_EQ(JSTaggedValue::True(), valueFlag);
}
// test entries
{
auto callInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo2->SetFunction(JSTaggedValue::Undefined());
callInfo2->SetThis(tMap.GetTaggedValue());
[[maybe_unused]] auto prev6 = TestHelper::SetupFrame(thread, callInfo2);
JSHandle<JSTaggedValue> iter(thread, ContainersHashMap::Entries(callInfo2));
TestHelper::TearDownFrame(thread, prev6);
EXPECT_TRUE(iter->IsJSAPIHashMapIterator());
JSHandle<JSTaggedValue> first(thread, JSTaggedValue(0));
JSHandle<JSTaggedValue> second(thread, JSTaggedValue(1));
JSMutableHandle<JSTaggedValue> result3(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> entries(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(iter.GetTaggedValue());
[[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo);
result3.Update(JSAPIHashMapIterator::Next(callInfo));
TestHelper::TearDownFrame(thread, prev1);
entries.Update(JSIterator::IteratorValue(thread, result3).GetTaggedValue());
JSHandle<JSTaggedValue> iterKey = JSObject::GetProperty(thread, entries, first).GetValue();
JSTaggedValue keyFlag = tMap->HasKey(thread, iterKey.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), keyFlag);
JSHandle<JSTaggedValue> iterValue = JSObject::GetProperty(thread, entries, second).GetValue();
JSTaggedValue valueFlag = JSAPIHashMap::HasValue(thread, tMap, iterValue);
EXPECT_EQ(JSTaggedValue::True(), valueFlag);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
JSTaggedValue keyFlag = tMap->HasKey(thread, key.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), keyFlag);
JSTaggedValue valueFlag = JSAPIHashMap::HasValue(thread, tMap, value);
EXPECT_EQ(JSTaggedValue::True(), valueFlag);
}
}
}
// hashmap.remove(key)
HWTEST_F_L0(ContainersHashMapTest, Remove)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashMap> tMap = CreateJSAPIHashMap();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
callInfo->SetCallArg(1, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(NODE_NUMBERS / 2));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue rValue = ContainersHashMap::Remove(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(rValue, JSTaggedValue(NODE_NUMBERS / 2));
EXPECT_EQ(tMap->GetSize(), NODE_NUMBERS - 1);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
if (i == (NODE_NUMBERS / 2)) {
EXPECT_EQ(result, JSTaggedValue::Undefined());
} else {
EXPECT_EQ(result, JSTaggedValue(i));
}
}
// test add string
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
callInfo->SetCallArg(1, value.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), NODE_NUMBERS + i);
}
{
std::string iKey = myKey + std::to_string(NODE_NUMBERS / 2);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
std::string iValue = myValue + std::to_string(NODE_NUMBERS / 2);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue rValue = ContainersHashMap::Remove(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(JSTaggedValue::SameValue(rValue, value.GetTaggedValue()));
EXPECT_EQ(tMap->GetSize(), NODE_NUMBERS * 2 - 2);
}
}
// hashmap.setAll(map)
HWTEST_F_L0(ContainersHashMapTest, SetAll)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashMap> sMap = CreateJSAPIHashMap();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(sMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
callInfo->SetCallArg(1, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
JSHandle<JSAPIHashMap> dMap = CreateJSAPIHashMap();
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(dMap.GetTaggedValue());
callInfo->SetCallArg(0, sMap.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
ContainersHashMap::SetAll(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(dMap->GetSize(), NODE_NUMBERS);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(dMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue(i));
}
// test add string
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(sMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
callInfo->SetCallArg(1, value.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), NODE_NUMBERS + i + 1);
}
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(dMap.GetTaggedValue());
callInfo->SetCallArg(0, sMap.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
ContainersHashMap::SetAll(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(dMap->GetSize(), NODE_NUMBERS * 2);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(dMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(JSTaggedValue::SameValue(result, value.GetTaggedValue()));
}
EXPECT_EQ(dMap->GetSize(), 2 * NODE_NUMBERS);
}
// hashmap.clear()
HWTEST_F_L0(ContainersHashMapTest, Clear)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashMap> tMap = CreateJSAPIHashMap();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
callInfo->SetCallArg(1, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
// test clear
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
ContainersHashMap::Clear(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(tMap->GetSize(), (uint32_t)0);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::Undefined());
}
// test add string
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
callInfo->SetCallArg(1, value.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
// test clear
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
ContainersHashMap::Clear(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(tMap->GetSize(), (uint32_t)0);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::Undefined());
}
}
// hashmap.replace(key, value)
HWTEST_F_L0(ContainersHashMapTest, Replace)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashMap> tMap = CreateJSAPIHashMap();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
callInfo->SetCallArg(1, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(NODE_NUMBERS / 2));
callInfo->SetCallArg(1, JSTaggedValue(NODE_NUMBERS));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Replace(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::True());
EXPECT_EQ(tMap->GetSize(), NODE_NUMBERS);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
if (i == (NODE_NUMBERS / 2)) {
EXPECT_EQ(result, JSTaggedValue(NODE_NUMBERS));
} else {
EXPECT_EQ(result, JSTaggedValue(i));
}
}
// test add string
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
callInfo->SetCallArg(1, value.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), NODE_NUMBERS + i + 1);
}
{
std::string iKey = myKey + std::to_string(NODE_NUMBERS / 2);
std::string iValue = myValue + std::to_string(NODE_NUMBERS);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
callInfo->SetCallArg(1, value.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Replace(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::True());
EXPECT_EQ(tMap->GetSize(), NODE_NUMBERS * 2);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
std::string iValue;
if (i == (NODE_NUMBERS / 2)) {
iValue = myValue + std::to_string(NODE_NUMBERS);
} else {
iValue = myValue + std::to_string(i);
}
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(JSTaggedValue::SameValue(result, value.GetTaggedValue()));
}
}
// hashmap.ForEach(callbackfn, this)
HWTEST_F_L0(ContainersHashMapTest, ForEach)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashMap> tMap = CreateJSAPIHashMap();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
callInfo->SetCallArg(1, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
// test foreach function with TestForEachFunc;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSAPIHashMap> dMap = CreateJSAPIHashMap();
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestForEachFunc));
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, func.GetTaggedValue());
callInfo->SetCallArg(1, dMap.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
ContainersHashMap::ForEach(callInfo);
TestHelper::TearDownFrame(thread, prev);
}
EXPECT_EQ(dMap->GetSize(), NODE_NUMBERS);
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue(i * 2));
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(dMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue(i));
}
// test add string
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
callInfo->SetCallArg(1, value.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Set(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashMap());
EXPECT_EQ(JSAPIHashMap::Cast(result.GetTaggedObject())->GetSize(), NODE_NUMBERS + i + 1);
}
EXPECT_EQ(tMap->GetSize(), NODE_NUMBERS * 2);
EXPECT_EQ(dMap->GetSize(), NODE_NUMBERS);
// test foreach function with TestForEachFunc;
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestForEachFunc));
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, func.GetTaggedValue());
callInfo->SetCallArg(1, dMap.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
ContainersHashMap::ForEach(callInfo);
TestHelper::TearDownFrame(thread, prev);
}
EXPECT_EQ(dMap->GetSize(), NODE_NUMBERS * 2);
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue(i * 4)); // 4 means 4 times
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(dMap.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue(i * 2));
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(dMap.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashMap::Get(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, value.GetTaggedValue());
}
}
} // namespace panda::test

View File

@ -0,0 +1,408 @@
/*
* 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 "ecmascript/containers/containers_private.h"
#include "ecmascript/containers/containers_hashset.h"
#include "ecmascript/ecma_runtime_call_info.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/js_api_hashset.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda::ecmascript;
using namespace panda::ecmascript::containers;
namespace panda::test {
class ContainersHashSetTest : public testing::Test {
public:
static void SetUpTestCase()
{
GTEST_LOG_(INFO) << "SetUpTestCase";
}
static void TearDownTestCase()
{
GTEST_LOG_(INFO) << "TearDownCase";
}
void SetUp() override
{
TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
}
void TearDown() override
{
TestHelper::DestroyEcmaVMWithScope(instance, scope);
}
EcmaVM *instance {nullptr};
EcmaHandleScope *scope {nullptr};
JSThread *thread {nullptr};
protected:
JSTaggedValue InitializeHashSetConstructor()
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> globalObject = env->GetJSGlobalObject();
JSHandle<JSTaggedValue> key(factory->NewFromASCII("ArkPrivate"));
JSHandle<JSTaggedValue> value =
JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(globalObject), key).GetValue();
auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
objCallInfo->SetFunction(JSTaggedValue::Undefined());
objCallInfo->SetThis(value.GetTaggedValue());
objCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(ContainerTag::HashSet)));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
JSTaggedValue result = ContainersPrivate::Load(objCallInfo);
TestHelper::TearDownFrame(thread, prev);
return result;
}
JSHandle<JSAPIHashSet> CreateJSAPIHashSet()
{
JSHandle<JSFunction> newTarget(thread, InitializeHashSetConstructor());
auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
objCallInfo->SetFunction(newTarget.GetTaggedValue());
objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
objCallInfo->SetThis(JSTaggedValue::Undefined());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
JSTaggedValue result = ContainersHashSet::HashSetConstructor(objCallInfo);
TestHelper::TearDownFrame(thread, prev);
JSHandle<JSAPIHashSet> map(thread, result);
return map;
}
};
// new HashSet()
HWTEST_F_L0(ContainersHashSetTest, HashSetConstructor)
{
InitializeHashSetConstructor();
JSHandle<JSFunction> newTarget(thread, InitializeHashSetConstructor());
auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
objCallInfo->SetFunction(newTarget.GetTaggedValue());
objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
objCallInfo->SetThis(JSTaggedValue::Undefined());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
JSTaggedValue result = ContainersHashSet::HashSetConstructor(objCallInfo);
TestHelper::TearDownFrame(thread, prev);
ASSERT_TRUE(result.IsJSAPIHashSet());
JSHandle<JSAPIHashSet> setHandle(thread, result);
JSTaggedValue resultProto = JSTaggedValue::GetPrototype(thread, JSHandle<JSTaggedValue>(setHandle));
JSTaggedValue funcProto = newTarget->GetFunctionPrototype();
ASSERT_EQ(resultProto, funcProto);
int size = setHandle->GetSize();
ASSERT_EQ(size, 0);
}
// hashset.add(key), hashset.has(key)
HWTEST_F_L0(ContainersHashSetTest, AddAndHas)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashSet> tSet = CreateJSAPIHashSet();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashSet::Add(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashSet());
EXPECT_EQ(JSAPIHashSet::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashSet::Has(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::True());
}
}
// hashset.remove(key)
HWTEST_F_L0(ContainersHashSetTest, Remove)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashSet> tSet = CreateJSAPIHashSet();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashSet::Add(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashSet());
EXPECT_EQ(JSAPIHashSet::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(NODE_NUMBERS / 2));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue rValue = ContainersHashSet::Remove(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(rValue, JSTaggedValue::True());
EXPECT_EQ(tSet->GetSize(), NODE_NUMBERS - 1);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashSet::Has(callInfo);
TestHelper::TearDownFrame(thread, prev);
if (i == (NODE_NUMBERS / 2)) {
EXPECT_EQ(result, JSTaggedValue::False());
} else {
EXPECT_EQ(result, JSTaggedValue::True());
}
}
// test add string
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashSet::Add(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashSet());
EXPECT_EQ(JSAPIHashSet::Cast(result.GetTaggedObject())->GetSize(), NODE_NUMBERS + i);
}
{
std::string iKey = myKey + std::to_string(NODE_NUMBERS / 2);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue rValue = ContainersHashSet::Remove(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(JSTaggedValue::SameValue(rValue, JSTaggedValue::True()));
EXPECT_EQ(tSet->GetSize(), NODE_NUMBERS * 2 - 2);
}
}
// hashset.clear()
HWTEST_F_L0(ContainersHashSetTest, Clear)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashSet> tSet = CreateJSAPIHashSet();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashSet::Add(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashSet());
EXPECT_EQ(JSAPIHashSet::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
// test clear
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
ContainersHashSet::Clear(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(tSet->GetSize(), (uint32_t)0);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashSet::Has(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::False());
}
// test add string
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashSet::Add(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashSet());
EXPECT_EQ(JSAPIHashSet::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
// test clear
{
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
ContainersHashSet::Clear(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(tSet->GetSize(), (uint32_t)0);
}
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
callInfo->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSTaggedValue result = ContainersHashSet::Has(callInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_EQ(result, JSTaggedValue::False());
}
}
// hashset.values(), hashset.entries()
HWTEST_F_L0(ContainersHashSetTest, KeysAndValuesAndEntries)
{
constexpr uint32_t NODE_NUMBERS = 8;
JSHandle<JSAPIHashSet> tSet = CreateJSAPIHashSet();
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo1->SetFunction(JSTaggedValue::Undefined());
callInfo1->SetThis(tSet.GetTaggedValue());
callInfo1->SetCallArg(0, JSTaggedValue(i));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo1);
JSTaggedValue result = ContainersHashSet::Add(callInfo1);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsJSAPIHashSet());
EXPECT_EQ(JSAPIHashSet::Cast(result.GetTaggedObject())->GetSize(), i + 1);
}
// test values
auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo->SetFunction(JSTaggedValue::Undefined());
callInfo->SetThis(tSet.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
JSHandle<JSTaggedValue> iterValues(thread, ContainersHashSet::Values(callInfo));
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(iterValues->IsJSAPIHashSetIterator());
JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
auto callInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo2->SetFunction(JSTaggedValue::Undefined());
callInfo2->SetThis(iterValues.GetTaggedValue());
[[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo2);
result.Update(JSAPIHashSetIterator::Next(callInfo2));
TestHelper::TearDownFrame(thread, prev1);
JSHandle<JSTaggedValue> iterValue = JSIterator::IteratorValue(thread, result);
JSTaggedValue valueFlag = tSet->Has(thread, iterValue.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), valueFlag);
}
// test add string
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
auto callInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
callInfo3->SetFunction(JSTaggedValue::Undefined());
callInfo3->SetThis(tSet.GetTaggedValue());
callInfo3->SetCallArg(0, key.GetTaggedValue());
[[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, callInfo3);
JSTaggedValue result1 = ContainersHashSet::Add(callInfo3);
TestHelper::TearDownFrame(thread, prev2);
EXPECT_TRUE(result1.IsJSAPIHashSet());
EXPECT_EQ(tSet->GetSize(), NODE_NUMBERS + i + 1);
}
EXPECT_EQ(tSet->GetSize(), NODE_NUMBERS * 2);
{
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
JSTaggedValue keyFlag = tSet->Has(thread, key.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), keyFlag);
}
}
// test entries
{
auto callInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo4->SetFunction(JSTaggedValue::Undefined());
callInfo4->SetThis(tSet.GetTaggedValue());
[[maybe_unused]] auto prev6 = TestHelper::SetupFrame(thread, callInfo4);
JSHandle<JSTaggedValue> iter(thread, ContainersHashSet::Entries(callInfo4));
TestHelper::TearDownFrame(thread, prev6);
EXPECT_TRUE(iter->IsJSAPIHashSetIterator());
JSHandle<JSTaggedValue> first(thread, JSTaggedValue(0));
JSHandle<JSTaggedValue> second(thread, JSTaggedValue(1));
JSMutableHandle<JSTaggedValue> result2(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> entries(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS * 2; i++) {
auto callInfo6 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
callInfo6->SetFunction(JSTaggedValue::Undefined());
callInfo6->SetThis(iter.GetTaggedValue());
[[maybe_unused]] auto prev4 = TestHelper::SetupFrame(thread, callInfo6);
result2.Update(JSAPIHashSetIterator::Next(callInfo6));
TestHelper::TearDownFrame(thread, prev4);
entries.Update(JSIterator::IteratorValue(thread, result2).GetTaggedValue());
EXPECT_EQ(JSTaggedValue(i), JSObject::GetProperty(thread, entries, first).GetValue().GetTaggedValue());
JSHandle<JSTaggedValue> iterValue = JSObject::GetProperty(thread, entries, second).GetValue();
JSTaggedValue valueFlag = tSet->Has(thread, iterValue.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), valueFlag);
}
}
}
}

View File

@ -399,6 +399,14 @@ CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry)
return GetString("ArrayList");
case JSType::JS_API_ARRAYLIST_ITERATOR:
return GetString("ArrayListIterator");
case JSType::JS_API_HASH_MAP:
return GetString("HashMap");
case JSType::JS_API_HASH_SET:
return GetString("HashSet");
case JSType::JS_API_HASHMAP_ITERATOR:
return GetString("HashMapIterator");
case JSType::JS_API_HASHSET_ITERATOR:
return GetString("HashSetIterator");
case JSType::JS_API_LIGHT_WEIGHT_MAP:
return GetString("LightWeightMap");
case JSType::JS_API_LIGHT_WEIGHT_MAP_ITERATOR:

View File

@ -34,6 +34,10 @@
#include "ecmascript/js_api_arraylist_iterator.h"
#include "ecmascript/js_api_deque.h"
#include "ecmascript/js_api_deque_iterator.h"
#include "ecmascript/js_api_hashmap.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/js_api_hashset.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/js_api_lightweightmap.h"
#include "ecmascript/js_api_lightweightmap_iterator.h"
#include "ecmascript/js_api_lightweightset.h"
@ -105,6 +109,7 @@
#include "ecmascript/require/js_cjs_exports.h"
#include "ecmascript/tagged_array.h"
#include "ecmascript/tagged_dictionary.h"
#include "ecmascript/tagged_hash_array.h"
#include "ecmascript/tagged_list.h"
#include "ecmascript/tagged_tree.h"
#include "ecmascript/template_map.h"
@ -336,6 +341,18 @@ CString JSHClass::DumpJSType(JSType type)
return "TSArrayType";
case JSType::JS_API_ARRAYLIST_ITERATOR:
return "JSArraylistIterator";
case JSType::LINKED_NODE:
return "LinkedNode";
case JSType::RB_TREENODE:
return "RBTreeNode";
case JSType::JS_API_HASH_MAP:
return "HashMap";
case JSType::JS_API_HASH_SET:
return "HashSet";
case JSType::JS_API_HASHMAP_ITERATOR:
return "HashMapIterator";
case JSType::JS_API_HASHSET_ITERATOR:
return "HashSetIterator";
case JSType::JS_API_LIGHT_WEIGHT_MAP:
return "LightWeightMap";
case JSType::JS_API_LIGHT_WEIGHT_MAP_ITERATOR:
@ -799,6 +816,21 @@ static void DumpObject(TaggedObject *obj, std::ostream &os)
case JSType::TS_ARRAY_TYPE:
TSArrayType::Cast(obj)->Dump(os);
break;
case JSType::LINKED_NODE:
case JSType::RB_TREENODE:
break;
case JSType::JS_API_HASH_MAP:
JSAPIHashMap::Cast(obj)->Dump(os);
break;
case JSType::JS_API_HASH_SET:
JSAPIHashSet::Cast(obj)->Dump(os);
break;
case JSType::JS_API_HASHMAP_ITERATOR:
JSAPIHashMapIterator::Cast(obj)->Dump(os);
break;
case JSType::JS_API_HASHSET_ITERATOR:
JSAPIHashSetIterator::Cast(obj)->Dump(os);
break;
case JSType::JS_API_TREE_MAP:
JSAPITreeMap::Cast(obj)->Dump(os);
break;
@ -1767,6 +1799,56 @@ void JSAPILightWeightMapIterator::Dump(std::ostream &os) const
JSObject::Dump(os);
}
void JSAPIHashMap::Dump(std::ostream &os) const
{
TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable().GetTaggedObject());
os << " - elements: " << std::dec << GetSize() << "\n";
os << " - table capacity: " << std::dec << static_cast<int>(hashArray->GetLength()) << "\n";
JSObject::Dump(os);
}
void JSAPIHashMap::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &vec) const
{
JSObject::DumpForSnapshot(vec);
}
void JSAPIHashSet::Dump(std::ostream &os) const
{
TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable().GetTaggedObject());
os << " - elements: " << std::dec << GetSize() << "\n";
os << " - table capacity: " << std::dec << static_cast<int>(hashArray->GetLength()) << "\n";
JSObject::Dump(os);
}
void JSAPIHashSet::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &vec) const
{
JSObject::DumpForSnapshot(vec);
}
void JSAPIHashMapIterator::Dump(std::ostream &os) const
{
os << " - nextIndex: " << std::dec << GetNextIndex() << "\n";
os << " - IterationKind: " << std::dec << static_cast<int>(GetIterationKind()) << "\n";
JSObject::Dump(os);
}
void JSAPIHashMapIterator::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &vec) const
{
JSObject::DumpForSnapshot(vec);
}
void JSAPIHashSetIterator::Dump(std::ostream &os) const
{
os << " - nextIndex: " << std::dec << GetNextIndex() << "\n";
os << " - IterationKind: " << std::dec << static_cast<int>(GetIterationKind()) << "\n";
JSObject::Dump(os);
}
void JSAPIHashSetIterator::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &vec) const
{
JSObject::DumpForSnapshot(vec);
}
void JSAPILightWeightSet::Dump(std::ostream &os) const
{
TaggedArray *keys = TaggedArray::Cast(GetHashes().GetTaggedObject());
@ -3459,6 +3541,21 @@ static void DumpObject(TaggedObject *obj,
case JSType::JS_API_ARRAYLIST_ITERATOR:
JSAPIArrayListIterator::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::LINKED_NODE:
case JSType::RB_TREENODE:
return;
case JSType::JS_API_HASH_MAP:
JSAPIHashMap::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::JS_API_HASH_SET:
JSAPIHashSet::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::JS_API_HASHMAP_ITERATOR:
JSAPIHashMapIterator::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::JS_API_HASHSET_ITERATOR:
JSAPIHashSetIterator::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::JS_API_LIGHT_WEIGHT_MAP:
JSAPILightWeightMap::Cast(obj)->DumpForSnapshot(vec);
return;
@ -4259,6 +4356,8 @@ void GlobalEnv::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &
vec.push_back(std::make_pair(CString("Undefined"), globalConst->GetUndefined()));
vec.push_back(std::make_pair(CString("ArrayListFunction"), globalConst->GetArrayListFunction()));
vec.push_back(std::make_pair(CString("ArrayListIteratorPrototype"), globalConst->GetArrayListIteratorPrototype()));
vec.push_back(std::make_pair(CString("HashMapIteratorPrototype"), globalConst->GetHashMapIteratorPrototype()));
vec.push_back(std::make_pair(CString("HashSetIteratorPrototype"), globalConst->GetHashSetIteratorPrototype()));
vec.push_back(
std::make_pair(CString("LightWeightMapIteratorPrototype"), globalConst->GetLightWeightMapIteratorPrototype()));
vec.push_back(

View File

@ -36,6 +36,8 @@
#include "ecmascript/js_api_plain_array_iterator.h"
#include "ecmascript/js_api_queue_iterator.h"
#include "ecmascript/js_api_stack_iterator.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/js_api_tree_map_iterator.h"
#include "ecmascript/js_api_tree_set_iterator.h"
#include "ecmascript/js_api_vector_iterator.h"
@ -65,6 +67,7 @@
#include "ecmascript/js_thread.h"
#include "ecmascript/module/js_module_source_text.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_node.h"
#include "ecmascript/ts_types/ts_type.h"
namespace panda::ecmascript {
@ -219,10 +222,18 @@ void GlobalEnvConstants::InitRootsClass([[maybe_unused]] JSThread *thread, JSHCl
factory->NewEcmaDynClass(dynClassClass, JSAPIStackIterator::SIZE, JSType::JS_API_STACK_ITERATOR));
SetConstant(ConstantIndex::JS_API_VECTOR_ITERATOR_CLASS_INDEX,
factory->NewEcmaDynClass(dynClassClass, JSAPIVectorIterator::SIZE, JSType::JS_API_VECTOR_ITERATOR));
SetConstant(ConstantIndex::JS_API_HASH_MAP_ITERATOR_CLASS_INDEX,
factory->NewEcmaDynClass(dynClassClass, JSAPIHashMapIterator::SIZE, JSType::JS_API_HASHMAP_ITERATOR));
SetConstant(ConstantIndex::JS_API_HASH_SET_ITERATOR_CLASS_INDEX,
factory->NewEcmaDynClass(dynClassClass, JSAPIHashSetIterator::SIZE, JSType::JS_API_HASHSET_ITERATOR));
SetConstant(ConstantIndex::JS_API_TREE_MAP_ITERATOR_CLASS_INDEX,
factory->NewEcmaDynClass(dynClassClass, JSAPITreeMapIterator::SIZE, JSType::JS_API_TREEMAP_ITERATOR));
SetConstant(ConstantIndex::JS_API_TREE_SET_ITERATOR_CLASS_INDEX,
factory->NewEcmaDynClass(dynClassClass, JSAPITreeSetIterator::SIZE, JSType::JS_API_TREESET_ITERATOR));
SetConstant(ConstantIndex::LINKED_NODE_CLASS_INDEX,
factory->NewEcmaDynClass(dynClassClass, LinkedNode::SIZE, JSType::LINKED_NODE));
SetConstant(ConstantIndex::RB_TREENODE_CLASS_INDEX,
factory->NewEcmaDynClass(dynClassClass, RBTreeNode::SIZE, JSType::RB_TREENODE));
SetConstant(ConstantIndex::CELL_RECORD_CLASS_INDEX,
factory->NewEcmaReadOnlyDynClass(dynClassClass, CellRecord::SIZE, JSType::CELL_RECORD));
SetConstant(ConstantIndex::OBJECT_DYN_CLASS_INDEX, factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT));

View File

@ -92,8 +92,12 @@ class JSThread;
V(JSTaggedValue, JSAPIQueueIteratorClass, JS_API_QUEUE_ITERATOR_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, JSAPIStackIteratorClass, JS_API_STACK_ITERATOR_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, JSAPIVectorIteratorClass, JS_API_VECTOR_ITERATOR_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, JSAPIHashMapIteratorClass, JS_API_HASH_MAP_ITERATOR_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, JSAPIHashSetIteratorClass, JS_API_HASH_SET_ITERATOR_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, JSAPITreeMapIteratorClass, JS_API_TREE_MAP_ITERATOR_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, JSAPITreeSetIteratorClass, JS_API_TREE_SET_ITERATOR_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, LinkedNode, LINKED_NODE_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, RBTreeNode, RB_TREENODE_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, JSAPIIteratorFuncDynClass, JS_API_ITERATOR_FUNC_DYN_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, CellRecordClass, CELL_RECORD_CLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ObjectDynClass, OBJECT_DYN_CLASS_INDEX, initial_object_dynclass) \
@ -123,6 +127,8 @@ class JSThread;
/* non ECMA standard jsapi containers iterators */ \
V(JSTaggedValue, ArrayListFunction, ARRAYLIST_FUNCTION_INDEX, ArrayListFunction) \
V(JSTaggedValue, ArrayListIteratorPrototype, ARRAYLIST_ITERATOR_PROTOTYPE_INDEX, ArrayListIterator) \
V(JSTaggedValue, HashMapIteratorPrototype, HASHMAP_ITERATOR_PROTOTYPE_INDEX, HashMapIterator) \
V(JSTaggedValue, HashSetIteratorPrototype, HASHSET_ITERATOR_PROTOTYPE_INDEX, HashSetIterator) \
V(JSTaggedValue, LightWeightMapIteratorPrototype, LIGHTWEIGHTMAP_ITERATOR_PROTOTYPE_INDEX, LightWeightMapIterator) \
V(JSTaggedValue, LightWeightSetIteratorPrototype, LIGHTWEIGHTSET_ITERATOR_PROTOTYPE_INDEX, LightWeightSetIterator) \
V(JSTaggedValue, TreeMapIteratorPrototype, TREEMAP_ITERATOR_PROTOTYPE_INDEX, TreeMapIterator) \

View File

@ -0,0 +1,179 @@
/*
* 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 "js_api_hashmap.h"
#include "ecmascript/tagged_hash_array.h"
#include "ecmascript/tagged_queue.h"
#include "js_handle.h"
#include "object_factory.h"
#include "tagged_node.h"
namespace panda::ecmascript {
JSTaggedValue JSAPIHashMap::IsEmpty()
{
return JSTaggedValue(GetSize() == 0);
}
JSTaggedValue JSAPIHashMap::HasKey(JSThread *thread, JSTaggedValue key)
{
TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable().GetTaggedObject());
int hash = TaggedNode::Hash(key);
return JSTaggedValue(!(hashArray->GetNode(thread, hash, key).IsHole()));
}
JSTaggedValue JSAPIHashMap::HasValue(JSThread *thread, JSHandle<JSAPIHashMap> hashMap,
JSHandle<JSTaggedValue> value)
{
JSHandle<TaggedHashArray> hashArray(thread, hashMap->GetTable());
uint32_t tabLength = hashArray->GetLength();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<TaggedQueue> queue(factory->NewTaggedQueue(0));
JSMutableHandle<TaggedNode> node(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> currentValue(thread, JSTaggedValue::Undefined());
uint32_t index = 0;
while (index < tabLength) {
node.Update(TaggedHashArray::GetCurrentNode(thread, queue, hashArray, index));
if (!node.GetTaggedValue().IsHole()) {
currentValue.Update(node->GetValue());
if (JSTaggedValue::SameValue(value, currentValue)) {
return JSTaggedValue::True();
}
}
}
return JSTaggedValue::False();
}
JSTaggedValue JSAPIHashMap::Replace(JSThread *thread, JSTaggedValue key, JSTaggedValue newValue)
{
TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable().GetTaggedObject());
int hash = TaggedNode::Hash(key);
JSTaggedValue nodeVa = hashArray->GetNode(thread, hash, key);
if (nodeVa.IsHole()) {
return JSTaggedValue::False();
}
if (nodeVa.IsLinkedNode()) {
LinkedNode::Cast(nodeVa.GetTaggedObject())->SetValue(thread, newValue);
} else {
RBTreeNode::Cast(nodeVa.GetTaggedObject())->SetValue(thread, newValue);
}
return JSTaggedValue::True();
}
void JSAPIHashMap::Set(JSThread *thread, JSHandle<JSAPIHashMap> hashMap,
JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value)
{
if (!TaggedHashArray::IsKey(key.GetTaggedValue())) {
THROW_TYPE_ERROR(thread, "the value must be Key of JS");
}
JSHandle<TaggedHashArray> hashArray(thread, hashMap->GetTable());
int hash = TaggedNode::Hash(key.GetTaggedValue());
JSTaggedValue setValue = TaggedHashArray::SetVal(thread, hashArray, hash, key, value);
uint32_t nodeNum = hashMap->GetSize();
if (!setValue.IsUndefined()) {
hashMap->SetSize(++nodeNum);
}
uint32_t tableLength = (hashArray->GetLength()) * TaggedHashArray::DEFAULT_LOAD_FACTOR;
if (nodeNum > tableLength) {
hashArray = TaggedHashArray::Resize(thread, hashArray, hashArray->GetLength());
hashMap->SetTable(thread, hashArray);
}
}
JSTaggedValue JSAPIHashMap::Get(JSThread *thread, JSTaggedValue key)
{
TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable().GetTaggedObject());
int hash = TaggedNode::Hash(key);
JSTaggedValue node = hashArray->GetNode(thread, hash, key);
if (node.IsHole()) {
return JSTaggedValue::Undefined();
} else if (node.IsRBTreeNode()) {
return RBTreeNode::Cast(node.GetTaggedObject())->GetValue();
} else {
return LinkedNode::Cast(node.GetTaggedObject())->GetValue();
}
}
void JSAPIHashMap::SetAll(JSThread *thread, JSHandle<JSAPIHashMap> dst, JSHandle<JSAPIHashMap> src)
{
JSHandle<TaggedHashArray> hashArray(thread, src->GetTable());
uint32_t srcTabLength = hashArray->GetLength();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<TaggedQueue> queue(factory->NewTaggedQueue(0));
JSHandle<TaggedHashArray> thisHashArray(thread, dst->GetTable());
uint32_t nodeNum = dst->GetSize();
uint32_t index = 0;
JSMutableHandle<TaggedNode> node(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> currentKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> currentValue(thread, JSTaggedValue::Undefined());
while (index < srcTabLength) {
node.Update(TaggedHashArray::GetCurrentNode(thread, queue, hashArray, index));
if (!node.GetTaggedValue().IsHole()) {
currentKey.Update(node->GetKey());
currentValue.Update(node->GetValue());
JSTaggedValue setValue =
TaggedHashArray::SetVal(thread, thisHashArray, node->GetHash().GetInt(), currentKey, currentValue);
if (!setValue.IsUndefined()) {
dst->SetSize(++nodeNum);
}
uint32_t tableLength = (thisHashArray->GetLength()) * TaggedHashArray::DEFAULT_LOAD_FACTOR;
if (nodeNum > tableLength) {
thisHashArray = TaggedHashArray::Resize(thread, thisHashArray, thisHashArray->GetLength());
}
}
}
dst->SetTable(thread, thisHashArray);
}
void JSAPIHashMap::Clear(JSThread *thread)
{
TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable().GetTaggedObject());
uint32_t nodeLength = GetSize();
if (nodeLength > 0) {
hashArray->Clear(thread);
SetSize(0);
}
}
JSTaggedValue JSAPIHashMap::Remove(JSThread *thread, JSHandle<JSAPIHashMap> hashMap, JSTaggedValue key)
{
if (!TaggedHashArray::IsKey(key)) {
return JSTaggedValue::Undefined();
}
JSHandle<TaggedHashArray> hashArray(thread, hashMap->GetTable());
uint32_t nodeNum = hashMap->GetSize();
if (nodeNum == 0) {
return JSTaggedValue::Undefined();
}
int hash = TaggedNode::Hash(key);
JSHandle<JSTaggedValue> removeValue(thread, hashArray->RemoveNode(thread, hash, key));
if (removeValue->IsHole()) {
return JSTaggedValue::Undefined();
}
hashMap->SetSize(--nodeNum);
uint32_t length = hashArray->GetLength();
uint32_t index = (length - 1) & hash;
JSTaggedValue rootVa = hashArray->Get(index);
if (rootVa.IsRBTreeNode()) {
uint32_t numTreeNode = RBTreeNode::Count(rootVa);
if (numTreeNode < TaggedHashArray::UNTREEIFY_THRESHOLD) {
JSHandle<RBTreeNode> root(thread, rootVa);
JSHandle<LinkedNode> head = RBTreeNode::Detreeing(thread, root);
hashArray->Set(thread, index, head);
}
}
return removeValue.GetTaggedValue();
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_JS_API_HASHMAP_H
#define ECMASCRIPT_JS_API_HASHMAP_H
#include "ecmascript/js_object-inl.h"
#include "js_object.h"
#include "js_tagged_value-inl.h"
#include "tagged_node.h"
namespace panda::ecmascript {
class JSAPIHashMap : public JSObject {
public:
static JSAPIHashMap *Cast(TaggedObject *object)
{
ASSERT(JSTaggedValue(object).IsJSAPIHashMap());
return static_cast<JSAPIHashMap *>(object);
}
static void Set(JSThread *thread, JSHandle<JSAPIHashMap> hashMap,
JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value);
static JSTaggedValue HasValue(JSThread *thread, JSHandle<JSAPIHashMap> hashMap,
JSHandle<JSTaggedValue> value);
static void SetAll(JSThread *thread, JSHandle<JSAPIHashMap> dst, JSHandle<JSAPIHashMap> src);
static JSTaggedValue Remove(JSThread *thread, JSHandle<JSAPIHashMap> hashMap, JSTaggedValue key);
JSTaggedValue IsEmpty();
JSTaggedValue HasKey(JSThread *thread, JSTaggedValue key);
JSTaggedValue Replace(JSThread *thread, JSTaggedValue key, JSTaggedValue newValue);
void Clear(JSThread *thread);
JSTaggedValue Get(JSThread *thread, JSTaggedValue key);
inline JSTaggedValue GetLength()
{
return JSTaggedValue(GetSize());
}
static constexpr size_t HASHMAP_TABLE_INDEX = JSObject::SIZE;
ACCESSORS(Table, HASHMAP_TABLE_INDEX, HASHMAP_SIZE_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(Size, uint32_t, HASHMAP_SIZE_OFFSET, LAST_OFFSET)
DEFINE_ALIGN_SIZE(LAST_OFFSET);
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, HASHMAP_TABLE_INDEX, HASHMAP_SIZE_OFFSET)
DECL_DUMP()
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_JS_API_HASHMAP_H

View File

@ -0,0 +1,134 @@
/*
* 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 "js_api_hashmap_iterator.h"
#include "builtins/builtins_errors.h"
#include "js_api_hashmap.h"
#include "js_array.h"
#include "object_factory.h"
#include "tagged_node.h"
#include "tagged_queue.h"
namespace panda::ecmascript {
using BuiltinsBase = base::BuiltinsBase;
JSTaggedValue JSAPIHashMapIterator::Next(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> input(BuiltinsBase::GetThis(argv));
if (!input->IsJSAPIHashMapIterator()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not a hashmap iterator", JSTaggedValue::Exception());
}
JSHandle<JSAPIHashMapIterator> iter = JSHandle<JSAPIHashMapIterator>::Cast(input);
JSHandle<JSTaggedValue> iteratedHashMap(thread, iter->GetIteratedHashMap());
// If m is undefined, return CreateIterResultObject(undefined, true).
JSHandle<JSTaggedValue> undefinedHandle = thread->GlobalConstants()->GetHandledUndefined();
if (iteratedHashMap->IsUndefined()) {
return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue();
}
JSHandle<TaggedHashArray> tableArr(thread, JSHandle<JSAPIHashMap>::Cast(iteratedHashMap)->GetTable());
uint32_t tableLength = tableArr->GetLength();
uint32_t index = iter->GetNextIndex();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<TaggedQueue> queue(thread, iter->GetTaggedQueue());
JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
JSMutableHandle<TaggedNode> currentNode(thread, JSTaggedValue::Undefined());
JSMutableHandle<TaggedArray> array(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> keyAndValue(thread, JSTaggedValue::Undefined());
IterationKind itemKind = iter->GetIterationKind();
while (index < tableLength) {
currentNode.Update(JSAPIHashMapIterator::GetCurrentNode(thread, iter, queue, tableArr));
if (!currentNode.GetTaggedValue().IsHole()) {
JSTaggedValue key = currentNode->GetKey();
keyHandle.Update(key);
if (itemKind == IterationKind::KEY) {
return JSIterator::CreateIterResultObject(thread, keyHandle, false).GetTaggedValue();
}
valueHandle.Update(currentNode->GetValue());
if (itemKind == IterationKind::VALUE) {
return JSIterator::CreateIterResultObject(thread, valueHandle, false).GetTaggedValue();
}
// Else
array.Update(factory->NewTaggedArray(2)); // 2 means the length of array
array->Set(thread, 0, keyHandle);
array->Set(thread, 1, valueHandle);
keyAndValue.Update(JSArray::CreateArrayFromList(thread, array));
return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue();
}
index++;
}
// Set [[IteratedMap]] to undefined.
iter->SetIteratedHashMap(thread, JSTaggedValue::Undefined());
return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue();
}
// level traversal
JSHandle<JSTaggedValue> JSAPIHashMapIterator::GetCurrentNode(JSThread *thread,
JSHandle<JSAPIHashMapIterator> &iter,
JSMutableHandle<TaggedQueue> &queue,
JSHandle<TaggedHashArray> &tableArr)
{
JSHandle<JSTaggedValue> rootValue(thread, JSTaggedValue::Undefined());
uint32_t index = iter->GetNextIndex();
if (queue->Empty()) {
rootValue = JSHandle<JSTaggedValue>(thread, tableArr->Get(index));
if (rootValue->IsHole()) {
iter->SetNextIndex(++index);
return rootValue;
}
} else {
rootValue = JSHandle<JSTaggedValue>(thread, queue->Pop(thread));
}
if (rootValue->IsRBTreeNode()) {
JSHandle<RBTreeNode> root = JSHandle<RBTreeNode>::Cast(rootValue);
if (!root->GetLeft().IsHole()) {
JSHandle<JSTaggedValue> left(thread, root->GetLeft());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, left)));
}
if (!root->GetRight().IsHole()) {
JSHandle<JSTaggedValue> right(thread, root->GetRight());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, right)));
}
} else { // linkedNode
JSHandle<LinkedNode> root = JSHandle<LinkedNode>::Cast(rootValue);
if (!root->GetNext().IsHole()) {
JSHandle<JSTaggedValue> next(thread, root->GetNext());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, next)));
}
}
iter->SetTaggedQueue(thread, queue.GetTaggedValue());
if (queue->Empty()) {
iter->SetNextIndex(++index);
}
return rootValue;
}
JSHandle<JSTaggedValue> JSAPIHashMapIterator::CreateHashMapIterator(JSThread *thread,
const JSHandle<JSTaggedValue> &obj,
IterationKind kind)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
if (!obj->IsJSAPIHashMap()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashMap",
thread->GlobalConstants()->GetHandledUndefined());
}
JSHandle<JSTaggedValue> iter(factory->NewJSAPIHashMapIterator(JSHandle<JSAPIHashMap>(obj), kind));
return iter;
}
} // namespace panda::ecmascript

View File

@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_JS_API_HASHMAP_ITERATOR_H
#define ECMASCRIPT_JS_API_HASHMAP_ITERATOR_H
#include "js_iterator.h"
#include "js_object.h"
#include "tagged_hash_array.h"
namespace panda::ecmascript {
class JSAPIHashMapIterator : public JSObject {
public:
static JSAPIHashMapIterator *Cast(TaggedObject *obj)
{
ASSERT(JSTaggedValue(obj).IsJSAPIHashMapIterator());
return static_cast<JSAPIHashMapIterator *>(obj);
}
static JSTaggedValue Next(EcmaRuntimeCallInfo *argv);
static JSHandle<JSTaggedValue> CreateHashMapIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
IterationKind kind);
static JSHandle<JSTaggedValue> GetCurrentNode(JSThread *thread, JSHandle<JSAPIHashMapIterator> &iter,
JSMutableHandle<TaggedQueue> &queue,
JSHandle<TaggedHashArray> &tableArr);
static constexpr size_t ITERATED_HASHMAP_OFFSET = JSObject::SIZE;
ACCESSORS(IteratedHashMap, ITERATED_HASHMAP_OFFSET, TAGGED_QUEUE_OFFSET);
ACCESSORS(TaggedQueue, TAGGED_QUEUE_OFFSET, NEXT_INDEX_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(NextIndex, uint32_t, NEXT_INDEX_OFFSET, BIT_FIELD_OFFSET)
ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET)
DEFINE_ALIGN_SIZE(LAST_OFFSET);
// define BitField
static constexpr size_t ITERATION_KIND_BITS = 2;
FIRST_BIT_FIELD(BitField, IterationKind, IterationKind, ITERATION_KIND_BITS)
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_HASHMAP_OFFSET, NEXT_INDEX_OFFSET)
DECL_DUMP()
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_JS_API_HASHMAP_ITERATOR_H

View File

@ -0,0 +1,94 @@
/*
* 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 "js_api_hashset.h"
#include "ecmascript/tagged_queue.h"
#include "tagged_hash_array.h"
#include "tagged_node.h"
namespace panda::ecmascript {
JSTaggedValue JSAPIHashSet::IsEmpty()
{
return JSTaggedValue(GetSize() == 0);
}
JSTaggedValue JSAPIHashSet::Has(JSThread *thread, JSTaggedValue value)
{
TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable().GetTaggedObject());
int hash = TaggedNode::Hash(value);
return JSTaggedValue(!(hashArray->GetNode(thread, hash, value).IsHole()));
}
void JSAPIHashSet::Add(JSThread *thread, JSHandle<JSAPIHashSet> hashSet, JSHandle<JSTaggedValue> value)
{
if (!TaggedHashArray::IsKey(value.GetTaggedValue())) {
THROW_TYPE_ERROR(thread, "the value must be Key of JS");
}
JSHandle<TaggedHashArray> hashArray(thread, hashSet->GetTable());
int hash = TaggedNode::Hash(value.GetTaggedValue());
JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
JSTaggedValue setValue = TaggedHashArray::SetVal(thread, hashArray, hash, value, nullHandle);
uint32_t nodeNum = hashSet->GetSize();
if (!setValue.IsUndefined()) {
hashSet->SetSize(++nodeNum);
}
uint32_t tableLength = hashArray->GetLength() * TaggedHashArray::DEFAULT_LOAD_FACTOR;
if (nodeNum > tableLength) {
hashArray = TaggedHashArray::Resize(thread, hashArray, hashArray->GetLength());
}
hashSet->SetTable(thread, hashArray);
}
void JSAPIHashSet::Clear(JSThread *thread)
{
TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable().GetTaggedObject());
uint32_t nodeLength = GetSize();
if (nodeLength > 0) {
hashArray->Clear(thread);
SetSize(0);
}
}
JSTaggedValue JSAPIHashSet::Remove(JSThread *thread, JSHandle<JSAPIHashSet> hashSet, JSTaggedValue key)
{
if (!TaggedHashArray::IsKey(key)) {
return JSTaggedValue::False();
}
JSHandle<TaggedHashArray> hashArray(thread, hashSet->GetTable());
uint32_t nodeNum = hashSet->GetSize();
if (nodeNum == 0) {
return JSTaggedValue::False();
}
int hash = TaggedNode::Hash(key);
JSTaggedValue removeValue = hashArray->RemoveNode(thread, hash, key);
if (removeValue.IsHole()) {
return JSTaggedValue::False();
}
hashSet->SetSize(--nodeNum);
uint32_t length = hashArray->GetLength();
uint32_t index = (length - 1) & hash;
JSTaggedValue rootVa = hashArray->Get(index);
if (rootVa.IsRBTreeNode()) {
uint32_t numTreeNode = RBTreeNode::Count(rootVa);
if (numTreeNode < TaggedHashArray::UNTREEIFY_THRESHOLD) {
JSHandle<RBTreeNode> root(thread, rootVa);
JSHandle<LinkedNode> head = RBTreeNode::Detreeing(thread, root);
hashArray->Set(thread, index, head);
}
}
return JSTaggedValue::True();
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_JS_API_HASHSET_H
#define ECMASCRIPT_JS_API_HASHSET_H
#include "ecmascript/js_object-inl.h"
#include "js_tagged_value-inl.h"
namespace panda::ecmascript {
class JSAPIHashSet : public JSObject {
public:
static JSAPIHashSet *Cast(TaggedObject *object)
{
ASSERT(JSTaggedValue(object).IsJSAPIHashSet());
return static_cast<JSAPIHashSet *>(object);
}
static void Add(JSThread *thread, JSHandle<JSAPIHashSet> hashSet, JSHandle<JSTaggedValue> value);
static JSTaggedValue Remove(JSThread *thread, JSHandle<JSAPIHashSet> hashSet, JSTaggedValue key);
JSTaggedValue IsEmpty();
JSTaggedValue Has(JSThread *thread, JSTaggedValue value);
void Clear(JSThread *thread);
inline JSTaggedValue GetLength()
{
return JSTaggedValue(GetSize());
}
static constexpr size_t HASHSET_TABLE_INDEX = JSObject::SIZE;
ACCESSORS(Table, HASHSET_TABLE_INDEX, HASHSET_SIZE_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(Size, uint32_t, HASHSET_SIZE_OFFSET, LAST_OFFSET)
DEFINE_ALIGN_SIZE(LAST_OFFSET);
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, HASHSET_TABLE_INDEX, HASHSET_SIZE_OFFSET)
DECL_DUMP()
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_JS_API_HASHSET_H

View File

@ -0,0 +1,129 @@
/*
* 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 "js_api_hashset_iterator.h"
#include "builtins/builtins_errors.h"
#include "js_api_hashset.h"
#include "js_array.h"
#include "object_factory.h"
#include "tagged_node.h"
#include "tagged_queue.h"
namespace panda::ecmascript {
using BuiltinsBase = base::BuiltinsBase;
JSTaggedValue JSAPIHashSetIterator::Next(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> input(BuiltinsBase::GetThis(argv));
if (!input->IsJSAPIHashSetIterator()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not a hashset iterator", JSTaggedValue::Exception());
}
JSHandle<JSAPIHashSetIterator> iter = JSHandle<JSAPIHashSetIterator>::Cast(input);
JSHandle<JSTaggedValue> iteratedHashSet(thread, iter->GetIteratedHashSet());
JSHandle<JSTaggedValue> undefinedHandle = thread->GlobalConstants()->GetHandledUndefined();
if (iteratedHashSet->IsUndefined()) {
return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue();
}
JSHandle<JSAPIHashSet> hashSet = JSHandle<JSAPIHashSet>::Cast(iteratedHashSet);
JSHandle<TaggedHashArray> tableArr(thread, hashSet->GetTable());
uint32_t tableLength = tableArr->GetLength();
uint32_t tableIndex = iter->GetTableIndex();
uint32_t index = iter->GetNextIndex();
uint32_t size = hashSet->GetSize();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<TaggedQueue> queue(thread, iter->GetTaggedQueue());
JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
JSMutableHandle<TaggedNode> currentNode(thread, JSTaggedValue::Undefined());
JSMutableHandle<TaggedArray> array(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> keyAndValue(thread, JSTaggedValue::Undefined());
IterationKind itemKind = iter->GetIterationKind();
while (tableIndex < tableLength && index < size) {
currentNode.Update(GetCurrentNode(thread, iter, queue, tableArr));
if (!currentNode.GetTaggedValue().IsHole()) {
iter->SetNextIndex(++index);
valueHandle.Update(currentNode->GetKey());
if (itemKind == IterationKind::VALUE) {
return JSIterator::CreateIterResultObject(thread, valueHandle, false).GetTaggedValue();
}
// Else
array.Update(factory->NewTaggedArray(2)); // 2 means the length of array
array->Set(thread, 0, JSTaggedValue(--index));
array->Set(thread, 1, valueHandle);
keyAndValue.Update(JSArray::CreateArrayFromList(thread, array));
return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue();
}
tableIndex++;
}
// Set O.[[IteratedMap]] to undefined.
iter->SetIteratedHashSet(thread, JSTaggedValue::Undefined());
return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue();
}
// level traversal
JSHandle<JSTaggedValue> JSAPIHashSetIterator::GetCurrentNode(JSThread *thread, JSHandle<JSAPIHashSetIterator> &iter,
JSMutableHandle<TaggedQueue> &queue,
JSHandle<TaggedHashArray> &tableArr)
{
JSHandle<JSTaggedValue> rootValue(thread, JSTaggedValue::Undefined());
uint32_t index = iter->GetTableIndex();
if (queue->Empty()) {
rootValue = JSHandle<JSTaggedValue>(thread, tableArr->Get(index));
if (rootValue->IsHole()) {
iter->SetTableIndex(++index);
return rootValue;
}
} else {
rootValue = JSHandle<JSTaggedValue>(thread, queue->Pop(thread));
}
if (rootValue->IsRBTreeNode()) {
JSHandle<RBTreeNode> root = JSHandle<RBTreeNode>::Cast(rootValue);
if (!root->GetLeft().IsHole()) {
JSHandle<JSTaggedValue> left(thread, root->GetLeft());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, left)));
}
if (!root->GetRight().IsHole()) {
JSHandle<JSTaggedValue> right(thread, root->GetRight());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, right)));
}
} else { // linkedNode
JSHandle<LinkedNode> root = JSHandle<LinkedNode>::Cast(rootValue);
if (!root->GetNext().IsHole()) {
JSHandle<JSTaggedValue> next(thread, root->GetNext());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, next)));
}
}
iter->SetTaggedQueue(thread, queue.GetTaggedValue());
if (queue->Empty()) {
iter->SetTableIndex(++index);
}
return rootValue;
}
JSHandle<JSTaggedValue> JSAPIHashSetIterator::CreateHashSetIterator(JSThread *thread,
const JSHandle<JSTaggedValue> &obj,
IterationKind kind)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
if (!obj->IsJSAPIHashSet()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSAPIHashSet",
thread->GlobalConstants()->GetHandledUndefined());
}
JSHandle<JSTaggedValue> iter(factory->NewJSAPIHashSetIterator(JSHandle<JSAPIHashSet>(obj), kind));
return iter;
}
} // namespace panda::ecmascript

View File

@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_JS_API_HASHSET_ITERATOR_H
#define ECMASCRIPT_JS_API_HASHSET_ITERATOR_H
#include "js_iterator.h"
#include "js_object.h"
#include "tagged_hash_array.h"
namespace panda::ecmascript {
class JSAPIHashSetIterator : public JSObject {
public:
static JSAPIHashSetIterator *Cast(TaggedObject *obj)
{
ASSERT(JSTaggedValue(obj).IsJSAPIHashSetIterator());
return static_cast<JSAPIHashSetIterator *>(obj);
}
static JSTaggedValue Next(EcmaRuntimeCallInfo *argv);
static JSHandle<JSTaggedValue> CreateHashSetIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
IterationKind kind);
static JSHandle<JSTaggedValue> GetCurrentNode(JSThread *thread, JSHandle<JSAPIHashSetIterator> &iter,
JSMutableHandle<TaggedQueue> &queue,
JSHandle<TaggedHashArray> &tableArr);
static constexpr size_t ITERATED_HASHSET_OFFSET = JSObject::SIZE;
ACCESSORS(IteratedHashSet, ITERATED_HASHSET_OFFSET, TAGGED_QUEUE_OFFSET);
ACCESSORS(TaggedQueue, TAGGED_QUEUE_OFFSET, NEXT_INDEX_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(NextIndex, uint32_t, NEXT_INDEX_OFFSET, TABLE_INDEX_OFFSET)
ACCESSORS_PRIMITIVE_FIELD(TableIndex, uint32_t, TABLE_INDEX_OFFSET, BIT_FIELD_OFFSET)
ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET)
DEFINE_ALIGN_SIZE(LAST_OFFSET);
// define BitField
static constexpr size_t ITERATION_KIND_BITS = 2;
FIRST_BIT_FIELD(BitField, IterationKind, IterationKind, ITERATION_KIND_BITS)
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_HASHSET_OFFSET, NEXT_INDEX_OFFSET)
DECL_DUMP()
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_JS_API_HASHSET_ITERATOR_H

View File

@ -105,6 +105,8 @@ class ProtoChangeDetails;
JS_REG_EXP_ITERATOR, /* ////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_ARRAYLIST_ITERATOR, /* /////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_DEQUE_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_HASHMAP_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_HASHSET_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_LIGHT_WEIGHT_MAP_ITERATOR, /* //////////////////////////////////////////////////////////////-PADDING */ \
JS_API_LIGHT_WEIGHT_SET_ITERATOR, /* /////////////////////////////////////////////////////////////-PADDING */ \
JS_API_PLAIN_ARRAY_ITERATOR, /* //////////////////////////////////////////////////////////////////-PADDING */ \
@ -143,6 +145,8 @@ class ProtoChangeDetails;
JS_API_VECTOR, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_LINKED_LIST, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_LIST, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_HASH_MAP, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_HASH_SET, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_TREE_MAP, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_TREE_SET, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_API_DEQUE, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \
@ -174,6 +178,8 @@ class ProtoChangeDetails;
BIGINT, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \
TAGGED_ARRAY, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \
TAGGED_DICTIONARY, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \
LINKED_NODE, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \
RB_TREENODE, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \
FREE_OBJECT_WITH_ONE_FIELD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \
FREE_OBJECT_WITH_NONE_FIELD, /* ///////////////////////////////////////////////////////////////////-PADDING */ \
FREE_OBJECT_WITH_TWO_FIELD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \
@ -761,6 +767,35 @@ public:
{
return GetObjectType() == JSType::JS_API_DEQUE;
}
inline bool IsLinkedNode() const
{
return GetObjectType() == JSType::LINKED_NODE;
}
inline bool IsRBTreeNode() const
{
return GetObjectType() == JSType::RB_TREENODE;
}
inline bool IsJSAPIHashMap() const
{
return GetObjectType() == JSType::JS_API_HASH_MAP;
}
inline bool IsJSAPIHashSet() const
{
return GetObjectType() == JSType::JS_API_HASH_SET;
}
inline bool IsJSAPIHashMapIterator() const
{
return GetObjectType() == JSType::JS_API_HASHMAP_ITERATOR;
}
inline bool IsJSAPIHashSetIterator() const
{
return GetObjectType() == JSType::JS_API_HASHSET_ITERATOR;
}
inline bool IsJSAPIQueue() const
{
return GetObjectType() == JSType::JS_API_QUEUE;

View File

@ -458,6 +458,16 @@ inline bool JSTaggedValue::IsTaggedArray() const
return IsHeapObject() && GetTaggedObject()->GetClass()->IsTaggedArray();
}
inline bool JSTaggedValue::IsLinkedNode() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsLinkedNode();
}
inline bool JSTaggedValue::IsRBTreeNode() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsRBTreeNode();
}
inline bool JSTaggedValue::IsNativePointer() const
{
return IsJSNativePointer();
@ -593,6 +603,16 @@ inline bool JSTaggedValue::IsJSAPIArrayList() const
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIArrayList();
}
inline bool JSTaggedValue::IsJSAPIHashMap() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIHashMap();
}
inline bool JSTaggedValue::IsJSAPIHashSet() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIHashSet();
}
inline bool JSTaggedValue::IsJSAPITreeMap() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPITreeMap();
@ -981,6 +1001,16 @@ inline bool JSTaggedValue::IsJSMapIterator() const
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSMapIterator();
}
inline bool JSTaggedValue::IsJSAPIHashMapIterator() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIHashMapIterator();
}
inline bool JSTaggedValue::IsJSAPIHashSetIterator() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPIHashSetIterator();
}
inline bool JSTaggedValue::IsJSAPITreeMapIterator() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAPITreeMapIterator();

View File

@ -971,6 +971,8 @@ bool JSTaggedValue::HasContainerProperty(JSThread *thread, const JSHandle<JSTagg
JSHandle<JSAPILinkedList> linkedList = JSHandle<JSAPILinkedList>::Cast(obj);
return linkedList->Has(key.GetTaggedValue());
}
case JSType::JS_API_HASH_MAP:
case JSType::JS_API_HASH_SET:
case JSType::JS_API_LIGHT_WEIGHT_MAP:
case JSType::JS_API_LIGHT_WEIGHT_SET:
case JSType::JS_API_TREE_MAP:
@ -995,13 +997,6 @@ JSHandle<TaggedArray> JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *threa
case JSType::JS_API_ARRAY_LIST: {
return JSAPIArrayList::OwnKeys(thread, JSHandle<JSAPIArrayList>::Cast(obj));
}
case JSType::JS_API_LIGHT_WEIGHT_MAP: {
return JSHandle<TaggedArray>(thread,
JSHandle<JSAPILightWeightMap>::Cast(obj)->GetValues());
}
case JSType::JS_API_LIGHT_WEIGHT_SET: {
return JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>(obj));
}
case JSType::JS_API_QUEUE: {
return JSAPIQueue::OwnKeys(thread, JSHandle<JSAPIQueue>::Cast(obj));
}
@ -1020,6 +1015,10 @@ JSHandle<TaggedArray> JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *threa
case JSType::JS_API_LINKED_LIST: {
return JSAPILinkedList::OwnKeys(thread, JSHandle<JSAPILinkedList>::Cast(obj));
}
case JSType::JS_API_HASH_MAP:
case JSType::JS_API_HASH_SET:
case JSType::JS_API_LIGHT_WEIGHT_MAP:
case JSType::JS_API_LIGHT_WEIGHT_SET:
case JSType::JS_API_TREE_MAP:
case JSType::JS_API_TREE_SET: {
return JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>(obj));

View File

@ -474,6 +474,8 @@ public:
bool IsString() const;
bool IsStringOrSymbol() const;
bool IsTaggedArray() const;
bool IsLinkedNode() const;
bool IsRBTreeNode() const;
bool IsNativePointer() const;
bool IsJSNativePointer() const;
bool IsBoolean() const;
@ -566,6 +568,10 @@ public:
// non ECMA standard jsapis
bool IsJSAPIArrayList() const;
bool IsJSAPIArrayListIterator() const;
bool IsJSAPIHashMap() const;
bool IsJSAPIHashMapIterator() const;
bool IsJSAPIHashSet() const;
bool IsJSAPIHashSetIterator() const;
bool IsJSAPILightWeightMap() const;
bool IsJSAPILightWeightMapIterator() const;
bool IsJSAPILightWeightSet() const;

View File

@ -30,6 +30,10 @@
#include "ecmascript/js_api_arraylist_iterator.h"
#include "ecmascript/js_api_deque.h"
#include "ecmascript/js_api_deque_iterator.h"
#include "ecmascript/js_api_hashmap.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/js_api_hashset.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/js_api_lightweightmap.h"
#include "ecmascript/js_api_lightweightmap_iterator.h"
#include "ecmascript/js_api_lightweightset.h"
@ -92,6 +96,7 @@
#include "ecmascript/mem/slots.h"
#include "ecmascript/module/js_module_namespace.h"
#include "ecmascript/module/js_module_source_text.h"
#include "ecmascript/tagged_node.h"
#include "ecmascript/ts_types/ts_type.h"
#include "ecmascript/ts_types/ts_type_table.h"
#include "ecmascript/require/js_cjs_module.h"
@ -503,6 +508,24 @@ public:
break;
case JSType::TS_ARRAY_TYPE:
break;
case JSType::RB_TREENODE:
RBTreeNode::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::LINKED_NODE:
LinkedNode::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::JS_API_HASH_MAP:
JSAPIHashMap::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::JS_API_HASH_SET:
JSAPIHashSet::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::JS_API_HASHMAP_ITERATOR:
JSAPIHashMapIterator::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::JS_API_HASHSET_ITERATOR:
JSAPIHashSetIterator::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::JS_API_TREE_MAP:
JSAPITreeMap::Cast(object)->VisitRangeSlot(visitor);
break;

View File

@ -45,6 +45,10 @@
#include "ecmascript/js_api_arraylist_iterator.h"
#include "ecmascript/js_api_deque.h"
#include "ecmascript/js_api_deque_iterator.h"
#include "ecmascript/js_api_hashmap.h"
#include "ecmascript/js_api_hashset.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/js_api_lightweightmap.h"
#include "ecmascript/js_api_lightweightmap_iterator.h"
#include "ecmascript/js_api_lightweightset.h"
@ -113,7 +117,9 @@
#include "ecmascript/record.h"
#include "ecmascript/shared_mm/shared_mm.h"
#include "ecmascript/symbol_table.h"
#include "ecmascript/tagged_hash_array.h"
#include "ecmascript/tagged_list.h"
#include "ecmascript/tagged_node.h"
#include "ecmascript/tagged_tree.h"
#include "ecmascript/template_map.h"
#include "ecmascript/ts_types/ts_obj_layout_info.h"
@ -1126,6 +1132,14 @@ void ObjectFactory::InitializeJSObject(const JSHandle<JSObject> &obj, const JSHa
case JSType::JS_API_ARRAY_LIST:
JSAPIArrayList::Cast(*obj)->SetLength(thread_, JSTaggedValue(0));
break;
case JSType::JS_API_HASH_MAP:
JSAPIHashMap::Cast(*obj)->SetSize(0);
JSAPIHashMap::Cast(*obj)->SetTable(thread_, JSTaggedValue::Undefined());
break;
case JSType::JS_API_HASH_SET:
JSAPIHashSet::Cast(*obj)->SetSize(0);
JSAPIHashSet::Cast(*obj)->SetTable(thread_, JSTaggedValue::Undefined());
break;
case JSType::JS_API_TREE_MAP:
JSAPITreeMap::Cast(*obj)->SetTreeMap(thread_, JSTaggedValue::Undefined());
break;
@ -1250,6 +1264,8 @@ void ObjectFactory::InitializeJSObject(const JSHandle<JSObject> &obj, const JSHa
case JSType::JS_API_LIGHT_WEIGHT_SET_ITERATOR:
case JSType::JS_API_STACK_ITERATOR:
case JSType::JS_API_VECTOR_ITERATOR:
case JSType::JS_API_HASHMAP_ITERATOR:
case JSType::JS_API_HASHSET_ITERATOR:
case JSType::JS_ARRAY_ITERATOR:
case JSType::JS_API_PLAIN_ARRAY_ITERATOR:
break;
@ -2032,6 +2048,46 @@ JSHandle<TaggedArray> ObjectFactory::NewTaggedArray(uint32_t length, JSTaggedVal
return array;
}
JSHandle<TaggedHashArray> ObjectFactory::NewTaggedHashArray(uint32_t length)
{
if (length == 0) {
return JSHandle<TaggedHashArray>::Cast(EmptyArray());
}
size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length);
auto header = heap_->AllocateYoungOrHugeObject(
JSHClass::Cast(thread_->GlobalConstants()->GetArrayClass().GetTaggedObject()), size);
JSHandle<TaggedHashArray> array(thread_, header);
array->InitializeWithSpecialValue(JSTaggedValue::Hole(), length);
return array;
}
JSHandle<LinkedNode> ObjectFactory::NewLinkedNode(int hash, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value,
const JSHandle<LinkedNode> &next)
{
NewObjectHook();
const GlobalEnvConstants *globalConst = thread_->GlobalConstants();
auto header = heap_->AllocateYoungOrHugeObject(
JSHClass::Cast(globalConst->GetLinkedNode().GetTaggedObject()), LinkedNode::SIZE);
JSHandle<LinkedNode> node(thread_, header);
node->InitLinkedNode(thread_, hash, key, value, next);
return node;
}
JSHandle<RBTreeNode> ObjectFactory::NewTreeNode(int hash, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value)
{
NewObjectHook();
const GlobalEnvConstants *globalConst = thread_->GlobalConstants();
auto header = heap_->AllocateYoungOrHugeObject(
JSHClass::Cast(globalConst->GetRBTreeNode().GetTaggedObject()), RBTreeNode::SIZE);
JSHandle<RBTreeNode> treenode(thread_, header);
treenode->InitRBTreeNode(thread_, hash, key, value, 1);
return treenode;
}
JSHandle<TaggedArray> ObjectFactory::NewDictionaryArray(uint32_t length)
{
NewObjectHook();
@ -2442,6 +2498,45 @@ JSHandle<JSMapIterator> ObjectFactory::NewJSMapIterator(const JSHandle<JSMap> &m
return iter;
}
JSHandle<JSAPIHashMapIterator> ObjectFactory::NewJSAPIHashMapIterator(const JSHandle<JSAPIHashMap> &hashMap,
IterationKind kind)
{
NewObjectHook();
JSHandle<JSTaggedValue> proto(thread_, thread_->GlobalConstants()->GetHashMapIteratorPrototype());
const GlobalEnvConstants *globalConst = thread_->GlobalConstants();
JSHandle<JSHClass> dynHandle(globalConst->GetHandledJSAPIHashMapIteratorClass());
dynHandle->SetPrototype(thread_, proto);
JSHandle<JSAPIHashMapIterator> iter(NewJSObject(dynHandle));
iter->GetJSHClass()->SetExtensible(true);
iter->SetIteratedHashMap(thread_, hashMap);
iter->SetNextIndex(0);
iter->SetTaggedQueue(JSTaggedValue::Undefined());
JSHandle<TaggedQueue> queue = NewTaggedQueue(0);
iter->SetTaggedQueue(thread_, queue);
iter->SetIterationKind(kind);
return iter;
}
JSHandle<JSAPIHashSetIterator> ObjectFactory::NewJSAPIHashSetIterator(const JSHandle<JSAPIHashSet> &hashSet,
IterationKind kind)
{
NewObjectHook();
JSHandle<JSTaggedValue> proto(thread_, thread_->GlobalConstants()->GetHashSetIteratorPrototype());
const GlobalEnvConstants *globalConst = thread_->GlobalConstants();
JSHandle<JSHClass> dynHandle(globalConst->GetHandledJSAPIHashSetIteratorClass());
dynHandle->SetPrototype(thread_, proto);
JSHandle<JSAPIHashSetIterator> iter(NewJSObject(dynHandle));
iter->GetJSHClass()->SetExtensible(true);
iter->SetIteratedHashSet(thread_, hashSet);
iter->SetNextIndex(0);
iter->SetTableIndex(0);
iter->SetTaggedQueue(JSTaggedValue::Undefined());
JSHandle<TaggedQueue> queue = NewTaggedQueue(0);
iter->SetTaggedQueue(thread_, queue);
iter->SetIterationKind(kind);
return iter;
}
JSHandle<JSArrayIterator> ObjectFactory::NewJSArrayIterator(const JSHandle<JSObject> &array, IterationKind kind)
{
JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();

View File

@ -108,6 +108,13 @@ class JSAPIArrayList;
class JSAPIArrayListIterator;
class JSAPIDeque;
class JSAPIDequeIterator;
class TaggedHashArray;
class LinkedNode;
class RBTreeNode;
class JSAPIHashMap;
class JSAPIHashSet;
class JSAPIHashMapIterator;
class JSAPIHashSetIterator;
class JSAPILightWeightMap;
class JSAPILightWeightMapIterator;
class JSAPILightWeightSet;
@ -508,6 +515,14 @@ public:
JSHandle<JSAPIStackIterator> NewJSAPIStackIterator(const JSHandle<JSAPIStack> &stack);
JSHandle<JSAPIVector> NewJSAPIVector(uint32_t capacity);
JSHandle<JSAPIVectorIterator> NewJSAPIVectorIterator(const JSHandle<JSAPIVector> &vector);
JSHandle<JSAPIHashMapIterator> NewJSAPIHashMapIterator(const JSHandle<JSAPIHashMap> &hashMap, IterationKind kind);
JSHandle<JSAPIHashSetIterator> NewJSAPIHashSetIterator(const JSHandle<JSAPIHashSet> &hashSet, IterationKind kind);
JSHandle<TaggedHashArray> NewTaggedHashArray(uint32_t length);
JSHandle<LinkedNode> NewLinkedNode(int hash, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value,
const JSHandle<LinkedNode> &next);
JSHandle<RBTreeNode> NewTreeNode(int hash, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value);
// --------------------------------------module--------------------------------------------
JSHandle<ModuleNamespace> NewModuleNamespace();
JSHandle<ImportEntry> NewImportEntry();

View File

@ -654,6 +654,30 @@ namespace panda::ecmascript {
V(PlainArray, SetValueAt) \
V(PlainArray, GetValueAt) \
V(PlainArray, GetSize) \
V(HashMap, Constructor) \
V(HashMap, HasKey) \
V(HashMap, HasValue) \
V(HashMap, Replace) \
V(HashMap, Keys) \
V(HashMap, Values) \
V(HashMap, Entries) \
V(HashMap, ForEach) \
V(HashMap, Set) \
V(HashMap, SetAll) \
V(HashMap, Remove) \
V(HashMap, Get) \
V(HashMap, Clear) \
V(HashMap, GetLength) \
V(HashMap, IsEmpty) \
V(HashSet, Constructor) \
V(HashSet, IsEmpty) \
V(HashSet, Has) \
V(HashSet, Add) \
V(HashSet, Remove) \
V(HashSet, Clear) \
V(HashSet, GetLength) \
V(HashSet, Values) \
V(HashSet, Entries) \
V(TreeMap, Constructor) \
V(TreeMap, HasKey) \
V(TreeMap, HasValue) \

View File

@ -65,6 +65,8 @@
#include "ecmascript/builtins/builtins_weak_set.h"
#include "ecmascript/containers/containers_arraylist.h"
#include "ecmascript/containers/containers_deque.h"
#include "ecmascript/containers/containers_hashmap.h"
#include "ecmascript/containers/containers_hashset.h"
#include "ecmascript/containers/containers_lightweightmap.h"
#include "ecmascript/containers/containers_lightweightset.h"
#include "ecmascript/containers/containers_linked_list.h"
@ -98,6 +100,8 @@
#include "ecmascript/js_map_iterator.h"
#include "ecmascript/js_regexp_iterator.h"
#include "ecmascript/js_set_iterator.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/jspandafile/js_pandafile.h"
#include "ecmascript/mem/heap.h"
@ -166,6 +170,8 @@ using RelativeTimeFormat = builtins::BuiltinsRelativeTimeFormat;
using Collator = builtins::BuiltinsCollator;
using PluralRules = builtins::BuiltinsPluralRules;
using ArrayList = containers::ContainersArrayList;
using HashMap = containers::ContainersHashMap;
using HashSet = containers::ContainersHashSet;
using LightWeightMap = containers::ContainersLightWeightMap;
using LightWeightSet = containers::ContainersLightWeightSet;
using TreeMap = containers::ContainersTreeMap;
@ -714,6 +720,32 @@ static uintptr_t g_nativeTable[] = {
reinterpret_cast<uintptr_t>(ArrayList::Set),
reinterpret_cast<uintptr_t>(ArrayList::GetSize),
reinterpret_cast<uintptr_t>(JSAPIArrayListIterator::Next),
reinterpret_cast<uintptr_t>(HashMap::HashMapConstructor),
reinterpret_cast<uintptr_t>(HashMap::HasKey),
reinterpret_cast<uintptr_t>(HashMap::HasValue),
reinterpret_cast<uintptr_t>(HashMap::Replace),
reinterpret_cast<uintptr_t>(HashMap::Keys),
reinterpret_cast<uintptr_t>(HashMap::Values),
reinterpret_cast<uintptr_t>(HashMap::Entries),
reinterpret_cast<uintptr_t>(HashMap::ForEach),
reinterpret_cast<uintptr_t>(HashMap::Set),
reinterpret_cast<uintptr_t>(HashMap::SetAll),
reinterpret_cast<uintptr_t>(HashMap::Remove),
reinterpret_cast<uintptr_t>(HashMap::Get),
reinterpret_cast<uintptr_t>(HashMap::Clear),
reinterpret_cast<uintptr_t>(HashMap::GetLength),
reinterpret_cast<uintptr_t>(HashMap::IsEmpty),
reinterpret_cast<uintptr_t>(HashSet::HashSetConstructor),
reinterpret_cast<uintptr_t>(HashSet::IsEmpty),
reinterpret_cast<uintptr_t>(HashSet::Has),
reinterpret_cast<uintptr_t>(HashSet::Add),
reinterpret_cast<uintptr_t>(HashSet::Remove),
reinterpret_cast<uintptr_t>(HashSet::Clear),
reinterpret_cast<uintptr_t>(HashSet::GetLength),
reinterpret_cast<uintptr_t>(HashSet::Values),
reinterpret_cast<uintptr_t>(HashSet::Entries),
reinterpret_cast<uintptr_t>(JSAPIHashMapIterator::Next),
reinterpret_cast<uintptr_t>(JSAPIHashSetIterator::Next),
reinterpret_cast<uintptr_t>(LightWeightMap::HasAll),
reinterpret_cast<uintptr_t>(LightWeightMap::HasKey),
reinterpret_cast<uintptr_t>(LightWeightMap::HasValue),

View File

@ -0,0 +1,292 @@
/*
* 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 "tagged_hash_array.h"
#include "ecmascript/base/number_helper.h"
#include "ecmascript/tagged_queue.h"
#include "object_factory.h"
#include "js_handle.h"
namespace panda::ecmascript {
JSTaggedValue TaggedHashArray::Create(const JSThread *thread, uint32_t numberOfElements)
{
ASSERT_PRINT(numberOfElements > 0, "size must be a non-negative integer");
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedHashArray> tableArray = factory->NewTaggedHashArray(numberOfElements);
return tableArray.GetTaggedValue();
}
void TaggedHashArray::Clear(JSThread *thread)
{
for (uint32_t i = 0; i < GetLength(); ++i) {
Set(thread, i, JSTaggedValue::Hole());
}
}
JSTaggedValue TaggedHashArray::GetNode(JSThread *thread, int hash, JSTaggedValue key)
{
uint32_t nodeLength = GetLength();
JSTaggedValue nodeValue = Get(((nodeLength - 1) & hash));
if (nodeValue.IsHole()) {
return JSTaggedValue::Hole();
}
if (nodeValue.IsRBTreeNode()) {
JSHandle<JSTaggedValue> node(thread, nodeValue);
JSHandle<JSTaggedValue> handleKey(thread, key);
return RBTreeNode::GetTreeNode(thread, node, hash, handleKey);
} else {
JSTaggedValue nextNodeVa = nodeValue;
JSTaggedValue findKey = JSTaggedValue::Hole();
while (!nextNodeVa.IsHole()) {
LinkedNode *nextNode = LinkedNode::Cast(nextNodeVa.GetTaggedObject());
findKey = nextNode->GetKey();
if (nextNode->GetHash().GetInt() == hash && (!key.IsHole() && JSTaggedValue::SameValue(key, findKey))) {
return nextNodeVa;
}
nextNodeVa = nextNode->GetNext();
}
}
return JSTaggedValue::Hole();
}
JSHandle<LinkedNode> TaggedHashArray::NewLinkedNode(JSThread *thread, int hash, JSHandle<JSTaggedValue> key,
JSHandle<JSTaggedValue> value)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<LinkedNode> hole(thread, JSTaggedValue::Hole());
return factory->NewLinkedNode(hash, key, value, hole);
}
JSHandle<LinkedNode> TaggedHashArray::CreateLinkedNodeFrom(JSThread *thread, JSHandle<RBTreeNode> treeNode)
{
JSHandle<JSTaggedValue> key(thread, treeNode->GetKey());
JSHandle<JSTaggedValue> value(thread, treeNode->GetValue());
return NewLinkedNode(thread, treeNode->GetHash().GetInt(), key, value);
}
JSHandle<RBTreeNode> TaggedHashArray::NewTreeNode(JSThread *thread, int hash, JSHandle<JSTaggedValue> key,
JSHandle<JSTaggedValue> value)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
return factory->NewTreeNode(hash, key, value);
}
JSHandle<RBTreeNode> TaggedHashArray::CreateTreeNodeFrom(JSThread *thread, JSHandle<LinkedNode> linkedNode)
{
JSHandle<JSTaggedValue> key(thread, linkedNode->GetKey());
JSHandle<JSTaggedValue> value(thread, linkedNode->GetValue());
return NewTreeNode(thread, linkedNode->GetHash().GetInt(), key, value);
}
void TaggedHashArray::TreeingBin(JSThread *thread, const JSHandle<TaggedHashArray> &tab, int hash)
{
uint32_t length = tab->GetLength();
uint32_t index = (length - 1) & hash;
JSTaggedValue nodeVa = tab->Get(index);
if (!nodeVa.IsHole()) {
JSHandle<LinkedNode> node(thread, nodeVa);
JSHandle<RBTreeNode> root = LinkedNode::Treeing(thread, node);
tab->Set(thread, index, root);
}
}
JSHandle<TaggedHashArray> TaggedHashArray::Resize(JSThread *thread, const JSHandle<TaggedHashArray> &oldTab,
uint32_t Capacity)
{
uint32_t oldCapacity = oldTab->GetLength();
if (oldCapacity >= MAXIMUM_CAPACITY) {
return oldTab;
}
uint32_t newCapacity = Capacity << 1;
JSTaggedValue newTabValue = Create(thread, newCapacity);
JSHandle<TaggedHashArray> newTab(thread, newTabValue);
JSMutableHandle<JSTaggedValue> oldValue(thread, JSTaggedValue::Undefined());
for (uint32_t j = 0; j < oldCapacity; ++j) {
oldValue.Update(oldTab->Get(j));
if (oldValue->IsHole()) {
continue;
} else if (oldValue->IsRBTreeNode()) {
RBTreeNode::Divide(thread, newTab, oldValue, j, oldCapacity);
} else if (oldValue->IsLinkedNode()) {
LinkedNode *node = LinkedNode::Cast(oldValue.GetTaggedValue().GetTaggedObject());
if (node->GetNext().IsHole()) {
int newhash = (node->GetHash().GetInt()) & (newCapacity - 1);
newTab->Set(thread, newhash, JSTaggedValue(node));
} else { // preserve order
NodeDisperse(thread, newTab, oldValue.GetTaggedValue(), j, oldCapacity);
}
}
}
return newTab;
}
void TaggedHashArray::NodeDisperse(JSThread *thread, const JSHandle<TaggedHashArray> &newTab,
JSTaggedValue nodeVa, int index, int oldCapacity)
{
JSTaggedValue loHead = JSTaggedValue::Hole();
JSTaggedValue loTail = JSTaggedValue::Hole();
JSTaggedValue hiHead = JSTaggedValue::Hole();
JSTaggedValue hiTail = JSTaggedValue::Hole();
JSTaggedValue next = JSTaggedValue::Hole();
do {
next = LinkedNode::Cast(nodeVa.GetTaggedObject())->GetNext();
if (((LinkedNode::Cast(nodeVa.GetTaggedObject())->GetHash().GetInt()) & oldCapacity) == 0) {
if (loTail.IsHole()) {
loHead = nodeVa;
} else {
LinkedNode::Cast(loTail.GetTaggedObject())->SetNext(thread, nodeVa);
}
loTail = nodeVa;
} else {
if (hiTail.IsHole()) {
hiHead = nodeVa;
} else {
LinkedNode::Cast(hiTail.GetTaggedObject())->SetNext(thread, nodeVa);
}
hiTail = nodeVa;
}
nodeVa = next;
} while (!(next.IsHole()));
if (!loTail.IsHole()) {
LinkedNode::Cast(loTail.GetTaggedObject())->SetNext(thread, JSTaggedValue::Hole());
newTab->Set(thread, index, loHead);
}
if (!hiTail.IsHole()) {
LinkedNode::Cast(hiTail.GetTaggedObject())->SetNext(thread, JSTaggedValue::Hole());
newTab->Set(thread, index + oldCapacity, hiHead);
}
}
JSTaggedValue TaggedHashArray::SetVal(JSThread *thread, JSHandle<TaggedHashArray> table, int hash,
JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value)
{
uint32_t length = table->GetLength();
uint32_t index = (length - 1) & hash;
JSHandle<JSTaggedValue> node(thread, table->Get(index));
if (node->IsHole()) {
JSHandle<LinkedNode> newNode = TaggedHashArray::NewLinkedNode(thread, hash, key, value);
table->Set(thread, index, newNode.GetTaggedValue());
return JSTaggedValue::True();
} else if (node->IsLinkedNode()) {
JSMutableHandle<LinkedNode> root(thread, JSTaggedValue::Undefined());
uint32_t count = 0;
JSMutableHandle<JSTaggedValue> currentKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> nextVa(thread, node.GetTaggedValue());
do {
root.Update(nextVa);
currentKey.Update(root->GetKey());
if (root->GetHash().GetInt() == hash && (!key->IsHole() &&
JSTaggedValue::Equal(thread, key, currentKey))) {
root->SetValue(thread, value.GetTaggedValue());
return JSTaggedValue::Undefined();
}
nextVa.Update(root->GetNext());
count++;
} while (!nextVa->IsHole());
JSHandle<LinkedNode> newNode = TaggedHashArray::NewLinkedNode(thread, hash, key, value);
root->SetNext(thread, newNode);
table->Set(thread, index, node.GetTaggedValue());
if (count >= TREEIFY_THRESHOLD - 1) {
TaggedHashArray::TreeingBin(thread, table, hash);
}
return JSTaggedValue::True();
} else if (node->IsRBTreeNode()) {
JSHandle<RBTreeNode> root = JSHandle<RBTreeNode>::Cast(node);
int curCount = root->GetCount();
JSHandle<RBTreeNode> changeNode = RBTreeNode::Set(thread, root, hash, key, value);
changeNode->SetIsRed(thread, JSTaggedValue(false));
table->Set(thread, index, changeNode);
int updateCount = changeNode->GetCount();
if (curCount == updateCount) {
return JSTaggedValue::Undefined();
}
return JSTaggedValue::True();
}
return JSTaggedValue::Undefined();
}
JSTaggedValue TaggedHashArray::RemoveNode(JSThread *thread, int hash, JSTaggedValue key)
{
uint32_t length = GetLength();
uint32_t index = (length - 1) & hash;
JSTaggedValue node = Get(index);
if (node.IsHole()) {
return JSTaggedValue::Hole();
} else if (node.IsLinkedNode()) {
LinkedNode *head = LinkedNode::Cast(node.GetTaggedObject());
JSTaggedValue newKey = head->GetKey();
if (head->GetHash().GetInt() == hash && (!key.IsHole() && JSTaggedValue::SameValue(key, newKey))) {
Set(thread, index, head->GetNext());
return head->GetValue();
}
JSTaggedValue nodeNextVa = head->GetNext();
LinkedNode *previousNode = head;
while (!nodeNextVa.IsHole()) {
LinkedNode *nodeNext = LinkedNode::Cast(nodeNextVa.GetTaggedObject());
newKey = nodeNext->GetKey();
if (nodeNext->GetHash().GetInt() == hash && (!key.IsHole() && JSTaggedValue::SameValue(key, newKey))) {
previousNode->SetNext(thread, nodeNext->GetNext());
Set(thread, index, node);
return nodeNext->GetValue();
}
previousNode = LinkedNode::Cast(nodeNextVa.GetTaggedObject());
nodeNextVa = nodeNext->GetNext();
}
} else if (node.IsRBTreeNode()) {
JSTaggedValue oldValue = JSTaggedValue::Hole();
JSTaggedValue rootTreeNodeVa = RBTreeNode::Delete(thread, node, hash, key, oldValue);
if (!rootTreeNodeVa.IsHole()) {
Set(thread, index, rootTreeNodeVa);
return oldValue;
}
}
return JSTaggedValue::Hole();
}
JSHandle<JSTaggedValue> TaggedHashArray::GetCurrentNode(JSThread *thread, JSMutableHandle<TaggedQueue> &queue,
const JSHandle<TaggedHashArray> &tableArr, uint32_t &index)
{
JSMutableHandle<JSTaggedValue> rootValue(thread, JSTaggedValue::Undefined());
if (queue->Empty()) {
rootValue.Update(tableArr->Get(index));
if (rootValue->IsHole()) {
++index;
return rootValue;
}
} else {
rootValue.Update(queue->Pop(thread));
}
if (rootValue->IsRBTreeNode()) {
JSHandle<RBTreeNode> root = JSHandle<RBTreeNode>::Cast(rootValue);
if (!root->GetLeft().IsHole()) {
JSHandle<JSTaggedValue> left(thread, root->GetLeft());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, left)));
}
if (!root->GetRight().IsHole()) {
JSHandle<JSTaggedValue> right(thread, root->GetRight());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, right)));
}
} else {
JSHandle<LinkedNode> root = JSHandle<LinkedNode>::Cast(rootValue);
if (!root->GetNext().IsHole()) {
JSHandle<JSTaggedValue> next(thread, root->GetNext());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, next)));
}
}
if (queue->Empty()) {
++index;
}
return rootValue;
}
} // namespace panda::ecmascript

View File

@ -0,0 +1,66 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_TAGGED_HASH_ARRAY_H
#define ECMASCRIPT_TAGGED_HASH_ARRAY_H
#include "ecmascript/js_object-inl.h"
#include "js_api_hashmap.h"
#include "js_object.h"
#include "js_tagged_value-inl.h"
#include "tagged_node.h"
namespace panda::ecmascript {
class TaggedHashArray : public TaggedArray {
public:
static constexpr uint32_t MAXIMUM_CAPACITY = 1 << 30;
static constexpr uint32_t UNTREEIFY_THRESHOLD = 6;
static constexpr uint32_t DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16
static constexpr float DEFAULT_LOAD_FACTOR = 0.75f;
static constexpr uint32_t TREEIFY_THRESHOLD = 8;
static TaggedHashArray *Cast(TaggedObject *object)
{
ASSERT(JSTaggedValue(object).IsTaggedArray());
return static_cast<TaggedHashArray *>(object);
}
static JSTaggedValue Create(const JSThread *thread, uint32_t numberOfElements = DEFAULT_INITIAL_CAPACITY);
static JSTaggedValue SetVal(JSThread *thread, JSHandle<TaggedHashArray> table, int hash,
JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value);
static JSHandle<TaggedHashArray> Resize(JSThread *thread, const JSHandle<TaggedHashArray> &oldTab,
uint32_t Capacity);
static JSHandle<LinkedNode> NewLinkedNode(JSThread *thread, int hash, JSHandle<JSTaggedValue> key,
JSHandle<JSTaggedValue> value);
static JSHandle<LinkedNode> CreateLinkedNodeFrom(JSThread *thread, JSHandle<RBTreeNode> treeNode);
static JSHandle<RBTreeNode> NewTreeNode(JSThread *thread, int hash, JSHandle<JSTaggedValue> key,
JSHandle<JSTaggedValue> value);
static JSHandle<RBTreeNode> CreateTreeNodeFrom(JSThread *thread, JSHandle<LinkedNode> linkedNode);
static JSHandle<JSTaggedValue> GetCurrentNode(JSThread *thread, JSMutableHandle<TaggedQueue> &queue,
const JSHandle<TaggedHashArray> &tableArr, uint32_t &index);
JSTaggedValue GetNode(JSThread *thread, int hash, JSTaggedValue key);
JSTaggedValue RemoveNode(JSThread *thread, int hash, JSTaggedValue key);
void Clear(JSThread *thread);
inline static bool IsKey(JSTaggedValue key)
{
return !key.IsHole();
}
private:
static void TreeingBin(JSThread *thread, const JSHandle<TaggedHashArray> &tab, int hash);
static void NodeDisperse(JSThread *thread, const JSHandle<TaggedHashArray> &newTab,
JSTaggedValue nodeVa, int index, int oldCapacity);
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_TAGGED_HASH_ARRAY_H

422
ecmascript/tagged_node.cpp Normal file
View File

@ -0,0 +1,422 @@
/*
* 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 "tagged_node.h"
#include "ecmascript/tagged_hash_array.h"
#include "ecmascript/tagged_queue.h"
namespace panda::ecmascript {
JSHandle<RBTreeNode> LinkedNode::Treeing(JSThread *thread, const JSHandle<LinkedNode> &head)
{
JSMutableHandle<RBTreeNode> rootNode(thread, JSTaggedValue::Hole());
JSMutableHandle<LinkedNode> next(thread, head);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
while (!next.GetTaggedValue().IsHole()) {
key.Update(next->GetKey());
value.Update(next->GetValue());
rootNode.Update(RBTreeNode::Set(thread, rootNode, next->GetHash().GetInt(), key, value));
rootNode->SetIsRed(thread, JSTaggedValue(false));
next.Update(next->GetNext());
}
return rootNode;
}
void RBTreeNode::InitRBTreeNode(JSThread *thread, int hash, JSHandle<JSTaggedValue> key,
JSHandle<JSTaggedValue> value, int count)
{
this->InitTaggedNode(thread, hash, key, value);
this->SetLeft(thread, JSTaggedValue::Hole());
this->SetRight(thread, JSTaggedValue::Hole());
this->SetIsRed(thread, JSTaggedValue(true));
this->SetCount(count);
}
// number of node in subtree rooted at treeNode; 0 if treeNode is Hole
uint32_t RBTreeNode::Count(JSTaggedValue nodeValue)
{
if (nodeValue.IsHole()) {
return 0;
}
return RBTreeNode::Cast(nodeValue.GetTaggedObject())->GetCount();
}
void RBTreeNode::InOrderTraverse(JSThread *thread, const JSHandle<RBTreeNode> &treeNode,
JSHandle<LinkedNode> &head, JSHandle<LinkedNode> &tail)
{
if (!treeNode.GetTaggedValue().IsHole()) {
JSHandle<RBTreeNode> leftChild = JSHandle<RBTreeNode>(thread, treeNode->GetLeft());
InOrderTraverse(thread, leftChild, head, tail);
JSHandle<LinkedNode> linkedNode = TaggedHashArray::CreateLinkedNodeFrom(thread, treeNode);
if (tail.GetTaggedValue().IsHole()) {
head = linkedNode;
} else {
tail->SetNext(thread, linkedNode.GetTaggedValue());
}
tail = linkedNode;
JSHandle<RBTreeNode> rightChild(thread, treeNode->GetRight());
InOrderTraverse(thread, rightChild, head, tail);
}
}
JSHandle<LinkedNode> RBTreeNode::Detreeing(JSThread *thread, const JSHandle<RBTreeNode> &root)
{
JSHandle<LinkedNode> head(thread, JSTaggedValue::Hole());
JSHandle<LinkedNode> tail(thread, JSTaggedValue::Hole());
InOrderTraverse(thread, root, head, tail);
return head;
}
void RBTreeNode::InOrderTraverse(JSThread *thread, const JSHandle<RBTreeNode> &treeNode, int bit,
LinkedNodeStruct &nodeStruct)
{
if (!treeNode.GetTaggedValue().IsHole()) {
JSHandle<RBTreeNode> leftChild(thread, treeNode->GetLeft());
InOrderTraverse(thread, leftChild, bit, nodeStruct);
JSHandle<LinkedNode> linkedNode = TaggedHashArray::CreateLinkedNodeFrom(thread, treeNode);
// the elements from each bin must either stay at same index,
// or move with a power of two offset in the new table
if ((linkedNode->GetHash().GetInt() & bit) == 0) {
if (nodeStruct.lowerTail.GetTaggedValue().IsHole()) {
nodeStruct.lowerHead = linkedNode;
} else {
nodeStruct.lowerTail->SetNext(thread, linkedNode.GetTaggedValue());
}
nodeStruct.lowerTail = linkedNode;
} else {
if (nodeStruct.higherTail.GetTaggedValue().IsHole()) {
nodeStruct.higherHead = linkedNode;
} else {
nodeStruct.higherTail->SetNext(thread, linkedNode.GetTaggedValue());
}
nodeStruct.higherTail = linkedNode;
}
JSHandle<RBTreeNode> rightChild(thread, treeNode->GetRight());
InOrderTraverse(thread, rightChild, bit, nodeStruct);
}
return;
}
void RBTreeNode::Divide(JSThread *thread, JSHandle<TaggedHashArray> table,
JSHandle<JSTaggedValue> nodeVa, int index, int bit)
{
JSHandle<RBTreeNode> self = JSHandle<RBTreeNode>::Cast(nodeVa);
LinkedNodeStruct nodeStruct {JSHandle<LinkedNode>(thread, JSTaggedValue::Hole()),
JSHandle<LinkedNode>(thread, JSTaggedValue::Hole()),
JSHandle<LinkedNode>(thread, JSTaggedValue::Hole()),
JSHandle<LinkedNode>(thread, JSTaggedValue::Hole())};
InOrderTraverse(thread, self, bit, nodeStruct);
uint32_t loCount = 0;
uint32_t hiCount = 0;
JSMutableHandle<LinkedNode> lowerHead(thread, nodeStruct.lowerHead);
while (!lowerHead.GetTaggedValue().IsHole()) {
loCount++;
lowerHead.Update(lowerHead->GetNext());
}
JSMutableHandle<LinkedNode> higherHead(thread, nodeStruct.higherHead);
while (!higherHead.GetTaggedValue().IsHole()) {
loCount++;
higherHead.Update(higherHead->GetNext());
}
if (!nodeStruct.lowerHead.GetTaggedValue().IsHole()) {
if (loCount >= TaggedHashArray::TREEIFY_THRESHOLD) {
JSHandle<RBTreeNode> loRoot = LinkedNode::Treeing(thread, nodeStruct.lowerHead);
table->Set(thread, index, loRoot);
} else {
table->Set(thread, index, nodeStruct.lowerHead);
}
}
if (!nodeStruct.higherHead.GetTaggedValue().IsHole()) {
if (hiCount >= TaggedHashArray::TREEIFY_THRESHOLD) {
JSHandle<RBTreeNode> hiRoot = LinkedNode::Treeing(thread, nodeStruct.higherHead);
table->Set(thread, index + bit, hiRoot);
} else {
table->Set(thread, index + bit, nodeStruct.higherHead);
}
}
}
int RBTreeNode::Compare(int hash1, JSTaggedValue key1, int hash2, JSTaggedValue key2)
{
ASSERT(!key1.IsHole() && !key2.IsHole());
if (JSTaggedValue::SameValue(key1, key2)) {
return 0;
}
if (hash1 < hash2) {
return -1;
} else {
return 1;
}
}
bool RBTreeNode::IsRed(JSTaggedValue treeNodeValue)
{
if (treeNodeValue.IsHole()) {
return false;
}
RBTreeNode *treeNode = RBTreeNode::Cast(treeNodeValue.GetTaggedObject());
return treeNode->GetIsRed().ToBoolean();
}
// insert the key-value pair in the subtree rooted at treeNode
JSHandle<RBTreeNode> RBTreeNode::Set(JSThread *thread, JSHandle<RBTreeNode> treeNode, int hash,
JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value)
{
if (treeNode.GetTaggedValue().IsHole()) {
treeNode = TaggedHashArray::NewTreeNode(thread, hash, key, value);
return treeNode;
}
JSHandle<JSTaggedValue> treeNodeKey(thread, treeNode->GetKey());
int cmp = Compare(hash, key.GetTaggedValue(), treeNode->GetHash().GetInt(), treeNodeKey.GetTaggedValue());
JSHandle<RBTreeNode> leftChild(thread, treeNode->GetLeft());
JSHandle<RBTreeNode> rightChild(thread, treeNode->GetRight());
if (cmp < 0) {
JSHandle<RBTreeNode> left = Set(thread, leftChild, hash, key, value);
treeNode->SetLeft(thread, left);
} else if (cmp > 0) {
JSHandle<RBTreeNode> right = Set(thread, rightChild, hash, key, value);
treeNode->SetRight(thread, right);
} else {
treeNode->SetValue(thread, value);
}
if (IsRed(treeNode->GetRight()) && !IsRed(treeNode->GetLeft())) {
treeNode = JSHandle<RBTreeNode>(thread, treeNode->RotateLeft(thread));
}
JSTaggedValue leftChildVa = treeNode->GetLeft();
if (!leftChildVa.IsHole()) {
leftChild = JSHandle<RBTreeNode>(thread, leftChildVa);
if (IsRed(treeNode->GetLeft()) && IsRed(leftChild->GetLeft())) {
treeNode = JSHandle<RBTreeNode>(thread, treeNode->RotateRight(thread));
}
}
if (IsRed(treeNode->GetLeft()) && IsRed(treeNode->GetRight())) {
treeNode->FlipColors(thread);
}
// 1 : root count
int count = Count(treeNode->GetLeft()) + Count(treeNode->GetRight()) + 1;
treeNode->SetCount(count);
return treeNode;
}
// make a right-leaning link lean to the left
RBTreeNode *RBTreeNode::RotateLeft(JSThread *thread)
{
ASSERT(!JSTaggedValue(this).IsHole() && IsRed(GetRight()));
RBTreeNode *temp = RBTreeNode::Cast(GetRight().GetTaggedObject());
SetRight(thread, temp->GetLeft());
temp->SetLeft(thread, JSTaggedValue(this));
RBTreeNode *tempLeft = RBTreeNode::Cast(temp->GetLeft().GetTaggedObject());
temp->SetIsRed(thread, tempLeft->GetIsRed());
tempLeft->SetIsRed(thread, JSTaggedValue(true));
temp->SetCount(GetCount());
// 1 : root count
uint32_t count = Count(GetLeft()) + Count(GetRight()) + 1;
SetCount(count);
return temp;
}
// make a left-leaning link lean to the right
RBTreeNode *RBTreeNode::RotateRight(JSThread *thread)
{
ASSERT(!JSTaggedValue(this).IsHole() && IsRed(GetLeft()));
RBTreeNode *temp = RBTreeNode::Cast(GetLeft().GetTaggedObject());
SetLeft(thread, temp->GetRight());
temp->SetRight(thread, JSTaggedValue(this));
RBTreeNode *tempRight = RBTreeNode::Cast(temp->GetRight().GetTaggedObject());
temp->SetIsRed(thread, tempRight->GetIsRed());
tempRight->SetIsRed(thread, JSTaggedValue(true));
temp->SetCount(GetCount());
// 1 : root count
uint32_t count = Count(GetLeft()) + Count(GetRight()) + 1;
SetCount(count);
return temp;
}
// flip the colors of a node and its two children
void RBTreeNode::FlipColors(JSThread *thread)
{
SetIsRed(thread, JSTaggedValue(!GetIsRed().ToBoolean()));
RBTreeNode *leftChild = RBTreeNode::Cast(GetLeft().GetTaggedObject());
leftChild->SetIsRed(thread, JSTaggedValue(!leftChild->GetIsRed().ToBoolean()));
RBTreeNode *rightChild = RBTreeNode::Cast(GetRight().GetTaggedObject());
rightChild->SetIsRed(thread, JSTaggedValue(!rightChild->GetIsRed().ToBoolean()));
}
// restore red-black tree invariant
JSTaggedValue RBTreeNode::Balance(JSThread *thread, RBTreeNode *treeNode)
{
if (IsRed(treeNode->GetRight()) && !IsRed(treeNode->GetLeft())) {
treeNode = treeNode->RotateLeft(thread);
}
JSTaggedValue leftValue = treeNode->GetLeft();
if (!leftValue.IsHole()) {
RBTreeNode *leftChild = RBTreeNode::Cast(leftValue.GetTaggedObject());
if (IsRed(treeNode->GetLeft()) && IsRed(leftChild->GetLeft())) {
treeNode = treeNode->RotateRight(thread);
}
}
if (IsRed(treeNode->GetLeft()) && IsRed(treeNode->GetRight())) {
treeNode->FlipColors(thread);
}
// 1 : root count
int count = Count(treeNode->GetLeft()) + Count(treeNode->GetRight()) + 1;
treeNode->SetCount(count);
return JSTaggedValue(treeNode);
}
RBTreeNode *RBTreeNode::MoveRedLeft(JSThread *thread)
{
RBTreeNode *treeNode = this;
treeNode->FlipColors(thread);
RBTreeNode *rightChild = RBTreeNode::Cast(treeNode->GetRight().GetTaggedObject());
if (IsRed(rightChild->GetLeft())) {
rightChild = rightChild->RotateRight(thread);
treeNode->SetRight(thread, JSTaggedValue(rightChild));
treeNode = treeNode->RotateLeft(thread);
treeNode->FlipColors(thread);
}
return treeNode;
}
RBTreeNode *RBTreeNode::MoveRedRight(JSThread *thread)
{
RBTreeNode *treeNode = this;
treeNode->FlipColors(thread);
RBTreeNode *leftChild = RBTreeNode::Cast(treeNode->GetLeft().GetTaggedObject());
if (IsRed(leftChild->GetLeft())) {
treeNode = treeNode->RotateRight(thread);
treeNode->FlipColors(thread);
}
return treeNode;
}
// delete the key-value pair with the minimum key rooted at treeNode
JSTaggedValue RBTreeNode::DeleteMin(JSThread *thread, RBTreeNode *treeNode)
{
if (treeNode->GetLeft().IsHole()) {
return JSTaggedValue::Hole();
}
RBTreeNode *leftChild = RBTreeNode::Cast(treeNode->GetLeft().GetTaggedObject());
if (!IsRed(treeNode->GetLeft()) && !IsRed(leftChild->GetLeft())) {
treeNode = treeNode->MoveRedLeft(thread);
}
treeNode->SetLeft(thread, DeleteMin(thread, leftChild));
return Balance(thread, treeNode);
}
// delete the key-value pair with the given key rooted at treeNode
JSTaggedValue RBTreeNode::Delete(JSThread *thread, const JSTaggedValue &treeNodeVa, int hash,
const JSTaggedValue &key, JSTaggedValue &oldValue)
{
RBTreeNode *treeNode = RBTreeNode::Cast(treeNodeVa.GetTaggedObject());
JSTaggedValue leftChildVa = treeNode->GetLeft();
JSTaggedValue treeNodeKey = treeNode->GetKey();
int cmp = Compare(hash, key, treeNode->GetHash().GetInt(), treeNodeKey);
if (cmp < 0) {
if (!IsRed(treeNode->GetLeft()) && !IsRed(RBTreeNode::Cast(leftChildVa.GetTaggedObject())->GetLeft())) {
treeNode = treeNode->MoveRedLeft(thread);
}
leftChildVa = treeNode->GetLeft();
JSTaggedValue leftValue = Delete(thread, leftChildVa, hash, key, oldValue);
treeNode->SetLeft(thread, leftValue);
} else {
if (IsRed(treeNode->GetLeft())) {
treeNode = treeNode->RotateRight(thread);
treeNodeKey = treeNode->GetKey();
}
cmp = Compare(hash, key, treeNode->GetHash().GetInt(), treeNodeKey);
if (cmp == 0 && treeNode->GetRight().IsHole()) {
return JSTaggedValue::Hole();
}
JSTaggedValue rightChildVa = treeNode->GetRight();
if (!IsRed(rightChildVa) && !IsRed(RBTreeNode::Cast(rightChildVa.GetTaggedObject())->GetLeft())) {
treeNode = treeNode->MoveRedRight(thread);
treeNodeKey = treeNode->GetKey();
}
cmp = Compare(hash, key, treeNode->GetHash().GetInt(), treeNodeKey);
rightChildVa = treeNode->GetRight();
RBTreeNode *rightChild = RBTreeNode::Cast(rightChildVa.GetTaggedObject());
if (cmp == 0) {
oldValue = treeNode->GetValue();
RBTreeNode *minNode = rightChild->Min();
treeNode->SetKey(thread, minNode->GetKey());
treeNode->SetValue(thread, minNode->GetValue());
treeNode->SetHash(thread, minNode->GetHash());
treeNode->SetRight(thread, DeleteMin(thread, rightChild));
} else {
JSTaggedValue tmpValue = Delete(thread, rightChildVa, rightChild->GetHash().GetInt(), key, oldValue);
treeNode->SetRight(thread, tmpValue);
}
}
return Balance(thread, treeNode);
}
// the smallest key in subtree rooted at treeNode; hole if no such key
RBTreeNode *RBTreeNode::Min()
{
if (GetLeft().IsHole()) {
return this;
} else {
return RBTreeNode::Cast(GetLeft().GetTaggedObject())->Min();
}
}
// node associated with the given key in subtree rooted at treeNode; null if no such key
JSTaggedValue RBTreeNode::GetTreeNode(JSThread *thread, JSHandle<JSTaggedValue> treeNodeVa,
int hash, JSHandle<JSTaggedValue> key)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<TaggedQueue> queue(factory->NewTaggedQueue(0));
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, treeNodeVa)));
JSMutableHandle<RBTreeNode> root(thread, JSTaggedValue::Hole());
JSMutableHandle<JSTaggedValue> currentKey(thread, JSTaggedValue::Hole());
JSMutableHandle<JSTaggedValue> left(thread, JSTaggedValue::Hole());
JSMutableHandle<JSTaggedValue> right(thread, JSTaggedValue::Hole());
while (!queue->Empty()) {
root.Update(queue->Pop(thread));
currentKey.Update(root->GetKey());
if (root->GetHash().GetInt() == hash && (!currentKey->IsHole() && JSTaggedValue::SameValue(key, currentKey))) {
return root.GetTaggedValue();
}
if (!root->GetLeft().IsHole()) {
left.Update(root->GetLeft());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, left)));
}
if (!root->GetRight().IsHole()) {
right.Update(root->GetRight());
queue.Update(JSTaggedValue(TaggedQueue::Push(thread, queue, right)));
}
}
return JSTaggedValue::Hole();
}
} // namespace panda::ecmascript

157
ecmascript/tagged_node.h Normal file
View File

@ -0,0 +1,157 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_TAGGED_NODE_H
#define ECMASCRIPT_TAGGED_NODE_H
#include "ecmascript/js_thread.h"
#include "ecmascript/mem/tagged_object.h"
#include "js_tagged_value-inl.h"
namespace panda::ecmascript {
class TaggedNode : public TaggedObject {
public:
void InitTaggedNode(JSThread *thread, int hash, JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value)
{
this->SetHash(thread, JSTaggedValue(hash));
this->SetKey(thread, key.GetTaggedValue());
this->SetValue(thread, value.GetTaggedValue());
}
static TaggedNode *Cast(TaggedObject *object)
{
return static_cast<TaggedNode *>(object);
}
static constexpr size_t HASH_OFFSET = TaggedObject::TaggedObjectSize();
ACCESSORS(Hash, HASH_OFFSET, KEY_OFFSET);
ACCESSORS(Key, KEY_OFFSET, VALUE_OFFSET);
ACCESSORS(Value, VALUE_OFFSET, DATA_OFFSET);
static constexpr size_t SIZE = DATA_OFFSET;
DECL_VISIT_OBJECT(HASH_OFFSET, SIZE);
void VisitObjects(const EcmaObjectRangeVisitor &visitor)
{
// no field in this object
VisitRangeSlot(visitor);
}
static int Hash(JSTaggedValue key)
{
if (key.IsDouble() && key.GetDouble() == 0.0) {
key = JSTaggedValue(0);
}
if (key.IsSymbol()) {
auto symbolString = JSSymbol::Cast(key.GetTaggedObject());
return static_cast<JSTaggedNumber>(symbolString->GetHashField()).GetInt();
}
if (key.IsString()) {
auto keyString = reinterpret_cast<EcmaString *>(key.GetTaggedObject());
return keyString->GetHashcode();
}
if (key.IsECMAObject()) {
int32_t hash = ECMAObject::Cast(key.GetTaggedObject())->GetHash();
if (hash == 0) {
uint64_t keyValue = key.GetRawData();
hash = GetHash32(reinterpret_cast<uint8_t *>(&keyValue), sizeof(keyValue) / sizeof(uint8_t));
ECMAObject::Cast(key.GetTaggedObject())->SetHash(hash);
}
return hash;
}
// Int, Double, Special and HeapObject(except symbol and string)
uint64_t keyValue = key.GetRawData();
return GetHash32(reinterpret_cast<uint8_t *>(&keyValue), sizeof(keyValue) / sizeof(uint8_t));
}
};
class LinkedNode : public TaggedNode {
public:
void InitLinkedNode(JSThread *thread, int hash, JSHandle<JSTaggedValue> key,
JSHandle<JSTaggedValue> value, JSHandle<LinkedNode> next)
{
this->SetNext(thread, next);
this->InitTaggedNode(thread, hash, key, value);
}
static LinkedNode *Cast(TaggedObject *object)
{
ASSERT(JSTaggedValue(object).IsLinkedNode());
return static_cast<LinkedNode *>(object);
}
static constexpr size_t NEXT_OFFSET = TaggedNode::SIZE;
ACCESSORS(Next, NEXT_OFFSET, DATA_OFFSET);
static constexpr size_t SIZE = DATA_OFFSET;
DECL_VISIT_OBJECT_FOR_JS_OBJECT(TaggedNode, NEXT_OFFSET, SIZE)
static JSHandle<RBTreeNode> Treeing(JSThread *thread, const JSHandle<LinkedNode> &head);
};
class RBTreeNode : public TaggedNode {
public:
struct LinkedNodeStruct {
JSHandle<LinkedNode> lowerHead;
JSHandle<LinkedNode> lowerTail;
JSHandle<LinkedNode> higherHead;
JSHandle<LinkedNode> higherTail;
};
static RBTreeNode *Cast(TaggedObject *object)
{
ASSERT(JSTaggedValue(object).IsRBTreeNode());
return static_cast<RBTreeNode *>(object);
}
static constexpr size_t LEFT_OFFSET = TaggedNode::SIZE;
ACCESSORS(Left, LEFT_OFFSET, RIGHT_OFFSET);
ACCESSORS(Right, RIGHT_OFFSET, ISRED_OFFSET);
ACCESSORS(IsRed, ISRED_OFFSET, COUNT_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(Count, uint32_t, COUNT_OFFSET, LAST_OFFSET)
DEFINE_ALIGN_SIZE(LAST_OFFSET);
DECL_VISIT_OBJECT_FOR_JS_OBJECT(TaggedNode, LEFT_OFFSET, COUNT_OFFSET)
void InitRBTreeNode(JSThread *thread, int hash, JSHandle<JSTaggedValue> key,
JSHandle<JSTaggedValue> value, int count);
static void Divide(JSThread *thread, JSHandle<TaggedHashArray> table,
JSHandle<JSTaggedValue> nodeVa, int index, int bit);
static JSHandle<RBTreeNode> Set(JSThread *thread, JSHandle<RBTreeNode> treeNode, int hash,
JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value);
static JSTaggedValue Delete(JSThread *thread, const JSTaggedValue &treeNodeVa,
int hash, const JSTaggedValue &key, JSTaggedValue &oldValue);
static JSTaggedValue GetTreeNode(JSThread *thread, JSHandle<JSTaggedValue> treeNodeVa, int hash,
JSHandle<JSTaggedValue> key);
static void InOrderTraverse(JSThread *thread, const JSHandle<RBTreeNode> &treeNode,
JSHandle<LinkedNode> &head, JSHandle<LinkedNode> &tail);
static JSHandle<LinkedNode> Detreeing(JSThread *thread, const JSHandle<RBTreeNode> &root);
static uint32_t Count(JSTaggedValue nodeValue);
static int Compare(int hash1, JSTaggedValue key1, int hash2, JSTaggedValue key2);
private:
static void InOrderTraverse(JSThread *thread, const JSHandle<RBTreeNode> &treeNode,
int bit, LinkedNodeStruct &nodeStruct);
static bool IsRed(JSTaggedValue treeNodeValue);
static JSTaggedValue Balance(JSThread *thread, RBTreeNode *treeNode);
static JSTaggedValue DeleteMin(JSThread *thread, RBTreeNode *treeNode);
RBTreeNode *RotateLeft(JSThread *thread);
RBTreeNode *RotateRight(JSThread *thread);
void FlipColors(JSThread *thread);
RBTreeNode *MoveRedLeft(JSThread *thread);
RBTreeNode *MoveRedRight(JSThread *thread);
RBTreeNode *Min();
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_TAGGED_NODE_H

View File

@ -40,6 +40,8 @@ host_unittest_action("EcmaVm_001_Test") {
"js_api_arraylist_test.cpp",
"js_api_deque_iterator_test.cpp",
"js_api_deque_test.cpp",
"js_api_hashmap_test.cpp",
"js_api_hashset_test.cpp",
"js_api_lightweightmap_test.cpp",
"js_api_lightweightset_test.cpp",
"js_api_linked_list_test.cpp",
@ -110,11 +112,13 @@ host_unittest_action("EcmaVm_002_Test") {
"js_verification_test.cpp",
"lexical_env_test.cpp",
"linked_hash_table_test.cpp",
"linked_node_test.cpp",
"mem_controller_test.cpp",
"name_dictionary_test.cpp",
"native_pointer_test.cpp",
"object_factory_test.cpp",
"object_operator_test.cpp",
"rb_tree_node_test.cpp",
"read_only_space_test.cpp",
"symbol_table_test.cpp",
"tagged_tree_test.cpp",

View File

@ -69,6 +69,10 @@
#include "ecmascript/js_generator_object.h"
#include "ecmascript/js_global_object.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_api_hashmap.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/js_api_hashset.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/js_intl.h"
#include "ecmascript/js_locale.h"
#include "ecmascript/js_map.h"
@ -103,7 +107,9 @@
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_array.h"
#include "ecmascript/tagged_dictionary.h"
#include "ecmascript/tagged_hash_array.h"
#include "ecmascript/tagged_list.h"
#include "ecmascript/tagged_node.h"
#include "ecmascript/tagged_tree.h"
#include "ecmascript/template_map.h"
#include "ecmascript/tests/test_helper.h"
@ -187,6 +193,28 @@ static JSHandle<JSSet> NewJSSet(JSThread *thread, ObjectFactory *factory, JSHand
return jsSet;
}
static JSHandle<JSAPIHashMap> NewJSAPIHashMap(JSThread *thread, ObjectFactory *factory)
{
auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> proto = globalEnv->GetObjectFunctionPrototype();
JSHandle<JSHClass> mapClass = factory->NewEcmaDynClass(JSAPIHashMap::SIZE, JSType::JS_API_HASH_MAP, proto);
JSHandle<JSAPIHashMap> jsHashMap = JSHandle<JSAPIHashMap>::Cast(factory->NewJSObjectWithInit(mapClass));
jsHashMap->SetTable(thread, TaggedHashArray::Create(thread));
jsHashMap->SetSize(0);
return jsHashMap;
}
static JSHandle<JSAPIHashSet> NewJSAPIHashSet(JSThread *thread, ObjectFactory *factory)
{
auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> proto = globalEnv->GetObjectFunctionPrototype();
JSHandle<JSHClass> setClass = factory->NewEcmaDynClass(JSAPIHashSet::SIZE, JSType::JS_API_HASH_SET, proto);
JSHandle<JSAPIHashSet> jsHashSet = JSHandle<JSAPIHashSet>::Cast(factory->NewJSObjectWithInit(setClass));
jsHashSet->SetTable(thread, TaggedHashArray::Create(thread));
jsHashSet->SetSize(0);
return jsHashSet;
}
static JSHandle<JSAPITreeMap> NewJSAPITreeMap(JSThread *thread, ObjectFactory *factory)
{
auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
@ -954,6 +982,42 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump)
DUMP_FOR_HANDLE(jsArrayListIter)
break;
}
case JSType::LINKED_NODE: {
CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), LinkedNode::SIZE, 4U);
break;
}
case JSType::RB_TREENODE: {
CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), RBTreeNode::SIZE, 7U);;
break;
}
case JSType::JS_API_HASH_MAP: {
CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIHashMap::SIZE, 2U);
JSHandle<JSAPIHashMap> jsHashMap = NewJSAPIHashMap(thread, factory);
DUMP_FOR_HANDLE(jsHashMap)
break;
}
case JSType::JS_API_HASH_SET: {
CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIHashSet::SIZE, 2U);
JSHandle<JSAPIHashSet> jsHashSet = NewJSAPIHashSet(thread, factory);
DUMP_FOR_HANDLE(jsHashSet)
break;
}
case JSType::JS_API_HASHMAP_ITERATOR: {
CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIHashMapIterator::SIZE, 3U);
JSHandle<JSAPIHashMap> jsHashMap = NewJSAPIHashMap(thread, factory);
JSHandle<JSAPIHashMapIterator> jsHashMapIter =
factory->NewJSAPIHashMapIterator(jsHashMap, IterationKind::KEY);
DUMP_FOR_HANDLE(jsHashMapIter)
break;
}
case JSType::JS_API_HASHSET_ITERATOR: {
CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIHashSetIterator::SIZE, 4U);
JSHandle<JSAPIHashSet> jsHashSet = NewJSAPIHashSet(thread, factory);
JSHandle<JSAPIHashSetIterator> jsHashSetIter =
factory->NewJSAPIHashSetIterator(jsHashSet, IterationKind::KEY);
DUMP_FOR_HANDLE(jsHashSetIter)
break;
}
case JSType::JS_API_LIGHT_WEIGHT_MAP: {
CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPILightWeightMap::SIZE, 4U);
JSHandle<JSAPILightWeightMap> jSAPILightWeightMap = NewJSAPILightWeightMap(thread, factory);

View File

@ -0,0 +1,317 @@
/*
* 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 "ecmascript/containers/containers_private.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_iterator.h"
#include "ecmascript/js_api_hashmap.h"
#include "ecmascript/js_api_hashmap_iterator.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda;
using namespace panda::ecmascript;
namespace panda::test {
class JSAPIHashMapTest : public testing::Test {
public:
static void SetUpTestCase()
{
GTEST_LOG_(INFO) << "SetUpTestCase";
}
static void TearDownTestCase()
{
GTEST_LOG_(INFO) << "TearDownCase";
}
void SetUp() override
{
TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
}
void TearDown() override
{
TestHelper::DestroyEcmaVMWithScope(instance, scope);
}
EcmaVM *instance {nullptr};
ecmascript::EcmaHandleScope *scope {nullptr};
JSThread *thread {nullptr};
protected:
JSAPIHashMap *CreateHashMap()
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> globalObject = env->GetJSGlobalObject();
JSHandle<JSTaggedValue> key(factory->NewFromASCII("ArkPrivate"));
JSHandle<JSTaggedValue> value =
JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(globalObject), key).GetValue();
auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
objCallInfo->SetFunction(JSTaggedValue::Undefined());
objCallInfo->SetThis(value.GetTaggedValue());
objCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(containers::ContainerTag::HashMap)));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
JSTaggedValue result = containers::ContainersPrivate::Load(objCallInfo);
TestHelper::TearDownFrame(thread, prev);
JSHandle<JSTaggedValue> constructor(thread, result);
JSHandle<JSAPIHashMap> map(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
JSTaggedValue hashMapArray = TaggedHashArray::Create(thread);
map->SetTable(thread, hashMapArray);
map->SetSize(0);
return *map;
}
};
HWTEST_F_L0(JSAPIHashMapTest, HashMapCreate)
{
JSAPIHashMap *map = CreateHashMap();
EXPECT_TRUE(map != nullptr);
}
HWTEST_F_L0(JSAPIHashMapTest, HashMapSetAndGet)
{
constexpr uint32_t NODE_NUMBERS = 8;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
// test JSAPIHashMap
JSHandle<JSAPIHashMap> hashMap(thread, CreateHashMap());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
JSAPIHashMap::Set(thread, hashMap, key, value);
}
EXPECT_EQ(hashMap->GetSize(), NODE_NUMBERS);
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test get
JSTaggedValue gValue = hashMap->Get(thread, key.GetTaggedValue());
EXPECT_EQ(gValue, value.GetTaggedValue());
}
}
HWTEST_F_L0(JSAPIHashMapTest, HashMapRemoveAndHas)
{
constexpr uint32_t NODE_NUMBERS = 8;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
// test JSAPIHashMap
JSHandle<JSAPIHashMap> hashMap(thread, CreateHashMap());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
JSAPIHashMap::Set(thread, hashMap, key, value);
}
EXPECT_EQ(hashMap->GetSize(), NODE_NUMBERS);
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
[[maybe_unused]] JSTaggedValue rValue = JSAPIHashMap::Remove(thread, hashMap, key.GetTaggedValue());
}
EXPECT_EQ(hashMap->GetSize(), NODE_NUMBERS / 2);
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test has
JSTaggedValue hasKey = hashMap->HasKey(thread, key.GetTaggedValue());
EXPECT_EQ(hasKey, JSTaggedValue::False());
JSTaggedValue hasValue = JSAPIHashMap::HasValue(thread, hashMap, value);
EXPECT_EQ(hasValue, JSTaggedValue::False());
}
for (uint32_t i = NODE_NUMBERS / 2; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test has
JSTaggedValue hasKey = hashMap->HasKey(thread, key.GetTaggedValue());
EXPECT_EQ(hasKey, JSTaggedValue::True());
JSTaggedValue hasValue = JSAPIHashMap::HasValue(thread, hashMap, value);
EXPECT_EQ(hasValue, JSTaggedValue::True());
}
}
HWTEST_F_L0(JSAPIHashMapTest, HashMapReplaceAndClear)
{
constexpr uint32_t NODE_NUMBERS = 8;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
// test TaggedHashMap
JSHandle<JSAPIHashMap> hashMap(thread, CreateHashMap());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
JSAPIHashMap::Set(thread, hashMap, key, value);
}
EXPECT_EQ(hashMap->GetSize(), NODE_NUMBERS);
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i + 1);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test replace
JSTaggedValue success = hashMap->Replace(thread, key.GetTaggedValue(), value.GetTaggedValue());
EXPECT_EQ(success, JSTaggedValue::True());
}
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i + 1);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test get
JSTaggedValue gValue = hashMap->Get(thread, key.GetTaggedValue());
EXPECT_EQ(gValue, value.GetTaggedValue());
}
for (uint32_t i = NODE_NUMBERS / 2; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test get
JSTaggedValue gValue = hashMap->Get(thread, key.GetTaggedValue());
EXPECT_EQ(gValue, value.GetTaggedValue());
}
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
std::string iKey = myKey + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
[[maybe_unused]] JSTaggedValue rValue = JSAPIHashMap::Remove(thread, hashMap, key.GetTaggedValue());
}
hashMap->Clear(thread);
EXPECT_EQ(hashMap->GetSize(), (uint32_t)0);
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test get
JSTaggedValue gValue = hashMap->Get(thread, key.GetTaggedValue());
EXPECT_EQ(gValue, JSTaggedValue::Undefined());
// test has
JSTaggedValue hasKey = hashMap->HasKey(thread, key.GetTaggedValue());
EXPECT_EQ(hasKey, JSTaggedValue::False());
JSTaggedValue hasValue = JSAPIHashMap::HasValue(thread, hashMap, value);
EXPECT_EQ(hasValue, JSTaggedValue::False());
}
}
HWTEST_F_L0(JSAPIHashMapTest, JSAPIHashMapIterator)
{
constexpr uint32_t NODE_NUMBERS = 8;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSAPIHashMap> hashMap(thread, CreateHashMap());
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
key.Update(JSTaggedValue(i));
value.Update(JSTaggedValue(i));
JSAPIHashMap::Set(thread, hashMap, key, value);
}
// test key or value
JSHandle<JSTaggedValue> keyIter(factory->NewJSAPIHashMapIterator(hashMap, IterationKind::KEY));
JSHandle<JSTaggedValue> valueIter(factory->NewJSAPIHashMapIterator(hashMap, IterationKind::VALUE));
JSMutableHandle<JSTaggedValue> keyIterResult(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> valueIterResult(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
keyIterResult.Update(JSIterator::IteratorStep(thread, keyIter).GetTaggedValue());
valueIterResult.Update(JSIterator::IteratorStep(thread, valueIter).GetTaggedValue());
JSHandle<JSTaggedValue> tmpIterKey = JSIterator::IteratorValue(thread, keyIterResult);
JSTaggedValue iterKeyFlag = hashMap->HasKey(thread, tmpIterKey.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), iterKeyFlag);
JSHandle<JSTaggedValue> tmpIterValue = JSIterator::IteratorValue(thread, valueIterResult);
JSTaggedValue iterValueFlag = JSAPIHashMap::HasValue(thread, hashMap, tmpIterValue);
EXPECT_EQ(JSTaggedValue::True(), iterValueFlag);
}
// test key and value
JSHandle<JSTaggedValue> indexKey(thread, JSTaggedValue(0));
JSHandle<JSTaggedValue> elementKey(thread, JSTaggedValue(1));
JSHandle<JSTaggedValue> iter(factory->NewJSAPIHashMapIterator(hashMap, IterationKind::KEY_AND_VALUE));
JSMutableHandle<JSTaggedValue> iterResult(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
iterResult.Update(JSIterator::IteratorStep(thread, iter).GetTaggedValue());
result.Update(JSIterator::IteratorValue(thread, iterResult).GetTaggedValue());
JSHandle<JSTaggedValue> tmpKey = JSObject::GetProperty(thread, result, indexKey).GetValue();
JSTaggedValue iterKeyFlag = hashMap->HasKey(thread, tmpKey.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), iterKeyFlag);
JSHandle<JSTaggedValue> tmpValue = JSObject::GetProperty(thread, result, elementKey).GetValue();
JSTaggedValue iterValueFlag = JSAPIHashMap::HasValue(thread, hashMap, tmpValue);
EXPECT_EQ(JSTaggedValue::True(), iterValueFlag);
}
// test delete
key.Update(JSTaggedValue(NODE_NUMBERS / 2));
JSTaggedValue rValue = JSAPIHashMap::Remove(thread, hashMap, key.GetTaggedValue());
EXPECT_EQ(rValue, JSTaggedValue(NODE_NUMBERS / 2));
for (uint32_t i = NODE_NUMBERS / 2 + 1; i < NODE_NUMBERS; i++) {
keyIterResult.Update(JSIterator::IteratorStep(thread, keyIter).GetTaggedValue());
valueIterResult.Update(JSIterator::IteratorStep(thread, valueIter).GetTaggedValue());
JSHandle<JSTaggedValue> tmpIterKey = JSIterator::IteratorValue(thread, keyIterResult);
JSTaggedValue iterKeyFlag = hashMap->HasKey(thread, tmpIterKey.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), iterKeyFlag);
JSHandle<JSTaggedValue> tmpIterValue = JSIterator::IteratorValue(thread, valueIterResult);
JSTaggedValue iterValueFlag = JSAPIHashMap::HasValue(thread, hashMap, tmpIterValue);
EXPECT_EQ(JSTaggedValue::True(), iterValueFlag);
}
// test set
key.Update(JSTaggedValue(NODE_NUMBERS));
JSAPIHashMap::Set(thread, hashMap, key, key);
keyIterResult.Update(JSIterator::IteratorStep(thread, keyIter).GetTaggedValue());
JSHandle<JSTaggedValue> tmpIterKey = JSIterator::IteratorValue(thread, keyIterResult);
JSTaggedValue iterKeyFlag = hashMap->HasKey(thread, tmpIterKey.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), iterKeyFlag);
EXPECT_EQ(hashMap->GetSize(), NODE_NUMBERS);
keyIterResult.Update(JSIterator::IteratorStep(thread, keyIter).GetTaggedValue());
EXPECT_EQ(JSTaggedValue::False(), keyIterResult.GetTaggedValue());
}
} // namespace panda::test

View File

@ -0,0 +1,224 @@
/*
* 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 "ecmascript/containers/containers_private.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_iterator.h"
#include "ecmascript/js_api_hashset.h"
#include "ecmascript/js_api_hashset_iterator.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda;
using namespace panda::ecmascript;
namespace panda::test {
class JSAPIHashSetTest : public testing::Test {
public:
static void SetUpTestCase()
{
GTEST_LOG_(INFO) << "SetUpTestCase";
}
static void TearDownTestCase()
{
GTEST_LOG_(INFO) << "TearDownCase";
}
void SetUp() override
{
TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
}
void TearDown() override
{
TestHelper::DestroyEcmaVMWithScope(instance, scope);
}
EcmaVM *instance {nullptr};
ecmascript::EcmaHandleScope *scope {nullptr};
JSThread *thread {nullptr};
protected:
JSAPIHashSet *CreateHashSet()
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> globalObject = env->GetJSGlobalObject();
JSHandle<JSTaggedValue> key(factory->NewFromASCII("ArkPrivate"));
JSHandle<JSTaggedValue> value =
JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(globalObject), key).GetValue();
auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
objCallInfo->SetFunction(JSTaggedValue::Undefined());
objCallInfo->SetThis(value.GetTaggedValue());
objCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(containers::ContainerTag::HashSet)));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
JSTaggedValue result = containers::ContainersPrivate::Load(objCallInfo);
TestHelper::TearDownFrame(thread, prev);
JSHandle<JSTaggedValue> constructor(thread, result);
JSHandle<JSAPIHashSet> set(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
JSTaggedValue hashSetArray = TaggedHashArray::Create(thread);
set->SetTable(thread, hashSetArray);
set->SetSize(0);
return *set;
}
};
HWTEST_F_L0(JSAPIHashSetTest, HashSetCreate)
{
JSAPIHashSet *set = CreateHashSet();
EXPECT_TRUE(set != nullptr);
}
HWTEST_F_L0(JSAPIHashSetTest, HashSetAddAndHas)
{
constexpr uint32_t NODE_NUMBERS = 8;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
// test JSAPIHashSet
JSHandle<JSAPIHashSet> hashSet(thread, CreateHashSet());
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
JSAPIHashSet::Add(thread, hashSet, value);
}
EXPECT_EQ(hashSet->GetSize(), NODE_NUMBERS);
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test has
JSTaggedValue bHas = hashSet->Has(thread, value.GetTaggedValue());
EXPECT_EQ(bHas, JSTaggedValue::True());
}
}
HWTEST_F_L0(JSAPIHashSetTest, HashSetRemoveAndHas)
{
constexpr uint32_t NODE_NUMBERS = 8;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
// test JSAPIHashSet
JSHandle<JSAPIHashSet> hashSet(thread, CreateHashSet());
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
JSAPIHashSet::Add(thread, hashSet, value);
}
EXPECT_EQ(hashSet->GetSize(), NODE_NUMBERS);
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
[[maybe_unused]] JSTaggedValue rvalue = JSAPIHashSet::Remove(thread, hashSet, value.GetTaggedValue());
}
EXPECT_EQ(hashSet->GetSize(), NODE_NUMBERS / 2);
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test has
JSTaggedValue has = hashSet->Has(thread, value.GetTaggedValue());
EXPECT_EQ(has, JSTaggedValue::False());
}
for (uint32_t i = NODE_NUMBERS / 2; i < NODE_NUMBERS; i++) {
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
// test has
JSTaggedValue has = hashSet->Has(thread, value.GetTaggedValue());
EXPECT_EQ(has, JSTaggedValue::True());
}
}
HWTEST_F_L0(JSAPIHashSetTest, HashSetClearAddHas)
{
constexpr uint32_t NODE_NUMBERS = 8;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
// test JSAPIHashSet
JSHandle<JSAPIHashSet> hashSet(thread, CreateHashSet());
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iValue = myValue + std::to_string(i);
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
JSAPIHashSet::Add(thread, hashSet, value);
}
EXPECT_EQ(hashSet->GetSize(), NODE_NUMBERS);
hashSet->Clear(thread);
JSTaggedValue isEmpty = hashSet->IsEmpty();
EXPECT_EQ(isEmpty, JSTaggedValue::True());
}
HWTEST_F_L0(JSAPIHashSetTest, JSAPIHashSetIterator)
{
constexpr uint32_t NODE_NUMBERS = 8;
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSAPIHashSet> hashSet(thread, CreateHashSet());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
value.Update(JSTaggedValue(i));
JSAPIHashSet::Add(thread, hashSet, value);
}
// test value
JSHandle<JSTaggedValue> valueIter(factory->NewJSAPIHashSetIterator(hashSet, IterationKind::VALUE));
JSMutableHandle<JSTaggedValue> valueIterResult(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
valueIterResult.Update(JSIterator::IteratorStep(thread, valueIter).GetTaggedValue());
JSHandle<JSTaggedValue> tmpIterValue = JSIterator::IteratorValue(thread, valueIterResult);
JSTaggedValue iterValueFlag = hashSet->Has(thread, tmpIterValue.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), iterValueFlag);
}
// test end
valueIterResult.Update(JSIterator::IteratorStep(thread, valueIter).GetTaggedValue());
EXPECT_EQ(JSTaggedValue::False(), valueIterResult.GetTaggedValue());
// test key and value
JSHandle<JSTaggedValue> indexKey(thread, JSTaggedValue(0));
JSHandle<JSTaggedValue> elementKey(thread, JSTaggedValue(1));
JSHandle<JSTaggedValue> iter(factory->NewJSAPIHashSetIterator(hashSet, IterationKind::KEY_AND_VALUE));
JSMutableHandle<JSTaggedValue> iterResult(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
iterResult.Update(JSIterator::IteratorStep(thread, iter).GetTaggedValue());
result.Update(JSIterator::IteratorValue(thread, iterResult).GetTaggedValue());
EXPECT_EQ(JSTaggedValue(i), JSObject::GetProperty(thread, result, indexKey).GetValue().GetTaggedValue());
JSHandle<JSTaggedValue> tmpValue = JSObject::GetProperty(thread, result, elementKey).GetValue();
JSTaggedValue iterValueFlag = hashSet->Has(thread, tmpValue.GetTaggedValue());
EXPECT_EQ(JSTaggedValue::True(), iterValueFlag);
}
}
} // namespace panda::test

View File

@ -0,0 +1,102 @@
/*
* 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 <string>
#include "ecmascript/global_env.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_node.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda;
using namespace panda::ecmascript;
namespace panda::test {
class LinkedNodeTest : public testing::Test {
public:
static void SetUpTestCase()
{
GTEST_LOG_(INFO) << "SetUpTestCase";
}
static void TearDownTestCase()
{
GTEST_LOG_(INFO) << "TearDownCase";
}
void SetUp() override
{
TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
}
void TearDown() override
{
TestHelper::DestroyEcmaVMWithScope(instance, scope);
}
EcmaVM *instance {nullptr};
EcmaHandleScope *scope {nullptr};
JSThread *thread {nullptr};
JSHandle<GlobalEnv> GetGlobalEnv()
{
EcmaVM *ecma = thread->GetEcmaVM();
return ecma->GetGlobalEnv();
}
uint32_t NODE_NUMBERS = 8;
protected:
JSHandle<LinkedNode> CreateLinkedList()
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
std::string myKey("mykey");
std::string myValue("myvalue");
JSHandle<LinkedNode> head(thread, JSTaggedValue::Hole());
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
key.Update(factory->NewFromStdString(iKey).GetTaggedValue());
value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
int hash = TaggedNode::Hash(key.GetTaggedValue());
head = factory->NewLinkedNode(hash, key, value, head);
}
return head;
}
};
HWTEST_F_L0(LinkedNodeTest, LinkedNodeCreate)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
std::string k("testKey");
std::string v("testValue");
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(k).GetTaggedValue());
JSHandle<JSTaggedValue> value(thread, factory->NewFromStdString(v).GetTaggedValue());
int hash = TaggedNode::Hash(factory->NewFromStdString(k).GetTaggedValue());
JSHandle<LinkedNode> hole(thread, JSTaggedValue::Hole());
JSHandle<LinkedNode> newNode = factory->NewLinkedNode(hash, key, value, hole);
EXPECT_TRUE(!newNode.GetTaggedValue().IsHole());
EXPECT_TRUE(newNode.GetTaggedValue().IsLinkedNode());
}
HWTEST_F_L0(LinkedNodeTest, Treeify)
{
JSHandle<LinkedNode> head = CreateLinkedList();
JSHandle<RBTreeNode> root = LinkedNode::Treeing(thread, head);
EXPECT_EQ(root->GetCount(), NODE_NUMBERS);
}
}

View File

@ -0,0 +1,257 @@
/*
* 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 <string>
#include "ecmascript/global_env.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_hash_array.h"
#include "ecmascript/tagged_node.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda;
using namespace panda::ecmascript;
namespace panda::test {
class RBTreeNodeTest : public testing::Test {
public:
static void SetUpTestCase()
{
GTEST_LOG_(INFO) << "SetUpTestCase";
}
static void TearDownTestCase()
{
GTEST_LOG_(INFO) << "TearDownCase";
}
void SetUp() override
{
TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
}
void TearDown() override
{
TestHelper::DestroyEcmaVMWithScope(instance, scope);
}
EcmaVM *instance {nullptr};
EcmaHandleScope *scope {nullptr};
JSThread *thread {nullptr};
JSHandle<GlobalEnv> GetGlobalEnv()
{
EcmaVM *ecma = thread->GetEcmaVM();
return ecma->GetGlobalEnv();
}
uint32_t NODE_NUMBERS = 8;
uint32_t TREE_NODE_NUMBERS = 32;
};
HWTEST_F_L0(RBTreeNodeTest, RBTreeNodeCreate)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
std::string k("testKey");
std::string v("testValue");
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(k).GetTaggedValue());
JSHandle<JSTaggedValue> value(thread, factory->NewFromStdString(v).GetTaggedValue());
int hash = TaggedNode::Hash(factory->NewFromStdString(k).GetTaggedValue());
JSHandle<RBTreeNode> newNode = factory->NewTreeNode(hash, key, value);
EXPECT_TRUE(!newNode.GetTaggedValue().IsHole());
EXPECT_TRUE(newNode.GetTaggedValue().IsRBTreeNode());
}
HWTEST_F_L0(RBTreeNodeTest, RBTreeNodeSetAndGet)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// test RBTreeNode
JSHandle<RBTreeNode> rootNode(thread, JSTaggedValue::Hole());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(iKey).GetTaggedValue());
JSHandle<JSTaggedValue> value(thread, factory->NewFromStdString(iValue).GetTaggedValue());
int hash = TaggedNode::Hash(factory->NewFromStdString(iKey).GetTaggedValue());
rootNode = RBTreeNode::Set(thread, rootNode, hash, key, value);
rootNode->SetIsRed(thread, JSTaggedValue(false));
}
EXPECT_EQ(rootNode->GetCount(), NODE_NUMBERS);
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(iKey).GetTaggedValue());
JSHandle<JSTaggedValue> value(thread, factory->NewFromStdString(iValue).GetTaggedValue());
int hash = TaggedNode::Hash(key.GetTaggedValue());
// test get
JSHandle<JSTaggedValue> rootNodeVa(thread, rootNode.GetTaggedValue());
JSTaggedValue gValue = RBTreeNode::GetTreeNode(thread, rootNodeVa, hash, key);
JSTaggedValue resValue = RBTreeNode::Cast(gValue.GetTaggedObject())->GetValue();
EXPECT_EQ(resValue, value.GetTaggedValue());
}
}
HWTEST_F_L0(RBTreeNodeTest, RBTreeNodeDelete)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// test RBTreeNode
JSHandle<RBTreeNode> rootNode(thread, JSTaggedValue::Hole());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(iKey).GetTaggedValue());
JSHandle<JSTaggedValue> value(thread, factory->NewFromStdString(iValue).GetTaggedValue());
int hash = TaggedNode::Hash(factory->NewFromStdString(iKey).GetTaggedValue());
rootNode = RBTreeNode::Set(thread, rootNode, hash, key, value);
rootNode->SetIsRed(thread, JSTaggedValue(false));
}
EXPECT_EQ(rootNode->GetCount(), NODE_NUMBERS);
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
std::string iKey = myKey + std::to_string(i);
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(iKey).GetTaggedValue());
int hash = TaggedNode::Hash(key.GetTaggedValue());
JSTaggedValue holeValue = JSTaggedValue::Hole();
JSTaggedValue dValue =
RBTreeNode::Delete(thread, rootNode.GetTaggedValue(), hash, key.GetTaggedValue(), holeValue);
rootNode = JSHandle<RBTreeNode>(thread, dValue);
}
EXPECT_EQ(rootNode->GetCount(), (NODE_NUMBERS / 2));
for (uint32_t i = 0; i < NODE_NUMBERS / 2; i++) {
std::string iKey = myKey + std::to_string(i);
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(iKey).GetTaggedValue());
int hash = TaggedNode::Hash(key.GetTaggedValue());
JSHandle<JSTaggedValue> rootNodeVa(thread, rootNode.GetTaggedValue());
JSTaggedValue gValue = RBTreeNode::GetTreeNode(thread, rootNodeVa, hash, key);
EXPECT_EQ(gValue, JSTaggedValue::Hole());
}
for (uint32_t i = NODE_NUMBERS / 2; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(iKey).GetTaggedValue());
JSHandle<JSTaggedValue> value(thread, factory->NewFromStdString(iValue).GetTaggedValue());
int hash = TaggedNode::Hash(key.GetTaggedValue());
JSHandle<JSTaggedValue> rootNodeVa(thread, rootNode.GetTaggedValue());
JSTaggedValue gValue = RBTreeNode::GetTreeNode(thread, rootNodeVa, hash, key);
JSTaggedValue resValue = RBTreeNode::Cast(gValue.GetTaggedObject())->GetValue();
EXPECT_EQ(resValue, value.GetTaggedValue());
}
}
HWTEST_F_L0(RBTreeNodeTest, RBTreeNodeDivide)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<RBTreeNode> rootNode(thread, JSTaggedValue::Hole());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < TREE_NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(iKey).GetTaggedValue());
JSHandle<JSTaggedValue> value(thread, factory->NewFromStdString(iValue).GetTaggedValue());
int hash = TaggedNode::Hash(factory->NewFromStdString(iKey).GetTaggedValue());
rootNode = RBTreeNode::Set(thread, rootNode, hash, key, value);
rootNode->SetIsRed(thread, JSTaggedValue(false));
}
EXPECT_EQ(rootNode->GetCount(), TREE_NODE_NUMBERS);
uint32_t count = rootNode->GetCount();
JSHandle<TaggedHashArray> newTab =
JSHandle<TaggedHashArray>(thread,
TaggedHashArray::Create(thread, TaggedHashArray::DEFAULT_INITIAL_CAPACITY * 2));
JSHandle<JSTaggedValue> rootNodeVa = JSHandle<JSTaggedValue>::Cast(rootNode);
RBTreeNode::Divide(thread, newTab, rootNodeVa, 0, TaggedHashArray::DEFAULT_INITIAL_CAPACITY);
JSTaggedValue loNode = newTab->Get(0);
uint32_t loCount = 0;
uint32_t hiCount = 0;
if (loNode.IsLinkedNode()) {
for (JSHandle<LinkedNode> node = JSHandle<LinkedNode>(thread, loNode);
!node.GetTaggedValue().IsHole();
node = JSHandle<LinkedNode>(thread, node->GetNext())) {
loCount++;
}
} else {
JSHandle<RBTreeNode> node = JSHandle<RBTreeNode>(thread, loNode);
loCount = node->GetCount();
}
JSTaggedValue hiNode = newTab->Get(TaggedHashArray::DEFAULT_INITIAL_CAPACITY);
if (hiNode.IsLinkedNode()) {
for (JSHandle<LinkedNode> node = JSHandle<LinkedNode>(thread, hiNode);
!node.GetTaggedValue().IsHole();
node = JSHandle<LinkedNode>(thread, node->GetNext())) {
hiCount++;
}
} else {
JSHandle<RBTreeNode> node = JSHandle<RBTreeNode>(thread, hiNode);
hiCount = node->GetCount();
}
EXPECT_TRUE(count == loCount + hiCount);
}
HWTEST_F_L0(RBTreeNodeTest, RBTreeNodeUntreeify)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<RBTreeNode> rootNode(thread, JSTaggedValue::Hole());
std::string myKey("mykey");
std::string myValue("myvalue");
for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
std::string iKey = myKey + std::to_string(i);
std::string iValue = myValue + std::to_string(i);
JSHandle<JSTaggedValue> key(thread, factory->NewFromStdString(iKey).GetTaggedValue());
JSHandle<JSTaggedValue> value(thread, factory->NewFromStdString(iValue).GetTaggedValue());
int hash = TaggedNode::Hash(factory->NewFromStdString(iKey).GetTaggedValue());
rootNode = RBTreeNode::Set(thread, rootNode, hash, key, value);
rootNode->SetIsRed(thread, JSTaggedValue(false));
}
EXPECT_EQ(rootNode->GetCount(), NODE_NUMBERS);
JSHandle<LinkedNode> head = RBTreeNode::Detreeing(thread, rootNode);
uint32_t count = 0;
for (; !head.GetTaggedValue().IsHole(); head = JSHandle<LinkedNode>(thread, head->GetNext())) {
count++;
}
EXPECT_EQ(count, rootNode->GetCount());
}
HWTEST_F_L0(RBTreeNodeTest, RBTreeNodeCompare)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
std::string value1("Compare1");
std::string value2("Compare2");
std::string value3("Compare1");
std::string value4("name");
JSHandle<JSTaggedValue> a(thread, factory->NewFromStdString(value1).GetTaggedValue());
JSHandle<JSTaggedValue> b(thread, factory->NewFromStdString(value2).GetTaggedValue());
JSHandle<JSTaggedValue> c(thread, factory->NewFromStdString(value3).GetTaggedValue());
JSHandle<JSTaggedValue> d(thread, factory->NewFromStdString(value4).GetTaggedValue());
int rvalue = RBTreeNode::Compare(12345, a.GetTaggedValue(), 12345, b.GetTaggedValue());
EXPECT_TRUE(rvalue != 0);
rvalue = RBTreeNode::Compare(54321, a.GetTaggedValue(), 54321, c.GetTaggedValue());
EXPECT_EQ(rvalue, 0);
rvalue = RBTreeNode::Compare(3373707, d.GetTaggedValue(), 3373707, JSTaggedValue(38754584));
EXPECT_EQ(rvalue, 1);
}
}