mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-11-23 18:20:04 +00:00
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:
parent
4c7e59abb1
commit
86bda19980
3
BUILD.gn
3
BUILD.gn
@ -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",
|
||||
|
@ -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_);
|
||||
|
@ -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;
|
||||
|
131
ecmascript/builtins/builtins_finalization_registry.cpp
Normal file
131
ecmascript/builtins/builtins_finalization_registry.cpp
Normal 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
|
33
ecmascript/builtins/builtins_finalization_registry.h
Normal file
33
ecmascript/builtins/builtins_finalization_registry.h
Normal 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
|
70
ecmascript/builtins/builtins_weak_ref.cpp
Normal file
70
ecmascript/builtins/builtins_weak_ref.cpp
Normal 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
|
31
ecmascript/builtins/builtins_weak_ref.h
Normal file
31
ecmascript/builtins/builtins_weak_ref.h
Normal 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
|
@ -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",
|
||||
]
|
||||
|
||||
|
207
ecmascript/builtins/tests/builtins_finalizationregistry_test.cpp
Normal file
207
ecmascript/builtins/tests/builtins_finalizationregistry_test.cpp
Normal 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
|
162
ecmascript/builtins/tests/builtins_weak_ref_test.cpp
Normal file
162
ecmascript/builtins/tests/builtins_weak_ref_test.cpp
Normal 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
|
@ -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()));
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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) \
|
||||
|
247
ecmascript/js_finalization_registry.cpp
Normal file
247
ecmascript/js_finalization_registry.cpp
Normal 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
|
100
ecmascript/js_finalization_registry.h
Normal file
100
ecmascript/js_finalization_registry.h
Normal 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
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
67
ecmascript/js_weak_ref.h
Normal 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
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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) \
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
18
test/moduletest/finalizationregistry/BUILD.gn
Normal file
18
test/moduletest/finalizationregistry/BUILD.gn
Normal 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 = []
|
||||
}
|
16
test/moduletest/finalizationregistry/expect_output.txt
Normal file
16
test/moduletest/finalizationregistry/expect_output.txt
Normal 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
|
37
test/moduletest/finalizationregistry/finalizationregistry.js
Normal file
37
test/moduletest/finalizationregistry/finalizationregistry.js
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user