From 63fdeafb72e09b4e39d299ed9b8be9b1982ad0d2 Mon Sep 17 00:00:00 2001 From: Petrov Igor Date: Wed, 16 Nov 2022 16:12:21 +0300 Subject: [PATCH] [MM] Object pinning interface Signed-off-by: Petrov Igor --- runtime/builtins.cpp | 3 ++ runtime/builtins/builtins_global.cpp | 45 ++++++++++++++++++++++++++++ runtime/builtins/builtins_global.h | 3 ++ runtime/runtime_call_id.h | 3 ++ tests/runtime/common/CMakeLists.txt | 1 + tests/runtime/common/pinObject.js | 44 +++++++++++++++++++++++++++ 6 files changed, 99 insertions(+) create mode 100644 tests/runtime/common/pinObject.js diff --git a/runtime/builtins.cpp b/runtime/builtins.cpp index a5ea5ca35..05bcea140 100644 --- a/runtime/builtins.cpp +++ b/runtime/builtins.cpp @@ -352,6 +352,9 @@ void Builtins::InitializeGlobalObject(const JSHandle &env, const JSHa SetFunction(env, globalObject, "getMarkQueue", Global::GetMarkQueue, 0); SetFunction(env, globalObject, "clearMarkQueue", Global::ClearMarkQueue, 0); SetFunction(env, globalObject, "getObjectSpaceType", Global::GCObjectSpaceType, 1); + SetFunction(env, globalObject, "pinObject", Global::PinObject, 1); + SetFunction(env, globalObject, "unpinObject", Global::UnpinObject, 1); + SetFunction(env, globalObject, "getObjectAddress", Global::GetObjectAddress, 1); #if ECMASCRIPT_ENABLE_RUNTIME_STAT SetFunction(env, globalObject, "startRuntimeStat", Global::StartRuntimeStat, 0); SetFunction(env, globalObject, "stopRuntimeStat", Global::StopRuntimeStat, 0); diff --git a/runtime/builtins/builtins_global.cpp b/runtime/builtins/builtins_global.cpp index 9eaf693cf..52e72c8be 100644 --- a/runtime/builtins/builtins_global.cpp +++ b/runtime/builtins/builtins_global.cpp @@ -1040,6 +1040,51 @@ JSHandle BuiltinsGlobal::GCSpecialisedObjectSpaceType(JSThread *t return global_constants->GetHandledTenuredSpaceString(); } +JSTaggedValue BuiltinsGlobal::PinObject(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, PinObject); + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle arg = GetCallArg(msg, 0); + if (arg->IsHeapObject()) { + auto *obj_allocator = vm->GetHeapManager()->GetObjectAllocator().AsObjectAllocator(); + auto *gc = vm->GetGC(); + if (gc->GetType() == mem::GCType::GEN_GC && obj_allocator->IsObjectInYoungSpace(arg->GetHeapObject())) { + auto task = MakePandaUnique(GCTaskCause::YOUNG_GC_CAUSE); + gc->WaitForGCInManaged(*task); + } + obj_allocator->PinObject(arg->GetHeapObject()); + } else { + JSHandle err = vm->GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "The value must be an object"); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, err.GetTaggedValue(), JSTaggedValue::Exception()); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsGlobal::UnpinObject(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, UnpinObject); + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle arg = GetCallArg(msg, 0); + if (arg->IsHeapObject()) { + vm->GetHeapManager()->GetObjectAllocator().AsObjectAllocator()->UnpinObject(arg->GetHeapObject()); + } else { + JSHandle err = vm->GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "The value must be an object"); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, err.GetTaggedValue(), JSTaggedValue::Exception()); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsGlobal::GetObjectAddress(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, GetObjectAddress); + PandaStringStream addr_stream; + addr_stream << GetCallArg(msg, 0)->GetHeapObject(); + return thread->GetEcmaVM()->GetFactory()->NewFromString(addr_stream.str()).GetTaggedValue(); +} + JSTaggedValue BuiltinsGlobal::CallJsBoundFunction(EcmaRuntimeCallInfo *msg) { JSThread *thread = msg->GetThread(); diff --git a/runtime/builtins/builtins_global.h b/runtime/builtins/builtins_global.h index 8375805c2..5beba0792 100644 --- a/runtime/builtins/builtins_global.h +++ b/runtime/builtins/builtins_global.h @@ -57,6 +57,9 @@ public: static JSTaggedValue GetMarkQueue(EcmaRuntimeCallInfo *msg); static JSTaggedValue ClearMarkQueue(EcmaRuntimeCallInfo *msg); static JSTaggedValue GCObjectSpaceType(EcmaRuntimeCallInfo *msg); + static JSTaggedValue PinObject(EcmaRuntimeCallInfo *msg); + static JSTaggedValue UnpinObject(EcmaRuntimeCallInfo *msg); + static JSTaggedValue GetObjectAddress(EcmaRuntimeCallInfo *msg); static JSTaggedValue CallJsBoundFunction(EcmaRuntimeCallInfo *msg); static JSTaggedValue CallJsProxy(EcmaRuntimeCallInfo *msg); #if ECMASCRIPT_ENABLE_RUNTIME_STAT diff --git a/runtime/runtime_call_id.h b/runtime/runtime_call_id.h index e20e66776..607dcdbfc 100644 --- a/runtime/runtime_call_id.h +++ b/runtime/runtime_call_id.h @@ -333,6 +333,9 @@ namespace panda::ecmascript { V(Global, MarkObject) \ V(Global, GetMarkQueue) \ V(Global, ClearMarkQueue) \ + V(Global, PinObject) \ + V(Global, UnpinObject) \ + V(Global, GetObjectAddress) \ V(Global, AllocateArrayObject) \ V(Global, GCObjectSpaceType) \ V(Global, NewobjDynrange) \ diff --git a/tests/runtime/common/CMakeLists.txt b/tests/runtime/common/CMakeLists.txt index 5128b547a..cd7f53c1e 100644 --- a/tests/runtime/common/CMakeLists.txt +++ b/tests/runtime/common/CMakeLists.txt @@ -51,6 +51,7 @@ panda_add_ecma_test(FILE startGc.js OPTIONS "--gc-trigger-type=debug-never") panda_add_ecma_test(FILE scheduleGc.js OPTIONS "--gc-trigger-type=debug-never" "--gc-use-nth-alloc-trigger=true") panda_add_ecma_test(FILE spaceTypeTest.js OPTIONS "--gc-type=g1-gc" "--run-gc-in-place=true") panda_add_ecma_test(FILE concurrent.js OPTIONS "--gc-type=g1-gc" "--gc-trigger-type=debug-never") +panda_add_ecma_test(FILE pinObject.js OPTIONS "--gc-type=g1-gc" "--gc-trigger-type=debug-never") add_subdirectory(helloworld) add_subdirectory(newobjdynrange) diff --git a/tests/runtime/common/pinObject.js b/tests/runtime/common/pinObject.js new file mode 100644 index 000000000..680ee4f19 --- /dev/null +++ b/tests/runtime/common/pinObject.js @@ -0,0 +1,44 @@ +/* + * 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. + */ + +function assert(value, message) { + if (value) { + return; + } + print("Assertion failed: " + message); + throw Error(); +} + +function newWeakRefInYoung() { + // use a separate function because temporary object 'Object' + // are kept in vreg and GC treat it as a root. + return new WeakRef(new Object()); +} + +let ref = newWeakRefInYoung(); +{ + pinObject(ref); + assert(getObjectSpaceType(ref) === "young", "The object before GC must be placed in young space") + var addr_before_gc = getObjectAddress(ref); + // Run GC + let gc_id = startGC("mixed"); + waitForFinishGC(gc_id); + var addr_after_gc = getObjectAddress(ref); + assert(getObjectSpaceType(ref) === "tenured", "The pinned object after GC must be placed in tenured space") + unpinObject(ref); +} +assert(addr_before_gc === addr_after_gc, "Pinned object must has a fixed address.\n" + + "Address before gc = " + addr_before_gc + '\n' + + "Address after gc = " + addr_after_gc); -- Gitee