Add WeakRef and FinalizationRegistry interfaces

Add WeakRef and FinalizationRegistry interfaces

issue:https://gitee.com/openharmony/ark_js_runtime/issues/I57W11

Signed-off-by: xdmal <maxiaodong16@huawei.com>
This commit is contained in:
xdmal 2022-05-27 10:26:55 +08:00
parent 4c7e59abb1
commit 86bda19980
34 changed files with 1451 additions and 3 deletions

View File

@ -319,6 +319,7 @@ ecma_source = [
"ecmascript/builtins/builtins_dataview.cpp",
"ecmascript/builtins/builtins_date.cpp",
"ecmascript/builtins/builtins_errors.cpp",
"ecmascript/builtins/builtins_finalization_registry.cpp",
"ecmascript/builtins/builtins_function.cpp",
"ecmascript/builtins/builtins_generator.cpp",
"ecmascript/builtins/builtins_global.cpp",
@ -341,6 +342,7 @@ ecma_source = [
"ecmascript/builtins/builtins_symbol.cpp",
"ecmascript/builtins/builtins_typedarray.cpp",
"ecmascript/builtins/builtins_weak_map.cpp",
"ecmascript/builtins/builtins_weak_ref.cpp",
"ecmascript/builtins/builtins_weak_set.cpp",
"ecmascript/containers/containers_arraylist.cpp",
"ecmascript/containers/containers_deque.cpp",
@ -409,6 +411,7 @@ ecma_source = [
"ecmascript/js_dataview.cpp",
"ecmascript/js_date.cpp",
"ecmascript/js_for_in_iterator.cpp",
"ecmascript/js_finalization_registry.cpp",
"ecmascript/js_function.cpp",
"ecmascript/js_generator_object.cpp",
"ecmascript/js_hclass.cpp",

View File

@ -37,6 +37,7 @@
#include "ecmascript/builtins/builtins_date.h"
#include "ecmascript/builtins/builtins_date_time_format.h"
#include "ecmascript/builtins/builtins_errors.h"
#include "ecmascript/builtins/builtins_finalization_registry.h"
#include "ecmascript/builtins/builtins_function.h"
#include "ecmascript/builtins/builtins_generator.h"
#include "ecmascript/builtins/builtins_global.h"
@ -66,6 +67,7 @@
#include "ecmascript/builtins/builtins_symbol.h"
#include "ecmascript/builtins/builtins_typedarray.h"
#include "ecmascript/builtins/builtins_weak_map.h"
#include "ecmascript/builtins/builtins_weak_ref.h"
#include "ecmascript/builtins/builtins_weak_set.h"
#include "ecmascript/containers/containers_private.h"
#include "ecmascript/ecma_runtime_call_info.h"
@ -77,6 +79,7 @@
#include "ecmascript/js_dataview.h"
#include "ecmascript/js_date_time_format.h"
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/js_finalization_registry.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_hclass.h"
@ -99,6 +102,7 @@
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/js_typed_array.h"
#include "ecmascript/js_weak_container.h"
#include "ecmascript/js_weak_ref.h"
#include "ecmascript/mem/mem.h"
#include "ecmascript/module/js_module_namespace.h"
#include "ecmascript/napi/include/jsnapi.h"
@ -116,6 +120,8 @@ using BuiltinsMap = builtins::BuiltinsMap;
using BuiltinsSet = builtins::BuiltinsSet;
using BuiltinsWeakMap = builtins::BuiltinsWeakMap;
using BuiltinsWeakSet = builtins::BuiltinsWeakSet;
using BuiltinsWeakRef = builtins::BuiltinsWeakRef;
using BuiltinsFinalizationRegistry = builtins::BuiltinsFinalizationRegistry;
using BuiltinsArray = builtins::BuiltinsArray;
using BuiltinsTypedArray = builtins::BuiltinsTypedArray;
using BuiltinsIterator = builtins::BuiltinsIterator;
@ -299,6 +305,8 @@ void Builtins::Initialize(const JSHandle<GlobalEnv> &env, JSThread *thread)
InitializeMap(env, objFuncDynclass);
InitializeWeakMap(env, objFuncDynclass);
InitializeWeakSet(env, objFuncDynclass);
InitializeWeakRef(env, objFuncDynclass);
InitializeFinalizationRegistry(env, objFuncDynclass);
InitializeArray(env, objFuncPrototypeVal);
InitializeTypedArray(env, objFuncDynclass);
InitializeString(env, primRefObjDynclass);
@ -1355,6 +1363,67 @@ void Builtins::InitializeWeakSet(const JSHandle<GlobalEnv> &env, const JSHandle<
env->SetBuiltinsWeakSetFunction(thread_, weakSetFunction);
}
void Builtins::InitializeWeakRef(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const
{
[[maybe_unused]] EcmaHandleScope scope(thread_);
const GlobalEnvConstants *globalConst = thread_->GlobalConstants();
// WeakRef.prototype
JSHandle<JSObject> weakRefFuncPrototype = factory_->NewJSObject(objFuncDynclass);
JSHandle<JSTaggedValue> weakRefFuncPrototypeValue(weakRefFuncPrototype);
// WeakRef.prototype_or_dynclass
JSHandle<JSHClass> weakRefFuncInstanceDynclass =
factory_->NewEcmaDynClass(JSWeakRef::SIZE, JSType::JS_WEAK_REF, weakRefFuncPrototypeValue);
// WeakRef() = new Function()
JSHandle<JSTaggedValue> weakRefFunction(NewBuiltinConstructor(
env, weakRefFuncPrototype, BuiltinsWeakRef::WeakRefConstructor, "WeakRef", FunctionLength::ONE));
JSHandle<JSFunction>(weakRefFunction)->SetProtoOrDynClass(thread_, weakRefFuncInstanceDynclass.GetTaggedValue());
// "constructor" property on the prototype
JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
JSObject::SetProperty(thread_, JSHandle<JSTaggedValue>(weakRefFuncPrototype), constructorKey, weakRefFunction);
// WeakRef.prototype.deref()
SetFunction(env, weakRefFuncPrototype, "deref", BuiltinsWeakRef::Deref, FunctionLength::ZERO);
// @@ToStringTag
SetStringTagSymbol(env, weakRefFuncPrototype, "WeakRef");
env->SetBuiltinsWeakRefFunction(thread_, weakRefFunction);
}
void Builtins::InitializeFinalizationRegistry(const JSHandle<GlobalEnv> &env,
const JSHandle<JSHClass> &objFuncDynclass) const
{
[[maybe_unused]] EcmaHandleScope scope(thread_);
const GlobalEnvConstants *globalConst = thread_->GlobalConstants();
// FinalizationRegistry.prototype
JSHandle<JSObject> finalizationRegistryFuncPrototype = factory_->NewJSObject(objFuncDynclass);
JSHandle<JSTaggedValue> finalizationRegistryFuncPrototypeValue(finalizationRegistryFuncPrototype);
// FinalizationRegistry.prototype_or_dynclass
JSHandle<JSHClass> finalizationRegistryFuncInstanceDynclass =
factory_->NewEcmaDynClass(JSFinalizationRegistry::SIZE, JSType::JS_FINALIZATION_REGISTRY,
finalizationRegistryFuncPrototypeValue);
// FinalizationRegistry() = new Function()
JSHandle<JSTaggedValue> finalizationRegistryFunction(NewBuiltinConstructor(
env, finalizationRegistryFuncPrototype, BuiltinsFinalizationRegistry::FinalizationRegistryConstructor,
"FinalizationRegistry", FunctionLength::ONE));
JSHandle<JSFunction>(finalizationRegistryFunction)->SetProtoOrDynClass(
thread_, finalizationRegistryFuncInstanceDynclass.GetTaggedValue());
// "constructor" property on the prototype
JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
JSObject::SetProperty(thread_, JSHandle<JSTaggedValue>(finalizationRegistryFuncPrototype),
constructorKey, finalizationRegistryFunction);
// FinalizationRegistry.prototype.deref()
SetFunction(env, finalizationRegistryFuncPrototype, "register",
BuiltinsFinalizationRegistry::Register, FunctionLength::TWO);
SetFunction(env, finalizationRegistryFuncPrototype, "unregister",
BuiltinsFinalizationRegistry::Unregister, FunctionLength::ONE);
// @@ToStringTag
SetStringTagSymbol(env, finalizationRegistryFuncPrototype, "FinalizationRegistry");
env->SetBuiltinsFinalizationRegistryFunction(thread_, finalizationRegistryFunction);
}
void Builtins::InitializeMath(const JSHandle<GlobalEnv> &env, const JSHandle<JSTaggedValue> &objFuncPrototypeVal) const
{
[[maybe_unused]] EcmaHandleScope scope(thread_);

View File

@ -139,6 +139,11 @@ private:
void InitializeWeakSet(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeWeakRef(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeFinalizationRegistry(const JSHandle<GlobalEnv> &env,
const JSHandle<JSHClass> &objFuncDynclass) const;
void InitializeMath(const JSHandle<GlobalEnv> &env, const JSHandle<JSTaggedValue> &objFuncPrototypeVal) const;
void InitializeJson(const JSHandle<GlobalEnv> &env, const JSHandle<JSTaggedValue> &objFuncPrototypeVal) const;

View File

@ -0,0 +1,131 @@
/*
* 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/builtins/builtins_finalization_registry.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_finalization_registry.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/object_factory.h"
namespace panda::ecmascript::builtins {
// 26.2.1.1
JSTaggedValue BuiltinsFinalizationRegistry::FinalizationRegistryConstructor(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, FinalizationRegistry, Constructor);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// 1. If NewTarget is undefined, throw a TypeError exception.
if (newTarget->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception());
}
// 2. If IsCallable(cleanupCallback) is false, throw a TypeError exception.
JSHandle<JSTaggedValue> cleanupCallback = GetCallArg(argv, 0);
if (!cleanupCallback->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "cleanupCallback not Callable", JSTaggedValue::Exception());
}
// 3. Let finalizationRegistry be ? OrdinaryCreateFromConstructor(NewTarget, "%FinalizationRegistry.prototype%",
// « [[Realm]], [[CleanupCallback]], [[Cells]] »).
JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSFinalizationRegistry> finalization = JSHandle<JSFinalizationRegistry>::Cast(obj);
// 4. Let fn be the active function object.
// 5. Set finalizationRegistry.[[Realm]] to fn.[[Realm]].
// 6. Set finalizationRegistry.[[CleanupCallback]] to cleanupCallback.
finalization->SetCleanupCallback(thread, cleanupCallback);
// 7. Set finalizationRegistry.[[Cells]] to a new empty List.
JSHandle<CellRecordVector> noUnregister(CellRecordVector::Create(thread));
JSHandle<LinkedHashMap> maybeUnregister = LinkedHashMap::Create(thread);
finalization->SetNoUnregister(thread, noUnregister);
finalization->SetMaybeUnregister(thread, maybeUnregister);
JSHandle<JSTaggedValue> objValue(finalization);
// 8. Return finalizationRegistry.
return finalization.GetTaggedValue();
}
// 26.2.3.2
JSTaggedValue BuiltinsFinalizationRegistry::Register(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, BuiltinsFinalizationRegistry, Register);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> heldValue = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> unregisterToken = GetCallArg(argv, 2); // 2 : unregisterToken storage location
// 1. Let finalizationRegistry be the this value.
JSHandle<JSTaggedValue> finalizationRegistry = GetThis(argv);
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
if (!finalizationRegistry->IsJSFinalizationRegistry()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "thisValue is not object or does not have an internalSlot internal slot",
JSTaggedValue::Exception());
}
// 3. If Type(target) is not Object, throw a TypeError exception.
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "target is not object", JSTaggedValue::Exception());
}
// 4. If SameValue(target, heldValue) is true, throw a TypeError exception.
if (JSTaggedValue::SameValue(target, heldValue)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "target and heldValue should not be equal", JSTaggedValue::Exception());
}
// 5. If Type(unregisterToken) is not Object, then
// a. If unregisterToken is not undefined, throw a TypeError exception.
// b. Set unregisterToken to empty.
if (!unregisterToken->IsECMAObject() && !unregisterToken->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "unregisterToken should be object", JSTaggedValue::Exception());
}
// 6. Let cell be the Record { [[WeakRefTarget]]: target,
// [[HeldValue]]: heldValue, [[UnregisterToken]]: unregisterToken }.
// 7. Append cell to finalizationRegistry.[[Cells]].
JSHandle<JSFinalizationRegistry> finRegHandle(finalizationRegistry);
JSFinalizationRegistry::Register(thread, target, heldValue, unregisterToken, finRegHandle);
// 8. Return undefined.
return JSTaggedValue::Undefined();
}
// 26.2.3.3
JSTaggedValue BuiltinsFinalizationRegistry::Unregister(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, BuiltinsFinalizationRegistry, Unregister);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> unregisterToken = GetCallArg(argv, 0);
// 1. Let finalizationRegistry be the this value.
JSHandle<JSTaggedValue> finalizationRegistry = GetThis(argv);
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
if (!finalizationRegistry->IsJSFinalizationRegistry()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "thisValue is not object or does not have an internalSlot internal slot",
JSTaggedValue::Exception());
}
// 3. If Type(unregisterToken) is not Object, throw a TypeError exception.
if (!unregisterToken->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "unregisterToken should be object", JSTaggedValue::Exception());
}
// 4. Let removed be false.
// 5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] } cell of
// finalizationRegistry.[[Cells]], do
// a. If cell.[[UnregisterToken]] is not empty and SameValue(cell.[[UnregisterToken]], unregisterToken)
// is true, then
// i. Remove cell from finalizationRegistry.[[Cells]].
// ii. Set removed to true.
JSHandle<JSFinalizationRegistry> finRegHandle(finalizationRegistry);
bool removed = JSFinalizationRegistry::Unregister(thread, unregisterToken, finRegHandle);
// 6. Return removed.
return JSTaggedValue(removed);
}
} // namespace panda::ecmascript::builtins

View File

@ -0,0 +1,33 @@
/*
* 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_BUILTINS_BUILTINS_FINALIZATION_REGISTRY_H
#define ECMASCRIPT_BUILTINS_BUILTINS_FINALIZATION_REGISTRY_H
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/ecma_runtime_call_info.h"
namespace panda::ecmascript::builtins {
class BuiltinsFinalizationRegistry : public base::BuiltinsBase {
public:
// 26.2.1.1
static JSTaggedValue FinalizationRegistryConstructor(EcmaRuntimeCallInfo *argv);
// 26.2.3.2
static JSTaggedValue Register(EcmaRuntimeCallInfo *argv);
// 26.2.3.3
static JSTaggedValue Unregister(EcmaRuntimeCallInfo *argv);
};
} // namespace panda::ecmascript::builtins
#endif // ECMASCRIPT_BUILTINS_BUILTINS_FINALIZATION_REGISTRY_H

View File

@ -0,0 +1,70 @@
/*
* 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/builtins/builtins_weak_ref.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_weak_ref.h"
#include "ecmascript/object_factory.h"
namespace panda::ecmascript::builtins {
JSTaggedValue BuiltinsWeakRef::WeakRefConstructor(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, WeakRef, Constructor);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// 1. If NewTarget is undefined, throw a TypeError exception.
if (newTarget->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception());
}
// 2. If Type(target) is not Object, throw a TypeError exception.
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "target is not object", JSTaggedValue::Exception());
}
// 3. Let weakRef be ? OrdinaryCreateFromConstructor(NewTarget, "%WeakRef.prototype%", « [[WeakRefTarget]] »).
JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 4. Perform ! AddToKeptObjects(target).
thread->GetEcmaVM()->GetHeap()->AddToKeptObjects(target);
// 5. Set weakRef.[[WeakRefTarget]] to target.
// 6. Return weakRef.
JSHandle<JSWeakRef> weakRef = JSHandle<JSWeakRef>::Cast(obj);
weakRef->SetToWeak(target.GetTaggedValue());
return weakRef.GetTaggedValue();
}
JSTaggedValue BuiltinsWeakRef::Deref(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, WeakRef, Deref);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
// 1. Let weakRef be the this value.
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
// 2. Perform ? RequireInternalSlot(weakRef, [[WeakRefTarget]]).
if (!thisValue->IsJSWeakRef()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "thisValue is not object or does not have an internalSlot internal slot",
JSTaggedValue::Exception());
}
// 3. Return ! WeakRefDeref(weakRef).
JSHandle<JSWeakRef> weakRef = JSHandle<JSWeakRef>::Cast(thisValue);
return JSWeakRef::WeakRefDeref(thread, weakRef);
}
} // namespace panda::ecmascript::builtins

View File

@ -0,0 +1,31 @@
/*
* 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_BUILTINS_BUILTINS_WEAK_REF_H
#define ECMASCRIPT_BUILTINS_BUILTINS_WEAK_REF_H
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/ecma_runtime_call_info.h"
namespace panda::ecmascript::builtins {
class BuiltinsWeakRef : public base::BuiltinsBase {
public:
// 26.1.1.1
static JSTaggedValue WeakRefConstructor(EcmaRuntimeCallInfo *argv);
// 26.1.3.2
static JSTaggedValue Deref(EcmaRuntimeCallInfo *argv);
};
} // namespace panda::ecmascript::builtins
#endif // ECMASCRIPT_BUILTINS_BUILTINS_REF_H

View File

@ -56,6 +56,7 @@ host_unittest_action("BuiltinsNaturalTest") {
"builtins_dataview_test.cpp",
"builtins_date_test.cpp",
"builtins_errors_test.cpp",
"builtins_finalizationregistry_test.cpp",
"builtins_function_test.cpp",
"builtins_iterator_test.cpp",
"builtins_json_test.cpp",
@ -74,6 +75,7 @@ host_unittest_action("BuiltinsNaturalTest") {
"builtins_symbol_test.cpp",
"builtins_typedarray_test.cpp",
"builtins_weak_map_test.cpp",
"builtins_weak_ref_test.cpp",
"builtins_weak_set_test.cpp",
]

View File

@ -0,0 +1,207 @@
/*
* Copyright (c) 2021 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/builtins/builtins_finalization_registry.h"
#include "ecmascript/js_finalization_registry.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_array.h"
#include "ecmascript/js_array_iterator.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/object_operator.h"
#include "ecmascript/tests/test_helper.h"
#include "utils/bit_utils.h"
#include "ecmascript/jobs/micro_job_queue.h"
using namespace panda::ecmascript;
using namespace panda::ecmascript::builtins;
using BuiltinsBase = panda::ecmascript::base::BuiltinsBase;
static JSTaggedValue testArgv = JSTaggedValue(0);
namespace panda::test {
class BuiltinsFinalizationRegistryTest : 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 cleanupCallback()
{
testArgv = JSTaggedValue(10); // number of 10
return JSTaggedValue::Undefined();
}
};
};
JSTaggedValue CreateFinalizationRegistryConstructor(JSThread *thread)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> finalizationRegistry(env->GetBuiltinsFinalizationRegistryFunction());
JSHandle<JSFunction> handleFunc = factory->NewJSFunction(
env, reinterpret_cast<void *>(BuiltinsFinalizationRegistryTest::TestClass::cleanupCallback));
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*finalizationRegistry), 6);
ecmaRuntimeCallInfo->SetFunction(finalizationRegistry.GetTaggedValue());
ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetCallArg(0, handleFunc.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
return JSTaggedValue(BuiltinsFinalizationRegistry::FinalizationRegistryConstructor(ecmaRuntimeCallInfo.get()));
}
// new FinalizationRegistry (cleanupCallback)
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, FinalizationRegistryConstructor)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> finalizationRegistry(env->GetBuiltinsFinalizationRegistryFunction());
JSHandle<JSFunction> handleFunc = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::cleanupCallback));
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*finalizationRegistry), 6);
ecmaRuntimeCallInfo->SetFunction(finalizationRegistry.GetTaggedValue());
ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetCallArg(0, handleFunc.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
JSTaggedValue result = BuiltinsFinalizationRegistry::FinalizationRegistryConstructor(ecmaRuntimeCallInfo.get());
ASSERT_TRUE(result.IsECMAObject());
}
// finalizationRegistry.Register(target, heldValue [ , unregisterToken ])
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register1)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> objectFunc = env->GetObjectFunction();
JSTaggedValue result = CreateFinalizationRegistryConstructor(thread);
JSHandle<JSFinalizationRegistry> jsfinalizationRegistry(thread, result);
JSHandle<JSTaggedValue> target(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
JSHandle<JSTaggedValue> key(factory->NewFromASCII("1"));
JSHandle<JSTaggedValue> value(thread, JSTaggedValue(1));
JSObject::SetProperty(thread, target, key, value);
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(jsfinalizationRegistry.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(10));
ecmaRuntimeCallInfo->SetCallArg(2, target.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
BuiltinsFinalizationRegistry::Register(ecmaRuntimeCallInfo.get());
ASSERT_EQ(testArgv, JSTaggedValue(0));
}
// finalizationRegistry.Register(target, heldValue [ , unregisterToken ])
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Register2)
{
EcmaVM *vm = thread->GetEcmaVM();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> objectFunc = env->GetObjectFunction();
JSTaggedValue result = CreateFinalizationRegistryConstructor(thread);
JSHandle<JSFinalizationRegistry> jsfinalizationRegistry(thread, result);
JSTaggedValue target =
factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc).GetTaggedValue();
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(jsfinalizationRegistry.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(0, target);
ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(10));
ecmaRuntimeCallInfo->SetCallArg(2, target);
target = JSTaggedValue::Undefined();
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
BuiltinsFinalizationRegistry::Register(ecmaRuntimeCallInfo.get());
[[maybe_unused]] JSTaggedValue target22 =
factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc).GetTaggedValue();
vm->CollectGarbage(TriggerGCType::FULL_GC);
if (!thread->HasPendingException()) {
job::MicroJobQueue::ExecutePendingJob(thread, vm->GetMicroJobQueue());
}
ASSERT_EQ(testArgv, JSTaggedValue(0));
}
// finalizationRegistry.Unregister(unregisterToken ])
HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Unregister)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> objectFunc = env->GetObjectFunction();
JSTaggedValue result = CreateFinalizationRegistryConstructor(thread);
JSHandle<JSFinalizationRegistry> jsfinalizationRegistry(thread, result);
JSHandle<JSTaggedValue> target(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
JSHandle<JSTaggedValue> key(factory->NewFromASCII("1"));
JSHandle<JSTaggedValue> value(thread, JSTaggedValue(1));
JSObject::SetProperty(thread, target, key, value);
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(jsfinalizationRegistry.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(10));
ecmaRuntimeCallInfo->SetCallArg(2, target.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
BuiltinsFinalizationRegistry::Register(ecmaRuntimeCallInfo.get());
auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo1->SetThis(jsfinalizationRegistry.GetTaggedValue());
ecmaRuntimeCallInfo1->SetCallArg(0, target.GetTaggedValue());
[[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get());
BuiltinsFinalizationRegistry::Register(ecmaRuntimeCallInfo1.get());
ASSERT_EQ(testArgv, JSTaggedValue(0));
}
} // namespace panda::test

View File

@ -0,0 +1,162 @@
/*
* Copyright (c) 2021 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/base/builtins_base.h"
#include "ecmascript/builtins/builtins_weak_ref.h"
#include "ecmascript/js_weak_ref.h"
#include "ecmascript/tests/test_helper.h"
#include "ecmascript/ecma_runtime_call_info.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_array.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_array-inl.h"
using namespace panda::ecmascript;
using namespace panda::ecmascript::builtins;
using BuiltinsBase = panda::ecmascript::base::BuiltinsBase;
namespace panda::test {
using BuiltinsWeakRef = ecmascript::builtins::BuiltinsWeakRef;
class BuiltinsWeakRefTest : 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};
};
// new WeakRef(target)
HWTEST_F_L0(BuiltinsWeakRefTest, WeakRefConstructor)
{
JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> objectFunc(globalEnv->GetObjectFunction());
JSHandle<JSObject> target(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> weakRef(env->GetBuiltinsWeakRefFunction());
JSHandle<JSObject> globalObject(thread, env->GetGlobalObject());
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, weakRef.GetTaggedValue(), 6);
ecmaRuntimeCallInfo->SetFunction(weakRef.GetTaggedValue());
ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
JSTaggedValue result = BuiltinsWeakRef::WeakRefConstructor(ecmaRuntimeCallInfo.get());
ASSERT_TRUE(result.IsECMAObject());
}
// weakRef.Deref()
HWTEST_F_L0(BuiltinsWeakRefTest, Deref1)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> objectFunc(env->GetObjectFunction());
JSHandle<JSFunction> weakRef(env->GetBuiltinsWeakRefFunction());
JSHandle<JSObject> globalObject(thread, env->GetGlobalObject());
JSHandle<JSObject> target(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
JSHandle<JSTaggedValue> formatStyle = thread->GlobalConstants()->GetHandledStyleString();
JSHandle<JSTaggedValue> styleKey(factory->NewFromASCII("currency"));
JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("EUR"));
JSObject::SetProperty(thread, target, formatStyle, styleKey);
JSObject::SetProperty(thread, target, styleKey, styleValue);
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, weakRef.GetTaggedValue(), 6);
ecmaRuntimeCallInfo->SetFunction(weakRef.GetTaggedValue());
ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
JSTaggedValue result = BuiltinsWeakRef::WeakRefConstructor(ecmaRuntimeCallInfo.get());
JSHandle<JSWeakRef> jsWeakRef(thread, JSWeakRef::Cast(reinterpret_cast<TaggedObject *>(result.GetRawData())));
auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo1->SetThis(jsWeakRef.GetTaggedValue());
ecmaRuntimeCallInfo1->SetCallArg(0, target.GetTaggedValue());
[[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get());
JSTaggedValue result1 = BuiltinsWeakRef::Deref(ecmaRuntimeCallInfo1.get());
EXPECT_TRUE(!result1.IsUndefined());
ASSERT_EQ(result1, target.GetTaggedValue());
}
// weakRef.Deref()
HWTEST_F_L0(BuiltinsWeakRefTest, Deref2)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> objectFunc(env->GetObjectFunction());
JSHandle<JSFunction> weakRef(env->GetBuiltinsWeakRefFunction());
JSHandle<JSObject> globalObject(thread, env->GetGlobalObject());
JSHandle<JSObject> target(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objectFunc), objectFunc));
JSHandle<JSTaggedValue> formatStyle = thread->GlobalConstants()->GetHandledStyleString();
JSHandle<JSTaggedValue> styleKey(factory->NewFromASCII("currency"));
JSHandle<JSTaggedValue> styleValue(factory->NewFromASCII("EUR"));
JSObject::SetProperty(thread, target, formatStyle, styleKey);
JSObject::SetProperty(thread, target, styleKey, styleValue);
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, weakRef.GetTaggedValue(), 6);
ecmaRuntimeCallInfo->SetFunction(weakRef.GetTaggedValue());
ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
JSTaggedValue result = BuiltinsWeakRef::WeakRefConstructor(ecmaRuntimeCallInfo.get());
JSHandle<JSWeakRef> jsWeakRef(thread, JSWeakRef::Cast(reinterpret_cast<TaggedObject *>(result.GetRawData())));
auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo1->SetThis(jsWeakRef.GetTaggedValue());
ecmaRuntimeCallInfo1->SetCallArg(0, target.GetTaggedValue());
[[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get());
JSTaggedValue result1 = BuiltinsWeakRef::Deref(ecmaRuntimeCallInfo1.get());
JSObject::SetProperty(thread, target, styleKey, styleValue);
ASSERT_EQ(result1, target.GetTaggedValue());
}
} // namespace panda::test

View File

@ -56,6 +56,7 @@
#include "ecmascript/js_date.h"
#include "ecmascript/js_date_time_format.h"
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/js_finalization_registry.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_generator_object.h"
#include "ecmascript/js_global_object.h"
@ -81,6 +82,7 @@
#include "ecmascript/js_thread.h"
#include "ecmascript/js_typed_array.h"
#include "ecmascript/js_weak_container.h"
#include "ecmascript/js_weak_ref.h"
#include "ecmascript/layout_info-inl.h"
#include "ecmascript/lexical_env.h"
#include "ecmascript/linked_hash_table.h"
@ -149,6 +151,12 @@ CString JSHClass::DumpJSType(JSType type)
return "WeakSet";
case JSType::JS_WEAK_MAP:
return "WeakMap";
case JSType::JS_WEAK_REF:
return "WeakRef";
case JSType::JS_FINALIZATION_REGISTRY:
return "JSFinalizationRegistry";
case JSType::CELL_RECORD:
return "CellRecord";
case JSType::JS_DATE:
return "Date";
case JSType::JS_BOUND_FUNCTION:
@ -503,6 +511,15 @@ static void DumpObject(TaggedObject *obj, std::ostream &os)
case JSType::JS_WEAK_MAP:
JSWeakMap::Cast(obj)->Dump(os);
break;
case JSType::JS_WEAK_REF:
JSWeakRef::Cast(obj)->Dump(os);
break;
case JSType::JS_FINALIZATION_REGISTRY:
JSFinalizationRegistry::Cast(obj)->Dump(os);
break;
case JSType::CELL_RECORD:
CellRecord::Cast(obj)->Dump(os);
break;
case JSType::JS_REG_EXP:
JSRegExp::Cast(obj)->Dump(os);
break;
@ -1474,6 +1491,40 @@ void JSWeakSet::Dump(std::ostream &os) const
set->Dump(os);
}
void JSWeakRef::Dump(std::ostream &os) const
{
os << " - WeakObject : ";
GetWeakObject().DumpTaggedValue(os);
os << "\n";
JSObject::Dump(os);
}
void JSFinalizationRegistry::Dump(std::ostream &os) const
{
os << " - CleanupCallback : ";
GetCleanupCallback().DumpTaggedValue(os);
os << "\n";
os << " - NoUnregister : ";
GetNoUnregister().D();
os << "\n";
os << " - MaybeUnregister : ";
LinkedHashMap *map = LinkedHashMap::Cast(GetMaybeUnregister().GetTaggedObject());
os << " - elements: " << std::dec << map->NumberOfElements() << "\n";
os << " - deleted-elements: " << std::dec << map->NumberOfDeletedElements() << "\n";
os << " - capacity: " << std::dec << map->Capacity() << "\n";
JSObject::Dump(os);
}
void CellRecord::Dump(std::ostream &os) const
{
os << " - WeakRefTarget : ";
GetFromWeakRefTarget().DumpTaggedValue(os);
os << "\n";
os << " - HeldValue : ";
GetHeldValue().DumpTaggedValue(os);
os << "\n";
}
void JSSetIterator::Dump(std::ostream &os) const
{
LinkedHashSet *set = LinkedHashSet::Cast(GetIteratedSet().GetTaggedObject());
@ -1709,6 +1760,10 @@ void GlobalEnv::Dump(std::ostream &os) const
GetBuiltinsWeakSetFunction().GetTaggedValue().Dump(os);
os << " - BuiltinsWeakMapFunction: ";
GetBuiltinsWeakMapFunction().GetTaggedValue().Dump(os);
os << " - BuiltinsWeakRefFunction: ";
GetBuiltinsWeakRefFunction().GetTaggedValue().Dump(os);
os << " - BuiltinsFinalizationRegistryFunction: ";
GetBuiltinsFinalizationRegistryFunction().GetTaggedValue().Dump(os);
os << " - MathFunction: ";
GetMathFunction().GetTaggedValue().Dump(os);
os << " - JsonFunction: ";
@ -2831,6 +2886,15 @@ static void DumpObject(TaggedObject *obj,
case JSType::JS_WEAK_MAP:
JSWeakMap::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::JS_WEAK_REF:
JSWeakRef::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::JS_FINALIZATION_REGISTRY:
JSFinalizationRegistry::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::CELL_RECORD:
CellRecord::Cast(obj)->DumpForSnapshot(vec);
return;
case JSType::JS_REG_EXP:
JSRegExp::Cast(obj)->DumpForSnapshot(vec);
return;
@ -3406,6 +3470,28 @@ void JSWeakSet::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &
JSObject::DumpForSnapshot(vec);
}
void JSWeakRef::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &vec) const
{
vec.push_back(std::make_pair(CString("WeakObject"), GetWeakObject()));
JSObject::DumpForSnapshot(vec);
}
void JSFinalizationRegistry::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &vec) const
{
vec.push_back(std::make_pair(CString("CleanupCallback"), GetCleanupCallback()));
LinkedHashMap *map = LinkedHashMap::Cast(GetMaybeUnregister().GetTaggedObject());
map->DumpForSnapshot(vec);
vec.push_back(std::make_pair(CString("MaybeUnregister"), GetMaybeUnregister()));
JSObject::DumpForSnapshot(vec);
}
void CellRecord::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &vec) const
{
vec.push_back(std::make_pair(CString("WeakRefTarget"), GetWeakRefTarget()));
vec.push_back(std::make_pair(CString("HeldValue"), GetHeldValue()));
}
void JSSetIterator::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &vec) const
{
LinkedHashSet *set = LinkedHashSet::Cast(GetIteratedSet().GetTaggedObject());
@ -3588,6 +3674,9 @@ void GlobalEnv::DumpForSnapshot(std::vector<std::pair<CString, JSTaggedValue>> &
vec.push_back(std::make_pair(CString("BuiltinsMapFunction"), GetBuiltinsMapFunction().GetTaggedValue()));
vec.push_back(std::make_pair(CString("BuiltinsWeakSetFunction"), GetBuiltinsWeakSetFunction().GetTaggedValue()));
vec.push_back(std::make_pair(CString("BuiltinsWeakMapFunction"), GetBuiltinsWeakMapFunction().GetTaggedValue()));
vec.push_back(std::make_pair(CString("BuiltinsWeakRefFunction"), GetBuiltinsWeakRefFunction().GetTaggedValue()));
vec.push_back(std::make_pair(CString("BuiltinsFinalizationRegistryFunction"),
GetBuiltinsFinalizationRegistryFunction().GetTaggedValue()));
vec.push_back(std::make_pair(CString("MathFunction"), GetMathFunction().GetTaggedValue()));
vec.push_back(std::make_pair(CString("JsonFunction"), GetJsonFunction().GetTaggedValue()));
vec.push_back(std::make_pair(CString("StringFunction"), GetStringFunction().GetTaggedValue()));

View File

@ -79,6 +79,8 @@ class JSThread;
V(JSTaggedValue, BuiltinsMapFunction, BUILTINS_MAP_FUNCTION_INDEX) \
V(JSTaggedValue, BuiltinsWeakMapFunction, BUILTINS_WEAK_MAP_FUNCTION_INDEX) \
V(JSTaggedValue, BuiltinsWeakSetFunction, BUILTINS_WEAK_SET_FUNCTION_INDEX) \
V(JSTaggedValue, BuiltinsWeakRefFunction, BUILTINS_WEAK_REF_FUNCTION_INDEX) \
V(JSTaggedValue, BuiltinsFinalizationRegistryFunction, BUILTINS_FINALIZATION_REGISTRY_FUNCTION_INDEX) \
V(JSTaggedValue, MapPrototype, MAP_PROTOTYPE_INDEX) \
V(JSTaggedValue, MathFunction, MATH_FUNCTION_INDEX) \
V(JSTaggedValue, JsonFunction, JSON_FUNCTION_INDEX) \
@ -145,7 +147,9 @@ class JSThread;
V(JSTaggedValue, ListFormatLocales, LIST_FORMAT_LOCALES_INDEX) \
V(JSTaggedValue, GlobalRecord, GLOBAL_RECORD) \
V(JSTaggedValue, ModuleNamespaceClass, MODULENAMESPACE_CLASS) \
V(JSTaggedValue, ObjectLiteralHClassCache, OBJECT_LITERAL_HCLASS_CACHE)
V(JSTaggedValue, ObjectLiteralHClassCache, OBJECT_LITERAL_HCLASS_CACHE) \
V(JSTaggedValue, WeakRefKeepObjects, WEAK_REF_KEEP_OBJECTS) \
V(JSTaggedValue, FinRegLists, FIN_REG_LISTS)
class GlobalEnv : public TaggedObject {
public:

View File

@ -41,6 +41,7 @@
#include "ecmascript/js_array.h"
#include "ecmascript/js_array_iterator.h"
#include "ecmascript/js_arraybuffer.h"
#include "ecmascript/js_finalization_registry.h"
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/js_generator_object.h"
#include "ecmascript/js_hclass.h"
@ -202,6 +203,8 @@ void GlobalEnvConstants::InitRootsClass([[maybe_unused]] JSThread *thread, JSHCl
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::CELL_RECORD_CLASS_INDEX,
factory->NewEcmaDynClass(dynClassClass, CellRecord::SIZE, JSType::CELL_RECORD));
}
void GlobalEnvConstants::InitGlobalConstantSpecial(JSThread *thread)

View File

@ -90,7 +90,8 @@ class JSThread;
V(JSTaggedValue, JSAPIVectorIteratorClass, JS_API_VECTOR_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, JSAPIIteratorFuncDynClass, JS_API_ITERATOR_FUNC_DYN_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)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define GLOBAL_ENV_CONSTANT_SPECIAL(V) \

View File

@ -0,0 +1,247 @@
/*
* 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/js_finalization_registry.h"
#include "ecmascript/ecma_macros.h"
#include "ecmascript/global_env.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/jobs/micro_job_queue.h"
#include "ecmascript/js_function.h"
#include "ecmascript/linked_hash_table.h"
namespace panda::ecmascript {
// -------------------------------CellRecordVector-----------------------------------
JSHandle<CellRecordVector> CellRecordVector::Append(const JSThread *thread, const JSHandle<CellRecordVector> &array,
const JSHandle<JSTaggedValue> &value)
{
if (!array->Full()) {
array->PushBack(thread, value.GetTaggedValue());
return array;
}
// if exist hole, use it.
uint32_t holeIndex = CheckHole(array);
if (holeIndex != TaggedArray::MAX_ARRAY_INDEX) {
array->Set(thread, holeIndex, value.GetTaggedValue());
return array;
}
// the vector is full and no hole exists.
uint32_t newCapacity = array->GetCapacity() + DEFAULT_GROW_SIZE;
JSHandle<WeakVector> newArray = WeakVector::Grow(thread, JSHandle<WeakVector>(array), newCapacity);
[[maybe_unused]] uint32_t arrayIndex = newArray->PushBack(thread, value.GetTaggedValue());
ASSERT(arrayIndex != TaggedArray::MAX_ARRAY_INDEX);
return JSHandle<CellRecordVector>(newArray);
}
bool CellRecordVector::IsEmpty()
{
if (Empty()) {
return true;
}
for (uint32_t i = 0; i < GetEnd(); i++) {
JSTaggedValue value = Get(i);
if (value != JSTaggedValue::Hole()) {
return false;
}
}
return true;
}
uint32_t CellRecordVector::CheckHole(const JSHandle<CellRecordVector> &array)
{
for (uint32_t i = 0; i < array->GetEnd(); i++) {
JSTaggedValue value = array->Get(i);
if (value == JSTaggedValue::Hole()) {
return i;
}
}
return TaggedArray::MAX_ARRAY_INDEX;
}
// ---------------------------JSFinalizationRegistry-----------------------------------
void JSFinalizationRegistry::Register(JSThread *thread, JSHandle<JSTaggedValue> target,
JSHandle<JSTaggedValue> heldValue,
JSHandle<JSTaggedValue> unregisterToken, JSHandle<JSFinalizationRegistry> obj)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<CellRecord> cellRecord = factory->NewCellRecord();
cellRecord->SetToWeakRefTarget(target.GetTaggedValue());
cellRecord->SetHeldValue(thread, heldValue);
// If unregisterToken is undefined, we use vector to store
// otherwise we use hash map to store to facilitate subsequent delete operations
if (!unregisterToken->IsUndefined()) {
JSHandle<LinkedHashMap> maybeUnregister(thread, obj->GetMaybeUnregister());
JSHandle<JSTaggedValue> cell(cellRecord);
maybeUnregister = LinkedHashMap::SetWeakRef(thread, maybeUnregister, unregisterToken, cell);
obj->SetMaybeUnregister(thread, maybeUnregister);
} else {
JSHandle<CellRecordVector> noUnregister(thread, obj->GetNoUnregister());
JSHandle<JSTaggedValue> cellRecordValue(cellRecord);
noUnregister = CellRecordVector::Append(thread, noUnregister, cellRecordValue);
obj->SetNoUnregister(thread, noUnregister);
}
JSFinalizationRegistry::AddFinRegLists(thread, obj);
}
bool JSFinalizationRegistry::Unregister(JSThread *thread, JSHandle<JSTaggedValue> UnregisterToken,
JSHandle<JSFinalizationRegistry> obj)
{
// Because we have stored the object that may be unregistered in the hash map when registering,
// at this time we just need to find it in the map and delete it
JSHandle<LinkedHashMap> maybeUnregister(thread, obj->GetMaybeUnregister());
int entry = maybeUnregister->FindElement(UnregisterToken.GetTaggedValue());
if (entry == -1) {
return false;
}
maybeUnregister->RemoveEntry(thread, entry);
JSHandle<LinkedHashMap> newMaybeUnregister = LinkedHashMap::Shrink(thread, maybeUnregister);
obj->SetMaybeUnregister(thread, newMaybeUnregister);
return true;
}
void JSFinalizationRegistry::CleanFinRegLists(JSThread *thread, JSHandle<JSFinalizationRegistry> obj)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
if (obj->GetPrev().IsNull() && obj->GetNext().IsNull()) {
env->SetFinRegLists(thread, JSTaggedValue::Undefined());
return;
}
if (!obj->GetPrev().IsNull()) {
JSHandle<JSFinalizationRegistry> prev(thread, obj->GetPrev());
prev->SetNext(obj->GetNext());
}
if (!obj->GetNext().IsNull()) {
JSHandle<JSFinalizationRegistry> next(thread, obj->GetNext());
next->SetPrev(obj->GetPrev());
} else {
env->SetFinRegLists(thread, obj->GetPrev());
}
obj->SetPrev(thread, JSTaggedValue::Null());
obj->SetNext(thread, JSTaggedValue::Null());
}
void JSFinalizationRegistry::CheckAndCall(JSThread *thread)
{
[[maybe_unused]] CheckAndCallScrop scrop(thread);
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> prev = env->GetFinRegLists();
if (prev->IsUndefined()) {
return;
}
JSMutableHandle<JSTaggedValue> current(thread, prev.GetTaggedValue());
JSMutableHandle<JSFinalizationRegistry> jsFinalizationRegistry(thread, current.GetTaggedValue());
while (!current->IsNull()) {
jsFinalizationRegistry.Update(current.GetTaggedValue());
current.Update(jsFinalizationRegistry->GetPrev());
if (CleanupFinalizationRegistry(thread, jsFinalizationRegistry)) {
// If the objects registered on the current JSFinalizationRegistry object have all been gc,
// then we clean up this JSFinalizationRegistry object from the FinRegLists
CleanFinRegLists(thread, jsFinalizationRegistry);
}
}
}
bool JSFinalizationRegistry::CleanupFinalizationRegistry(JSThread *thread, JSHandle<JSFinalizationRegistry> obj)
{
// 1. Assert: finalizationRegistry has [[Cells]] and [[CleanupCallback]] internal slots.
// 2. Let callback be finalizationRegistry.[[CleanupCallback]].
// 3. While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is empty,
// an implementation may perform the following steps:
// a. Choose any such cell.
// b. Remove cell from finalizationRegistry.[[Cells]].
// c. Perform ? HostCallJobCallback(callback, undefined, « cell.[[HeldValue]] »).
// 4. Return unused.
ASSERT(obj->IsECMAObject());
auto ecmaVm = thread->GetEcmaVM();
JSHandle<job::MicroJobQueue> job = ecmaVm->GetMicroJobQueue();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSFunction> func(thread, obj->GetCleanupCallback());
JSHandle<CellRecordVector> noUnregister(thread, obj->GetNoUnregister());
if (!noUnregister->Empty()) {
uint32_t noUnregisterLen = noUnregister->GetEnd();
for (uint32_t i = 0; i < noUnregisterLen; ++i) {
JSTaggedValue value = noUnregister->Get(i);
if (value.IsHole()) {
continue;
}
JSHandle<CellRecord> cellRecord(thread, value);
// if WeakRefTarget have been gc, set callback to job and delete
if (cellRecord->GetFromWeakRefTarget().IsUndefined()) {
JSHandle<TaggedArray> argv = factory->NewTaggedArray(1);
argv->Set(thread, 0, cellRecord->GetHeldValue());
job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, func, argv);
noUnregister->Delete(thread, i);
}
}
}
JSMutableHandle<LinkedHashMap> maybeUnregister(thread, obj->GetMaybeUnregister());
int index = 0;
int totalElements = maybeUnregister->NumberOfElements() + maybeUnregister->NumberOfDeletedElements();
JSMutableHandle<JSTaggedValue> key(thread, maybeUnregister->GetKey(index));
while (index < totalElements) {
key.Update(maybeUnregister->GetKey(index++));
if (!key->IsHole()) {
JSMutableHandle<CellRecord> cellRecord(thread, maybeUnregister->GetValue(index - 1));
if (!cellRecord->GetFromWeakRefTarget().IsUndefined()) {
continue;
}
// WeakRefTarget have been gc, set callback to job and delete
JSHandle<TaggedArray> argv = factory->NewTaggedArray(1);
argv->Set(thread, 0, cellRecord->GetHeldValue());
job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, func, argv);
maybeUnregister->RemoveEntry(thread, index - 1);
// Maybe add or delete
JSTaggedValue nextTable = maybeUnregister->GetNextTable();
while (!nextTable.IsHole()) {
index -= maybeUnregister->GetDeletedElementsAt(index);
maybeUnregister.Update(nextTable);
nextTable = maybeUnregister->GetNextTable();
}
totalElements = maybeUnregister->NumberOfElements() + maybeUnregister->NumberOfDeletedElements();
}
}
JSHandle<LinkedHashMap> newMap = LinkedHashMap::Shrink(thread, maybeUnregister);
obj->SetMaybeUnregister(thread, newMap);
// Determine whether the objects registered on the current JSFinalizationRegistry object
// have all been gc
int remainSize = newMap->NumberOfElements() + newMap->NumberOfDeletedElements();
if (noUnregister->IsEmpty() && remainSize == 0) {
return true;
}
return false;
}
void JSFinalizationRegistry::AddFinRegLists(JSThread *thread, JSHandle<JSFinalizationRegistry> next)
{
// If any of prev and next is not null, it means that the current object is already in the linked list,
// ignore this addition
if (!next->GetPrev().IsNull() || !next->GetNext().IsNull()) {
return;
}
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> lists = env->GetFinRegLists();
if (!lists->IsUndefined()) {
JSHandle<JSFinalizationRegistry> prev(lists);
// Prevent the same object from being connected end to end,
// which should not happen and will lead to an infinite loop
if (JSTaggedValue::SameValue(next.GetTaggedValue(), prev.GetTaggedValue())) {
return;
}
prev->SetNext(thread, next);
next->SetPrev(thread, prev);
}
env->SetFinRegLists(thread, next);
}
} // namespace

View File

@ -0,0 +1,100 @@
/*
* 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_FINALIZATION_REGISTRY_H
#define ECMASCRIPT_JS_FINALIZATION_REGISTRY_H
#include "ecmascript/js_object.h"
#include "ecmascript/record.h"
#include "ecmascript/weak_vector.h"
namespace panda::ecmascript {
class CheckAndCallScrop {
public:
CheckAndCallScrop(JSThread *thread) : thread_(thread)
{
thread_->SetCheckAndCallEnterState(true);
}
~CheckAndCallScrop()
{
thread_->SetCheckAndCallEnterState(false);
}
private:
JSThread *thread_;
};
class CellRecord final : public Record {
public:
CAST_CHECK(CellRecord, IsCellRecord);
void SetToWeakRefTarget(JSTaggedValue value)
{
JSTaggedValue weakObj = JSTaggedValue(value.CreateAndGetWeakRef());
ASSERT(weakObj.IsWeak());
SetWeakRefTarget(weakObj);
}
JSTaggedValue GetFromWeakRefTarget() const
{
JSTaggedValue weakObj = GetWeakRefTarget();
if (!weakObj.IsUndefined()) {
return JSTaggedValue(weakObj.GetWeakReferent());
}
return weakObj;
}
static constexpr size_t WEAKREF_TARGET_OFFSET = Record::SIZE;
ACCESSORS(WeakRefTarget, WEAKREF_TARGET_OFFSET, HELD_VALUE_OFFSET)
ACCESSORS(HeldValue, HELD_VALUE_OFFSET, SIZE)
DECL_VISIT_OBJECT(WEAKREF_TARGET_OFFSET, SIZE)
DECL_DUMP()
};
class CellRecordVector : public WeakVector {
public:
static CellRecordVector *Cast(ObjectHeader *object)
{
return static_cast<CellRecordVector *>(object);
}
static constexpr uint32_t DEFAULT_GROW_SIZE = 5; // If the capacity is not enough, we Expansion five each time
static JSHandle<CellRecordVector> Append(const JSThread *thread, const JSHandle<CellRecordVector> &array,
const JSHandle<JSTaggedValue> &value);
bool IsEmpty();
private:
static uint32_t CheckHole(const JSHandle<CellRecordVector> &array);
};
class JSFinalizationRegistry : public JSObject {
public:
CAST_CHECK(JSFinalizationRegistry, IsJSFinalizationRegistry);
static void Register(JSThread *thread, JSHandle<JSTaggedValue> target, JSHandle<JSTaggedValue> heldValue,
JSHandle<JSTaggedValue> unregisterToken, JSHandle<JSFinalizationRegistry> obj);
static bool Unregister(JSThread *thread, JSHandle<JSTaggedValue> UnregisterToken,
JSHandle<JSFinalizationRegistry> obj);
static void CheckAndCall(JSThread *thread);
static bool CleanupFinalizationRegistry(JSThread *thread, JSHandle<JSFinalizationRegistry> obj);
static void AddFinRegLists(JSThread *thread, JSHandle<JSFinalizationRegistry> next);
static void CleanFinRegLists(JSThread *thread, JSHandle<JSFinalizationRegistry> obj);
static constexpr size_t CLEANUP_CALLBACK_OFFSET = JSObject::SIZE;
ACCESSORS(CleanupCallback, CLEANUP_CALLBACK_OFFSET, NO_UNREGISTER_OFFSET)
ACCESSORS(NoUnregister, NO_UNREGISTER_OFFSET, MAYBE_UNREGISTER_OFFSET)
ACCESSORS(MaybeUnregister, MAYBE_UNREGISTER_OFFSET, NEXT_OFFSET)
ACCESSORS(Next, NEXT_OFFSET, PREV_OFFSET)
ACCESSORS(Prev, PREV_OFFSET, SIZE)
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, CLEANUP_CALLBACK_OFFSET, SIZE)
DECL_DUMP()
};
} // namespace
#endif // ECMASCRIPT_JS_FINALIZATION_REGISTRY_H

View File

@ -90,6 +90,8 @@ class ProtoChangeDetails;
JS_MAP, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_WEAK_MAP, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_WEAK_SET, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_WEAK_REF, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_FINALIZATION_REGISTRY, /* //////////////////////////////////////////////////////////////////////-PADDING */ \
JS_DATE, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_ITERATOR, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \
JS_FORIN_ITERATOR, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \
@ -185,6 +187,7 @@ class ProtoChangeDetails;
IMPORTENTRY_RECORD, /* /////////////////////////////////////////////////////////////////////////-PADDING */ \
EXPORTENTRY_RECORD, /* /////////////////////////////////////////////////////////////////////////-PADDING */ \
RESOLVEDBINDING_RECORD, /* /////////////////////////////////////////////////////////////////////-PADDING */ \
CELL_RECORD, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \
COMPLETION_RECORD, /* JS_RECORD_END /////////////////////////////////////////////////////////////////////// */ \
MACHINE_CODE_OBJECT, \
CLASS_INFO_EXTRACTOR, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \
@ -590,6 +593,16 @@ public:
return GetObjectType() == JSType::JS_WEAK_SET;
}
bool IsJSWeakRef() const
{
return GetObjectType() == JSType::JS_WEAK_REF;
}
bool IsJSFinalizationRegistry() const
{
return GetObjectType() == JSType::JS_FINALIZATION_REGISTRY;
}
bool IsJSFunction() const
{
return GetObjectType() >= JSType::JS_FUNCTION_BEGIN && GetObjectType() <= JSType::JS_FUNCTION_END;
@ -954,6 +967,11 @@ public:
return GetObjectType() == JSType::PROMISE_REACTIONS;
}
inline bool IsCellRecord() const
{
return GetObjectType() == JSType::CELL_RECORD;
}
inline bool IsCompletionRecord() const
{
return GetObjectType() == JSType::COMPLETION_RECORD;

View File

@ -790,6 +790,21 @@ inline bool JSTaggedValue::IsJSSet() const
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSSet();
}
inline bool JSTaggedValue::IsJSWeakRef() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSWeakRef();
}
inline bool JSTaggedValue::IsJSFinalizationRegistry() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFinalizationRegistry();
}
inline bool JSTaggedValue::IsCellRecord() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsCellRecord();
}
inline bool JSTaggedValue::IsJSRegExp() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSRegExp();

View File

@ -472,6 +472,9 @@ public:
bool IsJSSet() const;
bool IsJSWeakMap() const;
bool IsJSWeakSet() const;
bool IsJSWeakRef() const;
bool IsJSFinalizationRegistry() const;
bool IsCellRecord() const;
bool IsJSRegExp() const;
bool IsNumber() const;
bool IsBigInt() const;

View File

@ -389,6 +389,16 @@ public:
return gcState_;
}
void SetCheckAndCallEnterState(bool checkAndCallEnterState)
{
checkAndCallEnterState_ = checkAndCallEnterState;
}
bool GetCheckAndCallEnterState() const
{
return checkAndCallEnterState_;
}
void EnableAsmInterpreter()
{
isAsmInterpreter_ = true;
@ -578,6 +588,7 @@ private:
bool stableArrayElementsGuardians_ {true};
GlueData glueData_;
bool checkAndCallEnterState_ {false};
friend class EcmaHandleScope;
friend class GlobalHandleCollection;
};

67
ecmascript/js_weak_ref.h Normal file
View File

@ -0,0 +1,67 @@
/*
* 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_WEAK_REF_H
#define ECMASCRIPT_JS_WEAK_REF_H
#include "ecmascript/js_object.h"
namespace panda::ecmascript {
class JSWeakRef : public JSObject {
public:
static JSWeakRef *Cast(ObjectHeader *object)
{
ASSERT(JSTaggedValue(object).IsJSWeakRef());
return static_cast<JSWeakRef *>(object);
}
// 26.1.4.1 WeakRefDeref ( weakRef )
static JSTaggedValue WeakRefDeref(JSThread *thread, const JSHandle<JSWeakRef> &weakRef)
{
// 1. Let target be weakRef.[[WeakRefTarget]].
// 2. If target is not empty, then
// a. Perform ! AddToKeptObjects(target).
// b. Return target.
// 3. Return undefined.
JSHandle<JSTaggedValue> target(thread, weakRef->GetFromWeak());
if (!target->IsUndefined()) {
thread->GetEcmaVM()->GetHeap()->AddToKeptObjects(target);
}
return target.GetTaggedValue();
}
void SetToWeak(JSTaggedValue value)
{
JSTaggedValue weakObj = JSTaggedValue(value.CreateAndGetWeakRef());
ASSERT(weakObj.IsWeak());
SetWeakObject(weakObj);
}
JSTaggedValue GetFromWeak() const
{
JSTaggedValue weakObj = GetWeakObject();
if (!weakObj.IsUndefined()) {
return JSTaggedValue(weakObj.GetWeakReferent());
}
return weakObj;
}
static constexpr size_t WEAK_OBJECT_OFFSET = JSObject::SIZE;
ACCESSORS(WeakObject, WEAK_OBJECT_OFFSET, SIZE)
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, WEAK_OBJECT_OFFSET, SIZE)
DECL_DUMP()
};
} // namespace
#endif // ECMASCRIPT_JS_WEAK_REF_H

View File

@ -104,6 +104,7 @@ void ConcurrentMarker::Reset(bool revertCSet)
auto callback = [](Region *region) {
region->ClearMarkGCBitset();
region->ClearCrossRegionRSet();
region->ResetAliveObject();
};
if (heap_->IsFullMark()) {
heap_->EnumerateRegions(callback);

View File

@ -24,6 +24,7 @@
#include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
#endif
#include "ecmascript/ecma_vm.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/mem/assert_scope.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/mem/concurrent_sweeper.h"
@ -40,6 +41,7 @@
#include "ecmascript/mem/gc_stats.h"
#include "ecmascript/ecma_string_table.h"
#include "ecmascript/runtime_call_id.h"
#include "ecmascript/js_finalization_registry.h"
namespace panda::ecmascript {
Heap::Heap(EcmaVM *ecmaVm) : ecmaVm_(ecmaVm), thread_(ecmaVm->GetJSThread()),
@ -327,6 +329,9 @@ void Heap::CollectGarbage(TriggerGCType gcType)
}
isVerifying_ = false;
#endif
if (!thread_->GetCheckAndCallEnterState()) {
JSFinalizationRegistry::CheckAndCall(thread_);
}
}
void Heap::ThrowOutOfMemoryError(size_t size, std::string functionName)
@ -392,6 +397,25 @@ void Heap::AdjustOldSpaceLimit()
<< " globalSpaceAllocLimit_" << globalSpaceAllocLimit_;
}
void Heap::AddToKeptObjects(JSHandle<JSTaggedValue> value) const
{
JSHandle<GlobalEnv> env = ecmaVm_->GetGlobalEnv();
JSHandle<LinkedHashSet> linkedSet;
if (env->GetWeakRefKeepObjects()->IsUndefined()) {
linkedSet = LinkedHashSet::Create(thread_);
} else {
linkedSet =
JSHandle<LinkedHashSet>(thread_, LinkedHashSet::Cast(env->GetWeakRefKeepObjects()->GetTaggedObject()));
}
linkedSet = LinkedHashSet::Add(thread_, linkedSet, value);
env->SetWeakRefKeepObjects(thread_, linkedSet);
}
void Heap::ClearKeptObjects() const
{
ecmaVm_->GetGlobalEnv()->SetWeakRefKeepObjects(thread_, JSTaggedValue::Undefined());
}
void Heap::RecomputeLimits()
{
double gcSpeed = memController_->CalculateMarkCompactSpeedPerMS();

View File

@ -356,7 +356,8 @@ public:
inline void OnAllocateEvent(uintptr_t address);
inline void OnMoveEvent(uintptr_t address, uintptr_t forwardAddress);
void AddToKeptObjects(JSHandle<JSTaggedValue> value) const;
void ClearKeptObjects() const;
/*
* Funtions used by heap verification.
*/

View File

@ -53,6 +53,7 @@
#include "ecmascript/js_date.h"
#include "ecmascript/js_date_time_format.h"
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/js_finalization_registry.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_generator_object.h"
#include "ecmascript/js_hclass.h"
@ -78,6 +79,7 @@
#include "ecmascript/js_weak_container.h"
#include "ecmascript/jspandafile/class_info_extractor.h"
#include "ecmascript/jspandafile/program_object.h"
#include "ecmascript/js_weak_ref.h"
#include "ecmascript/mem/machine_code.h"
#include "ecmascript/mem/mem.h"
#include "ecmascript/mem/slots.h"
@ -204,6 +206,15 @@ public:
case JSType::JS_WEAK_SET:
JSWeakSet::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::JS_WEAK_REF:
JSWeakRef::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::JS_FINALIZATION_REGISTRY:
JSFinalizationRegistry::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::CELL_RECORD:
CellRecord::Cast(object)->VisitRangeSlot(visitor);
break;
case JSType::JS_DATE:
JSDate::Cast(object)->VisitRangeSlot(visitor);
break;

View File

@ -992,6 +992,7 @@ Local<JSValueRef> FunctionRef::Call(const EcmaVM *vm, Local<JSValueRef> thisObj,
EcmaVM::ConstCast(vm)->ExecutePromisePendingJob();
RETURN_VALUE_IF_ABRUPT_NOT_CLEAR_EXCEPTION(thread, JSValueRef::Exception(vm));
vm->GetHeap()->ClearKeptObjects();
return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(resultValue));
}
@ -1138,6 +1139,7 @@ bool PromiseCapabilityRef::Resolve(const EcmaVM *vm, Local<JSValueRef> value)
EcmaVM::ConstCast(vm)->ExecutePromisePendingJob();
RETURN_VALUE_IF_ABRUPT(thread, false);
vm->GetHeap()->ClearKeptObjects();
return true;
}
@ -1159,6 +1161,7 @@ bool PromiseCapabilityRef::Reject(const EcmaVM *vm, Local<JSValueRef> reason)
EcmaVM::ConstCast(vm)->ExecutePromisePendingJob();
RETURN_VALUE_IF_ABRUPT(thread, false);
vm->GetHeap()->ClearKeptObjects();
return true;
}

