@@ -155,18 +155,25 @@ they were created. |
155 | 155 |
|
156 | 156 | The context-aware addon can be structured to avoid global static data by |
157 | 157 | performing the following steps: |
158 | | - |
159 | | -* defining a class which will hold per-addon-instance data. Such |
160 | | -a class should include a `v8::Global<v8::Object>` which will hold a weak |
161 | | -reference to the addon's `exports` object. The callback associated with the weak |
162 | | -reference will then destroy the instance of the class. |
163 | | -* constructing an instance of this class in the addon initializer such that the |
164 | | -`v8::Global<v8::Object>` is set to the `exports` object. |
165 | | -* storing the instance of the class in a `v8::External`, and |
166 | | -* passing the `v8::External` to all methods exposed to JavaScript by passing it |
167 | | -to the `v8::FunctionTemplate` constructor which creates the native-backed |
168 | | -JavaScript functions. The `v8::FunctionTemplate` constructor's third parameter |
169 | | -accepts the `v8::External`. |
| 158 | +* Define a class which will hold per-addon-instance data and which has a static |
| 159 | +member of the form |
| 160 | + ```C++ |
| 161 | + static void DeleteInstance(void* data) { |
| 162 | + // Cast `data` to an instance of the class and delete it. |
| 163 | + } |
| 164 | + ``` |
| 165 | +* Heap-allocate an instance of this class in the addon initializer. This can be |
| 166 | +accomplished using the `new` keyword. |
| 167 | +* Call `node::AddEnvironmentCleanupHook()`, passing it the above-created |
| 168 | +instance and a pointer to `DeleteInstance()`. This will ensure the instance is |
| 169 | +deleted when the environment is torn down. |
| 170 | +* Store the instance of the class in a `v8::External`, and |
| 171 | +* Pass the `v8::External` to all methods exposed to JavaScript by passing it |
| 172 | +to `v8::FunctionTemplate::New()` or `v8::Function::New()` which creates the |
| 173 | +native-backed JavaScript functions. The third parameter of |
| 174 | +`v8::FunctionTemplate::New()` or `v8::Function::New()` accepts the |
| 175 | +`v8::External` and makes it available in the native callback using the |
| 176 | +`v8::FunctionCallbackInfo::Data()` method. |
170 | 177 |
|
171 | 178 | This will ensure that the per-addon-instance data reaches each binding that can |
172 | 179 | be called from JavaScript. The per-addon-instance data must also be passed into |
@@ -181,25 +188,18 @@ using namespace v8; |
181 | 188 |
|
182 | 189 | class AddonData { |
183 | 190 | public: |
184 | | - AddonData(Isolate* isolate, Local<Object> exports): |
| 191 | + explicit AddonData(Isolate* isolate): |
185 | 192 | call_count(0) { |
186 | | - // Link the existence of this object instance to the existence of exports. |
187 | | - exports_.Reset(isolate, exports); |
188 | | - exports_.SetWeak(this, DeleteMe, WeakCallbackType::kParameter); |
| 193 | + // Ensure this per-addon-instance data is deleted at environment cleanup. |
| 194 | + node::AddEnvironmentCleanupHook(isolate, DeleteInstance, this); |
189 | 195 | } |
190 | 196 |
|
191 | 197 | // Per-addon data. |
192 | 198 | int call_count; |
193 | 199 |
|
194 | | - private: |
195 | | - // Method to call when "exports" is about to be garbage-collected. |
196 | | - static void DeleteMe(const WeakCallbackInfo<AddonData>& info) { |
197 | | - delete info.GetParameter(); |
| 200 | + static void DeleteInstance(void* data) { |
| 201 | + delete static_cast<AddonData*>(data); |
198 | 202 | } |
199 | | - |
200 | | - // Weak handle to the "exports" object. An instance of this class will be |
201 | | - // destroyed along with the exports object to which it is weakly bound. |
202 | | - v8::Global<v8::Object> exports_; |
203 | 203 | }; |
204 | 204 |
|
205 | 205 | static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) { |
@@ -214,14 +214,17 @@ static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) { |
214 | 214 | NODE_MODULE_INIT(/* exports, module, context */) { |
215 | 215 | Isolate* isolate = context->GetIsolate(); |
216 | 216 |
|
217 | | - // Create a new instance of AddonData for this instance of the addon. |
218 | | - AddonData* data = new AddonData(isolate, exports); |
219 | | - // Wrap the data in a v8::External so we can pass it to the method we expose. |
| 217 | + // Create a new instance of `AddonData` for this instance of the addon and |
| 218 | + // tie its life cycle to that of the Node.js environment. |
| 219 | + AddonData* data = new AddonData(isolate); |
| 220 | + |
| 221 | + // Wrap the data in a `v8::External` so we can pass it to the method we |
| 222 | + // expose. |
220 | 223 | Local<External> external = External::New(isolate, data); |
221 | 224 |
|
222 | | - // Expose the method "Method" to JavaScript, and make sure it receives the |
| 225 | + // Expose the method `Method` to JavaScript, and make sure it receives the |
223 | 226 | // per-addon-instance data we created above by passing `external` as the |
224 | | - // third parameter to the FunctionTemplate constructor. |
| 227 | + // third parameter to the `FunctionTemplate` constructor. |
225 | 228 | exports->Set(context, |
226 | 229 | String::NewFromUtf8(isolate, "method", NewStringType::kNormal) |
227 | 230 | .ToLocalChecked(), |
|