├── .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 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 53 | 54 | 56 | 58 | 61 | 65 | 66 | 69 | 73 | 74 | 77 | 81 | 82 | 85 | 89 | 90 | 93 | 97 | 98 | 101 | 105 | 106 | 109 | 113 | 114 | 117 | 121 | 122 | 125 | 129 | 130 | 133 | 137 | 138 | 141 | 145 | 146 | 149 | 153 | 154 | 157 | 161 | 162 | 163 | 164 | 168 | 176 | 177 | 181 | 191 | 192 | 196 | 206 | 207 | 211 | 217 | 223 | 224 | 228 | 234 | 240 | 241 | 245 | 253 | 254 | 258 | 264 | 265 | 269 | 275 | 276 | 280 | 286 | 287 | 291 | 299 | 300 | 304 | 312 | 313 | 317 | 323 | 329 | 330 | 334 | 340 | 346 | 347 | 351 | 359 | 360 | 364 | 370 | 376 | 377 | 381 | 389 | 390 | 394 | 402 | 403 | 407 | 413 | 419 | 420 | 424 | 434 | 435 | 439 | 449 | 450 | 454 | 460 | 461 | 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 |
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 | 2 |

Abstract Jobs

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Field NameValueMeaning
[[LittleEndian]]BooleanThe 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.
[[CanBlock]]BooleanDetermines whether the agent can block or not.
[[Signifier]]Any globally-unique valueUniquely identifies the agent within its agent cluster.
[[IsLockFree1]]Boolean*true* if atomic operations on one-byte values are lock-free, *false* otherwise.
[[IsLockFree2]]Boolean*true* if atomic operations on two-byte values are lock-free, *false* otherwise.
[[CandidateExecution]]A candidate execution RecordSee the memory model.
[[KeptAlive]]List of objectsInitially a new empty List, representing the list of objects to be kept alive until the end of the current Job
49 |
50 | 51 | 52 |

Processing model of WeakRef and FinalizationRegistry objects

53 | 54 | 55 | 56 |

Objectives

57 | 58 |

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 |

64 | 65 |

66 | The semantics of WeakRef and FinalizationRegistry objects is based on two 67 | operations which happen at particular points in time: 68 |

