├── .gitignore
├── .travis.yml
├── README.md
├── deploy.sh
├── deploy_key.enc
├── history
├── Weak References for EcmaScript.pdf
├── pre-structure.svg
├── spec.md
├── weakref-structure.svg
└── weakrefs.md
├── package-lock.json
├── package.json
├── reference.md
└── spec
├── abstract-jobs.html
├── finalization-registry.html
├── index.html
└── weak-ref.html
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | out/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: off
2 |
3 | language: node_js
4 |
5 | node_js:
6 | - stable
7 |
8 | script:
9 | - bash ./deploy.sh
10 |
11 | env:
12 | global:
13 | - ENCRYPTION_LABEL: "366b667bb40e"
14 | - GH_USER_NAME: "gsathya"
15 | - GH_USER_EMAIL: "gsathya.ceg@gmail.com"
16 | - PRIVATE_KEY_FILE_NAME: "deploy_key.enc"
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WeakRefs TC39 proposal
2 |
3 | ## Status
4 |
5 | * WeakRef and FinalizationRegistry are now Stage 4, since the July 2020 TC39 meeting
6 | * V8 -- shipping [Chrome 84](https://v8.dev/features/weak-references)
7 | * Spidermonkey -- shipping [Firefox 79](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/79)
8 | * JavaScriptCore -- shipping [Safari 14.1](https://webkit.org/blog/11648/new-webkit-features-in-safari-14-1)
9 | * engine262 -- [Initial patch](https://github.com/engine262/engine262/commit/0516660577b050b17e619af4c3f053c7506c670d), now all landed
10 | * XS -- [Shipping in Moddable XS 9.0.1](https://github.com/Moddable-OpenSource/moddable-xst/releases/tag/v9.0.1)
11 |
12 | ## Introduction
13 |
14 | The WeakRef proposal encompasses two major new pieces of functionality:
15 |
16 | 1. creating _weak references_ to objects with the `WeakRef` class
17 | 2. running user-defined _finalizers_ after objects are garbage-collected, with the `FinalizationRegistry` class
18 |
19 | These interfaces can be used independently or together, depending on the use case.
20 |
21 | For developer reference documentation, see [`reference.md`](reference.md).
22 |
23 | ## A note of caution
24 |
25 | This proposal contains two advanced features, `WeakRef` and `FinalizationRegistry`. Their correct use takes careful thought, and they are best avoided if possible.
26 |
27 | [Garbage collectors](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) are complicated. If an application or library depends on GC cleaning up a WeakRef or calling a finalizer in a timely, predictable manner, it's likely to be disappointed: the cleanup may happen much later than expected, or not at all. Sources of variability include:
28 | - One object might be garbage-collected much sooner than another object, even if they become unreachable at the same time, e.g., due to generational collection.
29 | - Garbage collection work can be split up over time using incremental and concurrent techniques.
30 | - Various runtime heuristics can be used to balance memory usage, responsiveness.
31 | - The JavaScript engine may hold references to things which look like they are unreachable (e.g., in closures, or inline caches).
32 | - Different JavaScript engines may do these things differently, or the same engine may change its algorithms across versions.
33 | - Complex factors may lead to objects being held alive for unexpected amounts of time, such as use with certain APIs.
34 |
35 | Important logic should not be placed in the code path of a finalizer. Doing so could create user-facing issues triggered by memory management bugs, or even differences between JavaScript garbage collector implementations. For example, if data is saved persistently solely from a finalizer, then a bug which accidentally keeps an additional reference around could lead to data loss.
36 |
37 | For this reason, the [W3C TAG Design Principles](https://w3ctag.github.io/design-principles/#js-gc) recommend against creating APIs that expose garbage collection. It's best if `WeakRef` objects and `FinalizationRegistry` objects are used as a way to avoid excess memory usage, or as a backstop against certain bugs, rather than as a normal way to clean up external resources or observe what's allocated.
38 |
39 | ## Weak references
40 |
41 | A weak reference to an object is not enough to keep the object alive: when the only remaining references to a _referent_ (i.e. an object which is referred to by a weak reference) are weak references, garbage collection is free to destroy the referent and reuse its memory for something else. However, until the object is actually destroyed, the weak reference may return the object even if there are no strong references to it.
42 |
43 | A primary use for weak references is to implement caches or mappings holding large objects, where it’s desired that a large object is not kept alive solely because it appears in a cache or mapping.
44 |
45 | For example, if you have a number of large binary image objects (e.g. represented as `ArrayBuffer`s), you may wish to associate a name with each image. Existing data structures just don't do what's needed here:
46 | - If you used a `Map` to map names to images, or images to names, the image objects would remain alive just because they appeared as values or keys in the map.
47 | - `WeakMap`s are not suitable for this purpose either: they are weak over their *keys*, but in this case, we need a structure which is weak over its *values*.
48 |
49 | Instead, we can use a `Map` whose values are `WeakRef` objects, which point to the `ArrayBuffer`. This way, we avoid holding these `ArrayBuffer` objects in memory longer than they would be otherwise: it's a way to find the image object if it's still around, but if it gets garbage collected, we'll regenerate it. This way, less memory is used in some situations.
50 |
51 | ```js
52 | // This technique is incomplete; see below.
53 | function makeWeakCached(f) {
54 | const cache = new Map();
55 | return key => {
56 | const ref = cache.get(key);
57 | if (ref) {
58 | const cached = ref.deref();
59 | if (cached !== undefined) return cached;
60 | }
61 |
62 | const fresh = f(key);
63 | cache.set(key, new WeakRef(fresh));
64 | return fresh;
65 | };
66 | }
67 |
68 | var getImageCached = makeWeakCached(getImage);
69 | ```
70 |
71 | This technique can help avoid spending a lot of memory on `ArrayBuffer`s that nobody is looking at anymore, but it still has the problem that, over time, the `Map` will fill up with strings which point to a `WeakRef` whose referent has already been collected. One way to address this is to periodically scavenge the cache and clear out dead entries. Another way is with finalizers, which we’ll come back to at the end of the article.
72 |
73 | A few elements of the API are visible in this example:
74 |
75 | - The `WeakRef` constructor takes an argument, which has to be an object, and returns a weak reference to it.
76 | - `WeakRef` instances have a `deref` method that returns one of two values:
77 | - The object passed into the constructor, if it’s still available.
78 | - `undefined`, if nothing else was pointing to the object and it was already garbage-collected.
79 |
80 | ## Finalizers
81 |
82 | _Finalization_ is the execution of code to clean up after an object that has become unreachable to program execution. User-defined _finalizers_ enable several new use cases, and can help prevent memory leaks when managing resources that the garbage collector doesn't know about.
83 |
84 | ### Another note of caution
85 |
86 | Finalizers are tricky business and it is best to avoid them. They can be invoked at unexpected times, or not at all---for example, they are not invoked when closing a browser tab or on process exit. They don’t help the garbage collector do its job; rather, they are a hindrance. Furthermore, they perturb the garbage collector’s internal accounting. The GC decides to scan the heap when it thinks that it is necessary, after some amount of allocation. Finalizable objects almost always represent an amount of allocation that is invisible to the garbage collector. The effect can be that the actual resource usage of a system with finalizable objects is higher than what the GC thinks it should be.
87 |
88 | The proposed specification allows conforming implementations to skip calling finalization callbacks for any reason or no reason. Some reasons why many JS environments and implementations may omit finalization callbacks:
89 | - If the program shuts down (e.g., process exit, closing a tab, navigating away from a page), finalization callbacks typically don't run on the way out. (Discussion: [#125](https://github.com/tc39/proposal-weakrefs/issues/125))
90 | - If the FinalizationRegistry becomes "dead" (approximately, unreachable), then finalization callbacks registered against it might not run. (Discussion: [#66](https://github.com/tc39/proposal-weakrefs/issues/66))
91 |
92 | All that said, sometimes finalizers are the right answer to a problem. The following examples show a few important problems that would be difficult to solve without finalizers.
93 |
94 | ### Locating and responding to external resource leaks
95 |
96 | Finalizers can locate external resource leaks. For example, if an open file is garbage collected, the underlying operating system resource could be leaked. Although the OS will likely free the resources when the process exits, this sort of leak could make long-running processes eventually exhaust the number of file handles available. To catch these bugs, a `FinalizationRegistry` can be used to log the existence of file objects which are garbage collected before being closed.
97 |
98 | The `FinalizationRegistry` class represents a group of objects registered with a common finalizer callback. This construct can be used to inform the developer about the never-closed files.
99 |
100 | ```js
101 | class FileStream {
102 | static #cleanUp(heldValue) {
103 | console.error(`File leaked: ${file}!`);
104 | }
105 |
106 | static #finalizationGroup = new FinalizationRegistry(FileStream.#cleanUp);
107 |
108 | #file;
109 |
110 | constructor(fileName) {
111 | this.#file = new File(fileName);
112 | FileStream.#finalizationGroup.register(this, this.#file, this);
113 | // eagerly trigger async read of file contents into this.data
114 | }
115 |
116 | close() {
117 | FileStream.#finalizationGroup.unregister(this);
118 | File.close(this.#file);
119 | // other cleanup
120 | }
121 |
122 | async *[Symbol.iterator]() {
123 | // read data from this.#file
124 | }
125 | }
126 |
127 | const fs = new FileStream('path/to/some/file');
128 |
129 | for await (const data of fs) {
130 | // do something
131 | }
132 | fs.close();
133 | ```
134 |
135 | Note, it's not a good idea to close files automatically through a finalizer, as this technique is unreliable and may lead to resource exhaustion. Instead, explicit release of resources (e.g., though `try`/`finally`) is recommended. For this reason, this example logs errors rather than transparently closing the file.
136 |
137 | This example shows usage of the whole `FinalizationRegistry` API:
138 | - An object can have a finalizer referenced by calling the `register` method of `FinalizationRegistry`. In this case, three arguments are passed to the `register` method:
139 | - The object whose lifetime we're concerned with. Here, that's `this`, the `FileStream` object.
140 | - A held value, which is used to represent that object when cleaning it up in the finalizer. Here, the held value is the underlying `File` object. (Note: the held value should not have a reference to the weak target, as that would prevent the target from being collected.)
141 | - An unregistration token, which is passed to the `unregister` method when the finalizer is no longer needed. Here we use `this`, the `FileStream` object itself, since `FinalizationRegistry` doesn't hold a strong reference to the unregister token.
142 | - The `FinalizationRegistry` constructor is called with a callback as an argument. This callback is called with a held value.
143 |
144 | The finalizer callback is called *after* the object is garbage collected, a pattern which is sometimes called "post-mortem". For this reason, the `FinalizerRegistry` callback is called with a separate held value, rather than the original object--the object's already gone, so it can't be used.
145 |
146 | In the above code sample, the `fs` object will be unregistered as part of the `close` method, which will mean that the finalizer will not be called, and there will be no error log statement. Unregistration can be useful to avoid other sorts of "double free" scenarios.
147 |
148 | ### Exposing WebAssembly memory to JavaScript
149 |
150 | Whenever you have a JavaScript object that is backed by something in WebAssembly, you might want to run custom cleanup code (in WebAssembly or JavaScript) when the object goes away. A [previous proposal](https://github.com/lars-t-hansen/moz-sandbox/blob/master/refmap/ReferenceMap.md) exposed a collection of weak references, with the idea that finalization actions could be taken by periodically checking if they are still alive. This proposal includes a first-class concept of finalizers in order to give developers a way to avoid that repeated scanning.
151 |
152 | For example, imagine if you have a big `WebAssembly.Memory` object, and you want to create an allocator to give fixed-size portions of it to JavaScript. In some cases, it may be practical to explicitly free this memory, but typically, JavaScript code passes around references freely, without thinking about ownership. So it's helpful to be able to rely on the garbage collector to release this memory. A `FinalizationRegistry` can be used to free the memory.
153 |
154 | ```js
155 | function makeAllocator(size, length) {
156 | const freeList = Array.from({length}, (v, i) => size * i);
157 | const memory = new ArrayBuffer(size * length);
158 | const finalizationGroup = new FinalizationRegistry(
159 | held => freeList.unshift(held));
160 | return { memory, size, freeList, finalizationGroup };
161 | }
162 |
163 | function allocate(allocator) {
164 | const { memory, size, freeList, finalizationGroup } = allocator;
165 | if (freeList.length === 0) throw new RangeError('out of memory');
166 | const index = freeList.shift();
167 | const buffer = new Uint8Array(memory, index * size, size);
168 | finalizationGroup.register(buffer, index);
169 | return buffer;
170 | }
171 | ```
172 |
173 | This code uses a few features of the `FinalizationRegistry` API:
174 | - An object can have a finalizer referenced by calling the `register` method of `FinalizationRegistry`. In this case, two arguments are passed to the `register` method:
175 | - The object whose lifetime we're concerned with. Here, that's the `Uint8Array`
176 | - A held value, which is used to represent that object when cleaning it up in the finalizer. In this case, the held value is an integer corresponding to the offset within the `WebAssembly.Memory` object.
177 | - The `FinalizationRegistry` constructor is called with a callback as an argument. This callback is called with a held value.
178 |
179 | The `FinalizationRegistry` callback is called potentially multiple times, once for each registered object that becomes dead, with a relevant held value. The callback is not called during execution of other JavaScript code, but rather "in between turns". The engine is free to batch calls, and a batch of calls only runs after all of the Promises have been processed. How the engine batches callbacks is implementation-dependent, and how those callbacks intersperse with Promise work should not be depended upon.
180 |
181 | ### Avoid memory leaks for cross-worker proxies
182 |
183 | In a browser with [web workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers), a programmer can create a system with multiple JavaScript processes, and thus multiple isolated heaps and multiple garbage collectors. Developers often want to be able to address a "remote" object from some other process, for example to be able to manipulate the DOM from a worker. A common solution to this problem is to implement a proxy library; two examples are [Comlink](https://github.com/GoogleChromeLabs/comlink) and [via.js](https://github.com/AshleyScirra/via.js).
184 |
185 | In a system with proxies and processes, remote proxies need to keep local objects alive, and vice versa. Usually this is implemented by having each process keep a table mapping remote descriptors to each local object that has been proxied. However, these entries should be removed from the table when there are no more remote proxies. With the finalization functionality in the WeakRef proposal, libraries like via.js can [send a message when a proxy becomes collectable](https://github.com/AshleyScirra/via.js#memory-management), to inform the object's process that the object is no longer referenced remotely. Without finalization, via.js and other remote-proxy systems have to fall back to leaking memory, or to manual resource management.
186 |
187 | Note: This kind of setup cannot collect cycles across workers. If in each worker the local object holds a reference to a proxy for the remote object, then the remote descriptor for the local object prevents the collection of the proxy for the remote object. None of the objects can be collected automatically when code outside the proxy library no longer references them. To avoid leaking, cycles across isolated heaps must be explicitly broken.
188 |
189 | ## Using `WeakRef` objects and `FinalizationRegistry` objects together
190 |
191 | It sometimes makes sense to use `WeakRef` and `FinalizationRegistry` together. There are several kinds of data structures that want to weakly point to a value, and do some kind of cleanup when that value goes away. Note however that weak refs are cleared when their object is collected, but their associated `FinalizationRegistry` cleanup handler only runs in a later task; programming idioms that use weak refs and finalizers on the same object need to mind the gap.
192 |
193 | ### Weak caches
194 |
195 | In the initial example from this README, `makeWeakCached` used a `Map` whose values were wrapped in `WeakRef` instances. This allowed the cached values to be collected, but leaked memory in the form of the entries in the map. A more complete version of `makeWeakCached` uses finalizers to fix this memory leak.
196 |
197 | ```js
198 | // Fixed version that doesn't leak memory.
199 | function makeWeakCached(f) {
200 | const cache = new Map();
201 | const cleanup = new FinalizationRegistry(key => {
202 | // See note below on concurrency considerations.
203 | const ref = cache.get(key);
204 | if (ref && !ref.deref()) cache.delete(key);
205 | });
206 |
207 | return key => {
208 | const ref = cache.get(key);
209 | if (ref) {
210 | const cached = ref.deref();
211 | // See note below on concurrency considerations.
212 | if (cached !== undefined) return cached;
213 | }
214 |
215 | const fresh = f(key);
216 | cache.set(key, new WeakRef(fresh));
217 | cleanup.register(fresh, key);
218 | return fresh;
219 | };
220 | }
221 |
222 | var getImageCached = makeWeakCached(getImage);
223 | ```
224 |
225 | This example illustrates two important considerations about finalizers:
226 |
227 | 1. Finalizers introduce concurrency between the "main" program and the cleanup callbacks. The weak cache cleanup function has to check if the "main" program re-added an entry to the map between the time that a cached value was collected and the time the cleanup function runs, to avoid deleting live entries. Likewise when looking up a key in the ref map, it's possible that the value has been collected but the cleanup callback hasn't run yet.
228 | 2. Given that finalizers can behave in surprising ways, they are best deployed behind careful abstractions that prevent misuse, like `makeWeakCached` above. A profusion of `FinalizationRegistry` uses spread throughout a code-base is a code smell.
229 |
230 | ### Iterable WeakMaps
231 |
232 | In certain advanced cases, `WeakRef` objects and `FinalizationRegistry` objects can be very effective complements. For example, WeakMaps have the limitation that they cannot be iterated over or cleared. The WeakRefs proposal enables creating an “iterable + clearable WeakMap”:
233 |
234 | Such “iterable WeakMaps” are already used in existing DOM APIs such as `document.getElementsByClassName` or `document.getElementsByTagName`, which return live `HTMLCollection`s. As such, the `WeakRef` proposal [adds missing functionality that helps explain existing web platform features](https://extensiblewebmanifesto.org/). [Issue #17 describes a similar use case.](https://github.com/tc39/proposal-weakrefs/issues/17)
235 |
236 | ```js
237 | class IterableWeakMap {
238 | #weakMap = new WeakMap();
239 | #refSet = new Set();
240 | #finalizationGroup = new FinalizationRegistry(IterableWeakMap.#cleanup);
241 |
242 | static #cleanup({ set, ref }) {
243 | set.delete(ref);
244 | }
245 |
246 | constructor(iterable) {
247 | for (const [key, value] of iterable) {
248 | this.set(key, value);
249 | }
250 | }
251 |
252 | set(key, value) {
253 | const ref = new WeakRef(key);
254 |
255 | this.#weakMap.set(key, { value, ref });
256 | this.#refSet.add(ref);
257 | this.#finalizationGroup.register(key, {
258 | set: this.#refSet,
259 | ref
260 | }, ref);
261 | }
262 |
263 | get(key) {
264 | const entry = this.#weakMap.get(key);
265 | return entry && entry.value;
266 | }
267 |
268 | delete(key) {
269 | const entry = this.#weakMap.get(key);
270 | if (!entry) {
271 | return false;
272 | }
273 |
274 | this.#weakMap.delete(key);
275 | this.#refSet.delete(entry.ref);
276 | this.#finalizationGroup.unregister(entry.ref);
277 | return true;
278 | }
279 |
280 | *[Symbol.iterator]() {
281 | for (const ref of this.#refSet) {
282 | const key = ref.deref();
283 | if (!key) continue;
284 | const { value } = this.#weakMap.get(key);
285 | yield [key, value];
286 | }
287 | }
288 |
289 | entries() {
290 | return this[Symbol.iterator]();
291 | }
292 |
293 | *keys() {
294 | for (const [key, value] of this) {
295 | yield key;
296 | }
297 | }
298 |
299 | *values() {
300 | for (const [key, value] of this) {
301 | yield value;
302 | }
303 | }
304 | }
305 |
306 |
307 | const key1 = { a: 1 };
308 | const key2 = { b: 2 };
309 | const keyValuePairs = [[key1, 'foo'], [key2, 'bar']];
310 | const map = new IterableWeakMap(keyValuePairs);
311 |
312 | for (const [key, value] of map) {
313 | console.log(`key: ${JSON.stringify(key)}, value: ${value}`);
314 | }
315 | // key: {"a":1}, value: foo
316 | // key: {"b":2}, value: bar
317 |
318 | for (const key of map.keys()) {
319 | console.log(`key: ${JSON.stringify(key)}`);
320 | }
321 | // key: {"a":1}
322 | // key: {"b":2}
323 |
324 | for (const value of map.values()) {
325 | console.log(`value: ${value}`);
326 | }
327 | // value: foo
328 | // value: bar
329 |
330 | map.get(key1);
331 | // → foo
332 |
333 | map.delete(key1);
334 | // → true
335 |
336 | for (const key of map.keys()) {
337 | console.log(`key: ${JSON.stringify(key)}`);
338 | }
339 | // key: {"b":2}
340 | ```
341 |
342 | Remember to be cautious with use of powerful constructs like this iterable WeakMap. Web APIs designed with semantics analogous to these are widely considered to be legacy mistakes. It’s best to avoid exposing garbage collection timing in your applications, and to use weak references and finalizers only where a problem cannot be reasonably solved in other ways.
343 |
344 | #### WeakMaps remain fundamental
345 |
346 | It is not possible to re-create a `WeakMap` simply by using a `Map` with `WeakRef` objects as keys: if the value in such a map references its key, the entry cannot be collected. A real `WeakMap` implementation uses [ephemerons](http://www.jucs.org/jucs_14_21/eliminating_cycles_in_weak/jucs_14_21_3481_3497_barros.pdf) to allow the garbage collector to handle such cycles.\
347 | This is the reason the [`IterableWeakMap` example](#iterable-weakmaps) keeps the value in a `WeakMap` and only puts the `WeakRef` in a `Set` for iterations. If the value had instead been added to a `Map` such as `this.#refMap.set(ref, value)`, then the following would have leaked:
348 | ```js
349 | let key = { foo: 'bar' };
350 | const map = new IterableWeakMap(key, { data: 123, key });
351 | ```
352 |
353 | ### Scheduling of finalizers and consistency of multiple `.deref()` calls
354 |
355 | There are several conditions where implementations may call finalization callbacks later or not at all. The WeakRefs proposal works with host environments (e.g., HTML, Node.js) to define exactly how the `FinalizationRegistry` callback is scheduled. The intention is to coarsen the granularity of observability of garbage collection, making it less likely that programs will depend too closely on the details of any particular implementation.
356 |
357 | In [the definition for HTML](https://github.com/whatwg/html/pull/4571), the callback is scheduled in [task queued in the event loop](https://html.spec.whatwg.org/multipage/webappapis.html#event-loops). What this means is that, on the web, finalizers will never interrupt synchronous JavaScript, and that they also won't be interspersed to Promise reactions. Instead, they are run only after JavaScript yields to the event loop.
358 |
359 | The WeakRefs proposal guarantees that multiple calls to `WeakRef.prototype.deref()` return the same result within a certain timespan: either all should return `undefined`, or all should return the object. In HTML, this timespan runs until a [microtask checkpoint](https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint), where HTML performs a microtask checkpoint when the JavaScript execution stack becomes empty, after all Promise reactions have run.
360 |
361 | ## Historical documents
362 |
363 | - [OLD Explanation](https://github.com/tc39/proposal-weakrefs/blob/master/history/weakrefs.md) of a previous version of the proposal
364 | - [WeakRefGroups](https://github.com/tc39/proposal-weakrefs/wiki/WeakRefGroups): Previously proposed interface
365 | - [Previous Spec-text](https://github.com/tc39/proposal-weakrefs/blob/master/history/spec.md) for an earlier draft of the proposal
366 | - [Slides](https://github.com/tc39/proposal-weakrefs/blob/master/history/Weak%20References%20for%20EcmaScript.pdf): Some design considerations that went into this proposal
367 |
368 | ## Champions
369 |
370 | * Dean Tribble
371 | * Mark Miller
372 | * Till Schneidereit
373 | * Sathya Gunasekaran
374 | * Daniel Ehrenberg
375 |
376 | ## Status
377 |
378 | * WeakRefs are now Stage 4
379 | * Chrome 84
380 | * Firefox 79
381 | * Safari 14.1
382 | * [Available in Moddable XS](https://github.com/Moddable-OpenSource/moddable-xst/releases/tag/v9.0.1)
383 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -ev
4 |
5 | # Pull requests and commits to other branches shouldn't try to deploy, just build to verify
6 | if [[ "$TRAVIS_BRANCH" != master ]]; then
7 | echo "Skipping deploy; just doing a build."
8 | npm run build
9 | exit 0
10 | fi
11 |
12 | # Enable SSH authentication
13 |
14 | ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key"
15 | ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv"
16 | ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR}
17 | ENCRYPTED_IV=${!ENCRYPTED_IV_VAR}
18 |
19 | if [[ $ENCRYPTED_KEY == "" ]]; then
20 | echo "Auto-deploy GitHub key missing; exiting without a build"
21 | exit 0
22 | fi
23 |
24 | $(npm bin)/set-up-ssh --key "$ENCRYPTED_KEY" \
25 | --iv "$ENCRYPTED_IV" \
26 | --path-encrypted-key "$PRIVATE_KEY_FILE_NAME"
27 |
28 | # Update the content from the `gh-pages` branch
29 |
30 | $(npm bin)/update-branch --commands "npm run build" \
31 | --commit-message "Update gh-pages [skip ci]" \
32 | --directory "out" \
33 | --distribution-branch "gh-pages" \
34 | --source-branch "master"
35 |
--------------------------------------------------------------------------------
/deploy_key.enc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tc39/proposal-weakrefs/a13c3efc5d3b547e05731fa2af7e50348cf61173/deploy_key.enc
--------------------------------------------------------------------------------
/history/Weak References for EcmaScript.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tc39/proposal-weakrefs/a13c3efc5d3b547e05731fa2af7e50348cf61173/history/Weak References for EcmaScript.pdf
--------------------------------------------------------------------------------
/history/pre-structure.svg:
--------------------------------------------------------------------------------
1 |
2 |
462 |
--------------------------------------------------------------------------------
/history/spec.md:
--------------------------------------------------------------------------------
1 | [TOC]
2 |
3 | # Specification [Work In Progress]
4 |
5 | The specification for weak references and finalization will include
6 | the `WeakCell` type, the `WeakRef` type, the `WeakFactory` type, some
7 | abstract functions, and some characteristics of the runtime garbage
8 | collector.
9 |
10 | ## WeakFactory Objects
11 |
12 | A `WeakFactory` is an object that creates `WeakCells` and `WeakRefs` for a
13 | related group of target objects, and implements and manages the cleanup after
14 | each of those targets have been reclaimed.
15 |
16 | ### The WeakFactory Constructor
17 |
18 | The Ream constructor is the %WeakFactory% intrinsic object and the initial value of the *WeakFactory* property of the global object. When called as a constructor it creates and initializes a new WeakFactory object. WeakFactory is not intended to be called as a function and will throw an exception when called in that manner.
19 |
20 | The WeakFactory constructor is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified WeakFactory behaviour must include a super call to the WeakFactory constructor to create and initialize the subclass instance with the internal state necessary to support the WeakFactory.prototype built-in methods.
21 |
22 | When WeakFactory is called with argument _cleanup_, it performs the following steps:
23 |
24 | 1. If NewTarget is *undefined*, throw a *TypeError* exception.
25 | 1. Let _O_ be ? OrdinaryCreateFromConstructor(NewTarget, "%WeakFactoryPrototype%", « [[WeakFactory]] »).
26 | 1. If _cleanup_ is *undefined*, then
27 | 1. Set _O_.[[Cleanup]] to undefined.
28 | 1. Else
29 | 1. Set _O_.[[Cleanup]] to _cleanup_.
30 | 1. Set _O_.[[ActiveCells]] to a new empty List.
31 | 1. Set _O_.[[HasDirty]] to false.
32 | 1. Perform ! RegisterWeakFactory(_O_).
33 | 1. Return _O_
34 |
35 | ### The WeakFactory.prototype Object
36 |
37 | The initial value of WeakMap.prototype is the intrinsic object
38 | `%WeakFactoryPrototype%`. The `%WeakFactoryPrototype%` object is an
39 | ordinary object and its `[[Prototype]]` internal slot is the
40 | `%ObjectPrototype%` intrinsic object. In addition,
41 | `WeakFactory.prototype` has the following properties:
42 |
43 | ### WeakFactory.prototype.makeRef(target, holdings = undefined)
44 |
45 | The operation `WeakFactory.prototype.makeRef` with arguments `target`
46 | and `holdings` is used to create `WeakRef` objects.
47 |
48 | It performs the following steps:
49 |
50 | 1. Let _O_ be the this value.
51 | 1. If Type(_O_) is not Object, throw a TypeError exception.
52 | 1. If _O_ does not have all of the internal slots of a WeakFactory Instance, throw a TypeError exception.
53 | 1. If Type(target) is not Object, throw a TypeError exception
54 | 1. If SameValue(target, holdings), throw a TypeError exception
55 | 1. If SameRealm(target, **this**), then
56 | 1. Let _weakRef_ be ObjectCreate(%WeakRefPrototype%, ([[Factory]], [[WeakRefTarget]], [[Holdings]])).
57 | 1. Set _weakRef_.[[Factory]] to _O_.
58 | 1. Set _weakRef_.[[WeakRefTarget]] to _target_.
59 | 1. Set _weakRef_.[[Holdings]] to _holdings_.
60 | 1. Set all weakRef's garbage collection internal operations.
61 | 1. Perform ! KeepDuringJob(_target_).
62 | 1. Else
63 | 1. Let _weakRef_ be ObjectCreate(%WeakRefPrototype%, ([[Factory]], [[WeakRefTarget]], [[Holdings]], [[Strong]])).
64 | 1. Set _weakRef_.[[Factory]] to _O_.
65 | 1. Set _weakRef_.[[WeakRefTarget]] to _target_.
66 | 1. Set _weakRef_.[[Holdings]] to _holdings_.
67 | 1. Set _weakRef_.[[Strong]] to _target_.
68 | 1. Add _weakref_ to _O_.[[ActiveCells]].
69 | 1. Return weakRef.
70 |
71 | ### WeakFactory.prototype.shutdown()
72 |
73 | Disable all cleanup for `WeakCell`s created by this `WeakFactory`. This is used to
74 | enable a subsystem using this `WeakFactory` to shutdown completely without
75 | triggering unnecessary finalization.
76 |
77 | 1. Let _O_ be the this value.
78 | 2. If Type(_O_) is not Object, throw a TypeError exception.
79 | 3. If _O_ does not have all of the internal slots of a WeakFactory
80 | Instance, throw a TypeError exception.
81 | 4. Set _O_.[[Cleanup]] to undefined.
82 | 4. Set _O_.[[ActiveCells]] to undefined.
83 | 4. Set _O_.[[HasDirty]] to false.
84 | 7. Return undefined.
85 |
86 | ### WeakFactory.prototype [ @@toStringTag ]
87 |
88 | The initial value of the @@toStringTag property is the string value
89 | "WeakFactory".
90 |
91 | ### Properties of WeakFactory Instances
92 |
93 | WeakFactory instances are ordinary objects that inherit properties
94 | from the WeakFactory.prototype intrinsic object. WeakFactory
95 | instances are initially created with the internal slots listed in
96 | Table K.
97 |
98 | *Table K — Internal Slots of WeakFactory Instances*
99 |
100 | | Internal Slot | Description |
101 | | ----- | ----- |
102 | | [[Cleanup]] | An optional reference to a function |
103 | | [[ActiveCells]] | WeakRefs that are Active or Dirty. |
104 | | [[HasDirty]] | True if some of ActiveCells has become Dirty. |
105 |
106 | ## WeakCell Objects
107 |
108 | A `WeakCell` is an object that is used to refer to a target object
109 | without preserving it from garbage collection, and to enable code to
110 | be run to clean up after the target is garbage collected. Correct
111 | instances can only be created via a `WeakFactory` object because the
112 | instances must contain a pointer to such a factory.
113 |
114 | ### The WeakCell.prototype Object
115 |
116 | All `WeakCell` objects inherit properties from the
117 | `%WeakCellPrototype%` intrinsic object. The `WeakCell.prototype`
118 | object is an ordinary object and its `[[Prototype]]` internal slot is
119 | the `%ObjectPrototype%` intrinsic object. In addition,
120 | `WeakCell.prototype` has the following properties:
121 |
122 | ### WeakCell.prototype.clear()
123 |
124 | WeakCell.prototype.clear() performs the following steps:
125 |
126 | 1. Let _O_ be the **this** value.
127 | 1. If Type(_O_) is not Object, throw a TypeError exception.
128 | 1. If _O_ does not have all of the internal slots of a WeakCell
129 | Instance, throw a TypeError exception.
130 | 1. Let _factory be _O_.[[Factory]].
131 | 1. If _factory_ is not **undefined**.
132 | 1. Remove _O_ from _factory_.[[ActiveCells]].
133 | 1. Set _O_.[[WeakRefTarget]] to undefined.
134 | 1. Set _O_.[[Factory]] to undefined.
135 | 1. Set _O_.[[Holdings]] to undefined.
136 | 1. Return **undefined**.
137 |
138 | ### get WeakCell.prototype.holdings
139 |
140 | **WeakCell.prototype.holdings** is an accessor property whose set
141 | accessor function is **undefined**. Its get accessor function performs
142 | the following steps:
143 |
144 | 1. Let _O_ be the **this** value.
145 | 1. If Type(_O_) is not Object, throw a TypeError exception.
146 | 1. If _O_ does not have all of the internal slots of a WeakCell Instance, throw a TypeError exception.
147 | 1. Return _O_.[[Holdings]].
148 |
149 | ### WeakCell.prototype [ @@toStringTag ]
150 |
151 | The initial value of the @@toStringTag property is the string value
152 | "WeakCell".
153 |
154 | ### Properties of WeakCell Instances
155 |
156 | WeakCell instances are ordinary objects that inherit properties from
157 | the WeakCell.prototype intrinsic object. WeakCell instances are
158 | initially created with the internal slots listed in Table L.
159 |
160 | *Table L — Internal Slots of WeakCell Instances*
161 |
162 | | Internal Slot | Description |
163 | | ----- | ----- |
164 | | [[WeakRefTarget]] | The reference to the target object |
165 | | [[Factory]] | AThe factory that created the object |
166 | | [[Holdings]] | Any value, passed as a parameter to the cleanup function in the factory |
167 |
168 | ## WeakRef Objects
169 |
170 | A `WeakRef` is a kind of WeakCell that can also be dereferenced. Correct
171 | instances can only be created via a WeakFactory object because the instances
172 | must contain a pointer to such a factory.
173 |
174 | ### The WeakRef.prototype Object
175 |
176 | All `WeakRef` objects inherit properties from the
177 | `%WeakRefPrototype%` intrinsic object. The `%WeakRefPrototype%`
178 | object is an ordinary object and its `[[Prototype]]` internal slot is
179 | the `%WeakCellPrototype%` intrinsic object. In addition,
180 | `WeakRef.prototype` has the following properties:
181 |
182 | ### WeakRef.prototype.deref()
183 |
184 | WeakRef.prototype.deref() performs the following steps:
185 |
186 | 1. Let _O_ be the this value.
187 | 2. If Type(_O_) is not Object, throw a TypeError exception.
188 | 3. If _O_ does not have all of the internal slots of a WeakRef Instance, throw a TypeError exception.
189 | 6. Let _a_ be the value of the [[WeakRefTarget]] internal slot of _O_.
190 | 4. If _a_ is not **undefined**, perform ! KeepDuringJob(_a_).
191 | 7. Return _a_.
192 |
193 | ### WeakRef.prototype [ @@toStringTag ]
194 |
195 | The initial value of the @@toStringTag property is the string value
196 | "WeakRef".
197 |
198 | ### Properties of WeakRef Instances
199 |
200 | All properties on WeakRef instances are inherited from the WeakCell.prototype "superclass".
201 |
202 | ## Job and Agent Extensions
203 |
204 | This section describes extensions to existing standard elements.
205 |
206 | ### Agent Additions
207 |
208 | Each Agent contains a new property [[WeakFactories]] which points weakly (via
209 | records TBD) to the WeakFactory instances created in that agent.
210 |
211 | ### WeakCellJobs Queue
212 |
213 | Add a separate "WeakCellJobs" JobQueue for all cleanup operations in the agent. (Section 8.4)
214 |
215 | ## Abstract Operations
216 |
217 | This section decribes additional abstract operations to support WeakCells and cleanup.
218 |
219 | ### SameRealm Abstract Operation
220 |
221 | When the abstract operation `SameRealm` is called with two references, _a_ and
222 | _b_, it returns true if they were created in the same Realm.
223 |
224 | ### KeepDuringJob Abstract Operation
225 |
226 | When the abstract operation `KeepDuringJob` is called with a target
227 | object reference, it adds the target to an identity Set that will
228 | point strongly at the target until the end of the current Job. This
229 | may be abstractly implemented as a Set in the current Job that becomes
230 | unreachable when the Job ends because the Job becomes unreachable, or
231 | as a Set in the current Agent that gets cleared at the end of each
232 | Job.
233 |
234 | ### WeakTargetReclaimed
235 |
236 | The garbage collector calls this abstract operation when it has made a target
237 | object unreachable and set all weakRef [[WeakRefTarget]] fields that point to it to
238 | **undefined**.
239 |
240 | When the abstract operation `WeakTargetReclaimed` is called with a
241 | _weakcell_ argument (by the garbage collector), it performs the following
242 | steps:
243 |
244 | 1. Assert: Type(_weakcell_) is Object.
245 | 1. Assert: _weakcell_ has all of the internal slots of a WeakCell Instance.
246 | 1. Let _factory_ be _weakcell_.[[Factory]].
247 | 1. If _factory_ is not **undefined**, then Set _factory_.[[HasDirty]] to **true**.
248 | 1. Return **undefined**.
249 |
250 | ### RegisterWeakFactory
251 |
252 | Add a `WeakFactory` to the [[WeakFactories]] list of the current agent.
253 |
254 | ### DoAgentFinalization
255 |
256 | When the abstract operation `DoAgentFinalization` is called with an argument
257 | _agent_, it performs the following steps:
258 |
259 | 1. Assert: Type(_agent_) is Object.
260 | 1. Assert: _agent_ has all of the internal slots of an Agent Instance.
261 | 1. For each _factory_ in _agent_.[[WeakFactories]]
262 | 1. If _factory_.[[HasDirty]], then
263 | 1. Perform EnqueueJob("WeakCellJobs", WeakFactoryCleanupJob, « _factory_ »).
264 |
265 | ### WeakFactoryCleanupJob(_factory_)
266 |
267 | The job `WeakFactoryCleanupJob` with parameter _factory_, and then performs
268 | the following steps:
269 |
270 | 1. Assert: Type(_factory_) is Object.
271 | 1. Assert: _factory_ has all of the internal slots of a WeakFactory Instance.
272 | 1. Let _iterator_ be ObjectCreate(%ReclaimedIteratorPrototype%, « [[Factory]] »).
273 | 1. Set _iterator_.[[WeakFactory]] = _factory_.
274 | 1. Let _next_ be a new built-in function defined in `WeakFactoryCleanupIterator` **next**.
275 | 1. Perform CreateMethodProperty(_iterator_, **next**, _next_).
276 | 1. Call _factory_.[[Cleanup]]\(_iterator_).
277 | 1. Return **undefined**
278 |
279 | ### WeakFactoryCleanupIterator.next()
280 |
281 | The WeakFactoryCleanupIterator.next method is a standard built-in function object that performs the following steps:
282 |
283 | TBD: search the _factory_.[[ActiveCells]] for a dirty WeakCell
284 | TDB: how is done set correctly?
285 |
--------------------------------------------------------------------------------
/history/weakrefs.md:
--------------------------------------------------------------------------------
1 | # NOT CURRENT. PLEASE SEE PRESENTATION
2 |
3 | While much of this document is still relevant, the spec and background
4 | information _documents_ are still being revised. For information on
5 | the current proposal, please see the [current
6 | slides](https://github.com/tc39/proposal-weakrefs/blob/master/specs/Weak%20References%20for%20EcmaScript.pdf).
7 | They are up to date.
8 |
9 | [Initial Spec-text](https://github.com/tc39/proposal-weakrefs/blob/master/specs/spec.md)
10 |
11 | [Slides](https://github.com/tc39/proposal-weakrefs/blob/master/specs/Weak%20References%20for%20EcmaScript.pdf)
12 |
13 | # Background
14 |
15 | In a simple garbage-collected language, memory is a graph of
16 | references between objects, anchored in the roots such as global data
17 | structures and the program stack. The garbage collector may reclaim
18 | any object that is not reachable through that graph from the
19 | roots. Some patterns of computation that are important to JavaScript
20 | community are difficult to implement in this simple model because
21 | their straightforward implementation causes no-longer used objects to
22 | still be reachable. This results in storage leaks or requires
23 | difficult manual cycle breaking. Example include:
24 |
25 | * MVC and data binding frameworks
26 | * reactive-style libraries and languages that compile to JS
27 | * caches that require cleanup after keys are no longer referenced
28 | * proxies/stubs for comm or object persistence frameworks
29 | * objects implemented using external or manually-managed resources
30 |
31 | Two related enhancements to the language runtime will enable
32 | programmers to implement these patterns of computation: weak
33 | references and finalization. A strong reference is a reference that
34 | causes an object to be retained; in the simple model all references
35 | are strong. A *weak reference* is a reference that allows access to an
36 | object that has not yet been garbage collected, but does not prevent
37 | that object from being garbage collected. *Finalization* is the
38 | execution of code to clean up after an object that has become
39 | unreachable to program execution.
40 |
41 | For example, MVC frameworks often use an observer pattern: the view
42 | points at the model, and also registers as an observer of the model.
43 | If the view is no longer referenced from the view hierarchy, it should
44 | be reclaimable. However in the observer pattern, the model points at
45 | its observers, so the model retains the view (even though the view is
46 | no longer displayed). By having the model point at its observers using
47 | a weak reference, the view can just be garbage collected normally with
48 | no complicated reference management code.
49 |
50 | Similarly, a graphics widget might have a reference to a primitive
51 | external bitmap resource that requires manual cleanup (e.g., it must
52 | be returned to a buffer pool when done). With finalization, code can
53 | cleanup the bitmap resource when the graphics widget is reclaimed,
54 | avoiding the need for pervasive manual disposal discipline across the
55 | widget library.
56 |
57 | # Intended Audience
58 |
59 | The garbage collection challenges addressed here largely arise in the
60 | implementation of libraries and frameworks. The features proposed here
61 | are advanced features (e.g., like proxies) that are primarily intended
62 | for use by library and framework creators, not their clients. Thus,
63 | the priority is enabling library implementors to correctly,
64 | efficiently, and securely manage object lifetimes and finalization.
65 |
66 | # Solution Approach
67 |
68 | This proposal is based on Jackson et.al.'s **Weak References and
69 | Post-mortem Finalization**. Post-mortem finalization was a major
70 | innovation in garbage collection pioneered for Smalltalk and since
71 | applied in numerous garbage-collected systems. It completely and
72 | simply addresses two major challenges for finalization: resurrection
73 | and (what I will call) layered-collection.
74 |
75 | An object that the garbage collector has decided to reclaim is called
76 | a condemned object. Resurrection happens if a condemned object becomes
77 | reachable again as the result of the finalization; for example, if the
78 | finalization code stores the object into a data structure.
79 |
80 | Layered collection happens when a multi-object data structure such as
81 | a tree becomes unreachable by its client. For such a tree, if the
82 | finalization for each node requires the children, then the first GC
83 | can only condemn and reclaim the root node; the children can be
84 | reclaimed at the second GC; the grandchildren at the third GC, etc.
85 |
86 | The post-mortem finalization approach eliminates resurrection entirely
87 | and avoids layered collection naturally. The central insight is that
88 | finalization never touches the condemned object. Once an object is
89 | condemned, it stays condemned (the transition is monotonic).
90 |
91 | To support finalization, an object called an *executor* can be
92 | associated with any target object. When the target object is
93 | condemned, the executor will execute to clean up after the target. The
94 | executor needs to use some state to clean up after target; the
95 | condemned target is no longer available. That state importantly might
96 | *not* be state previously "owned" by the target. For example, cached
97 | XML nodes for an XML parser would not have been owned by the file they
98 | are parsed from. Therefore the proposal uses the general terms
99 | *holdings* for this state (rather than the attractive but misleading
100 | term "estate").
101 |
102 | By never allowing a reference to the condemned object, resurrection is
103 | simply precluded. For a layered data structure, the entire data
104 | structure can be reclaimed in one GC, and then the executors for the
105 | individual nodes run.
106 |
107 | Resurrection and related finalization issues are especially subtle
108 | when composing libraries and abstractions that use finalization
109 | internally. For further background, Chris Brumme discusses some of
110 | C#'s [pre-mortem finalization challenges](http://blogs.msdn.com/b/cbrumme/archive/2004/02/20/77460.aspx).
111 |
112 | # Proposed Solution
113 |
114 | We specify the proposed API in terms of a new function:
115 | `makeWeakRef`. It takes a `target` object, an optional `executor`,
116 | and an optional `holdings` object, and returns a fresh weak reference
117 | that internally has a weak pointer to the target. The target can be
118 | retrieved from the resulting weak reference unless the target has been
119 | condemned, in which case `null` is returned. Once target has been
120 | condemned, there are no references to it in the system. If the target
121 | is condemned and the weak reference is not, then the weak reference's
122 | optional executor will eventually be invoked in its own job (aka
123 | turn), with the optional holdings passed as parameter.
124 |
125 | Note that objects may be unreachable but not yet condemned, e.g.,
126 | because they are in a different generation. The "condemned" objects
127 | are the subset of unreachable objects that the garbage noticed it can
128 | reclaim. Because condemned objects can never become reachable again,
129 | it is not visible to the program whether they were actually reclaimed
130 | or not at any particular time.
131 |
132 | ## Usage Overview
133 |
134 | [Pre-weakref](pre-structure.svg) The first diagram shows the objects
135 | in an example scenario before weak references are used.
136 |
137 | * A client gets the target (e.g., a remote reference) from the
138 | service object (e.g., the remote connection)
139 | * The target allocates/uses Holdings to accomplish it's function
140 | (e.g., a remote reference index)
141 | * The Service Object wants to clean up using the holdings after the
142 | target is "done"
143 |
144 | The problem is that the Service Object will retain the Target, thus
145 | preventing it from getting collected.
146 |
147 | [With-weakref](weakref-structure.svg) The second diagram inserts the
148 | weak reference in the reference path from the Service Object to the
149 | Target. It adds the Executor, which will be invoked to clean up after
150 | the Target, using the Holdings (e.g., drop the remote reference).
151 |
152 | ## Basic Usage
153 |
154 | The root of the API is the `makeWeakRef` operation to construct a weak
155 | reference.
156 |
157 | ```js
158 | // Make a new weak reference.
159 | // The target is a strong pointer to the object that will be pointed
160 | // at weakly by the result.
161 | // The executor is an optional argument that will be invoked after the
162 | // target becomes unreachable.
163 | // The holdings is an optional argument that will be provided to the
164 | // executor when it is invoked for target.
165 | makeWeakRef(target, executor, holdings);
166 | ```
167 |
168 | This brief example shows the usage and core behavior of the API.
169 | ```js
170 | let buf = pool.getBuf();
171 | let original = someObject(buf);
172 | let executor = buffer => buffer.release();
173 | let wr = makeWeakRef(original, executor, buf);
174 | // full GC and finalization happens here
175 | assert(wr.get() === original);
176 | original = undefined;
177 | // full GC and finalization happens here
178 | assert(wr.get() === null);
179 | assert(buf.isReleased);
180 | ```
181 |
182 | ## Expanded Example
183 |
184 | This example shows a typical pattern of finalization for a simple
185 | remote reference implementation. `RemoteConnection` manages the client
186 | side references to objects on a server. Each remote reference is
187 | represented on the wire with an index (called `remoteId`, below). The
188 | `makeResultRemoteRef` operation creates the appropriate local object
189 | and arranges so that when that object is condemned, finalization will
190 | invoke `dropRef` in order to remove the associated bookkeeping and
191 | notify the server that the client is no longer referencing that
192 | object.
193 |
194 | ```js
195 |
196 | class RemoteConnection {
197 |
198 | constructor(transport) {
199 | this.transport = transport; // the low-level communication channel
200 | this.executor = remoteId => this.dropRef(remoteId);
201 | this.remotes = new Map(); // map from id to weakRef
202 | ??? // more construction
203 | }
204 |
205 | // Create a remote reference for a remote return result. Setup
206 | // finalization to clean up after it if it is garbge collected.
207 | makeResultRemoteRef(remoteId) {
208 | let remoteRef = ???; // remoteRef construction elided
209 | this.remotes.set(remoteId, makeWeakRef(remoteRef, this.executor, remoteId));
210 | return remoteRef;
211 | }
212 |
213 | // The remote reference object has been garbage collected. Discard
214 | // the associated bookeeping and notify the server that it is no
215 | // longer referenced.
216 | dropRef(remoteId) {
217 | this.transport.send("DROP", remoteId);
218 | this.remotes.delete(remoteId);
219 | }
220 | }
221 | ```
222 |
223 | It is useful to note that in this scenario, the holdings for each
224 | remote reference is simply the remoteId itself. Additionally, if the
225 | entire remote connection is garbage-collected, then there's no need
226 | to perform finalization for any of these remote references. Thus
227 | finalization of the remote references created for the connection is
228 | scoped to the connection.
229 |
230 | # Additional Requirements and Discussion
231 |
232 | ## Finalization between turns
233 |
234 | The finalization code is user code that may modify application,
235 | library, and system state. It's crucial that it not run in the midst
236 | of other user code because invariants may be in transition. ECMAScript
237 | can address this ideally by running executors only between turns;
238 | i.e., when the application stack is empty.
239 |
240 | Because there may be a large number of finalizers and they are user
241 | code that could run for unbounded periods, it crucial for system
242 | responsiveness that the finalizers can run interleaved with multiple
243 | program jobs. Therefore the proposal specifies that finalizers are
244 | scheduled conceptually on a separate job queue (or queues) for
245 | finalization. This allows an implementation to progress finalization
246 | while preserving application program responsiveness.
247 |
248 | ## Multiple Independent Executors
249 |
250 | Finalization is sometimes about an object's internal implementation
251 | details and sometimes about the client usage of an object. An example
252 | of internal usage is file handle cleanup -- when a File object is
253 | collected, the implementation calls a system primitive to close the
254 | corresponding file handle. An example of client usage could be a cache
255 | of parsed content from a file -- when the file is collected, the cache
256 | wants to clear the corresponding cache information. These scenarios
257 | can both be implemented with the WeakRef and finalization approach,
258 | using holdings of a file handle and a cache key, respectively. A
259 | single file object might participate independently in both these uses.
260 |
261 | ## Unregistering from Finalization
262 |
263 | The finalization needs for a given target can change depending on
264 | circumstances. For example, if a file is manually closed (e.g.,
265 | because it was in a stream and the stream read all the contents) then
266 | finalization would be unnecessary and potentially a source of errors
267 | or exceptions. To avoid this source of complexity and bugs, this
268 | proposal specifies that clearing a WeakRef before the executor runs
269 | atomically prevents the executor for being invoked for the original
270 | target. In practice, this tends to eliminate the need for conditional
271 | finalization code.
272 |
273 | ## Portability and Observable Garbage Collection
274 |
275 | Revealing the non-deterministic behavior of the garbage collector
276 | creates a potential for portability bugs. Different host environments
277 | may collect a weakly-held object at different times, which a
278 | `WeakRef` exposes to the program. More generally, the
279 | `makeWeakRef` function is not safe for general access since it
280 | grants access to the non-determinism inherent in observing garbage
281 | collection. The resulting side channel reveals information that may
282 | violate the [confidentiality assumptions](https://web.archive.org/web/20160318124045/http://wiki.ecmascript.org/doku.php?id=strawman:gc_semantics)
283 | of other programs. Therefore we add `makeWeakRef` to the `System`
284 | object, just like other authority-bearing objects, e.g.,
285 | [the default loader](https://github.com/whatwg/loader/issues/34).
286 |
287 | WeakRef instances may often be referenced by code that should not have
288 | the same authority. Therefore the constructor for WeakRefs must not be
289 | available via WeakRef instances.
290 |
291 | ## Reference stability during turns
292 |
293 | To further eliminate unnecessary non-determinism, we tie the
294 | observable collection of weak references to the event loop semantics.
295 | The informal invariant is:
296 |
297 | *A program cannot observe a weak reference be automatically deleted
298 | within a turn of the event loop.*
299 |
300 | Specifically, this means that, unless the weak reference is explicitly
301 | modified during the turn (e.g., via `clear`):
302 |
303 | * When a weak reference is created, subsequent calls to its
304 | `get` method within the same turn must return the object with
305 | which it was created.
306 | * If a weak reference's `get` method is called and produces an
307 | object, subsequent calls to its `get` method within the same
308 | turn must return the same object.
309 |
310 | A naive approach to this is to simply restrict garbage collection to
311 | between turns. However, some programs have significant, non-retained
312 | allocation during turns. For example, if a large tree is made
313 | unreachable during a turn, and replaced with a new tree (e.g., virtual
314 | DOM creation in React), this strategy would prevent the original,
315 | now-unreachable tree from being collected until the next GC between
316 | turns. Such a restriction would lead to unexpected OOM for otherwise
317 | correct, non-leaking programs. Therefore this approach is not
318 | sufficient or acceptable.
319 |
320 | The example pseudocode below illustrates a simple approach that
321 | straightforwardly and efficiently achieves the reference stability
322 | requirements. The example is a builtin that has access to some (all
323 | upper case) magic that is not available to normal JavaScript. This
324 | pseudocode is more magical than a self-hosted builtin, because we
325 | assume the following code blocks are atomic wrt gc.
326 |
327 | The MARK method explains how the normal mark phase of gc marks through
328 | a weak reference. It treats the `target` pointer as strong *unless* it
329 | has not been observed in the current turn.
330 |
331 | ```js
332 | function makeWeakRef(target, executor = void 0, holdings = void 0) {
333 | if (target !== Object(executor)) {
334 | throw new TypeError('Object expected');
335 | }
336 | let observedTurn = CURRENT_TURN;
337 | WEAK_LET weakPtr = target;
338 |
339 | const weakReference = {
340 | get() {
341 | observedTurn = CURRENT_TURN;
342 | return weakPtr;
343 | }
344 | clear() {
345 | weakPtr = void 0;
346 | executor = null;
347 | holdings = null;
348 | }
349 | // MARK is only called by gc, as part of its normal mark phase.
350 | // Obviously, not visible to JS code.
351 | MARK() {
352 | MARK(executor);
353 | MARK(holdings);
354 | if (observedTurn === CURRENT_TURN) {
355 | MARK(weakPtr);
356 | }
357 | }
358 | // FINALIZE is called in its own turn and only if the target was
359 | // condemned. Obviously, not visible to JS code.
360 | FINALIZE() {
361 | if (typeof executor === 'function') {
362 | let exec = executor;
363 | let hold = holdings;
364 | weakReference.clear();
365 | exec(hold);
366 | }
367 | }
368 | };
369 | return weakReference;
370 | }
371 | ```
372 | NOTE A more complete version of this code is at the end of the
373 | document, which avoids allocations and covers more cases and
374 | subtleties.
375 |
376 | ## Allocation During GC and Finalization
377 |
378 | It is essential that the garbage collection itself not be forced to
379 | allocate; that can substantially complicate the garbage collector
380 | implementation and lead to storage allocation thrashing. The design in
381 | this proposal allows all space required for bookkeeping to be
382 | preallocated so that the GC is never required to allocate. Since
383 | finalization is not executed directly by the garbage collector, user
384 | finalization code is allowed to allocate normally.
385 |
386 | ## Exceptions during Finalization
387 |
388 | Each finalization action occurs in its own turn. Therefore exceptions
389 | thrown at the top level of finalization can use normal exception
390 | handling behavior.
391 |
392 | ## Unintended Retention
393 |
394 | Post-mortem finalization prevents resurrection bugs. However, some
395 | patterns of code using finalization may inadvertently retain objects
396 | that would otherwise have been garbage. This proposal is designed to
397 | minimize these leaks, because bugs of this nature tend to only
398 | manifest when there's enough load that a small leak matters. The most
399 | basic cause is if the executor is setup to use the target itself to
400 | clean up after itself (i.e., someone tries to emulate destructors).
401 | Providing the target itself as the executor or holdings prevents the
402 | entire purpose of WeakRefs because the WeakRef points strongly at
403 | those. In practice this is almost always an error and so should be
404 | detected and signaled at WeakRef creation time.
405 |
406 | Another cause of unexpected retention is functions whose stored
407 | context includes the target (this may be because the functions
408 | mentions the target explicitly, or because the runtime used a shared
409 | context for multiple functions). A pattern of allocating new executor
410 | functions for each new weak reference is more susceptible to this
411 | issue.
412 |
413 | An example with file stream construction that arranges the underlying
414 | file to be closed (with an optional warning). The last line of the
415 | constructor is deliberately unrelated code that uses a function.
416 | ```js
417 | const openfiles = new Map();
418 |
419 | // closeFile is the shared executor for all files.
420 | const closeFile(file) {
421 | file.close();
422 | openFiles.delete(file);
423 | console.info("Filestream dropped before close: ", file.name)
424 | }
425 |
426 | class FileStream {
427 | constructor(filename) {
428 | this.file = new File(filename, "r");
429 | openFiles.set(file, makeWeakRef(this, () => closeFile(this.file)));
430 | // now eagerly load the contents
431 | this.loading = file.readAsync().then(data => this.setData(data));
432 | }, ...
433 | }
434 | ```
435 | The constructor code straightforward, but unfortunately closes over
436 | the file stream target `this`, and therefore prevents garbage
437 | collection of it. A more careful variant is:
438 | ```js
439 | class FileStream {
440 | constructor(filename) {
441 | let file = new File(filename, "r");
442 | this.file = file;
443 | openFiles.set(file, makeWeakRef(this, () => closeFile(file)));
444 | // now eagerly load the contents
445 | this.loading = file.readAsync().then(data => this.setData(data));
446 | }, ...
447 | ```
448 | With sufficient care, the finalization avoids retaining `this`.
449 | However, as mentioned above, a wide variety of function implementation
450 | approaches use shared records for multiple functions in the same
451 | scope. The mere existence of the `data => this.setData(data)` function
452 | in the same scope could cause the executor function
453 | `() => closeFile(file)` to additionally point at `this`. The pattern
454 | using `holdings` avoids this:
455 | ```js
456 | class FileStream {
457 | constructor(filename) {
458 | let file = new File(filename, "r");
459 | this.file = file;
460 | openFiles.set(file, makeWeakRef(this, closefile, file));
461 | // now eagerly load the contents
462 | this.loading = file.readAsync().then(data => this.setData(data));
463 | }, ...
464 | ```
465 |
466 | ## WeakRef collection
467 |
468 | Execution of the executor associated with a WeakRef is only required
469 | if the WeakRef itself is still retained. Allowing unreachable WeakRefs
470 | to be condemned without handling their executor prevents resurrection
471 | issues with holdings and executor functions, and allows efficient
472 | collection when the application discards entire subsystems that
473 | internally use finalization for resources that are also discarded
474 | (e.g., an entire cache is dropped, not just the keys).
475 |
476 | ## Cross-realm references
477 |
478 | Weak references enable observation of the behavior of the garbage
479 | collector, which can provide a channel of communication between
480 | isolated subgraphs that share only transitively immutable objects, and
481 | therefore should not be able to communicate. Security-sensitive code
482 | would most likely need to virtualize or censor access to the
483 | `makeWeakRef` function from untrusted code. However, such
484 | restrictions do not enable one realm to police other realms. To plug
485 | this leak, a weak reference created within realm A should only point
486 | weakly within realm A. When set to point at an object from another
487 | realm, this can be straightforwardly enforced in the WeakRef
488 | construction: if the Target is from another realm, then the Target is
489 | also stored in the strong holdings pointer. Because the WeakRef
490 | thereby retains the Target, Target will only become unreachable if the
491 | WeakRef is unreachable, and therefore will never require finalization.
492 | Security demands only that such inter-realm references not point
493 | weakly.
494 |
495 | See [https://mail.mozilla.org/pipermail/es-discuss/2013-January/028542.html](https://mail.mozilla.org/pipermail/es-discuss/2013-January/028542.html)
496 | for a nice refinement for pointing weakly at objects from a set of
497 | realms.
498 |
499 | ## Information leak via Executor
500 |
501 | Particularly in a library context, the same executor might be used for
502 | multiple otherwise-unrelated objects. A misbehaving executor could
503 | pass secrets from one object to another. In order to facilitate
504 | pre-existing, shared executors without compromising encapsulation, the
505 | protocol is designed to not require mutation within the
506 | executor. Thus, a deeply immutable function can safely be used as the
507 | executor without inadvertently creating a communications channel.
508 |
509 | ## Process Termination
510 |
511 | Finalization is for the purpose of application code, and not to
512 | protect system resources. The runtime for resources must ensure that
513 | resources they use are appropriately reclaimed upon process
514 | shutdown. Process/worker shutdown must not require garbage collection
515 | or execution of finalizers.
516 |
517 | ## WeakRefs vs. WeakArrays
518 |
519 | Most early post-mortem finalization systems used a WeakArray
520 | abstraction rather than individual WeakRefs. With WeakArrays, all
521 | array slots point weakly at their target, and the executor for the
522 | array is provided the index that is no longer reachable. Among other
523 | things, this was intended to amortize the overhead of weak reference
524 | management while providing convenient bulk finalization support.
525 | Modern generational garbage collectors change these trade-offs: for
526 | many use cases, the Weak Array gets promoted to an older generation
527 | and ends up pointing at objects in newer generations. The resulting
528 | "remember set" overhead and impact on GC outweighs that potential
529 | advantage. Therefore this proposal specifies the simpler WeakRef
530 | approach here.
531 |
532 | ## The API
533 |
534 | This shows pseudo typing and behavioral description for the proposed
535 | weak references API.
536 |
537 | ```js
538 | makeWeakRef : function(
539 | target : object,
540 | executor : undefined | function(holdings : any) -> undefined,
541 | holdings : any) -> WeakRef
542 | WeakRef : object {
543 | get : function() -> object | void 0
544 | clear : function() -> void 0
545 | }
546 | ```
547 |
548 | * `makeWeakRef(target, executor = void 0, holdings = void 0)` -
549 | returns a new weak reference to `target`. Throws `TypeError` if
550 | `target` is not an object or if it is the same as `holdings` or
551 | `executor`.
552 |
553 | * `get` - returns the weakly held target object, or `undefined`
554 | if the object has been collected.
555 |
556 | * `clear` - sets the internal weak reference to `undefined` and
557 | prevents the executor from getting invoked.
558 |
559 | If an executor is provided, then it may be invoked on the holdings
560 | (with **this** bound to **undefined**) in it's own turn if:
561 |
562 | * the `target` is condemned
563 | * the `WeakRef` is not condemned
564 | * the `WeakRef` has not been `clear()`ed.
565 |
566 | Constructing a new `WeakRef` or retrieving the `target` from an
567 | existing `WeakRef` (using `get()`) causes the `WeakRef` to act as a
568 | normal (strong) reference to the target for the remainder of the
569 | current turn. Thus for example, if a GC is performed during that turn
570 | but after such a `get()` call the `WeakRef` is processed as a normal
571 | object.
572 |
573 | ## Specification [Work In Progress]
574 |
575 | The specification for Weak references and finalization will include
576 | the `WeakRef` type and some characteristics of the runtime garbage
577 | collector.
578 |
579 | ### WeakRef Objects
580 |
581 | A `WeakRef` is an object that is used to refer to a target object
582 | without preserving it from garbage collection, and to enable code to
583 | be run to clean up after the target is garbage collected. There is not
584 | a named constructor for `WeakRef` objects. Instead, `WeakRef` objects
585 | are created by calling a privileged system function [TBD].
586 |
587 | #### MakeWeakRef Abstract Operation
588 |
589 | The abstract operation `makeWeakRef` with arguments `target`,
590 | `executor`, and `holdings` is used to create such `WeakRef` objects.
591 | It performs the following steps:
592 |
593 | 1. If Type(target) is not Object, throw a TypeError exception
594 | 1. If Type(executor) is neither Undefined nor Function, throw a TypeError exception
595 | 2. If SameValue(target, holdings), throw a TypeError exception
596 | 3. If SameValue(target, executor), throw a TypeError exception
597 | 4. Let _currentTurn_ be ! GetCurrentJobReference().
598 | 5. Let _targetRealm_ be ! GetFunctionRealm(target).
599 | 6. Let _thisRealm_ be ! GetFunctionRealm(**this**).
600 | 7. If SameValue(targetRealm, thisRealm), then
601 | 1. Let _weakRef_ be ObjectCreate(%WeakRefPrototype%, ([[Target]], [[ObservedTurn]], [[Executor]], [[Holdings]])).
602 | 2. Set weakRef's [[Target]] internal slot to _target_.
603 | 3. Set weakRef's [[ObservedTurn]] internal slot to _currentTurn_.
604 | 4. Set weakRef's [[Executor]] internal slot to _executor_.
605 | 5. Set weakRef's [[Holdings]] internal slot to _holdings_.
606 | 6. Set all weakRef's garbage collection internal operations.
607 | 8. Else
608 | 1. Let _weakRef_ be ObjectCreate(%WeakRefPrototype%, ([[Target]], [[ObservedTurn]], [[Executor]], [[Holdings]])).
609 | 2. Set weakRef's [[Target]] internal slot to _target_.
610 | 3. Set weakRef's [[ObservedTurn]] internal slot to _currentTurn_.
611 | 4. Set weakRef's [[Executor]] internal slot to **undefined**.
612 | 5. Set weakRef's [[Holdings]] internal slot to **undefined**.
613 | 9. Return weakRef.
614 |
615 | #### The %WeakRefPrototype% Object
616 |
617 | All `WeakRef` objects inherit properties from the
618 | `%WeakRefPrototype%` intrinsic object. The `%WeakRefPrototype%`
619 | object is an ordinary object and its `[[Prototype]]` internal slot is
620 | the `%ObjectPrototype%` intrinsic object. In addition,
621 | `%WeakRefPrototype%` has the following properties:
622 |
623 | ##### %WeakRefPrototype%.get( )
624 |
625 | 1. Let _O_ be the this value.
626 | 2. If Type(_O_) is not Object, throw a TypeError exception.
627 | 3. If _O_ does not have all of the internal slots of a WeakRef Instance, throw a TypeError exception.
628 | 4. Let _currentTurn_ be ! GetCurrentJobReference().
629 | 5. Set the value of the [[ObservedTurn]] internal slot of _O_ to _currentTurn_.
630 | 6. Let _a_ be the value of the [[Target]] internal slot of _O_.
631 | 7. Return a.
632 |
633 | ##### %WeakRefPrototype%.clear( )
634 | 1. Let _O_ be the this value.
635 | 2. If Type(_O_) is not Object, throw a TypeError exception.
636 | 3. If _O_ does not have all of the internal slots of a WeakRef
637 | Instance, throw a TypeError exception.
638 | 4. Set the value of the [[Target]] internal slot of O to undefined.
639 | 5. Set the value of the [[Executor]] internal slot of O to null.
640 | 6. Set the value of the [[Holdings]] internal slot of O to null.
641 | 7. Return undefined.
642 |
643 | ##### %WeakRefPrototype% [ @@toStringTag ]
644 |
645 | The initial value of the @@toStringTag property is the string value
646 | "WeakRef".
647 |
648 | #### Properties of WeakRef Instances
649 |
650 | WeakRef instances are ordinary objects that inherit properties from
651 | the %WeakRefPrototype% intrinsic object. WeakRef instances are
652 | initially created with the internal slots listed in Table K.
653 |
654 | *Table K — Internal Slots of WeakRef Instances*
655 |
656 | | Internal Slot | Description |
657 | | ----- | ----- |
658 | | [[Target]] | The reference to the target object |
659 | | [[ObservedTurn]] | A unique reference associated with the the turn |
660 | | [[Executor]] | An optional reference to a function |
661 | | [[Holdings]] | Any value, passed as a parameter to [[Executor]] |
662 |
663 | #### GetCurrentJobReference
664 |
665 | `WeakRef` operations rely on runtime state. This operation returns
666 | reference that is uniquely associated with the current turn.
667 |
668 | # GC Implementation Approach
669 |
670 | First we present pseudo-code for the garbage collector phases, and
671 | then a more complete sample of the weak reference implementation.
672 |
673 | The approach is illustrated in the context of a mark-and-sweep
674 | collector. It uses two additional elements over simple gc:
675 |
676 | * ENCOUNTERED set -- the WeakRefs encountered during marking.
677 | * Finalization QUEUE -- the WeakRefs whose targets have been
678 | condemned.
679 |
680 | WeakRefs whose targets are collected will have their contents set to
681 | undefined.
682 |
683 | ### The algorithm
684 |
685 | #### Setup
686 |
687 | * Clear the finalization QUEUE. It will be rebuilt by the current GC
688 | cycle and we don't want to retains things because of it.
689 |
690 | #### Marking
691 |
692 | * Mark the reachable graph from roots, as normal
693 | * Remember any encountered weak refs in the ENCOUNTERED set
694 | * WeakRefs mark their targets only if they were accessed during the
695 | current turn
696 |
697 | #### Schedule finalization
698 |
699 | * Atomically revisit encountered WeakRefs
700 | * If the target is not marked or already undefined
701 | * Set the target pointer to undefined
702 | * Schedule the WeakRef for finalization
703 |
704 | This phase sets all references still weakly pointing at condemned
705 | objects to be undefined. This phase must be atomic so that no code can
706 | dereference a WeakRef to a condemned target. Multiple WeakRefs to the
707 | same target must all get set to undefined atomically.
708 |
709 | By checking for `undefined` in this phase, GC and scheduling
710 | finalization is idempotent. If GC started over, a still-reachable weak
711 | ref with an executor and a collected target will be rescheduled for
712 | finalization.
713 |
714 | #### Sweep
715 |
716 | * Sweep as normal.
717 |
718 | Because everything condemned is truly unreachable, the sweep does not
719 | need to coordinate with other jobs, finalization, etc.
720 |
721 | #### Finalization
722 |
723 | For each weak ref that was selected for finalization, if they haven't
724 | been cleared, execute their executor in it's own turn. The executor
725 | reference must be cleared first since it's presence indicates the
726 | finalization is still required. This ensures that each executor will
727 | only execute once per WeakRef with a condemned target.
728 |
729 | ### The code
730 |
731 | ```js
732 | function makeWeakRef(target, executor = void 0, holdings = void 0) {
733 | if (target !== Object(target)) {
734 | throw new TypeError('Object expected');
735 | }
736 | if (target === holdings) {
737 | throw new TypeError('Target cannot be used to clean up itself');
738 | }
739 | if (target === executor) {
740 | throw new TypeError('Target cannot be used to clean up itself');
741 | }
742 | if (typeof executor !== 'function' || executor !== null) {
743 | throw new TypeError('Executor must be callable, if provided');
744 | }
745 | if (REALM_OF(target) === REALM_OF(makeWeakRef)) {
746 | return {
747 | __proto__: %WeakRefPrototype%,
748 | [[Target]]: target, // WEAK
749 | [[ObservedTurn]]: [[CURRENT_TURN]],
750 | [[Executor]]: executor,
751 | [[Holdings]]: holdings,
752 | };
753 | } else {
754 | // For cross-realm targets, the target is also copied into
755 | // holdings so that it is pointed to strongly by the WeakRef.
756 | // Since a cross-realm weakRef is just strong, the target will be
757 | // retained at least as long as the weakRef. Therefore
758 | // finalization can ever be triggered and the supplied executor
759 | // and holdings can never be used. So this just uses the holdings
760 | // internal slot to strongly point at the target.
761 | return {
762 | __proto__: %WeakRefPrototype%,
763 | [[Target]]: target,
764 | [[ObservedTurn]]: [[CURRENT_TURN]],
765 | // the Target is strongly held by the WeakRef so finalization
766 | // for Target will never happen. Therefore we don't need these
767 | [[Executor]]: void 0,
768 | [[Holdings]]: target,
769 | };
770 | }
771 | }
772 |
773 | %WeakRefPrototype% = {
774 | get() {
775 | this.[[ObservedTurn]] = [[CURRENT_TURN]];
776 | return this.[[Target]];
777 | },
778 | clear() {
779 | // Undefined is used to indicate the collected or cleared
780 | // state so that it's analogous to being now uninitialized.
781 | this.[[Target]] = void 0;
782 | this.[[Executor]] = null;
783 | this.[[Holdings]] = null;
784 | }
785 | };
786 |
787 | // The following internal Mark and finalization methods are on
788 | // each WeakRef instance.
789 |
790 | // Called by gc as part of its normal mark phase.
791 | function [[WeakRefMark]](weakref) {
792 | MARK(weakref.[[Executor]]);
793 | MARK(weakref.[[Holdings]]);
794 | MARK(weakref.[[ObservedTurn]]);
795 | if (weakref.[[ObservedTurn]] === [[CURRENT_TURN]]) {
796 | MARK(weakref.[[Target]]);
797 | return;
798 | }
799 | if (typeof weakref.[[Executor]] === 'function'
800 | && ![[IS_MARKED]](weakref.[[Target]])) {
801 | // weakref will only potentially need finalization if it has
802 | // an executor.
803 | [[ENCOUNTER]](weakref);
804 | }
805 | };
806 |
807 | // Called for each weak ref after all marking on all weak refs
808 | // encountered. Checks whether the WeakRef needs finalization.
809 | function [[WeakRefCheckFinalization]](weakref){
810 | // This step is only run if there was an executor, so this weak
811 | // ref needs to be finalized if the target is not strongly
812 | // reachable. The target will be undefined if the target became
813 | // unreachable during a previous collection and we are
814 | // rebuilding the finalization queue.
815 | let target = weakref.[[Target]]);
816 | if (! [[IS_MARKED]](target) || target === void 0) {
817 | weakref.[[Target]] = void 0;
818 | [[QUEUE_FINALIZATION]](weakref);
819 | }
820 | };
821 |
822 | // Called by gc in its own turn, if and only if this weak reference is
823 | // not already finalized. It is a no-op if the executor is null in
824 | // order to coordinate with user code that clears the weak ref.
825 | function [[WeakRefFinalize]](weakref){
826 | let exec = weakref.[[Executor]];
827 | if (typeof exec === 'function') {
828 | let hold = weakref.[[Holdings]];
829 | weakref.clear();
830 | exec(hold, weakRef);
831 | }
832 | };
833 |
834 | ```
835 |
836 | # Open questions
837 |
838 | * Should get() on a weak ref whose target is collected return null or
839 | undefined?
840 |
841 | The proposal is that it returns undefined. Collection effectively
842 | returns a reference to the uninitialized state.
843 |
844 | * Should the holdings default to null or undefined?
845 |
846 | This answer is user-visible to the invocation of the executor
847 | function. The proposed code currently defaults holdings to undefined.
848 |
849 | * Should a weak ref be obligated to preserve the holdings or
850 | executor until the target is collected?
851 |
852 | The proposal is that it is not required to retain any state it no
853 | longer needs. It will certainly drop them after finalization, so it
854 | doesn't have a long-term obligation. Following the design principle of
855 | "allow implementations to collect as much as possible", the
856 | implementation not be obligated to retain them. The implementation of
857 | cross-realm references was modified to reflect this.
858 |
859 | * Should weakRefs follow the class pattern?
860 |
861 | Yes, except that it must preserve the security property that
862 | construction of WeakRefs is restricted; the power to make a new weak
863 | ref must not be available from instances.
864 |
865 | * Should the weakRef be provided as an additional argument to the executor?
866 |
867 | NOTE: The current proposal provides only the holdings to the executor
868 | invocation. Adding the argument is upwards compatible, so this does
869 | not need to be resolved immediately.
870 |
871 | Additional usage examples are needed to determine the best pattern
872 | here. In examples so far, there is already a mapping from holdings to
873 | weakRef, so there's no need for the additional argument, but the
874 | presence of the weakRef may simplify some other usage patterns, and
875 | it's trivially available to provide.
876 |
877 | # References
878 |
879 | * [Automatic storage-reclamation postmortem finalization process](http://www.google.com/patents/US5274804)
880 | * [pre-mortem finalization challenges](http://blogs.msdn.com/b/cbrumme/archive/2004/02/20/77460.aspx)
881 | * [Python Weak References](https://docs.python.org/2/library/weakref.html)
882 | * [Java WeakReference](https://docs.oracle.com/javase/7/docs/api/java/lang/ref/WeakReference.html)
883 | * [C# Weak References](https://msdn.microsoft.com/en-us/library/ms404247.aspx)
884 | * [Racket Ephmerons](https://docs.racket-lang.org/reference/ephemerons.html)
885 | * [Ephemerons](https://pdfs.semanticscholar.org/1803/a320584a35b797ca6089e8240393505ad410.pdf)
886 | * [Smalltalk VisualWorks WeakArrays](http://esug.org/data/Old/vw-tutorials/Adv_VWNotPrintable/Weak_References.pdf)
887 | * [WeakRefs for wasm-gc](https://github.com/WebAssembly/gc/blob/master/proposals/gc/Overview.md#possible-extension-weak-references-and-finalisation)
888 | * [Mozilla feature request issue](https://bugzilla.mozilla.org/show_bug.cgi?id=1367476)
889 | * [Bradley Meck's "weakref" branch of v8](https://github.com/bmeck/v8/commits/weakref) starting at commit named "[api] Prototype WeakRef implementation"
890 |
891 |
892 | [ECMAScript language value]: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-language-types
893 | [property key]: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object-type
894 | [Completion Record]: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-completion-record-specification-type
895 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "lockfileVersion": 1,
4 | "requires": true,
5 | "dependencies": {
6 | "@alrra/travis-scripts": {
7 | "version": "3.0.1",
8 | "resolved": "https://registry.npmjs.org/@alrra/travis-scripts/-/travis-scripts-3.0.1.tgz",
9 | "integrity": "sha1-RdW5NXMXtsxVU9/ZmTGEOuCw5To=",
10 | "dev": true
11 | },
12 | "abab": {
13 | "version": "1.0.4",
14 | "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
15 | "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4="
16 | },
17 | "acorn": {
18 | "version": "5.7.3",
19 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
20 | "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
21 | },
22 | "acorn-globals": {
23 | "version": "4.3.0",
24 | "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz",
25 | "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==",
26 | "requires": {
27 | "acorn": "^6.0.1",
28 | "acorn-walk": "^6.0.1"
29 | },
30 | "dependencies": {
31 | "acorn": {
32 | "version": "6.0.5",
33 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz",
34 | "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg=="
35 | }
36 | }
37 | },
38 | "acorn-walk": {
39 | "version": "6.1.1",
40 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz",
41 | "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw=="
42 | },
43 | "ajv": {
44 | "version": "6.6.2",
45 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
46 | "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==",
47 | "requires": {
48 | "fast-deep-equal": "^2.0.1",
49 | "fast-json-stable-stringify": "^2.0.0",
50 | "json-schema-traverse": "^0.4.1",
51 | "uri-js": "^4.2.2"
52 | }
53 | },
54 | "ansi-regex": {
55 | "version": "2.1.1",
56 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
57 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
58 | },
59 | "ansi-styles": {
60 | "version": "2.2.1",
61 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
62 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
63 | },
64 | "argparse": {
65 | "version": "1.0.10",
66 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
67 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
68 | "requires": {
69 | "sprintf-js": "~1.0.2"
70 | }
71 | },
72 | "array-equal": {
73 | "version": "1.0.0",
74 | "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
75 | "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM="
76 | },
77 | "asn1": {
78 | "version": "0.2.4",
79 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
80 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
81 | "requires": {
82 | "safer-buffer": "~2.1.0"
83 | }
84 | },
85 | "assert-plus": {
86 | "version": "1.0.0",
87 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
88 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
89 | },
90 | "async-limiter": {
91 | "version": "1.0.0",
92 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
93 | "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
94 | },
95 | "asynckit": {
96 | "version": "0.4.0",
97 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
98 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
99 | },
100 | "aws-sign2": {
101 | "version": "0.7.0",
102 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
103 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
104 | },
105 | "aws4": {
106 | "version": "1.8.0",
107 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
108 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
109 | },
110 | "bcrypt-pbkdf": {
111 | "version": "1.0.2",
112 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
113 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
114 | "requires": {
115 | "tweetnacl": "^0.14.3"
116 | }
117 | },
118 | "bluebird": {
119 | "version": "3.5.3",
120 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
121 | "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw=="
122 | },
123 | "browser-process-hrtime": {
124 | "version": "0.1.3",
125 | "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
126 | "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw=="
127 | },
128 | "caseless": {
129 | "version": "0.12.0",
130 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
131 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
132 | },
133 | "chalk": {
134 | "version": "1.1.3",
135 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
136 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
137 | "requires": {
138 | "ansi-styles": "^2.2.1",
139 | "escape-string-regexp": "^1.0.2",
140 | "has-ansi": "^2.0.0",
141 | "strip-ansi": "^3.0.0",
142 | "supports-color": "^2.0.0"
143 | }
144 | },
145 | "combined-stream": {
146 | "version": "1.0.7",
147 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
148 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
149 | "requires": {
150 | "delayed-stream": "~1.0.0"
151 | }
152 | },
153 | "core-util-is": {
154 | "version": "1.0.2",
155 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
156 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
157 | },
158 | "cssom": {
159 | "version": "0.3.4",
160 | "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz",
161 | "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog=="
162 | },
163 | "cssstyle": {
164 | "version": "0.2.37",
165 | "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz",
166 | "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=",
167 | "requires": {
168 | "cssom": "0.3.x"
169 | }
170 | },
171 | "dashdash": {
172 | "version": "1.14.1",
173 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
174 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
175 | "requires": {
176 | "assert-plus": "^1.0.0"
177 | }
178 | },
179 | "data-urls": {
180 | "version": "1.1.0",
181 | "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
182 | "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
183 | "requires": {
184 | "abab": "^2.0.0",
185 | "whatwg-mimetype": "^2.2.0",
186 | "whatwg-url": "^7.0.0"
187 | },
188 | "dependencies": {
189 | "abab": {
190 | "version": "2.0.0",
191 | "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz",
192 | "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w=="
193 | },
194 | "whatwg-url": {
195 | "version": "7.0.0",
196 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
197 | "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
198 | "requires": {
199 | "lodash.sortby": "^4.7.0",
200 | "tr46": "^1.0.1",
201 | "webidl-conversions": "^4.0.2"
202 | }
203 | }
204 | }
205 | },
206 | "deep-is": {
207 | "version": "0.1.3",
208 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
209 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
210 | },
211 | "delayed-stream": {
212 | "version": "1.0.0",
213 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
214 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
215 | },
216 | "domexception": {
217 | "version": "1.0.1",
218 | "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
219 | "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
220 | "requires": {
221 | "webidl-conversions": "^4.0.2"
222 | }
223 | },
224 | "ecc-jsbn": {
225 | "version": "0.1.2",
226 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
227 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
228 | "requires": {
229 | "jsbn": "~0.1.0",
230 | "safer-buffer": "^2.1.0"
231 | }
232 | },
233 | "ecmarkdown": {
234 | "version": "3.0.9",
235 | "resolved": "https://registry.npmjs.org/ecmarkdown/-/ecmarkdown-3.0.9.tgz",
236 | "integrity": "sha1-Pl5oc7SO9YRHN8s4jMq9hQYaevE=",
237 | "requires": {
238 | "escape-html": "^1.0.1"
239 | }
240 | },
241 | "ecmarkup": {
242 | "version": "3.16.0",
243 | "resolved": "https://registry.npmjs.org/ecmarkup/-/ecmarkup-3.16.0.tgz",
244 | "integrity": "sha512-kG0i8Vy4TTDlvmsLyEWqqqrQH0Tiu3QcVsTFKrz82Wt8p8tdYh9RcVEamZa2CX3zA8yOj2zQLMCcDqzL5g3U0w==",
245 | "requires": {
246 | "bluebird": "^3.4.5",
247 | "chalk": "^1.1.1",
248 | "ecmarkdown": "^3.0.9",
249 | "grammarkdown": "^2.0.11",
250 | "he": "^1.1.1",
251 | "highlight.js": "^9.0.0",
252 | "html-escape": "^1.0.2",
253 | "js-yaml": "^3.4.6",
254 | "jsdom": "11.10.0",
255 | "nomnom": "^1.8.1",
256 | "prex": "^0.4.2",
257 | "promise-debounce": "^1.0.1"
258 | }
259 | },
260 | "escape-html": {
261 | "version": "1.0.3",
262 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
263 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
264 | },
265 | "escape-string-regexp": {
266 | "version": "1.0.5",
267 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
268 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
269 | },
270 | "escodegen": {
271 | "version": "1.11.0",
272 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz",
273 | "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==",
274 | "requires": {
275 | "esprima": "^3.1.3",
276 | "estraverse": "^4.2.0",
277 | "esutils": "^2.0.2",
278 | "optionator": "^0.8.1",
279 | "source-map": "~0.6.1"
280 | },
281 | "dependencies": {
282 | "esprima": {
283 | "version": "3.1.3",
284 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
285 | "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
286 | }
287 | }
288 | },
289 | "esprima": {
290 | "version": "4.0.1",
291 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
292 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
293 | },
294 | "estraverse": {
295 | "version": "4.2.0",
296 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
297 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
298 | },
299 | "esutils": {
300 | "version": "2.0.2",
301 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
302 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
303 | },
304 | "extend": {
305 | "version": "3.0.2",
306 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
307 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
308 | },
309 | "extsprintf": {
310 | "version": "1.3.0",
311 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
312 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
313 | },
314 | "fast-deep-equal": {
315 | "version": "2.0.1",
316 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
317 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
318 | },
319 | "fast-json-stable-stringify": {
320 | "version": "2.0.0",
321 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
322 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
323 | },
324 | "fast-levenshtein": {
325 | "version": "2.0.6",
326 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
327 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
328 | },
329 | "forever-agent": {
330 | "version": "0.6.1",
331 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
332 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
333 | },
334 | "form-data": {
335 | "version": "2.3.3",
336 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
337 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
338 | "requires": {
339 | "asynckit": "^0.4.0",
340 | "combined-stream": "^1.0.6",
341 | "mime-types": "^2.1.12"
342 | }
343 | },
344 | "getpass": {
345 | "version": "0.1.7",
346 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
347 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
348 | "requires": {
349 | "assert-plus": "^1.0.0"
350 | }
351 | },
352 | "grammarkdown": {
353 | "version": "2.0.12",
354 | "resolved": "https://registry.npmjs.org/grammarkdown/-/grammarkdown-2.0.12.tgz",
355 | "integrity": "sha512-GHlLjegNKZbNjt/Ip/dxMb4lFar2QNi8QVBB1FAot1RywQYB8nvyUW7j5hjZKjYaA1fJdMLW3oHc7QXSLyMs2Q==",
356 | "requires": {
357 | "prex": "^0.4.2"
358 | }
359 | },
360 | "har-schema": {
361 | "version": "2.0.0",
362 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
363 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
364 | },
365 | "har-validator": {
366 | "version": "5.1.3",
367 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
368 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
369 | "requires": {
370 | "ajv": "^6.5.5",
371 | "har-schema": "^2.0.0"
372 | }
373 | },
374 | "has-ansi": {
375 | "version": "2.0.0",
376 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
377 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
378 | "requires": {
379 | "ansi-regex": "^2.0.0"
380 | }
381 | },
382 | "has-color": {
383 | "version": "0.1.7",
384 | "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
385 | "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8="
386 | },
387 | "he": {
388 | "version": "1.2.0",
389 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
390 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
391 | },
392 | "highlight.js": {
393 | "version": "9.13.1",
394 | "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz",
395 | "integrity": "sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A=="
396 | },
397 | "html-encoding-sniffer": {
398 | "version": "1.0.2",
399 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
400 | "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
401 | "requires": {
402 | "whatwg-encoding": "^1.0.1"
403 | }
404 | },
405 | "html-escape": {
406 | "version": "1.0.2",
407 | "resolved": "https://registry.npmjs.org/html-escape/-/html-escape-1.0.2.tgz",
408 | "integrity": "sha1-X6eHwFaAkP4zLtWzz0qk9kZCGnQ="
409 | },
410 | "http-signature": {
411 | "version": "1.2.0",
412 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
413 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
414 | "requires": {
415 | "assert-plus": "^1.0.0",
416 | "jsprim": "^1.2.2",
417 | "sshpk": "^1.7.0"
418 | }
419 | },
420 | "iconv-lite": {
421 | "version": "0.4.24",
422 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
423 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
424 | "requires": {
425 | "safer-buffer": ">= 2.1.2 < 3"
426 | }
427 | },
428 | "is-typedarray": {
429 | "version": "1.0.0",
430 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
431 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
432 | },
433 | "isstream": {
434 | "version": "0.1.2",
435 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
436 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
437 | },
438 | "js-yaml": {
439 | "version": "3.13.1",
440 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
441 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
442 | "requires": {
443 | "argparse": "^1.0.7",
444 | "esprima": "^4.0.0"
445 | }
446 | },
447 | "jsbn": {
448 | "version": "0.1.1",
449 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
450 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
451 | },
452 | "jsdom": {
453 | "version": "11.10.0",
454 | "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.10.0.tgz",
455 | "integrity": "sha512-x5No5FpJgBg3j5aBwA8ka6eGuS5IxbC8FOkmyccKvObtFT0bDMict/LOxINZsZGZSfGdNomLZ/qRV9Bpq/GIBA==",
456 | "requires": {
457 | "abab": "^1.0.4",
458 | "acorn": "^5.3.0",
459 | "acorn-globals": "^4.1.0",
460 | "array-equal": "^1.0.0",
461 | "cssom": ">= 0.3.2 < 0.4.0",
462 | "cssstyle": ">= 0.2.37 < 0.3.0",
463 | "data-urls": "^1.0.0",
464 | "domexception": "^1.0.0",
465 | "escodegen": "^1.9.0",
466 | "html-encoding-sniffer": "^1.0.2",
467 | "left-pad": "^1.2.0",
468 | "nwmatcher": "^1.4.3",
469 | "parse5": "4.0.0",
470 | "pn": "^1.1.0",
471 | "request": "^2.83.0",
472 | "request-promise-native": "^1.0.5",
473 | "sax": "^1.2.4",
474 | "symbol-tree": "^3.2.2",
475 | "tough-cookie": "^2.3.3",
476 | "w3c-hr-time": "^1.0.1",
477 | "webidl-conversions": "^4.0.2",
478 | "whatwg-encoding": "^1.0.3",
479 | "whatwg-mimetype": "^2.1.0",
480 | "whatwg-url": "^6.4.0",
481 | "ws": "^4.0.0",
482 | "xml-name-validator": "^3.0.0"
483 | }
484 | },
485 | "json-schema": {
486 | "version": "0.2.3",
487 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
488 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
489 | },
490 | "json-schema-traverse": {
491 | "version": "0.4.1",
492 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
493 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
494 | },
495 | "json-stringify-safe": {
496 | "version": "5.0.1",
497 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
498 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
499 | },
500 | "jsprim": {
501 | "version": "1.4.1",
502 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
503 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
504 | "requires": {
505 | "assert-plus": "1.0.0",
506 | "extsprintf": "1.3.0",
507 | "json-schema": "0.2.3",
508 | "verror": "1.10.0"
509 | }
510 | },
511 | "left-pad": {
512 | "version": "1.3.0",
513 | "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
514 | "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA=="
515 | },
516 | "levn": {
517 | "version": "0.3.0",
518 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
519 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
520 | "requires": {
521 | "prelude-ls": "~1.1.2",
522 | "type-check": "~0.3.2"
523 | }
524 | },
525 | "lodash": {
526 | "version": "4.17.15",
527 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
528 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
529 | },
530 | "lodash.sortby": {
531 | "version": "4.7.0",
532 | "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
533 | "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg="
534 | },
535 | "mime-db": {
536 | "version": "1.37.0",
537 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
538 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
539 | },
540 | "mime-types": {
541 | "version": "2.1.21",
542 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
543 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
544 | "requires": {
545 | "mime-db": "~1.37.0"
546 | }
547 | },
548 | "minimist": {
549 | "version": "0.0.8",
550 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
551 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
552 | },
553 | "mkdirp": {
554 | "version": "0.5.1",
555 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
556 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
557 | "requires": {
558 | "minimist": "0.0.8"
559 | }
560 | },
561 | "nomnom": {
562 | "version": "1.8.1",
563 | "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
564 | "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=",
565 | "requires": {
566 | "chalk": "~0.4.0",
567 | "underscore": "~1.6.0"
568 | },
569 | "dependencies": {
570 | "ansi-styles": {
571 | "version": "1.0.0",
572 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
573 | "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg="
574 | },
575 | "chalk": {
576 | "version": "0.4.0",
577 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
578 | "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
579 | "requires": {
580 | "ansi-styles": "~1.0.0",
581 | "has-color": "~0.1.0",
582 | "strip-ansi": "~0.1.0"
583 | }
584 | },
585 | "strip-ansi": {
586 | "version": "0.1.1",
587 | "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
588 | "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE="
589 | }
590 | }
591 | },
592 | "nwmatcher": {
593 | "version": "1.4.4",
594 | "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz",
595 | "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ=="
596 | },
597 | "oauth-sign": {
598 | "version": "0.9.0",
599 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
600 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
601 | },
602 | "optionator": {
603 | "version": "0.8.2",
604 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
605 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
606 | "requires": {
607 | "deep-is": "~0.1.3",
608 | "fast-levenshtein": "~2.0.4",
609 | "levn": "~0.3.0",
610 | "prelude-ls": "~1.1.2",
611 | "type-check": "~0.3.2",
612 | "wordwrap": "~1.0.0"
613 | }
614 | },
615 | "parse5": {
616 | "version": "4.0.0",
617 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
618 | "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
619 | },
620 | "performance-now": {
621 | "version": "2.1.0",
622 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
623 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
624 | },
625 | "pn": {
626 | "version": "1.1.0",
627 | "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
628 | "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
629 | },
630 | "prelude-ls": {
631 | "version": "1.1.2",
632 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
633 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
634 | },
635 | "prex": {
636 | "version": "0.4.5",
637 | "resolved": "https://registry.npmjs.org/prex/-/prex-0.4.5.tgz",
638 | "integrity": "sha512-B+mpUmuQtIBqm0qwvsdwt57VXidHagZOLEudeZYyJRi9NmZvxixL2M86gv2InOieXKVvQXX9n7ol4lCV0kWcjw=="
639 | },
640 | "promise-debounce": {
641 | "version": "1.0.1",
642 | "resolved": "https://registry.npmjs.org/promise-debounce/-/promise-debounce-1.0.1.tgz",
643 | "integrity": "sha1-btdvj3nQFE/b0BzBVYnOV/nXHng="
644 | },
645 | "psl": {
646 | "version": "1.1.31",
647 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
648 | "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw=="
649 | },
650 | "punycode": {
651 | "version": "2.1.1",
652 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
653 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
654 | },
655 | "qs": {
656 | "version": "6.5.2",
657 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
658 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
659 | },
660 | "request": {
661 | "version": "2.88.0",
662 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
663 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
664 | "requires": {
665 | "aws-sign2": "~0.7.0",
666 | "aws4": "^1.8.0",
667 | "caseless": "~0.12.0",
668 | "combined-stream": "~1.0.6",
669 | "extend": "~3.0.2",
670 | "forever-agent": "~0.6.1",
671 | "form-data": "~2.3.2",
672 | "har-validator": "~5.1.0",
673 | "http-signature": "~1.2.0",
674 | "is-typedarray": "~1.0.0",
675 | "isstream": "~0.1.2",
676 | "json-stringify-safe": "~5.0.1",
677 | "mime-types": "~2.1.19",
678 | "oauth-sign": "~0.9.0",
679 | "performance-now": "^2.1.0",
680 | "qs": "~6.5.2",
681 | "safe-buffer": "^5.1.2",
682 | "tough-cookie": "~2.4.3",
683 | "tunnel-agent": "^0.6.0",
684 | "uuid": "^3.3.2"
685 | },
686 | "dependencies": {
687 | "punycode": {
688 | "version": "1.4.1",
689 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
690 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
691 | },
692 | "tough-cookie": {
693 | "version": "2.4.3",
694 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
695 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
696 | "requires": {
697 | "psl": "^1.1.24",
698 | "punycode": "^1.4.1"
699 | }
700 | }
701 | }
702 | },
703 | "request-promise-core": {
704 | "version": "1.1.1",
705 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
706 | "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
707 | "requires": {
708 | "lodash": "^4.13.1"
709 | }
710 | },
711 | "request-promise-native": {
712 | "version": "1.0.5",
713 | "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz",
714 | "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=",
715 | "requires": {
716 | "request-promise-core": "1.1.1",
717 | "stealthy-require": "^1.1.0",
718 | "tough-cookie": ">=2.3.3"
719 | }
720 | },
721 | "safe-buffer": {
722 | "version": "5.1.2",
723 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
724 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
725 | },
726 | "safer-buffer": {
727 | "version": "2.1.2",
728 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
729 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
730 | },
731 | "sax": {
732 | "version": "1.2.4",
733 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
734 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
735 | },
736 | "source-map": {
737 | "version": "0.6.1",
738 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
739 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
740 | "optional": true
741 | },
742 | "sprintf-js": {
743 | "version": "1.0.3",
744 | "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
745 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
746 | },
747 | "sshpk": {
748 | "version": "1.16.0",
749 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz",
750 | "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==",
751 | "requires": {
752 | "asn1": "~0.2.3",
753 | "assert-plus": "^1.0.0",
754 | "bcrypt-pbkdf": "^1.0.0",
755 | "dashdash": "^1.12.0",
756 | "ecc-jsbn": "~0.1.1",
757 | "getpass": "^0.1.1",
758 | "jsbn": "~0.1.0",
759 | "safer-buffer": "^2.0.2",
760 | "tweetnacl": "~0.14.0"
761 | }
762 | },
763 | "stealthy-require": {
764 | "version": "1.1.1",
765 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
766 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
767 | },
768 | "strip-ansi": {
769 | "version": "3.0.1",
770 | "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
771 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
772 | "requires": {
773 | "ansi-regex": "^2.0.0"
774 | }
775 | },
776 | "supports-color": {
777 | "version": "2.0.0",
778 | "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
779 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
780 | },
781 | "symbol-tree": {
782 | "version": "3.2.2",
783 | "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
784 | "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY="
785 | },
786 | "tough-cookie": {
787 | "version": "2.5.0",
788 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
789 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
790 | "requires": {
791 | "psl": "^1.1.28",
792 | "punycode": "^2.1.1"
793 | }
794 | },
795 | "tr46": {
796 | "version": "1.0.1",
797 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
798 | "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
799 | "requires": {
800 | "punycode": "^2.1.0"
801 | }
802 | },
803 | "tunnel-agent": {
804 | "version": "0.6.0",
805 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
806 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
807 | "requires": {
808 | "safe-buffer": "^5.0.1"
809 | }
810 | },
811 | "tweetnacl": {
812 | "version": "0.14.5",
813 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
814 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
815 | },
816 | "type-check": {
817 | "version": "0.3.2",
818 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
819 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
820 | "requires": {
821 | "prelude-ls": "~1.1.2"
822 | }
823 | },
824 | "underscore": {
825 | "version": "1.6.0",
826 | "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
827 | "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
828 | },
829 | "uri-js": {
830 | "version": "4.2.2",
831 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
832 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
833 | "requires": {
834 | "punycode": "^2.1.0"
835 | }
836 | },
837 | "uuid": {
838 | "version": "3.3.2",
839 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
840 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
841 | },
842 | "verror": {
843 | "version": "1.10.0",
844 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
845 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
846 | "requires": {
847 | "assert-plus": "^1.0.0",
848 | "core-util-is": "1.0.2",
849 | "extsprintf": "^1.2.0"
850 | }
851 | },
852 | "w3c-hr-time": {
853 | "version": "1.0.1",
854 | "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
855 | "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
856 | "requires": {
857 | "browser-process-hrtime": "^0.1.2"
858 | }
859 | },
860 | "webidl-conversions": {
861 | "version": "4.0.2",
862 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
863 | "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
864 | },
865 | "whatwg-encoding": {
866 | "version": "1.0.5",
867 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
868 | "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
869 | "requires": {
870 | "iconv-lite": "0.4.24"
871 | }
872 | },
873 | "whatwg-mimetype": {
874 | "version": "2.3.0",
875 | "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
876 | "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
877 | },
878 | "whatwg-url": {
879 | "version": "6.5.0",
880 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
881 | "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
882 | "requires": {
883 | "lodash.sortby": "^4.7.0",
884 | "tr46": "^1.0.1",
885 | "webidl-conversions": "^4.0.2"
886 | }
887 | },
888 | "wordwrap": {
889 | "version": "1.0.0",
890 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
891 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
892 | },
893 | "ws": {
894 | "version": "4.1.0",
895 | "resolved": "http://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
896 | "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
897 | "requires": {
898 | "async-limiter": "~1.0.0",
899 | "safe-buffer": "~5.1.0"
900 | }
901 | },
902 | "xml-name-validator": {
903 | "version": "3.0.0",
904 | "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
905 | "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
906 | }
907 | }
908 | }
909 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "description": "WeakRef proposal",
4 | "license": "Fair",
5 | "scripts": {
6 | "prebuild": "mkdirp out",
7 | "build": "ecmarkup spec/index.html out/index.html"
8 | },
9 | "dependencies": {
10 | "ecmarkup": "^3.14.0",
11 | "mkdirp": "^0.5.1"
12 | },
13 | "devDependencies": {
14 | "@alrra/travis-scripts": "^3.0.0"
15 | },
16 | "version": "1.0.0",
17 | "repository": {
18 | "type": "git"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/reference.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | The WeakRefs proposal adds WeakRefs and FinalizationRegistries to the standard library.
4 |
5 | * [WeakRef](#weakref) - weak references
6 | * [FinalizationRegistry](#finalizationregistry) - registries providing cleanup callbacks
7 |
8 | This page provides developer reference information for them.
9 |
10 | ----
11 |
12 | # Warning - Avoid Where Possible
13 |
14 | Please read [the WeakRef proposal's explainer](./README.md) for several words of caution regarding weak references and cleanup callbacks. Their correct use takes careful thought, and they are best avoided if possible. It's also important to avoid relying on any specific behaviors not guaranteed by the specification. When, how, and whether garbage collection occurs is down to the implementation of any given JavaScript engine. Any behavior you observe in one engine may be different in another engine, in another version of the same engine, or even in a slightly different situation with the same version of the same engine. Garbage collection is a hard problem that JavaScript engine implementers are constantly refining and improving their solutions to.
15 |
16 | ----
17 |
18 | # `WeakRef`
19 |
20 | A WeakRef instance contains a weak reference to an object, which is called its *target* or *referent*. A *weak reference* to an object is a reference to it that does not prevent it being reclaimed by the garbage collector. In contrast, a normal (or *strong*) reference keeps an object in memory. When an object no longer has any strong references to it, the JavaScript engine's garbage collector may destroy the object and reclaim its memory. If that happens, you can't get the object from a weak reference anymore.
21 |
22 | **WeakRef Contents:**
23 |
24 | * [Overview](#weakref-overview)
25 | * [WeakRef API](#weakref-api)
26 | * [The `WeakRef` Constructor](#the-weakref-constructor)
27 | * [`WeakRef.prototype.deref`](#weakrefprototypederef)
28 | * [Notes](#notes-on-weakrefs)
29 |
30 | ## WeakRef Overview
31 |
32 | To create a WeakRef, you use the `WeakRef` constructor, passing in the target object (the object you want a weak reference to):
33 |
34 | ```js
35 | let ref = new WeakRef(someObject);
36 | ```
37 |
38 | Then at some point you stop using `someObject` (it goes out of scope, etc.) while keeping the WeakRef instance (`ref`). At that point, to get the object from a WeakRef instance, use its `deref` method:
39 |
40 | ```js
41 | let obj = ref.deref();
42 | if (obj) {
43 | // ...use `obj`...
44 | }
45 | ```
46 |
47 | `deref` returns the object, or `undefined` if the object is no longer available.
48 |
49 | ## WeakRef API
50 |
51 | ### The `WeakRef` Constructor
52 |
53 | The `WeakRef` constructor creates a WeakRef instance for the given `target` object:
54 |
55 | ```js
56 | ref = new WeakRef(target)
57 | ```
58 |
59 | **Parameters**
60 |
61 | * `target` - The target object for the WeakRef instance.
62 |
63 | **Returns**
64 |
65 | * The WeakRef instance.
66 |
67 | **Notes**
68 |
69 | * `WeakRef` is a constructor function, so it throws if called as a function rather than as a constructor.
70 | * `WeakRef` is designed to be subclassed, and so can appear in the `extends` of a `class` definition and similar.
71 |
72 | ### `WeakRef.prototype.deref`
73 |
74 | The `deref` method returns the WeakRef instance's target object, or `undefined` if the target object has been reclaimed:
75 |
76 | ```js
77 | targetOrUndefined = weakRef.deref();
78 | ```
79 |
80 | **Parameters**
81 |
82 | * *(none)*
83 |
84 | **Returns**
85 |
86 | * The target object of the WeakRef, or `undefined`.
87 |
88 | **Errors**
89 |
90 | * *(none)*
91 |
92 | ## Notes on WeakRefs
93 |
94 | Some notes on WeakRefs:
95 |
96 | * If your code has just created a WeakRef for a target object, or has gotten a target object from a WeakRef's `deref` method, that target object will not be reclaimed until the end of the current JavaScript [job](https://tc39.es/ecma262/#job) (including any promise reaction jobs that run at the end of a script job). That is, you can only "see" an object get reclaimed between turns of the event loop. This is primarily to avoid making the behavior of any given JavaScript engine's garbage collector apparent in code — because if it were, people would write code relying on that behavior, which would break when the garbage collector's behavior changed. (Garbage collection is a hard problem; JavaScript engine implementers are constantly refining and improving how it works.)
97 | * If multiple WeakRefs have the same target, they're consistent with one another. The result of calling `deref` on one of them will match the result of calling `deref` on another of them (in the same job), you won't get the target object from one of them but `undefined` from another.
98 | * If the target of a WeakRef is also in a [`FinalizationRegistry`](#finalizationregistry), the WeakRef's target is cleared at the same time or before any cleanup callback associated with the registry is called; if your cleanup callback calls `deref` on a WeakRef for the object, it will receive `undefined`.
99 | * You cannot change the target of a WeakRef, it will always only ever be the original target object or `undefined` when that target has been reclaimed.
100 | * A WeakRef might never return `undefined` from `deref`, even if nothing strongly holds the target, because the garbage collector may never decide to reclaim the object.
101 |
102 | # `FinalizationRegistry`
103 |
104 | A finalization registry provides a way to request that a *cleanup callback* get called at some point after an object registered with the registry has been *reclaimed* (garbage collected). (Cleanup callbacks are sometimes called *finalizers*.)
105 |
106 | **NOTE:** Cleanup callbacks should not be used for essential program logic. See [*Notes on Cleanup Callbacks*](#notes-on-cleanup-callbacks) for more.
107 |
108 | **FinalizationRegistry Contents**:
109 |
110 | * [Overview](#finalizationregistry-overview)
111 | * [Notes on Cleanup Callbacks](#notes-on-cleanup-callbacks)
112 | * [FinalizationRegistry API](#finalizationregistry-api)
113 | * [The `FinalizationRegistry` Constructor](#the-finalizationregistry-constructor)
114 | * [`FinalizationRegistry.prototype.register`](#finalizationregistryprototyperegister)
115 | * [`FinalizationRegistry.prototype.unregister`](#finalizationregistryprototypeunregister)
116 |
117 | ## FinalizationRegistry Overview
118 |
119 | A `FinalizationRegistry` instance (a "registry") lets you get *cleanup callbacks* after objects registered with the registry are reclaimed. You create the registry passing in the callback:
120 |
121 | ```js
122 | const registry = new FinalizationRegistry(heldValue => {
123 | // ....
124 | });
125 | ```
126 |
127 | Then you register any objects you want a cleanup callback for by calling the `register` method, passing in the object and a *held value* for it:
128 |
129 | ```js
130 | registry.register(theObject, "some value");
131 | ```
132 |
133 | The registry does not keep a strong reference to the object, as that would defeat the purpose (if the registry held it strongly, the object would never be reclaimed).
134 |
135 | If `theObject` is reclaimed, your cleanup callback may be called at some point in the future with the *held value* you provided for it (`"some value"` in the above). The held value can be any value you like: a primitive or an object, even `undefined`. If the held value is an object, the registry keeps a *strong* reference to it (so it can pass it to your cleanup callback later).
136 |
137 | If you might want to unregister an object later, you pass a third value, which is the *unregistration token* you'll use later when calling the registry's `unregister` function to unregister the object. The registry only keeps a weak reference to the unregister token.
138 |
139 | It's common to use the object itself as the unregister token, which is just fine:
140 |
141 | ```js
142 | registry.register(theObject, "some value", theObject);
143 | // ...some time later, if you don't care about `theObject` anymore...
144 | registry.unregister(theObject);
145 | ```
146 |
147 | It doesn't have to be the same object, though; it can be a different one:
148 |
149 | ```js
150 | registry.register(theObject, "some value", tokenObject);
151 | // ...some time later, if you don't care about `theObject` anymore...
152 | registry.unregister(tokenObject);
153 | ```
154 |
155 | ## Notes on Cleanup Callbacks
156 |
157 | Developers shouldn't rely on cleanup callbacks for essential program logic. Cleanup callbacks may be useful for reducing memory usage across the course of a program, but are unlikely to be useful otherwise.
158 |
159 | A conforming JavaScript implementation, even one that does garbage collection, is not required to call cleanup callbacks. When and whether it does so is entirely down to the implementation of the JavaScript engine. When a registered object is reclaimed, the cleanup callbacks associated with any registries it's registered with may be called some time later, or not at all.
160 |
161 | It's likely that major implementations will call cleanup callbacks at some point during execution, but those calls may be substantially after the related object was reclaimed.
162 |
163 | There are also situations where even implementations that normally call cleanup callbacks are unlikely to call them:
164 |
165 | * When the JavaScript program shuts down entirely (for instance, closing a tab in a browser).
166 | * When the `FinalizationRegistry` instance itself is no longer reachable by JavaScript code.
167 |
168 | ## FinalizationRegistry API
169 |
170 | ### The `FinalizationRegistry` Constructor
171 |
172 | Creates a finalization registry with an associated cleanup callback:
173 |
174 | ```js
175 | registry = new FinalizationRegistry(cleanupCallback)
176 | ```
177 |
178 | **Parameters**
179 |
180 | * `cleanupCallback` - The callback to call after an object in the registry has been reclaimed. This is required and must be callable.
181 |
182 | **Returns**
183 |
184 | * The FinalizationRegistry instance.
185 |
186 | **Notes**
187 |
188 | * `FinalizationRegistry` is designed to be subclassed, and so can appear in the `extends` of a `class` definition and similar.
189 |
190 | **Example**
191 |
192 | ```js
193 | const registry = new FinalizationRegistry(heldValue => {
194 | // ...use `heldValue`...
195 | });
196 | ```
197 |
198 | ### `FinalizationRegistry.prototype.register`
199 |
200 | Registers an object with the registry:
201 |
202 | ```js
203 | registry.register(target, heldValue[, unregisterToken])
204 | ```
205 |
206 | **Parameters**
207 |
208 | * `target` - The target object to register.
209 | * `heldValue` - The value to pass to the finalizer for this object. This cannot be the `target` object.
210 | * `unregisterToken` - (Optional) The token to pass to the `unregister` method to unregister the target object. If provided (and not `undefined`), this must be an object. If not provided, the target cannot be unregistered.
211 |
212 | **Returns**
213 |
214 | * *(none)*
215 |
216 | **Examples**
217 |
218 | The following registers the target object referenced by `target`, passing in the held value `"some value"` and passing the target object itself as the unregistration token:
219 |
220 | ```js
221 | registry.register(target, "some value", target);
222 | ```
223 |
224 | The following registers the target object referenced by `target`, passing in another object as the held value, and not passing in any unregistration token (which means `target` can't be unregistered):
225 |
226 | ```js
227 | registry.register(target, {"useful": "info about target"});
228 | ```
229 |
230 | ### `FinalizationRegistry.prototype.unregister`
231 |
232 | Unregisters an object from the registry:
233 |
234 | ```js
235 | registry.unregister(unregisterToken)
236 | ```
237 |
238 | **Parameters**
239 |
240 | * `unregisterToken` - The token that was used as the `unregisterToken` argument when calling `regiter` to register the target object.
241 |
242 | **Returns**
243 |
244 | * *(none)*
245 |
246 | **Notes**
247 |
248 | When a target object has been reclaimed and its cleanup callback has been called, it is no longer registered in the registry. There is no need to all `unregister` in your cleanup callback. Only call `unregister` if you haven't received a cleanup callback and no longer need to receive one.
249 |
250 | **Example**
251 |
252 | This example shows registering a target object using that same object as the unregister token, then later unregistering it via `unregister`:
253 |
254 | ```js
255 | class Thingy {
256 | #cleanup = label => {
257 | // ^^^^^−−−−− held value
258 | console.error(
259 | `The \`release\` method was never called for the object with the label "${label}"`
260 | );
261 | };
262 | #registry = new FinalizationRegistry(this.#cleanup);
263 |
264 | /**
265 | * Constructs a `Thingy` instance. Be sure to call `release` when you're done with it.
266 | *
267 | * @param label A label for the `Thingy`.
268 | */
269 | constructor(label) {
270 | // vvvvv−−−−− held value
271 | this.#registry.register(this, label, this);
272 | // target −−−−−^^^^ ^^^^−−−−− unregister token
273 | }
274 |
275 | /**
276 | * Releases resources held by this `Thingy` instance.
277 | */
278 | release() {
279 | this.#registry.unregister(this);
280 | // ^^^^−−−−− unregister token
281 | }
282 | }
283 | ```
284 |
285 | This example shows registering a target object using a different object as its unregister token:
286 |
287 | ```js
288 | class Thingy {
289 | #file;
290 | #cleanup = file => {
291 | // ^^^^−−−−− held value
292 | console.error(
293 | `The \`release\` method was never called for the \`Thingy\` for the file "${file.name}"`
294 | );
295 | };
296 | #registry = new FinalizationRegistry(this.#cleanup);
297 |
298 | /**
299 | * Constructs a `Thingy` instance for the given file. Be sure to call `release` when you're done with it.
300 | *
301 | * @param filename The name of the file.
302 | */
303 | constructor(filename) {
304 | this.#file = File.open(filename);
305 | // vvvvv−−−−− held value
306 | this.#registry.register(this, label, this.#file);
307 | // target −−−−−^^^^ ^^^^^^^^^^−−−−− unregister token
308 | }
309 |
310 | /**
311 | * Releases resources held by this `Thingy` instance.
312 | */
313 | release() {
314 | if (this.#file) {
315 | this.#registry.unregister(this.#file);
316 | // ^^^^^^^^^^−−−−− unregister token
317 | File.close(this.#file);
318 | this.#file = null;
319 | }
320 | }
321 | }
322 | ```
323 |
324 |
325 |
59 | This specification does not make any guarantees about garbage collection.
60 | Objects which are unreachable from ECMAScript may be released after long periods
61 | of time, or never at all. For this reason, this specification uses the term
62 | "may" when describing behavior triggered by garbage collection.
63 |
66 | The semantics of WeakRef and FinalizationRegistry objects is based on two
67 | operations which happen at particular points in time:
68 |
88 | Neither of these actions (ClearKeptObjects or CleanupFinalizationRegistry)
89 | may interrupt synchronous ECMAScript execution. Because embedding
90 | environments may assemble longer, synchronous ECMAScript execution runs,
91 | this specification defers the scheduling of ClearKeptObjects and
92 | CleanupFinalizationRegistry to the embedding environment.
93 |
96 | Some ECMAScript implementations include garbage collector implementations
97 | which run in the background, including when ECMAScript is idle. Letting the
98 | embedding environment schedule CleanupFinalizationRegistry allows it to resume
99 | ECMAScript execution in order to run finalizer work, which may free up held values,
100 | reducing overall memory usage.
101 |
108 | For some set of objects _S_, a hypothetical WeakRef-oblivious
109 | execution with respect to _S_ is an execution whereby
110 | WeakRef.prototype.deref being called on a WeakRef whose referent is an
111 | element of _S_ always returns *undefined*.
112 |
130 | At any point during evaluation, a set of objects _S_ is
131 | considered live if either of the following conditions is
132 | met:
133 | At any time, if a set of objects _S_ is not live, an ECMAScript implementation may perform the following steps atomically:
192 | Together with the definition of liveness, this clause prescribes legal
193 | optimizations that an implementation may apply regarding WeakRefs.
194 |
197 | It is possible to access an object without observing its
198 | identity. Optimizations such as dead variable elimination, and scalar
199 | replacement on properties of non-escaping objects whose identity is not
200 | observed, are allowed. These optimizations are thus allowed to
201 | observably empty WeakRefs that point to such objects.
202 |
205 | On the other hand, if an object's identity is observable, and that
206 | object is in the [[WeakRefTarget]] internal slot of a WeakRef, optimizations
207 | such as rematerialization that observably empty the WeakRef are
208 | prohibited.
209 |
212 | Because calling HostCleanupFinalizationRegistry is optional, registered objects
213 | in a FinalizationRegistry do not necessarily hold that FinalizationRegistry live.
214 | Implementations may omit FinalizationRegistry callbacks for any reason,
215 | e.g., if the FinalizationRegistry itself becomes dead, or if the application
216 | is shutting down.
217 |
221 | Implementations are not obligated to empty WeakRefs for maximal sets
222 | of non-live objects.
223 |
225 | If an implementation chooses a non-live set _S_ in which to empty WeakRefs, it
226 | must empty WeakRefs for all objects in _S_ simultaneously. In other
227 | words, an implementation must not empty a WeakRef pointing to an object _obj_
228 | without emptying out other WeakRefs that, if not emptied, could result
229 | in an execution that observes the Object value of _obj_.
230 |
241 | HostCleanupFinalizationRegistry is an implementation-defined abstract
242 | operation that is expected to call CleanupFinalizationRegistry(_finalizationRegistry_) at
243 | some point in the future, if possible. The host's responsibility is to
244 | make this call at a time which does not interrupt synchronous
245 | ECMAScript code execution.
246 |
254 | ECMAScript implementations are expected to call ClearKeptObjects when a
255 | synchronous sequence of ECMAScript execution completes.
256 | The following steps are performed: The following steps are performed:
4 | A FinalizationRegistry is an object that manages registration and
5 | unregistration of cleanup operations that are performed when
6 | target objects are garbage collected.
7 | The FinalizationRegistry constructor:
40 | When the `FinalizationRegistry` function is called with argument _cleanupCallback_,
41 | the following steps are taken:
42 | The FinalizationRegistry constructor:
71 | The initial value of `FinalizationRegistry.prototype` is the
72 | intrinsic %FinalizationRegistryPrototype% object.
73 |
75 | This property has the attributes { [[Writable]]: *false*,
76 | [[Enumerable]]: *false*, [[Configurable]]: *false* }.
77 | The FinalizationRegistry prototype object: The initial value of
99 | `FinalizationRegistry.prototype.constructor` is the intrinsic
100 | object %FinalizationRegistry%. The following steps are taken: The following steps are taken: The initial value of the @@toStringTag property is the String value `"FinalizationRegistry"`. This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }.
158 | FinalizationRegistry instances are ordinary objects that inherit
159 | properties from the FinalizationRegistry prototype. FinalizationRegistry
160 | instances also have [[Cells]] and [[CleanupCallback]] internal slots.
161 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
--------------------------------------------------------------------------------
/spec/abstract-jobs.html:
--------------------------------------------------------------------------------
1 | Abstract Jobs
3 |
4 |
6 |
7 |
49 |
8 |
12 | Field Name
9 | Value
10 | Meaning
11 |
13 |
17 | [[LittleEndian]]
14 | Boolean
15 | The default value computed for the isLittleEndian parameter when it is needed by the algorithms GetValueFromBuffer and SetValueInBuffer. The choice is implementation-dependent and should be the alternative that is most efficient for the implementation. Once the value has been observed it cannot change.
16 |
18 |
22 | [[CanBlock]]
19 | Boolean
20 | Determines whether the agent can block or not.
21 |
23 |
27 | [[Signifier]]
24 | Any globally-unique value
25 | Uniquely identifies the agent within its agent cluster.
26 |
28 |
32 | [[IsLockFree1]]
29 | Boolean
30 | *true* if atomic operations on one-byte values are lock-free, *false* otherwise.
31 |
33 |
37 | [[IsLockFree2]]
34 | Boolean
35 | *true* if atomic operations on two-byte values are lock-free, *false* otherwise.
36 |
38 |
42 | [[CandidateExecution]]
39 | A candidate execution Record
40 | See the memory model.
41 |
43 |
47 |
48 | [[KeptAlive]]
44 | List of objects
45 | Initially a new empty List, representing the list of objects to be kept alive until the end of the current Job
46 | Processing model of WeakRef and FinalizationRegistry objects
53 |
54 |
55 | Objectives
57 |
58 |
71 |
86 |
87 | Liveness
106 |
107 |
136 |
144 | Execution
174 |
175 | Host Hooks
236 |
237 | HostCleanupFinalizationRegistry(_finalizationRegistry_)
239 |
240 | ClearKeptObjects( )
253 | AddToKeptObjects ( _object_ )
266 | CleanupFinalizationRegistry ( _finalizationRegistry_ [ , _callback_ ] )
280 | FinalizationRegistry Objects
3 | The FinalizationRegistry Constructor
11 |
13 |
36 |
37 | FinalizationRegistry ( _cleanupCallback_ )
39 | Properties of the FinalizationRegistry Constructor
59 |
61 |
67 |
68 | FinalizationRegistry.prototype
70 | Properties of the FinalizationRegistry Prototype Object
83 |
85 |
95 |
96 | FinalizationRegistry.prototype.constructor
98 | FinalizationRegistry.prototype.register ( _target_ , _heldValue_ [, _unregisterToken_ ] )
105 | FinalizationRegistry.prototype.unregister ( _unregisterToken_ )
131 | FinalizationRegistry.prototype [ @@toStringTag ]
150 | Properties of FinalizationRegistry Instances
157 |
4 | title: WeakRefs proposal 5 | status: proposal 6 | stage: 3 7 | location: https://github.com/tc39/proposal-weakrefs 8 | copyright: proposal 9 | contributors: Dean Tribble, Till Schneidereit, Sathya Gunasekaran 10 |11 | 24 | 25 |
4 | A WeakRef is an object that is used to refer to a target object 5 | without preserving it from garbage collection. WeakRefs can 6 | dereference to allow access to the target object, if the target 7 | object hasn't been reclaimed by garbage collection. 8 |
9 | 10 |The WeakRef constructor:
13 |41 | When the `WeakRef` function is called with argument _target_, 42 | the following steps are taken: 43 |
44 |The WeakRef constructor:
58 |69 | The initial value of `WeakRef.prototype` is the intrinsic 70 | %WeakRefPrototype% object. 71 |
72 |73 | This property has the attributes { [[Writable]]: *false*, 74 | [[Enumerable]]: *false*, [[Configurable]]: *false* }. 75 |
76 |The WeakRef prototype object:
82 |The initial value of `WeakRef.prototype.constructor` is the intrinsic object %WeakRef%.
96 | 97 |This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }.
98 | 99 |The following steps are taken:
107 |124 | target = { foo: function() {} }; 125 | let weakRef = new WeakRef(target); 126 | 127 | ... later ... 128 | 129 | if (weakRef.deref()) { 130 | weakRef.deref().foo(); 131 | } 132 |133 | 134 | In the above example, if the first deref evaluates to true 135 | then the second deref can not fail. 136 |
The initial value of the @@toStringTag property is the String value `"WeakRef"`.
142 |This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }.
143 |149 | WeakRef instances are ordinary objects that inherit properties from 150 | the WeakRef prototype. WeakRef instances also have a [[WeakRefTarget]] 151 | internal slot. 152 |
153 |160 | WeakMap and WeakSet keys are not kept alive just because a WeakRef points 161 | to them. This proposal rephrases the definition of WeakMaps and WeakSets 162 | to explain their observable effects on garbage collection, rather than 163 | specifying operationally that non-live keys or members are deleted. 164 |
165 |WeakMap objects are collections of key/value pairs where the keys are objects and values may be arbitrary ECMAScript language values. A WeakMap may be queried to see if it contains a key/value pair with a specific key, but no mechanism is provided for enumerating the objects it holds as keys. In certain conditions, objects which are not live are removed as WeakMap keys, as described in If an object that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap. WeakMap implementations must detect and remove such key/value pairs and any associated resources.
WeakSet objects are collections of objects. A distinct object may only occur once as an element of a WeakSet's collection. A WeakSet may be queried to see if it contains a specific object, but no mechanism is provided for enumerating the objects it holds. In certain conditions, objects which are not live are removed as WeakSet elements, as described in If an object that is contained by a WeakSet is only reachable by following a chain of references that start within that WeakSet, then that object is inaccessible and is automatically removed from the WeakSet. WeakSet implementations must detect and remove such objects and any associated resources.