diff --git a/src/node_util.cc b/src/node_util.cc index 2a7d90cfe2b661..4cca0cbb72aed0 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -1,6 +1,7 @@ #include "node_errors.h" #include "node_watchdog.h" #include "util.h" +#include "base_object-inl.h" namespace node { namespace util { @@ -11,6 +12,7 @@ using v8::Boolean; using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; using v8::IndexFilter; using v8::Integer; using v8::Isolate; @@ -181,6 +183,37 @@ void EnqueueMicrotask(const FunctionCallbackInfo& args) { isolate->EnqueueMicrotask(args[0].As()); } +class WeakReference : public BaseObject { + public: + WeakReference(Environment* env, Local object, Local target) + : BaseObject(env, object) { + MakeWeak(); + target_.Reset(env->isolate(), target); + target_.SetWeak(); + } + + static void New(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args.IsConstructCall()); + CHECK(args[0]->IsObject()); + new WeakReference(env, args.This(), args[0].As()); + } + + static void Get(const FunctionCallbackInfo& args) { + WeakReference* weak_ref = Unwrap(args.Holder()); + Isolate* isolate = args.GetIsolate(); + if (!weak_ref->target_.IsEmpty()) + args.GetReturnValue().Set(weak_ref->target_.Get(isolate)); + } + + SET_MEMORY_INFO_NAME(WeakReference) + SET_SELF_SIZE(WeakReference) + SET_NO_MEMORY_INFO() + + private: + Persistent target_; +}; + void Initialize(Local target, Local unused, Local context, @@ -241,6 +274,16 @@ void Initialize(Local target, should_abort_on_uncaught_toggle, env->should_abort_on_uncaught_toggle().GetJSArray()) .FromJust()); + + Local weak_ref_string = + FIXED_ONE_BYTE_STRING(env->isolate(), "WeakReference"); + Local weak_ref = + env->NewFunctionTemplate(WeakReference::New); + weak_ref->InstanceTemplate()->SetInternalFieldCount(1); + weak_ref->SetClassName(weak_ref_string); + env->SetProtoMethod(weak_ref, "get", WeakReference::Get); + target->Set(context, weak_ref_string, + weak_ref->GetFunction(context).ToLocalChecked()).FromJust(); } } // namespace util diff --git a/test/parallel/test-internal-util-weakreference.js b/test/parallel/test-internal-util-weakreference.js new file mode 100644 index 00000000000000..b48b34fe2309ea --- /dev/null +++ b/test/parallel/test-internal-util-weakreference.js @@ -0,0 +1,17 @@ +// Flags: --expose-internals --expose-gc +'use strict'; +require('../common'); +const assert = require('assert'); +const { internalBinding } = require('internal/test/binding'); +const { WeakReference } = internalBinding('util'); + +let obj = { hello: 'world' }; +const ref = new WeakReference(obj); +assert.strictEqual(ref.get(), obj); + +setImmediate(() => { + obj = null; + global.gc(); + + assert.strictEqual(ref.get(), undefined); +});