69 | 70 |
    71 |
  • 72 | When WeakRef.prototype.deref is called, the referent (if it's not 73 | already dead) is kept alive so that subsequent, synchronous accesses also 74 | return the object. This list is reset when synchronous work is done using 75 | the ClearKeptObjects abstract operation. 76 |
  • 77 | 78 |
  • 79 | When an object which is registered with a FinalizationRegistry becomes 80 | unreachable, a call of the FinalizationRegistry's cleanup callback may 81 | eventually be made, after synchronous ECMAScript execution completes. 82 | The FinalizationRegistry cleanup is performed with the 83 | CleanupFFinalizationRegistry abstract operation. 84 |
  • 85 |
86 | 87 |

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 |

94 | 95 |

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 |

102 |
103 | 104 | 105 |

Liveness

106 | 107 |

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 |

113 | 114 | WeakRef-obliviousness, together with liveness, capture two notions. One, 115 | that a WeakRef itself does not keep an object alive. Two, that cycles in 116 | liveness does not imply that an object is live. To be concrete, if 117 | determining _obj_'s liveness depends on determining the liveness of 118 | another WeakRef referent, _obj2_, _obj2_'s liveness cannot assume 119 | _obj_'s liveness, which would beg the question. 120 | 121 | 122 | WeakRef-obliviousness is defined on sets of objects instead of 123 | individual objects to account for cycles. If it were defined on 124 | individual objects, then an object in a cycle will be considered live 125 | even though its Object value is only observed via WeakRefs of other 126 | objects in the cycle. 127 | 128 | 129 |

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 |

134 | 135 |
    136 |
  • 137 | Any element in _S_ is included in any agent's [[KeptAlive]] List. 138 |
  • 139 |
  • 140 | There exists a valid future hypothetical WeakRef-oblivious execution 141 | with respect to _S_ that observes the Object value of any object in _S_. 142 |
  • 143 |
144 | 145 | The intuition the second condition above intends to capture is that an 146 | object is live if its identity is observable via non-WeakRef means. An 147 | object's identity may be observed by observing a strict equality 148 | comparison between objects or observing the object being used as key in 149 | a Map. 150 | 151 | 152 | Presence of an object in a field, an internal slot, or a property does 153 | not imply that the object is live. For example if the object in 154 | question is never passed back to the program, then it cannot be 155 | observed. 156 | 157 | This is the case for keys in a WeakMap, members of a WeakSet, as well 158 | as the [[WeakRefTarget]] and [[UnregisterToken]] fields of a FinalizationRegistry 159 | Cell record. 160 | 161 | The above definition implies that, if a key in a WeakMap is not live, 162 | then its corresponding value is not necessarily live either. 163 | 164 | 165 | Liveness is the lower bound for guaranteeing which WeakRefs that 166 | engines must not empty. In practice, liveness as defined here is 167 | undecidable and engines use conservative approximations. There is 168 | expected to be significant implementation leeway. 169 | 170 |
171 | 172 | 173 |

Execution

174 | 175 |

At any time, if a set of objects _S_ is not live, an ECMAScript implementation may perform the following steps atomically:

176 | 177 | 178 | 1. For each _obj_ of _S_, do 179 | 1. For each WeakRef _ref_ such that _ref_.[[WeakRefTarget]] is _obj_, do 180 | 1. Set _ref_.[[WeakRefTarget]] to ~empty~. 181 | 1. For each FinalizationRegistry _fg_ such that _fg_.[[Cells]] contains _cell_, such that _cell_.[[WeakRefTarget]] is _obj_, 182 | 1. Set _cell_.[[WeakRefTarget]] to ~empty~. 183 | 1. Optionally, perform ! HostCleanupFinalizationRegistry(_fg_). 184 | 1. For each WeakMap _map_ such that _map_.[[WeakMapData]] contains a record _r_ such that _r_.[[Key]] is _obj_, 185 | 1. Remove _r_ from _map_.[[WeakMapData]]. 186 | 1. For each WeakSet _set_ such that _set_.[[WeakSetData]] contains _obj_, 187 | 1. Remove _obj_ from _set_.[[WeakSetData]]. 188 | 189 | 190 | 191 |

192 | Together with the definition of liveness, this clause prescribes legal 193 | optimizations that an implementation may apply regarding WeakRefs. 194 |

195 | 196 |

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 |

203 | 204 |

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 |

210 | 211 |

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 |

218 |
219 | 220 |

221 | Implementations are not obligated to empty WeakRefs for maximal sets 222 | of non-live objects. 223 |

224 |

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 |

231 |
232 |
233 | 234 | 235 |

Host Hooks

236 | 237 | 238 |

HostCleanupFinalizationRegistry(_finalizationRegistry_)

239 | 240 |

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 |

247 |
248 |
249 |
250 | 251 | 252 |

ClearKeptObjects( )

253 |

254 | ECMAScript implementations are expected to call ClearKeptObjects when a 255 | synchronous sequence of ECMAScript execution completes. 256 |

257 |

The following steps are performed:

258 | 259 | 1. Let _agent_ be the surrounding agent. 260 | 1. Set _agent_.[[KeptAlive]] to a new empty List. 261 | 262 |
263 | 264 | 265 |

AddToKeptObjects ( _object_ )

266 | 267 | 1. Let _agent_ be the surrounding agent. 268 | 1. Append _object_ to _agent_.[[KeptAlive]]. 269 | 270 | 271 | When the abstract operation AddToKeptObjects is called with a 272 | target object reference, it adds the target to an identity Set 273 | that will point strongly at the target until ClearKeptObjects is 274 | called. 275 | 276 |
277 | 278 | 279 |

CleanupFinalizationRegistry ( _finalizationRegistry_ [ , _callback_ ] )

280 |

The following steps are performed:

281 | 282 | 1. Assert: _finalizationRegistry_ has [[Cells]] and [[CleanupCallback]] internal slots. 283 | 1. If _callback_ is not present or *undefined*, set _callback_ to _finalizationRegistry_.[[CleanupCallback]]. 284 | 1. While _finalizationRegistry_.[[Cells]] contains a Record _cell_ such that _cell_.[[WeakRefTarget]] is ~empty~, then an implementation may perform the following steps, 285 | 1. Choose any such _cell_. 286 | 1. Remove _cell_ from _finalizationRegistry_.[[Cells]]. 287 | 1. Perform ? Call(_callback_, *undefined*, « _cell_.[[HeldValue]] »). 288 | 1. Return NormalCompletion(*undefined*). 289 | 290 | When called from HostCleanupFinalizationRegistry, if calling the callback throws an error, this will be caught within the RunJobs algorithm and reported to the host. HTML does not apply the RunJobs algorithm, but will also report the error, which may call `window.onerror`. 291 |
292 |
293 | -------------------------------------------------------------------------------- /spec/finalization-registry.html: -------------------------------------------------------------------------------- 1 | 2 |

FinalizationRegistry Objects

3 |

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 |

8 | 9 | 10 |

The FinalizationRegistry Constructor

11 |

The FinalizationRegistry constructor:

12 | 36 | 37 | 38 |

FinalizationRegistry ( _cleanupCallback_ )

39 |

40 | When the `FinalizationRegistry` function is called with argument _cleanupCallback_, 41 | the following steps are taken: 42 |

43 | 44 | 1. If NewTarget is *undefined*, throw a *TypeError* exception. 45 | 1. If IsCallable(_cleanupCallback_) is *false*, throw a *TypeError* exception. 46 | 1. Let _finalizationRegistry_ be ? OrdinaryCreateFromConstructor(NewTarget, 47 | `"%FinalizationRegistryPrototype%"`, « [[Realm]], [[CleanupCallback]], 48 | [[Cells]] »). 49 | 1. Let _fn_ be the active function object. 50 | 1. Set _finalizationRegistry_.[[Realm]] to _fn_.[[Realm]]. 51 | 1. Set _finalizationRegistry_.[[CleanupCallback]] to _cleanupCallback_. 52 | 1. Set _finalizationRegistry_.[[Cells]] to be an empty List. 53 | 1. Return _finalizationRegistry_. 54 |
55 |
56 | 57 | 58 |

Properties of the FinalizationRegistry Constructor

59 |

The FinalizationRegistry constructor:

60 | 67 | 68 | 69 |

FinalizationRegistry.prototype

70 |

71 | The initial value of `FinalizationRegistry.prototype` is the 72 | intrinsic %FinalizationRegistryPrototype% object. 73 |

74 |

75 | This property has the attributes { [[Writable]]: *false*, 76 | [[Enumerable]]: *false*, [[Configurable]]: *false* }. 77 |

78 |
79 |
80 | 81 | 82 |

Properties of the FinalizationRegistry Prototype Object

83 |

The FinalizationRegistry prototype object:

84 | 95 | 96 | 97 |

FinalizationRegistry.prototype.constructor

98 |

The initial value of 99 | `FinalizationRegistry.prototype.constructor` is the intrinsic 100 | object %FinalizationRegistry%.

101 |
102 | 103 | 104 |

FinalizationRegistry.prototype.register ( _target_ , _heldValue_ [, _unregisterToken_ ] )

105 |

The following steps are taken:

106 | 107 | 1. Let _finalizationRegistry_ be the *this* value. 108 | 1. Perform ? RequireInternalSlot(_finalizationRegistry_, [[Cells]]). 109 | 1. If Type(_target_) is not Object, throw a *TypeError* exception. 110 | 1. If SameValue(_target_, _heldValue_), throw a *TypeError* exception. 111 | 1. If Type(_unregisterToken_) is not Object, 112 | 1. If _unregisterToken_ is not *undefined*, throw a *TypeError* exception. 113 | 1. Set _unregisterToken_ to ~empty~. 114 | 1. Let _cell_ be the Record { [[WeakRefTarget]] : _target_, [[HeldValue]]: _heldValue_, 115 | [[UnregisterToken]]: _unregisterToken_ }. 116 | 1. Append _cell_ to _finalizationRegistry_.[[Cells]]. 117 | 1. Return *undefined*. 118 | 119 | 120 | 121 | Based on the algorithms and definitions in this specification, across the time when 122 | _cell_ is in _finalizationRegistry_.[[Cells]], then _cell_.[[Holdings]] is live; 123 | however, _cell_.[[UnregisterToken]] and _cell_.[[Target]] are not necessarily live. 124 | For example, registering an object with itself as its unregister token would not 125 | keep the object alive forever. 126 | 127 |
128 | 129 | 130 |

FinalizationRegistry.prototype.unregister ( _unregisterToken_ )

131 |

The following steps are taken:

132 | 133 | 1. Let _finalizationRegistry_ be the *this* value. 134 | 1. Perform ? RequireInternalSlot(_finalizationRegistry_, [[Cells]]). 135 | 1. If Type(_unregisterToken_) is not Object, throw a *TypeError* exception. 136 | 1. Let _removed_ be *false*. 137 | 1. For each Record { [[WeakRefTarget]], [[HeldValue]], 138 | [[UnregisterToken]] } _cell_ that is an element of 139 | _finalizationRegistry_.[[Cells]], do 140 | 1. If _cell_.[[UnregisterToken]] is not ~empty~ and 141 | SameValue(_cell_.[[UnregisterToken]], _unregisterToken_) is *true*, then 142 | 1. Remove _cell_ from _finalizationRegistry_.[[Cells]]. 143 | 1. Set _removed_ to *true*. 144 | 1. Return _removed_. 145 | 146 |
147 | 148 | 149 |

FinalizationRegistry.prototype [ @@toStringTag ]

150 |

The initial value of the @@toStringTag property is the String value `"FinalizationRegistry"`.

151 |

This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }.

