diff --git a/src/node_util.cc b/src/node_util.cc index 4dc7ce1bbfd887..2f520aca989f12 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -1,5 +1,6 @@ #include "node_internals.h" #include "node_watchdog.h" +#include "base_object-inl.h" namespace node { namespace util { @@ -9,8 +10,10 @@ using v8::Array; using v8::Boolean; using v8::Context; using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; using v8::IndexFilter; using v8::Integer; +using v8::Isolate; using v8::KeyCollectionMode; using v8::Local; using v8::NewStringType; @@ -178,6 +181,37 @@ void SafeGetenv(const FunctionCallbackInfo& args) { NewStringType::kNormal).ToLocalChecked()); } +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) { @@ -235,6 +269,16 @@ void Initialize(Local target, target->Set(context, FIXED_ONE_BYTE_STRING(env->isolate(), "propertyFilter"), constants).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); +});