View File

@ -71,6 +71,7 @@
#include "ecmascript/js_date_time_format.h"
#include "ecmascript/js_displaynames.h"
#include "ecmascript/js_list_format.h"
#include "ecmascript/js_finalization_registry.h"
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/js_generator_object.h"
#include "ecmascript/js_hclass-inl.h"
@ -96,6 +97,7 @@
#include "ecmascript/js_thread.h"
#include "ecmascript/js_typed_array.h"
#include "ecmascript/js_weak_container.h"
#include "ecmascript/js_weak_ref.h"
#include "ecmascript/layout_info-inl.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/mem/heap-inl.h"
@ -1030,6 +1032,16 @@ void ObjectFactory::InitializeJSObject(const JSHandle<JSObject> &obj, const JSHa
case JSType::JS_WEAK_SET:
JSWeakSet::Cast(*obj)->SetLinkedSet(thread_, JSTaggedValue::Undefined());
break;
case JSType::JS_WEAK_REF:
JSWeakRef::Cast(*obj)->SetWeakObject(thread_, JSTaggedValue::Undefined());
break;
case JSType::JS_FINALIZATION_REGISTRY:
JSFinalizationRegistry::Cast(*obj)->SetCleanupCallback(thread_, JSTaggedValue::Undefined());
JSFinalizationRegistry::Cast(*obj)->SetNoUnregister(thread_, JSTaggedValue::Undefined());
JSFinalizationRegistry::Cast(*obj)->SetMaybeUnregister(thread_, JSTaggedValue::Undefined());
JSFinalizationRegistry::Cast(*obj)->SetNext(thread_, JSTaggedValue::Null());
JSFinalizationRegistry::Cast(*obj)->SetPrev(thread_, JSTaggedValue::Null());
break;
case JSType::JS_GENERATOR_OBJECT:
JSGeneratorObject::Cast(*obj)->SetGeneratorContext(thread_, JSTaggedValue::Undefined());
JSGeneratorObject::Cast(*obj)->SetResumeResult(thread_, JSTaggedValue::Undefined());
@ -3090,4 +3102,15 @@ JSHandle<ResolvedBinding> ObjectFactory::NewResolvedBindingRecord(const JSHandle
obj->SetBindingName(thread_, bindingName);
return obj;
}
JSHandle<CellRecord> ObjectFactory::NewCellRecord()
{
NewObjectHook();
TaggedObject *header = heap_->AllocateYoungOrHugeObject(
JSHClass::Cast(thread_->GlobalConstants()->GetCellRecordClass().GetTaggedObject()));
JSHandle<CellRecord> obj(thread_, header);
obj->SetWeakRefTarget(thread_, JSTaggedValue::Undefined());
obj->SetHeldValue(thread_, JSTaggedValue::Undefined());
return obj;
}
} // namespace panda::ecmascript