152 |
153 |
154 | 155 | 156 |

Properties of FinalizationRegistry Instances

157 |

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 |

162 |
163 |
164 | -------------------------------------------------------------------------------- /spec/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
 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 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /spec/weak-ref.html: -------------------------------------------------------------------------------- 1 | 2 |

WeakRef Objects

3 |

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 | 11 |

The WeakRef Constructor

12 |

The WeakRef constructor:

13 | 37 | 38 | 39 |

WeakRef ( _target_ )

40 |

41 | When the `WeakRef` function is called with argument _target_, 42 | the following steps are taken: 43 |

44 | 45 | 1. If NewTarget is *undefined*, throw a *TypeError* exception. 46 | 1. If Type(_target_) is not Object, throw a *TypeError* exception. 47 | 1. Let _weakRef_ be ? OrdinaryCreateFromConstructor(NewTarget, 48 | `"%WeakRefPrototype%"`, « [[WeakRefTarget]] »). 49 | 1. Perfom ! AddToKeptObjects(_target_). 50 | 1. Set _weakRef_.[[WeakRefTarget]] to _target_. 51 | 1. Return _weakRef_. 52 |
53 |
54 | 55 | 56 |

Properties of the WeakRef Constructor

57 |

The WeakRef constructor:

58 | 65 | 66 | 67 |

