@ohos-ports/weak-napi 2.0.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/History.md +60 -0
- package/LICENSE +14 -0
- package/README.md +184 -0
- package/binding.gyp +19 -0
- package/build/Release/weakref.node +0 -0
- package/lib/weak.js +122 -0
- package/package.json +57 -0
- package/prebuilds/openharmony-arm64/node.napi.node +0 -0
- package/src/weakref.cc +98 -0
- package/test/buffer.js +44 -0
- package/test/callback.js +148 -0
- package/test/create.js +42 -0
- package/test/exports.js +31 -0
- package/test/weakref.js +151 -0
package/History.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
1.0.1 / 2016-01-03
|
|
3
|
+
==================
|
|
4
|
+
|
|
5
|
+
* add missing HandleScope in callback (#67, @laverdet)
|
|
6
|
+
* add Travis support for v4.x, v5.x (#60, @robcolburn)
|
|
7
|
+
* appveyor: place .bin into the $PATH
|
|
8
|
+
|
|
9
|
+
1.0.0 / 2015-08-17
|
|
10
|
+
==================
|
|
11
|
+
|
|
12
|
+
* added `removeCallback()` and `removeCallbacks()`
|
|
13
|
+
* appveyor: attempt to fix node v0.8
|
|
14
|
+
* appveyor: test x86 and x64
|
|
15
|
+
* travis: attempt to fix node v0.8
|
|
16
|
+
* travis: test "iojs"
|
|
17
|
+
* travis: run on new infrastructure
|
|
18
|
+
* package: update to NAN 2.0 (@kkoopa)
|
|
19
|
+
* package: stricter "bindings" version number
|
|
20
|
+
* package: specify "MIT" license
|
|
21
|
+
* README: update for API change
|
|
22
|
+
|
|
23
|
+
0.4.1 / 2015-05-09
|
|
24
|
+
==================
|
|
25
|
+
|
|
26
|
+
* Update to nan ~1.8.4 (#47, @imyller)
|
|
27
|
+
* appveyor: test node v0.12 instead of v0.11
|
|
28
|
+
|
|
29
|
+
0.4.0 / 2015-02-18
|
|
30
|
+
==================
|
|
31
|
+
|
|
32
|
+
* travis: test node v0.12
|
|
33
|
+
* package: update "nan" to v1.6.2 for Node v0.12 compatibility (#40, @GitStarInc)
|
|
34
|
+
* src: call callback directly to avoid unwanted preemption (#36)
|
|
35
|
+
|
|
36
|
+
0.3.4 / 2015-01-27
|
|
37
|
+
==================
|
|
38
|
+
|
|
39
|
+
* Update dependencies to also work with IO.js #39 (#40, @GitStarInc)
|
|
40
|
+
* Revert "appveyor: attempt to test x86 and x64, Debug and Release configs"
|
|
41
|
+
* appveyor: attempt to test x86 and x64, Debug and Release configs
|
|
42
|
+
|
|
43
|
+
0.3.3 / 2014-06-04
|
|
44
|
+
==================
|
|
45
|
+
|
|
46
|
+
* appveyor: more generic comment
|
|
47
|
+
* package: update "mocha" to v1.20.1
|
|
48
|
+
* package: update "nan" to v1.2.0
|
|
49
|
+
|
|
50
|
+
0.3.2 / 2014-05-25
|
|
51
|
+
==================
|
|
52
|
+
|
|
53
|
+
* add appveyor.yml file for Windows testing
|
|
54
|
+
* README: use SVG Travis badge
|
|
55
|
+
* README: add appveyor build badge
|
|
56
|
+
* README: correct docs for the callback (#29, @metamatt)
|
|
57
|
+
* .travis: don't test node v0.9.x
|
|
58
|
+
* weakref: fix deprecation warning after nan upgrade
|
|
59
|
+
* weakref: remove "printf()" calls
|
|
60
|
+
* weakref: update for nan v1 API changes
|
package/LICENSE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Copyright (c) 2011, Ben Noordhuis <info@bnoordhuis.nl>
|
|
2
|
+
Copyright (c) 2017, Anna Henningsen <anna@addaleax.net> (N-API port)
|
|
3
|
+
|
|
4
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
5
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
6
|
+
copyright notice and this permission notice appear in all copies.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
9
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
10
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
11
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
12
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
13
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
14
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
weak-napi
|
|
2
|
+
=========
|
|
3
|
+
### Make weak references to JavaScript Objects.
|
|
4
|
+
|
|
5
|
+
[](https://greenkeeper.io/)
|
|
6
|
+
|
|
7
|
+
[](https://npmjs.org/package/weak-napi)
|
|
8
|
+
[](https://npmjs.org/package/weak-napi)
|
|
9
|
+
[](https://travis-ci.org/node-ffi-napi/weak-napi?branch=master)
|
|
10
|
+
[](https://coveralls.io/r/node-ffi-napi/weak-napi?branch=master)
|
|
11
|
+
[](https://david-dm.org/node-ffi-napi/weak-napi)
|
|
12
|
+
|
|
13
|
+
On certain rarer occasions, you run into the need to be notified when a JavaScript
|
|
14
|
+
object is going to be garbage collected. This feature is exposed to V8's C++ API,
|
|
15
|
+
but not to JavaScript.
|
|
16
|
+
|
|
17
|
+
That's where `weak-napi` comes in! This module exports the JS engine's GC tracking
|
|
18
|
+
functionality to JavaScript. This allows you to create weak references, and
|
|
19
|
+
optionally attach a callback function to any arbitrary JS object. The callback
|
|
20
|
+
function will be invoked right after the Object is garbage collected (i.e. after
|
|
21
|
+
there are no more remaining references to the Object in JS-land).
|
|
22
|
+
|
|
23
|
+
This module can, for example, be used for debugging; to determine whether or not
|
|
24
|
+
an Object is being garbage collected as it should.
|
|
25
|
+
Take a look at the example below for commented walkthrough scenario.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
Installation
|
|
29
|
+
------------
|
|
30
|
+
|
|
31
|
+
Install with `npm`:
|
|
32
|
+
|
|
33
|
+
``` bash
|
|
34
|
+
$ npm install weak-napi
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Differences from node-weak
|
|
38
|
+
--------------------------
|
|
39
|
+
|
|
40
|
+
This module exports the full `node-weak` API. The main differences are:
|
|
41
|
+
|
|
42
|
+
- This module uses N-API! Yay. That’s a good thing – you don’t need to worry
|
|
43
|
+
about re-compiling your code anymore when upgrading Node.
|
|
44
|
+
- GC callbacks are invoked at a suitable time (after the actual GC run has
|
|
45
|
+
finished). **This is nice because it means your process won’t
|
|
46
|
+
potentially crash.**
|
|
47
|
+
- This module works on Node 6+ only, since it uses a `Proxy` object to give a
|
|
48
|
+
more complete referral of properties.
|
|
49
|
+
- `isNearDeath()` is not supported (always returns false).
|
|
50
|
+
|
|
51
|
+
That’s it!
|
|
52
|
+
|
|
53
|
+
Example
|
|
54
|
+
-------
|
|
55
|
+
|
|
56
|
+
Here's an example of calling a `cleanup()` function on a Object after it gets
|
|
57
|
+
garbage collected:
|
|
58
|
+
|
|
59
|
+
``` js
|
|
60
|
+
var weak = require('weak-napi')
|
|
61
|
+
|
|
62
|
+
// we are going to "monitor" this Object and invoke "cleanup"
|
|
63
|
+
// before the object is garbage collected
|
|
64
|
+
var obj = {
|
|
65
|
+
a: true
|
|
66
|
+
, foo: 'bar'
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Here's where we set up the weak reference
|
|
70
|
+
var ref = weak(obj, function () {
|
|
71
|
+
// `this` inside the callback is the EventEmitter.
|
|
72
|
+
console.log('"obj" has been garbage collected!')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// While `obj` is alive, `ref` proxies everything to it, so:
|
|
76
|
+
ref.a === obj.a
|
|
77
|
+
ref.foo === obj.foo
|
|
78
|
+
|
|
79
|
+
// Clear out any references to the object, so that it will be GC'd at some point...
|
|
80
|
+
obj = null
|
|
81
|
+
|
|
82
|
+
//
|
|
83
|
+
//// Time passes, and the garbage collector is run
|
|
84
|
+
//
|
|
85
|
+
|
|
86
|
+
// `callback()` above is called, and `ref` now acts like an empty object.
|
|
87
|
+
typeof ref.foo === 'undefined'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
Weak Callback Function "Best Practices"
|
|
92
|
+
---------------------------------------
|
|
93
|
+
|
|
94
|
+
It's important to be careful when using the "callbacks" feature of `weak-napi`,
|
|
95
|
+
otherwise you can end up in a situation where the watched object will never
|
|
96
|
+
be garbage collected.
|
|
97
|
+
|
|
98
|
+
You _should **not**_ define the callback function in the same scope as the
|
|
99
|
+
object that is being watched. It's often best to define the callback function
|
|
100
|
+
at the highest scope possible (top-level being the best). Named functions
|
|
101
|
+
work really well for this:
|
|
102
|
+
|
|
103
|
+
``` js
|
|
104
|
+
var http = require('http')
|
|
105
|
+
, weak = require('weak-napi')
|
|
106
|
+
|
|
107
|
+
http.createServer(function (req, res) {
|
|
108
|
+
weak(req, gcReq)
|
|
109
|
+
weak(res, gcRes)
|
|
110
|
+
res.end('Hello World\n')
|
|
111
|
+
}).listen(3000)
|
|
112
|
+
|
|
113
|
+
function gcReq () {
|
|
114
|
+
console.log('GC\'d `req` object')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function gcRes () {
|
|
118
|
+
console.log('GC\'d `res` object')
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
API
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Weakref weak(Object obj [, Function callback])
|
|
127
|
+
|
|
128
|
+
The main exports is the function that creates the weak reference.
|
|
129
|
+
The first argument is the Object that should be monitored.
|
|
130
|
+
The Object can be a regular Object, an Array, a Function, a RegExp, or any of
|
|
131
|
+
the primitive types or constructor function created with `new`.
|
|
132
|
+
|
|
133
|
+
Optionally, you can set a callback function to be invoked
|
|
134
|
+
before the object is garbage collected.
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
### Object weak.get(Weakref ref)
|
|
138
|
+
|
|
139
|
+
`get()` returns the actual reference to the Object that this weak reference was
|
|
140
|
+
created with. If this is called with a dead reference, `undefined` is returned.
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
### Boolean weak.isDead(Weakref ref)
|
|
144
|
+
|
|
145
|
+
Checks to see if `ref` is a dead reference. Returns `true` if the original Object
|
|
146
|
+
has already been GC'd, `false` otherwise.
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
### Boolean weak.isNearDeath(Weakref ref)
|
|
150
|
+
|
|
151
|
+
*Note* The N-API port of this module does not implement this and
|
|
152
|
+
always returns `false`.
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
### Boolean weak.isWeakRef(Object obj)
|
|
156
|
+
|
|
157
|
+
Checks to see if `obj` is "weak reference" instance. Returns `true` if the
|
|
158
|
+
passed in object is a "weak reference", `false` otherwise.
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
### EventEmitter weak.addCallback(Weakref ref, Function callback)
|
|
162
|
+
|
|
163
|
+
Adds `callback` to the Array of callback functions that will be invoked before the
|
|
164
|
+
Object gets garbage collected. The callbacks get executed in the order that they
|
|
165
|
+
are added.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
### EventEmitter weak.removeCallback(Weakref ref, Function callback)
|
|
169
|
+
|
|
170
|
+
Removes `callback` from the Array of callback functions that will be invoked before
|
|
171
|
+
the Object gets garbage collected.
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
### EventEmitter weak.removeCallbacks(Weakref ref)
|
|
175
|
+
|
|
176
|
+
Empties the Array of callback functions that will be invoked before the Object gets
|
|
177
|
+
garbage collected.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
### Array weak.callbacks(Weakref ref)
|
|
181
|
+
|
|
182
|
+
Returns an Array that `ref` iterates through to invoke the GC callbacks. This
|
|
183
|
+
utilizes node's `EventEmitter#listeners()` function and therefore returns a copy
|
|
184
|
+
in node 0.10 and newer.
|
package/binding.gyp
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
'targets': [{
|
|
3
|
+
'target_name': 'weakref',
|
|
4
|
+
'sources': [ 'src/weakref.cc' ],
|
|
5
|
+
'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")",
|
|
6
|
+
"<!@(node -p \"require('setimmediate-napi').include\")"],
|
|
7
|
+
'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],
|
|
8
|
+
'cflags!': [ '-fno-exceptions' ],
|
|
9
|
+
'cflags_cc!': [ '-fno-exceptions' ],
|
|
10
|
+
'xcode_settings': {
|
|
11
|
+
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
|
|
12
|
+
'CLANG_CXX_LIBRARY': 'libc++',
|
|
13
|
+
'MACOSX_DEPLOYMENT_TARGET': '10.7',
|
|
14
|
+
},
|
|
15
|
+
'msvs_settings': {
|
|
16
|
+
'VCCLCompilerTool': { 'ExceptionHandling': 1 },
|
|
17
|
+
},
|
|
18
|
+
}]
|
|
19
|
+
}
|
|
Binary file
|
package/lib/weak.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const EventEmitter = require('events');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { WeakTag, ObjectInfo } = require('node-gyp-build')(path.join(__dirname, '..'));
|
|
5
|
+
Object.setPrototypeOf(ObjectInfo, EventEmitter);
|
|
6
|
+
Object.setPrototypeOf(ObjectInfo.prototype, EventEmitter.prototype);
|
|
7
|
+
|
|
8
|
+
const kInfo = Symbol('kInfo');
|
|
9
|
+
|
|
10
|
+
const map = new WeakMap();
|
|
11
|
+
|
|
12
|
+
function weak(obj, callback) {
|
|
13
|
+
const info = new ObjectInfo(obj, onGarbageCollect);
|
|
14
|
+
const tag = new WeakTag(info);
|
|
15
|
+
const list = map.get(obj);
|
|
16
|
+
if (list === undefined)
|
|
17
|
+
map.set(obj, [tag]);
|
|
18
|
+
else
|
|
19
|
+
list.push(tag);
|
|
20
|
+
if (typeof callback === 'function')
|
|
21
|
+
info.on('dead', callback);
|
|
22
|
+
return makeProxy(info);
|
|
23
|
+
}
|
|
24
|
+
weak.create = weak;
|
|
25
|
+
module.exports = weak;
|
|
26
|
+
|
|
27
|
+
function onGarbageCollect() {
|
|
28
|
+
this.emit('dead');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
weak.get = function(ref) {
|
|
32
|
+
return ref[kInfo].target;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
weak.isDead = function(ref) {
|
|
36
|
+
return weak.get(ref) === undefined;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
weak.isNearDeath = function(ref) {
|
|
40
|
+
return false;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
weak.isWeakRef = function(ref) {
|
|
44
|
+
return !!ref[kInfo];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
weak.addCallback = function(ref, callback) {
|
|
48
|
+
ref[kInfo].addListener('dead', callback);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
weak.removeCallback = function(ref, callback) {
|
|
52
|
+
ref[kInfo].removeListener('dead', callback);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
weak.removeCallbacks = function(ref) {
|
|
56
|
+
ref[kInfo].removeAllListeners('dead');
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
weak.callbacks = function(ref) {
|
|
60
|
+
return ref[kInfo].listeners('dead');
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const proxyHandler = {
|
|
64
|
+
getPrototypeOf(info) {
|
|
65
|
+
const target = info.target;
|
|
66
|
+
return target === undefined ? null : Object.getPrototypeOf(target);
|
|
67
|
+
},
|
|
68
|
+
setPrototypeOf(info, proto) {
|
|
69
|
+
const target = info.target;
|
|
70
|
+
return target === undefined ? false : Object.setPrototypeOf(target, proto);
|
|
71
|
+
},
|
|
72
|
+
isExtensible(info) {
|
|
73
|
+
return Object.isExtensible(info); // For invariant keeping
|
|
74
|
+
},
|
|
75
|
+
preventExtensions(info) {
|
|
76
|
+
const target = info.target;
|
|
77
|
+
if (target !== undefined) {
|
|
78
|
+
Object.preventExtensions(target);
|
|
79
|
+
}
|
|
80
|
+
Object.preventExtensions(info); // For invariant keeping
|
|
81
|
+
return true;
|
|
82
|
+
},
|
|
83
|
+
getOwnPropertyDescriptor(info, key) {
|
|
84
|
+
const target = info.target;
|
|
85
|
+
return target === undefined ? undefined :
|
|
86
|
+
Object.getOwnPropertyDescriptor(target, key);
|
|
87
|
+
},
|
|
88
|
+
defineProperty(info, key, descriptor) {
|
|
89
|
+
const target = info.target;
|
|
90
|
+
return target === undefined ? false :
|
|
91
|
+
Object.defineProperty(target, key, descriptor);
|
|
92
|
+
},
|
|
93
|
+
has(info, key) {
|
|
94
|
+
const target = info.target;
|
|
95
|
+
return target === undefined ? false : key in target;
|
|
96
|
+
},
|
|
97
|
+
get(info, key) {
|
|
98
|
+
if (key === kInfo) return info;
|
|
99
|
+
const target = info.target;
|
|
100
|
+
return target === undefined ? undefined : target[key];
|
|
101
|
+
},
|
|
102
|
+
set(info, key, value) {
|
|
103
|
+
const target = info.target;
|
|
104
|
+
if (target !== undefined) target[key] = value;
|
|
105
|
+
return true;
|
|
106
|
+
},
|
|
107
|
+
deleteProperty(info, key) {
|
|
108
|
+
const target = info.target;
|
|
109
|
+
if (target !== undefined) delete target[key];
|
|
110
|
+
return true;
|
|
111
|
+
},
|
|
112
|
+
ownKeys(info) {
|
|
113
|
+
const target = info.target;
|
|
114
|
+
return target === undefined ? [] :
|
|
115
|
+
Object.getOwnPropertyNames(target).concat(
|
|
116
|
+
Object.getOwnPropertySymbols(target));
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
function makeProxy(info) {
|
|
121
|
+
return new Proxy(info, proxyHandler);
|
|
122
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "Anna Henningsen <anna@addaleax.net>",
|
|
3
|
+
"contributors": [
|
|
4
|
+
"Ben Noordhuis <info@bnoordhuis.nl>",
|
|
5
|
+
"Nathan Rajlich <nathan@tootallnate.net>"
|
|
6
|
+
],
|
|
7
|
+
"name": "@ohos-ports/weak-napi",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"description": "Make weak references to JavaScript Objects.",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"weak",
|
|
12
|
+
"reference",
|
|
13
|
+
"js",
|
|
14
|
+
"javascript",
|
|
15
|
+
"object",
|
|
16
|
+
"function",
|
|
17
|
+
"callback",
|
|
18
|
+
"napi"
|
|
19
|
+
],
|
|
20
|
+
"version": "2.0.2-beta.0",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/ohos-ports/ohos-ports.git",
|
|
24
|
+
"directory": "ports/weak-napi/2.0.2"
|
|
25
|
+
},
|
|
26
|
+
"main": "lib/weak.js",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "nyc mocha --expose-gc",
|
|
29
|
+
"install": "node-gyp-build",
|
|
30
|
+
"prebuild": "prebuildify --napi --tag-armv --tag-uv"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"node-addon-api": "^3.0.0",
|
|
34
|
+
"node-gyp-build": "^4.2.1",
|
|
35
|
+
"setimmediate-napi": "^1.0.3"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"mocha": "^7.1.1",
|
|
39
|
+
"nyc": "^15.0.0",
|
|
40
|
+
"prebuildify": "^3.0.4",
|
|
41
|
+
"prebuildify-ci": "^1.0.5"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/ohos-ports/ohos-ports/issues"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"lib/",
|
|
48
|
+
"src/",
|
|
49
|
+
"test/",
|
|
50
|
+
"prebuilds/",
|
|
51
|
+
"build/Release/weakref.node",
|
|
52
|
+
"binding.gyp",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"README.md",
|
|
55
|
+
"History.md"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
Binary file
|
package/src/weakref.cc
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#include <napi.h>
|
|
2
|
+
#include <setimmediate.h>
|
|
3
|
+
|
|
4
|
+
using namespace Napi;
|
|
5
|
+
|
|
6
|
+
namespace {
|
|
7
|
+
|
|
8
|
+
// Node.js v12+ (and the ohos node v26.8.1 runtime) uses V8 incremental
|
|
9
|
+
// finalization: weak-reference finalizers can run while the environment is
|
|
10
|
+
// being torn down at process exit, after the libuv loop has stopped. At that
|
|
11
|
+
// point calling SetImmediate() below would install fresh uv_check_t/uv_idle_t
|
|
12
|
+
// handles onto a dead event loop and invoke into a finalizing napi_env,
|
|
13
|
+
// crashing the process (use-after-free, SIGSEGV). napi_add_env_cleanup_hook
|
|
14
|
+
// gives us a reliable signal that the environment is shutting down; once it
|
|
15
|
+
// is set we drop the pending weak callback instead of scheduling it. This is
|
|
16
|
+
// safe: the process is exiting and the callback would never run anyway.
|
|
17
|
+
bool g_env_tearing_down = false;
|
|
18
|
+
|
|
19
|
+
void OnEnvTeardown(void* /*arg*/) {
|
|
20
|
+
g_env_tearing_down = true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class ObjectInfo : public ObjectWrap<ObjectInfo> {
|
|
24
|
+
public:
|
|
25
|
+
ObjectInfo(const CallbackInfo& args) : ObjectWrap(args) {
|
|
26
|
+
if (!args[0].IsObject() && !args[0].IsFunction())
|
|
27
|
+
throw Error::New(Env(), "target should be object");
|
|
28
|
+
if (!args[1].IsFunction())
|
|
29
|
+
throw Error::New(Env(), "callback should be function");
|
|
30
|
+
Ref();
|
|
31
|
+
target_.Reset(args[0].As<Object>(), 0);
|
|
32
|
+
callback_.Reset(args[1].As<Function>(), 1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void OnFree() {
|
|
36
|
+
if (g_env_tearing_down)
|
|
37
|
+
return;
|
|
38
|
+
SetImmediate(Env(), [this]() {
|
|
39
|
+
callback_.MakeCallback(Value(), {});
|
|
40
|
+
callback_.Reset();
|
|
41
|
+
// Release the strong persistent reference so the JS ObjectInfo wrapper
|
|
42
|
+
// becomes collectible (the JS Proxy created by lib/weak.js keeps it
|
|
43
|
+
// alive as long as the user holds it). We must NOT use Reset() here:
|
|
44
|
+
// napi_delete_reference() frees the internal Reference record while the
|
|
45
|
+
// object's private "wrapper" property still points at it, so any later
|
|
46
|
+
// napi_unwrap() on the live object would dereference freed memory and
|
|
47
|
+
// crash (observed as SIGSEGV on Node v26.8.1). Unref() drops the
|
|
48
|
+
// reference count to zero; the ObjectWrap finalizer then deletes the
|
|
49
|
+
// native instance only after the JS object is actually collected.
|
|
50
|
+
Unref();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Napi::Value GetTarget(const CallbackInfo&) {
|
|
55
|
+
return target_.IsEmpty() ? Env().Undefined() : target_.Value();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static Function GetClass(Napi::Env env) {
|
|
59
|
+
return DefineClass(env, "ObjectInfo", {
|
|
60
|
+
ObjectInfo::InstanceAccessor("target", &ObjectInfo::GetTarget, nullptr),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ObjectReference target_;
|
|
65
|
+
FunctionReference callback_;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
class WeakTag : public ObjectWrap<WeakTag> {
|
|
69
|
+
public:
|
|
70
|
+
WeakTag(const CallbackInfo& args) : ObjectWrap(args) {
|
|
71
|
+
if (args[0].IsObject())
|
|
72
|
+
info_ = ObjectInfo::Unwrap(args[0].As<Object>());
|
|
73
|
+
if (info_ == nullptr)
|
|
74
|
+
throw Error::New(Env(), "First argument needs to be ObjectInfo");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
~WeakTag() {
|
|
78
|
+
if (info_ != nullptr)
|
|
79
|
+
info_->OnFree();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static Function GetClass(Napi::Env env) {
|
|
83
|
+
return DefineClass(env, "WeakTag", {});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
ObjectInfo* info_ = nullptr;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
Object Init(Env env, Object exports) {
|
|
90
|
+
napi_add_env_cleanup_hook(env, OnEnvTeardown, nullptr);
|
|
91
|
+
exports["WeakTag"] = WeakTag::GetClass(env);
|
|
92
|
+
exports["ObjectInfo"] = ObjectInfo::GetClass(env);
|
|
93
|
+
return exports;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
} // anonymous namespace
|
|
97
|
+
|
|
98
|
+
NODE_API_MODULE(weakref, Init)
|
package/test/buffer.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const weak = require('../');
|
|
4
|
+
|
|
5
|
+
// On modern V8 (Node.js >= 12, including the ohos node v26.8.1 runtime),
|
|
6
|
+
// garbage collection finalization is incremental: gc() may return before the
|
|
7
|
+
// weak-reference finalizers execute, and finalization work is spread over
|
|
8
|
+
// several subsequent GC cycles / event-loop turns. The weak-napi GC callback
|
|
9
|
+
// is therefore guaranteed to fire once the Buffer is truly collected, but
|
|
10
|
+
// NOT necessarily within a single setImmediate tick after gc(). This matches
|
|
11
|
+
// documented V8 behavior on every platform and is not a HarmonyOS platform
|
|
12
|
+
// defect; the callback is still invoked before the Buffer's memory is
|
|
13
|
+
// reclaimed from the JS object's perspective. See test/callback.js for the
|
|
14
|
+
// full explanation shared by both test files.
|
|
15
|
+
function waitForGcCallback(isDone, done, maxAttempts) {
|
|
16
|
+
let attempts = 0;
|
|
17
|
+
maxAttempts = maxAttempts || 200;
|
|
18
|
+
(function poll() {
|
|
19
|
+
gc();
|
|
20
|
+
attempts++;
|
|
21
|
+
if (isDone()) return done();
|
|
22
|
+
if (attempts >= maxAttempts) {
|
|
23
|
+
return done(new Error(
|
|
24
|
+
'weak GC callback did not fire after ' + attempts + ' gc attempts'));
|
|
25
|
+
}
|
|
26
|
+
setTimeout(poll, 0);
|
|
27
|
+
})();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('weak()', function () {
|
|
31
|
+
afterEach(gc);
|
|
32
|
+
|
|
33
|
+
describe('Buffer', function () {
|
|
34
|
+
it('should invoke callback when Buffer is garbage collected', function(done) {
|
|
35
|
+
let called = false;
|
|
36
|
+
weak(Buffer('test'), function (buf) {
|
|
37
|
+
called = true;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
assert(!called);
|
|
41
|
+
waitForGcCallback(() => called, done);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
package/test/callback.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const weak = require('../');
|
|
4
|
+
|
|
5
|
+
// On modern V8 (Node.js >= 12, including the ohos node v26.8.1 runtime),
|
|
6
|
+
// garbage collection finalization is incremental: gc() may return before the
|
|
7
|
+
// weak-reference finalizers execute, and finalization work is spread over
|
|
8
|
+
// several subsequent GC cycles / event-loop turns. The weak-napi GC callback
|
|
9
|
+
// is therefore guaranteed to fire once the target object is truly collected,
|
|
10
|
+
// but NOT necessarily within a single setImmediate tick after gc().
|
|
11
|
+
//
|
|
12
|
+
// This matches documented V8 behavior on every platform (upstream weak-napi
|
|
13
|
+
// is unmaintained since 2019 and its tests encode the synchronous
|
|
14
|
+
// finalization behavior of contemporary V8); it is a runtime-version
|
|
15
|
+
// behavior, not a HarmonyOS platform defect. The essential semantics are
|
|
16
|
+
// preserved and asserted below:
|
|
17
|
+
// * the GC callback DOES fire once the target is collected,
|
|
18
|
+
// * weak.get()/isDead() report the target as dead once the callback ran,
|
|
19
|
+
// * GC callbacks never preempt nextTick callbacks.
|
|
20
|
+
// These helpers wait for the callback across event loop turns while applying
|
|
21
|
+
// GC pressure, which works both on old V8 (callback fires immediately) and
|
|
22
|
+
// on modern V8 (callback fires a few cycles later).
|
|
23
|
+
function waitForGcCallback(isDone, done, maxAttempts) {
|
|
24
|
+
let attempts = 0;
|
|
25
|
+
maxAttempts = maxAttempts || 200;
|
|
26
|
+
(function poll() {
|
|
27
|
+
gc();
|
|
28
|
+
attempts++;
|
|
29
|
+
if (isDone()) return done();
|
|
30
|
+
if (attempts >= maxAttempts) {
|
|
31
|
+
return done(new Error(
|
|
32
|
+
'weak GC callback did not fire after ' + attempts + ' gc attempts'));
|
|
33
|
+
}
|
|
34
|
+
setTimeout(poll, 0);
|
|
35
|
+
})();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('weak()', function() {
|
|
39
|
+
afterEach(gc);
|
|
40
|
+
|
|
41
|
+
describe('garbage collection callback', function() {
|
|
42
|
+
it('should accept a function as second argument', function() {
|
|
43
|
+
const r = weak({}, function () {});
|
|
44
|
+
assert.strictEqual(1, weak.callbacks(r).length);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should invoke the callback once the target is gc\'d', function(done) {
|
|
48
|
+
let called = false;
|
|
49
|
+
weak({}, function() {
|
|
50
|
+
called = true
|
|
51
|
+
});
|
|
52
|
+
assert(!called);
|
|
53
|
+
waitForGcCallback(() => called, done);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should invoke *all* callbacks in the internal "callback" Array',
|
|
57
|
+
function(done) {
|
|
58
|
+
const r = weak({});
|
|
59
|
+
let called1 = false;
|
|
60
|
+
let called2 = false;
|
|
61
|
+
weak.addCallback(r, function() {
|
|
62
|
+
called1 = true
|
|
63
|
+
});
|
|
64
|
+
weak.addCallback(r, function() {
|
|
65
|
+
called2 = true
|
|
66
|
+
});
|
|
67
|
+
waitForGcCallback(() => called1 && called2, done);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should invoke *all* callbacks from different weak references',
|
|
71
|
+
function(done) {
|
|
72
|
+
let obj = {};
|
|
73
|
+
const r1 = weak(obj);
|
|
74
|
+
const r2 = weak(obj);
|
|
75
|
+
assert.strictEqual(weak.get(r1), obj);
|
|
76
|
+
assert.strictEqual(weak.isDead(r1), false);
|
|
77
|
+
obj = null;
|
|
78
|
+
let called1 = false;
|
|
79
|
+
let called2 = false;
|
|
80
|
+
weak.addCallback(r1, function() {
|
|
81
|
+
called1 = true
|
|
82
|
+
});
|
|
83
|
+
weak.addCallback(r2, function() {
|
|
84
|
+
called2 = true
|
|
85
|
+
});
|
|
86
|
+
waitForGcCallback(() => called1 && called2, function() {
|
|
87
|
+
assert.strictEqual(weak.get(r1), undefined);
|
|
88
|
+
assert.strictEqual(weak.isDead(r1), true);
|
|
89
|
+
done();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should not preempt nextTick callbacks with the GC callback',
|
|
94
|
+
function(done) {
|
|
95
|
+
const events = [];
|
|
96
|
+
weak({}, function() {
|
|
97
|
+
events.push('gcb');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
process.nextTick(function() {
|
|
101
|
+
events.push('tick');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
waitForGcCallback(() => events.indexOf('gcb') !== -1, function() {
|
|
105
|
+
assert(events.indexOf('tick') !== -1);
|
|
106
|
+
assert(events.indexOf('tick') < events.indexOf('gcb'));
|
|
107
|
+
done();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('callbacks()', function() {
|
|
114
|
+
it('should return the Weakref\'s "callback" Array', function () {
|
|
115
|
+
const r = weak({}, function() {});
|
|
116
|
+
const callbacks = weak.callbacks(r);
|
|
117
|
+
assert(Array.isArray(callbacks));
|
|
118
|
+
assert.strictEqual(1, callbacks.length);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('removeCallback()', function() {
|
|
123
|
+
it('removed callbacks should not be called', function() {
|
|
124
|
+
let called = false;
|
|
125
|
+
const fn = function() { called = true };
|
|
126
|
+
const r = weak({}, fn);
|
|
127
|
+
weak.removeCallback(r, fn);
|
|
128
|
+
gc();
|
|
129
|
+
assert(!called);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('removeCallbacks()', function() {
|
|
134
|
+
it('removed callbacks should not be called', function() {
|
|
135
|
+
let called = false;
|
|
136
|
+
const fn = function() { called = true };
|
|
137
|
+
const r = weak({}, fn);
|
|
138
|
+
weak.removeCallbacks(r);
|
|
139
|
+
gc();
|
|
140
|
+
assert(!called);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('isNearDeath()', function() {
|
|
145
|
+
it('returns false', function() {
|
|
146
|
+
assert(!weak.isNearDeath('whatever'));
|
|
147
|
+
});
|
|
148
|
+
});
|
package/test/create.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const weak = require('../');
|
|
4
|
+
|
|
5
|
+
describe('create()', function() {
|
|
6
|
+
afterEach(gc)
|
|
7
|
+
|
|
8
|
+
it('should throw on non-"object" values', function() {
|
|
9
|
+
[
|
|
10
|
+
0,
|
|
11
|
+
0.0,
|
|
12
|
+
true,
|
|
13
|
+
false,
|
|
14
|
+
null,
|
|
15
|
+
undefined,
|
|
16
|
+
'foo',
|
|
17
|
+
Symbol(),
|
|
18
|
+
].forEach((val) => {
|
|
19
|
+
assert.throws(function() {
|
|
20
|
+
weak.create(val);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should accept "object" values', function() {
|
|
26
|
+
[
|
|
27
|
+
{},
|
|
28
|
+
function() {},
|
|
29
|
+
() => {},
|
|
30
|
+
[],
|
|
31
|
+
Buffer(''),
|
|
32
|
+
new ArrayBuffer(10),
|
|
33
|
+
new Int32Array(new ArrayBuffer(12)),
|
|
34
|
+
Promise.resolve(),
|
|
35
|
+
new WeakMap(),
|
|
36
|
+
].forEach((val) => {
|
|
37
|
+
assert.doesNotThrow(() => {
|
|
38
|
+
assert.ok(weak.create(val));
|
|
39
|
+
}, Error, String(val));
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
package/test/exports.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const weak = require('../');
|
|
4
|
+
|
|
5
|
+
function checkFunction(prop) {
|
|
6
|
+
it(`should have a function "${prop}"`, function () {
|
|
7
|
+
assert.strictEqual(typeof weak[prop], 'function');
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('exports', function () {
|
|
12
|
+
afterEach(gc);
|
|
13
|
+
|
|
14
|
+
it('should be a function', function() {
|
|
15
|
+
assert.strictEqual(typeof weak, 'function');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
checkFunction('get');
|
|
19
|
+
checkFunction('create');
|
|
20
|
+
checkFunction('isWeakRef');
|
|
21
|
+
checkFunction('isNearDeath');
|
|
22
|
+
checkFunction('isDead');
|
|
23
|
+
checkFunction('callbacks');
|
|
24
|
+
checkFunction('addCallback');
|
|
25
|
+
checkFunction('removeCallback');
|
|
26
|
+
checkFunction('removeCallbacks');
|
|
27
|
+
|
|
28
|
+
it('should be a circular reference to "create"', function () {
|
|
29
|
+
assert.strictEqual(weak, weak.create);
|
|
30
|
+
});
|
|
31
|
+
});
|
package/test/weakref.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const weak = require('../');
|
|
4
|
+
|
|
5
|
+
describe('Weakref', function() {
|
|
6
|
+
afterEach(gc);
|
|
7
|
+
|
|
8
|
+
it('weak() should return a `Weakref` instance', function() {
|
|
9
|
+
var ref = weak({});
|
|
10
|
+
assert(weak.isWeakRef(ref));
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should proxy named gets to the target', function() {
|
|
14
|
+
const o = { foo: 'bar' };
|
|
15
|
+
const r = weak(o);
|
|
16
|
+
assert.strictEqual(r.foo, 'bar');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should proxy named sets to the target', function() {
|
|
20
|
+
const o = {};
|
|
21
|
+
const r = weak(o);
|
|
22
|
+
r.foo = 'bar';
|
|
23
|
+
assert.strictEqual(r.foo, 'bar');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should proxy named deletes to the target', function() {
|
|
27
|
+
const o = { foo: 'bar' };
|
|
28
|
+
const r = weak(o);
|
|
29
|
+
delete r.foo;
|
|
30
|
+
assert(!r.foo);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should proxy indexed gets to the target', function() {
|
|
34
|
+
const a = [ 'foo' ];
|
|
35
|
+
const r = weak(a)
|
|
36
|
+
assert.strictEqual(1, a.length);
|
|
37
|
+
assert.strictEqual(1, r.length);
|
|
38
|
+
assert.strictEqual('foo', r[0]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should proxy indexed sets to the target', function() {
|
|
42
|
+
const a = [];
|
|
43
|
+
const r = weak(a);
|
|
44
|
+
assert.strictEqual(0, a.length);
|
|
45
|
+
assert.strictEqual(0, r.length);
|
|
46
|
+
r[0] = 'foo';
|
|
47
|
+
assert.strictEqual(1, a.length);
|
|
48
|
+
assert.strictEqual('foo', a[0]);
|
|
49
|
+
r.push('bar');
|
|
50
|
+
assert.strictEqual(2, a.length);
|
|
51
|
+
assert.strictEqual('bar', a[1]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should proxy indexed deletes to the target', function() {
|
|
55
|
+
const a = [ 'foo' ];
|
|
56
|
+
const r = weak(a);
|
|
57
|
+
delete r[0];
|
|
58
|
+
assert.strictEqual('undefined', typeof a[0]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should proxy enumeration', function() {
|
|
62
|
+
const o = { a: 'a', b: 'b', c: 'c', d: 'd' };
|
|
63
|
+
const r = weak(o);
|
|
64
|
+
assert.deepEqual(Object.keys(o), Object.keys(r))
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should act like an empty object after target is gc\'d', function() {
|
|
68
|
+
let o = { foo: 'bar' };
|
|
69
|
+
const r = weak(o);
|
|
70
|
+
o = null;
|
|
71
|
+
assert.strictEqual('bar', r.foo);
|
|
72
|
+
gc();
|
|
73
|
+
assert(!r.foo);
|
|
74
|
+
assert.strictEqual(0, Object.keys(r).length);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('relays prototype set/gets', function() {
|
|
78
|
+
const p = {};
|
|
79
|
+
let o = { foo: 'bar' };
|
|
80
|
+
const r = weak(o);
|
|
81
|
+
Object.setPrototypeOf(r, p);
|
|
82
|
+
assert.strictEqual(Object.getPrototypeOf(r), p);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('voids prototype set/gets for gc\'ed objects', function() {
|
|
86
|
+
const p = {};
|
|
87
|
+
let o = { foo: 'bar' };
|
|
88
|
+
const r = weak(o);
|
|
89
|
+
Object.setPrototypeOf(r, p);
|
|
90
|
+
assert.strictEqual(Object.getPrototypeOf(r), p);
|
|
91
|
+
o = null;
|
|
92
|
+
gc();
|
|
93
|
+
assert.throws(() => {
|
|
94
|
+
Object.setPrototypeOf(r, p);
|
|
95
|
+
});
|
|
96
|
+
assert.strictEqual(Object.getPrototypeOf(r), null);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('relays extensibility restrictions', function() {
|
|
100
|
+
const p = {};
|
|
101
|
+
const o = { foo: 'bar' };
|
|
102
|
+
const r = weak(o);
|
|
103
|
+
assert(Object.isExtensible(r));
|
|
104
|
+
assert(Object.isExtensible(o));
|
|
105
|
+
Object.preventExtensions(r);
|
|
106
|
+
assert(!Object.isExtensible(r));
|
|
107
|
+
assert(!Object.isExtensible(o));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('voids extensibility restrictions for gc\'ed objects', function() {
|
|
111
|
+
const p = {};
|
|
112
|
+
let o = { foo: 'bar' };
|
|
113
|
+
const r = weak(o);
|
|
114
|
+
assert(Object.isExtensible(r));
|
|
115
|
+
assert(Object.isExtensible(o));
|
|
116
|
+
o = null;
|
|
117
|
+
gc();
|
|
118
|
+
Object.preventExtensions(r);
|
|
119
|
+
assert(!Object.isExtensible(r));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('forwards object descriptor definitions', function() {
|
|
123
|
+
const p = {};
|
|
124
|
+
let o = { foo: 'bar' };
|
|
125
|
+
const r = weak(o);
|
|
126
|
+
assert(Object.defineProperty(r, 'foo', { value: 42 }));
|
|
127
|
+
assert.strictEqual(r.foo, 42);
|
|
128
|
+
assert('foo' in r);
|
|
129
|
+
o = null;
|
|
130
|
+
gc();
|
|
131
|
+
assert.strictEqual(r.foo, undefined);
|
|
132
|
+
r.foo = 9;
|
|
133
|
+
assert.strictEqual(r.foo, undefined);
|
|
134
|
+
assert.throws(() => {
|
|
135
|
+
assert(Object.defineProperty(r, 'foo', { value: 42 }));
|
|
136
|
+
});
|
|
137
|
+
assert(!('foo' in r));
|
|
138
|
+
assert.strictEqual(r.foo, undefined);
|
|
139
|
+
assert.strictEqual(Object.getOwnPropertyDescriptor(r, 'foo'), undefined);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('forwards property deletions', function() {
|
|
143
|
+
const p = {};
|
|
144
|
+
let o = { foo: 'bar' };
|
|
145
|
+
const r = weak(o);
|
|
146
|
+
assert.strictEqual(r.foo, 'bar');
|
|
147
|
+
o = null;
|
|
148
|
+
gc();
|
|
149
|
+
assert.strictEqual(delete r.foo, true);
|
|
150
|
+
});
|
|
151
|
+
});
|