View File

@ -120,6 +120,7 @@ class ExportEntry;
class SourceTextModule;
class ResolvedBinding;
class BigInt;
class CellRecord;
namespace job {
class MicroJobQueue;
class PendingJob;
@ -468,6 +469,7 @@ public:
JSHandle<ResolvedBinding> NewResolvedBindingRecord();
JSHandle<ResolvedBinding> NewResolvedBindingRecord(const JSHandle<SourceTextModule> &module,
const JSHandle<JSTaggedValue> &bindingName);
JSHandle<CellRecord> NewCellRecord();
private:
friend class GlobalEnv;

View File

@ -542,6 +542,11 @@ namespace panda::ecmascript {
V(WeakSet, Delete) \
V(WeakSet, Add) \
V(WeakSet, Has) \
V(WeakRef, Constructor) \
V(WeakRef, Deref) \
V(FinalizationRegistry, Constructor) \
V(FinalizationRegistry, Register) \
V(FinalizationRegistry, Unregister) \
V(ArrayList, Constructor) \
V(ArrayList, Add) \
V(ArrayList, Insert) \

View File

@ -26,6 +26,7 @@
#include "ecmascript/builtins/builtins_date.h"
#include "ecmascript/builtins/builtins_date_time_format.h"
#include "ecmascript/builtins/builtins_errors.h"
#include "ecmascript/builtins/builtins_finalization_registry.h"
#include "ecmascript/builtins/builtins_function.h"
#include "ecmascript/builtins/builtins_generator.h"
#include "ecmascript/builtins/builtins_global.h"
@ -52,6 +53,7 @@
#include "ecmascript/builtins/builtins_symbol.h"
#include "ecmascript/builtins/builtins_typedarray.h"
#include "ecmascript/builtins/builtins_weak_map.h"
#include "ecmascript/builtins/builtins_weak_ref.h"
#include "ecmascript/builtins/builtins_weak_set.h"
#include "ecmascript/containers/containers_arraylist.h"
#include "ecmascript/containers/containers_deque.h"
@ -99,6 +101,8 @@ using BuiltinsMap = builtins::BuiltinsMap;
using BuiltinsSet = builtins::BuiltinsSet;
using BuiltinsWeakMap = builtins::BuiltinsWeakMap;
using BuiltinsWeakSet = builtins::BuiltinsWeakSet;
using BuiltinsWeakRef = builtins::BuiltinsWeakRef;
using BuiltinsFinalizationRegistry = builtins::BuiltinsFinalizationRegistry;
using BuiltinsArray = builtins::BuiltinsArray;
using BuiltinsTypedArray = builtins::BuiltinsTypedArray;
using BuiltinsIterator = builtins::BuiltinsIterator;
@ -325,6 +329,11 @@ static uintptr_t g_nativeTable[] = {
reinterpret_cast<uintptr_t>(BuiltinsWeakSet::Add),
reinterpret_cast<uintptr_t>(BuiltinsWeakSet::Delete),
reinterpret_cast<uintptr_t>(BuiltinsWeakSet::Has),
reinterpret_cast<uintptr_t>(BuiltinsWeakRef::WeakRefConstructor),
reinterpret_cast<uintptr_t>(BuiltinsWeakRef::Deref),
reinterpret_cast<uintptr_t>(BuiltinsFinalizationRegistry::FinalizationRegistryConstructor),
reinterpret_cast<uintptr_t>(BuiltinsFinalizationRegistry::Register),
reinterpret_cast<uintptr_t>(BuiltinsFinalizationRegistry::Unregister),
reinterpret_cast<uintptr_t>(BuiltinsArray::ArrayConstructor),
reinterpret_cast<uintptr_t>(BuiltinsArray::Concat),
reinterpret_cast<uintptr_t>(BuiltinsArray::CopyWithin),

View File

@ -55,6 +55,7 @@
#include "ecmascript/js_dataview.h"
#include "ecmascript/js_date.h"
#include "ecmascript/js_date_time_format.h"
#include "ecmascript/js_finalization_registry.h"
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_generator_object.h"
@ -83,6 +84,7 @@
#include "ecmascript/js_thread.h"
#include "ecmascript/js_typed_array.h"
#include "ecmascript/js_weak_container.h"
#include "ecmascript/js_weak_ref.h"
#include "ecmascript/layout_info-inl.h"
#include "ecmascript/lexical_env.h"
#include "ecmascript/linked_hash_table.h"
@ -425,6 +427,31 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump)
DUMP_FOR_HANDLE(jsWeakSet)
break;
}
case JSType::JS_WEAK_REF: {
CHECK_DUMP_FIELDS(JSObject::SIZE, JSWeakRef::SIZE, 1U)
JSHandle<JSHClass> weakRefClass = factory->NewEcmaDynClass(JSWeakRef::SIZE, JSType::JS_WEAK_REF, proto);
JSHandle<JSWeakRef> jsWeakRef = JSHandle<JSWeakRef>::Cast(factory->NewJSObjectWithInit(weakRefClass));
jsWeakRef->SetWeakObject(thread, JSTaggedValue::Undefined());
DUMP_FOR_HANDLE(jsWeakRef)
break;
}
case JSType::JS_FINALIZATION_REGISTRY: {
CHECK_DUMP_FIELDS(JSObject::SIZE, JSFinalizationRegistry::SIZE, 5U)
JSHandle<JSHClass> finalizationRegistryClass =
factory->NewEcmaDynClass(JSFinalizationRegistry::SIZE, JSType::JS_FINALIZATION_REGISTRY, proto);
JSHandle<JSFinalizationRegistry> jsFinalizationRegistry =
JSHandle<JSFinalizationRegistry>::Cast(factory->NewJSObjectWithInit(finalizationRegistryClass));
JSHandle<LinkedHashMap> weakLinkedMap(LinkedHashMap::Create(thread));
jsFinalizationRegistry->SetMaybeUnregister(thread, weakLinkedMap);
DUMP_FOR_HANDLE(jsFinalizationRegistry)
break;
}
case JSType::CELL_RECORD: {
CHECK_DUMP_FIELDS(Record::SIZE, CellRecord::SIZE, 2U)
JSHandle<CellRecord> cellRecord = factory->NewCellRecord();
DUMP_FOR_HANDLE(cellRecord)
break;
}
case JSType::JS_DATE: {
CHECK_DUMP_FIELDS(JSObject::SIZE, JSDate::SIZE, 2U)
JSHandle<JSHClass> dateClass = factory->NewEcmaDynClass(JSDate::SIZE, JSType::JS_DATE, proto);

View File

@ -0,0 +1,18 @@
# Copyright (c) 2021 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.
import("//ark/js_runtime/test/test_helper.gni")
host_moduletest_action("finalizationregistry") {
deps = []
}

View File

@ -0,0 +1,16 @@
# Copyright (c) 2021 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.
hello=====3333
hello====222
hello

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2021 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.
*/
var fn = function(held) {
print(held);
}
var obj = {name:'ddd'};
var obj2 = {name:'fff'};
var fin = new FinalizationRegistry(fn);
var fin2 = new FinalizationRegistry(fn);
var fin3 = new FinalizationRegistry(fn);
if(true){
fin.register(obj, 'hello');
fin.register(obj2, 'hello123');
fin2.register(obj, 'hello====222');
fin2.register(obj2, 'hello====456');
fin3.register(obj, 'hello=====3333');
fin3.register(obj2, 'hello=====789');
}
obj = undefined;
// Create variable to wait for variable obj to be GC
for(var i = 0; i < 500000; ++i) {
var a = BigInt(123);
}