@@ -506,13 +506,23 @@ PESAPI_EXTERN pesapi_env pesapi_get_env(pesapi_callback_info pinfo)
506506void * pesapi_get_native_holder_ptr (pesapi_callback_info pinfo)
507507{
508508 auto info = reinterpret_cast <const v8::FunctionCallbackInfo<v8::Value>*>(pinfo);
509+ #if V8_MAJOR_VERSION >= 13 && V8_MINOR_VERSION >= 6
510+ // V8 13.6+: FunctionCallbackInfo::Holder() was removed.
511+ // Use `This()` (the receiver) as the closest equivalent holder object.
512+ return puerts::DataTransfer::GetPointerFast<void >((*info).This ());
513+ #else
509514 return puerts::DataTransfer::GetPointerFast<void >((*info).Holder ());
515+ #endif
510516}
511517
512518const void * pesapi_get_native_holder_typeid (pesapi_callback_info pinfo)
513519{
514520 auto info = reinterpret_cast <const v8::FunctionCallbackInfo<v8::Value>*>(pinfo);
521+ #if V8_MAJOR_VERSION >= 13 && V8_MINOR_VERSION >= 6
522+ return puerts::DataTransfer::GetPointerFast<void >((*info).This (), 1 );
523+ #else
515524 return puerts::DataTransfer::GetPointerFast<void >((*info).Holder (), 1 );
525+ #endif
516526}
517527
518528void * pesapi_get_userdata (pesapi_callback_info pinfo)
@@ -682,6 +692,11 @@ void pesapi_release_value_ref(pesapi_value_ref value_ref)
682692 {
683693 if (!value_ref->env_life_cycle_tracker .expired ())
684694 {
695+ // v8::Persistent uses NonCopyablePersistentTraits (kResetInDestructor == false),
696+ // so ~pesapi_value_ref__() does NOT release the strong V8 global root. Without an
697+ // explicit Reset(), a JS function marshaled into a C# delegate (this path never calls
698+ // pesapi_set_ref_weak) and everything its closure captures leak for the isolate's lifetime.
699+ value_ref->value_persistent .Reset ();
685700 value_ref->~pesapi_value_ref__ ();
686701 }
687702 ::operator delete (static_cast <void *>(value_ref));
0 commit comments