WeakRef.prototype

68 |

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 |
77 |
78 | 79 | 80 |

Properties of the WeakRef Prototype Object

81 |

The WeakRef prototype object:

82 | 91 | 92 | 93 |

WeakRef.prototype.constructor

94 | 95 |

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 | 100 | This section is to be treated identically to the "Annex B" of ECMA-262, but to be written in-line with the main specification. 101 | 102 |
103 | 104 | 105 |

WeakRef.prototype.deref ( )

106 |

The following steps are taken:

107 | 108 | 1. Let _weakRef_ be the *this* value. 109 | 1. Perform ? RequireInternalSlot(_weakRef_, [[WeakRefTarget]]). 110 | 1. Let _target_ be the value of _weakRef_.[[WeakRefTarget]]. 111 | 1. If _target_ is not ~empty~, 112 | 1. Perform ! AddToKeptObjects(_target_). 113 | 1. Return _target_. 114 | 1. Return *undefined*. 115 | 116 | 117 | 118 | If the WeakRef returns a _target_ Object that is not 119 | *undefined*, then this _target_ object should not be garbage 120 | collected in that turn. The AddToKeptObjects operation makes sure 121 | read consistency is maintained. 122 | 123 |
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 |
137 |
138 | 139 | 140 |

WeakRef.prototype [ @@toStringTag ]

141 |

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 |
144 |
145 | 146 | 147 |

Properties of WeakRef Instances

148 |

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 |
154 |
155 | 156 | 157 |

Modifications to collection type definitions

158 | 159 |

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 |
166 | 167 | 168 |

WeakMap Objects

169 |

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.

170 |
171 | 172 | 173 |

WeakSet Objects

174 |

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.

175 |
176 |
177 | --------------------------------------------------------------------------------