diff --git a/runtime/ecma_vm.cpp b/runtime/ecma_vm.cpp index ee911c9300b7a03ed6229637a5d0e74ee557fe62..3ce7379e87e880ad103e171fb6e0101a2d59649f 100644 --- a/runtime/ecma_vm.cpp +++ b/runtime/ecma_vm.cpp @@ -872,6 +872,22 @@ bool EcmaVM::ExecutePromisePendingJob() const return false; } +void *EcmaVM::AllocAndRegisterNative(size_t size) +{ + ScopedNativeCodeThread s(ManagedThread::GetCurrent()); + GetGC()->RegisterNativeAllocation(size); + void *mem = mm_->GetHeapManager()->GetInternalAllocator()->Alloc(size); + ASSERT(mem != nullptr); + return mem; +} + +void EcmaVM::FreeAndRegisterNative(void *mem, size_t size) +{ + ASSERT(mem != nullptr); + GetGC()->RegisterNativeFree(size); + mm_->GetHeapManager()->GetInternalAllocator()->Free(mem); +} + void EcmaVM::CollectGarbage() const { mm_->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE)); diff --git a/runtime/ecma_vm.h b/runtime/ecma_vm.h index 42b97378d4aec3255373dccc8a82b58e320234e4..013a36c21410c87b15d85eb60aac53bd64c92cf3 100644 --- a/runtime/ecma_vm.h +++ b/runtime/ecma_vm.h @@ -381,6 +381,16 @@ public: return optional_log_enabled_; } + /** + * \brief Calls GC::RegisterNativeAllocation and allocates memory in internal space + */ + [[nodiscard]] void *AllocAndRegisterNative(size_t size); + + /** + * \brief Calls GC::RegisterNativeFree and frees memory in internal space + */ + void FreeAndRegisterNative(void *mem, size_t size); + void Iterate(const RootVisitor &v); void CollectGarbage() const; diff --git a/runtime/object_factory.cpp b/runtime/object_factory.cpp index f69a6eede2421353cc1572e03de4594aae9834f7..9f4c94d51f03b99e8f0c6b4eac403c6860a9bc9c 100644 --- a/runtime/object_factory.cpp +++ b/runtime/object_factory.cpp @@ -81,9 +81,16 @@ namespace panda::ecmascript { using ErrorType = base::ErrorType; using ErrorHelper = base::ErrorHelper; -static void PandaFreeBufferFunc([[maybe_unused]] void *buffer, [[maybe_unused]] void *data) +static void PandaFreeBufferFunc(void *buffer, void *size) { - Runtime::GetCurrent()->GetInternalAllocator()->Free(buffer); + auto *thread = Thread::GetCurrent(); + // Need to check if thread is available because + // we can enter here after the runtime started termination + if (thread == nullptr) { + Runtime::GetCurrent()->GetInternalAllocator()->Free(buffer); + return; + } + reinterpret_cast(thread)->GetEcmaVM()->FreeAndRegisterNative(buffer, reinterpret_cast(size)); } ObjectFactory::ObjectFactory(JSThread *thread) @@ -188,11 +195,12 @@ void ObjectFactory::NewJSArrayBufferData(const JSHandle &array, i if (length == 0) { return; } + auto size = length * sizeof(uint8_t); - JSTaggedValue data = array->GetArrayBufferData(); - if (data != JSTaggedValue::Undefined()) { - auto *pointer = JSNativePointer::Cast(data.GetTaggedObject()); - auto new_data = Runtime::GetCurrent()->GetInternalAllocator()->Alloc(length * sizeof(uint8_t)); + JSHandle data(thread_, array->GetArrayBufferData()); + if (data.GetTaggedValue() != JSTaggedValue::Undefined()) { + JSHandle pointer(data); + auto *new_data = vm_->AllocAndRegisterNative(size); if (memset_s(new_data, length, 0, length) != EOK) { LOG_ECMA(FATAL) << "memset_s failed"; UNREACHABLE(); @@ -201,12 +209,13 @@ void ObjectFactory::NewJSArrayBufferData(const JSHandle &array, i return; } - auto new_data = Runtime::GetCurrent()->GetInternalAllocator()->Alloc(length * sizeof(uint8_t)); + auto *new_data = vm_->AllocAndRegisterNative(size); if (memset_s(new_data, length, 0, length) != EOK) { LOG_ECMA(FATAL) << "memset_s failed"; UNREACHABLE(); } - JSHandle pointer = NewJSNativePointer(new_data, PandaFreeBufferFunc); + JSHandle pointer = + NewJSNativePointer(new_data, PandaFreeBufferFunc, reinterpret_cast(size)); array->SetArrayBufferData(thread_, pointer.GetTaggedValue()); vm_->PushToArrayDataList(*pointer); } @@ -220,12 +229,13 @@ JSHandle ObjectFactory::NewJSArrayBuffer(int32_t length) JSHandle array_buffer(NewJSObjectByConstructor(constructor, new_target)); array_buffer->SetArrayBufferByteLength(thread_, JSTaggedValue(length)); if (length > 0) { - auto new_data = Runtime::GetCurrent()->GetInternalAllocator()->Alloc(length); + auto *new_data = vm_->AllocAndRegisterNative(length); if (memset_s(new_data, length, 0, length) != EOK) { LOG_ECMA(FATAL) << "memset_s failed"; UNREACHABLE(); } - JSHandle pointer = NewJSNativePointer(new_data, PandaFreeBufferFunc); + JSHandle pointer = + NewJSNativePointer(new_data, PandaFreeBufferFunc, reinterpret_cast(length)); array_buffer->SetArrayBufferData(thread_, pointer.GetTaggedValue()); array_buffer->SetShared(thread_, JSTaggedValue::False()); vm_->PushToArrayDataList(*pointer); @@ -281,7 +291,7 @@ void ObjectFactory::NewJSRegExpByteCodeData(const JSHandle ®exp, vo return; } - auto new_buffer = Runtime::GetCurrent()->GetInternalAllocator()->Alloc(size); + auto *new_buffer = vm_->AllocAndRegisterNative(size); if (memcpy_s(new_buffer, size, buffer, size) != EOK) { LOG_ECMA(FATAL) << "memcpy_s failed"; UNREACHABLE(); @@ -292,7 +302,8 @@ void ObjectFactory::NewJSRegExpByteCodeData(const JSHandle ®exp, vo native->ResetExternalPointer(new_buffer); return; } - JSHandle pointer = NewJSNativePointer(new_buffer, PandaFreeBufferFunc); + JSHandle pointer = + NewJSNativePointer(new_buffer, PandaFreeBufferFunc, reinterpret_cast(size)); regexp->SetByteCodeBuffer(thread_, pointer.GetTaggedValue()); regexp->SetLength(thread_, JSTaggedValue(static_cast(size))); diff --git a/tests/runtime/common/gc/CMakeLists.txt b/tests/runtime/common/gc/CMakeLists.txt index 0423a7bfeaeb45c0faff6e74e6a6714cda454f41..26d98e05d707f1080d34ebecb8182d881f38c44b 100644 --- a/tests/runtime/common/gc/CMakeLists.txt +++ b/tests/runtime/common/gc/CMakeLists.txt @@ -139,6 +139,7 @@ panda_add_ecma_gc_test(FILE spaceTypeTest.js OPTIONS "--gc-type=g1-gc" "--run-gc panda_add_ecma_gc_test(FILE concurrent.js OPTIONS "--gc-type=g1-gc" "--gc-trigger-type=debug-never") panda_add_ecma_gc_test(FILE hclass_changing_during_concurrent.js OPTIONS "--gc-type=g1-gc" "--gc-trigger-type=debug-never" "--heap-verifier=fail_on_verification:post") panda_add_ecma_gc_test(FILE pinObject.js OPTIONS "--gc-type=g1-gc" "--gc-trigger-type=debug-never") +panda_add_ecma_gc_test(FILE registerNativeAllocation.js OPTIONS "--gc-trigger-type=debug-never") panda_add_ecma_gc_test(FILE copylexenvDynTest.js OPTIONS "--gc-type=g1-gc" "--gc-trigger-type=debug-never" "--gc-use-nth-alloc-trigger=true" "--heap-verifier=fail_on_verification:pre:into:post") panda_add_ecma_gc_test(FILE hclass_collected_before_object.js OPTIONS "--gc-type=g1-gc" "--gc-trigger-type=debug-never" "--g1-track-freed-objects=true") panda_add_ecma_gc_test(FILE write_prebarrier_primitive_value.js OPTIONS "--gc-type=g1-gc" "--gc-trigger-type=debug-never" "--g1-track-freed-objects=true") diff --git a/tests/runtime/common/gc/registerNativeAllocation.js b/tests/runtime/common/gc/registerNativeAllocation.js new file mode 100644 index 0000000000000000000000000000000000000000..37f1b65584c342f78bc10c975eb5b9e7e621f3a5 --- /dev/null +++ b/tests/runtime/common/gc/registerNativeAllocation.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 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. + */ + +/** + * Test that GC::RegisterNativeAllocation works well with ArrayBuffer. + */ + +// Regression testing that GC now triggers after allocating ArrayBuffers and frees memory in internal space +for (var i = 0; i < 30000; i++) { + new ArrayBuffer(1024 * 128); +}