From e158218fb23ae7cea7aa5d27ee2092a4b6d96184 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Fri, 12 Jul 2019 10:51:00 -0700 Subject: [PATCH] doc: update context-aware section of addon doc Replace the portion of the context-aware addon example that advocates for managing the life cycle of the per-addon-instance data using a weak reference with code that illustrates how to manage its life cycle using an environment cleanup hook. Signed-off-by: Gabriel Schulhof PR-URL: https://github.com/nodejs/node/pull/28659 Reviewed-By: Michael Dawson --- doc/api/addons.md | 61 +++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/doc/api/addons.md b/doc/api/addons.md index e6b5704e8fbdd5..ffbecb02d8ff4d 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -155,18 +155,25 @@ they were created. The context-aware addon can be structured to avoid global static data by performing the following steps: - -* defining a class which will hold per-addon-instance data. Such -a class should include a `v8::Global` which will hold a weak -reference to the addon's `exports` object. The callback associated with the weak -reference will then destroy the instance of the class. -* constructing an instance of this class in the addon initializer such that the -`v8::Global` is set to the `exports` object. -* storing the instance of the class in a `v8::External`, and -* passing the `v8::External` to all methods exposed to JavaScript by passing it -to the `v8::FunctionTemplate` constructor which creates the native-backed -JavaScript functions. The `v8::FunctionTemplate` constructor's third parameter -accepts the `v8::External`. +* Define a class which will hold per-addon-instance data and which has a static +member of the form + ```C++ + static void DeleteInstance(void* data) { + // Cast `data` to an instance of the class and delete it. + } + ``` +* Heap-allocate an instance of this class in the addon initializer. This can be +accomplished using the `new` keyword. +* Call `node::AddEnvironmentCleanupHook()`, passing it the above-created +instance and a pointer to `DeleteInstance()`. This will ensure the instance is +deleted when the environment is torn down. +* Store the instance of the class in a `v8::External`, and +* Pass the `v8::External` to all methods exposed to JavaScript by passing it +to `v8::FunctionTemplate::New()` or `v8::Function::New()` which creates the +native-backed JavaScript functions. The third parameter of +`v8::FunctionTemplate::New()` or `v8::Function::New()` accepts the +`v8::External` and makes it available in the native callback using the +`v8::FunctionCallbackInfo::Data()` method. This will ensure that the per-addon-instance data reaches each binding that can be called from JavaScript. The per-addon-instance data must also be passed into @@ -181,25 +188,18 @@ using namespace v8; class AddonData { public: - AddonData(Isolate* isolate, Local exports): + explicit AddonData(Isolate* isolate): call_count(0) { - // Link the existence of this object instance to the existence of exports. - exports_.Reset(isolate, exports); - exports_.SetWeak(this, DeleteMe, WeakCallbackType::kParameter); + // Ensure this per-addon-instance data is deleted at environment cleanup. + node::AddEnvironmentCleanupHook(isolate, DeleteInstance, this); } // Per-addon data. int call_count; - private: - // Method to call when "exports" is about to be garbage-collected. - static void DeleteMe(const WeakCallbackInfo& info) { - delete info.GetParameter(); + static void DeleteInstance(void* data) { + delete static_cast(data); } - - // Weak handle to the "exports" object. An instance of this class will be - // destroyed along with the exports object to which it is weakly bound. - v8::Global exports_; }; static void Method(const v8::FunctionCallbackInfo& info) { @@ -214,14 +214,17 @@ static void Method(const v8::FunctionCallbackInfo& info) { NODE_MODULE_INIT(/* exports, module, context */) { Isolate* isolate = context->GetIsolate(); - // Create a new instance of AddonData for this instance of the addon. - AddonData* data = new AddonData(isolate, exports); - // Wrap the data in a v8::External so we can pass it to the method we expose. + // Create a new instance of `AddonData` for this instance of the addon and + // tie its life cycle to that of the Node.js environment. + AddonData* data = new AddonData(isolate); + + // Wrap the data in a `v8::External` so we can pass it to the method we + // expose. Local external = External::New(isolate, data); - // Expose the method "Method" to JavaScript, and make sure it receives the + // Expose the method `Method` to JavaScript, and make sure it receives the // per-addon-instance data we created above by passing `external` as the - // third parameter to the FunctionTemplate constructor. + // third parameter to the `FunctionTemplate` constructor. exports->Set(context, String::NewFromUtf8(isolate, "method", NewStringType::kNormal) .ToLocalChecked(),