├── .gitignore ├── README.md ├── docs ├── ecmarkup.css ├── ecmarkup.js ├── index.html ├── pr-87.html └── pr-93.html ├── package.json └── spec.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # In-Place Resizable and Growable `ArrayBuffer`s 2 | 3 | Stage: 4, [landed in the specification](https://github.com/tc39/ecma262/pull/3116). 4 | 5 | Author: Shu-yu Guo (@syg) 6 | 7 | Champion: Shu-yu Guo (@syg) 8 | 9 | ## Introduction 10 | 11 | `ArrayBuffer`s have enabled in-memory handling of binary data and have enjoyed great success. This proposal extends the `ArrayBuffer` constructors to take an additional maximum length that allows in-place growth and shrinking of buffers. Similarly, `SharedArrayBuffer` is extended to take an additional maximum length that allows in-place growth. 12 | 13 | ## Motivation and use cases 14 | 15 | ### Better memory management 16 | 17 | Growing a new buffer right now requires allocating a new buffer and copying. Not only is this inefficient, it needlessly fragments the address space on 32-bit systems. 18 | 19 | ### Sync up capability with WebAssembly memory.grow 20 | 21 | WebAssembly memory can grow. Every time it does, wasm vends a new `ArrayBuffer` instance and detaches the old one. Any JS-side "pointers" into wasm memory would need to be updated when a grow happens. This is an [open problem](https://github.com/WebAssembly/design/issues/1296) and currently requires polling, which is super slow: 22 | 23 | ```javascript 24 | // The backing buffer gets detached on every grow in wasm! 25 | let U8 = new Uint8Array(WebAssembly.Memory.buffer); 26 | 27 | function derefPointerIntoWasmMemory(idx) { 28 | // Do we need to re-create U8 because memory grew, causing the old buffer 29 | // to detach? 30 | if (U8.length === 0) { 31 | U8 = new Uint8Array(WebAssembly.Memory.buffer); 32 | } 33 | doSomethingWith(U8[idx]); 34 | } 35 | ``` 36 | 37 | It also spurred proposals such as having a signal handler-like synchronous callback on growth events for wasm's JS API, which doesn't feel great due to the issues of signal handler re-entrancy being difficult to reason about. 38 | 39 | Having growable `ArrayBuffer`s and auto-tracking TypedArrays would solve this problem more cleanly. 40 | 41 | ### WebGPU buffers 42 | 43 | WebGPU would like to [repoint the same `ArrayBuffer` instances to different backing buffers](https://github.com/gpuweb/gpuweb/issues/747#issuecomment-642938376). This is important for performance during animations, as remaking `ArrayBuffer` instances multiple times per frame of animation incurs GC pressure and pauses. 44 | 45 | Having a resizable `ArrayBuffer` would let WebGPU explain repointing as a resize + overwrite. Under the hood, browsers can implement WebGPU-vended resizable `ArrayBuffer`s as repointable without actually adding a repointable `ArrayBuffer` into the language. 46 | 47 | ## Proposal 48 | 49 | ### `ArrayBuffer` 50 | 51 | ```javascript 52 | class ArrayBuffer { 53 | // If the options parameter is not an object with a "maxByteLength" 54 | // property, the ArrayBuffer can neither grow nor shrink (status quo). 55 | // Otherwise it is resizable. 56 | // 57 | // A resizable ArrayBuffer can grow up to the provided 58 | // options.maxByteLength and shrink. 59 | // 60 | // If options is an object with a "maxByteLength" property, 61 | // - Throws a RangeError if maxByteLength is not finite. 62 | // - Throws a RangeError if byteLength > maxByteLength. 63 | constructor(byteLength [, options ]); 64 | 65 | // Resizes the buffer. 66 | // 67 | // Grows are designed to be implemented in-place, i.e. address space is 68 | // reserved up front but the pages are not committed until grown. 69 | // 70 | // Shrinks are also designed to be in-place, with a length change and 71 | // no realloc. 72 | // 73 | // Throws a TypeError if the this value is not resizable. 74 | // Throws a RangeError unless 0 <= newByteLength <= this.maxByteLength. 75 | // 76 | // Can throw OOM. 77 | resize(newByteLength); 78 | 79 | // Returns a *non*-resizable ArrayBuffer. 80 | slice(start, end); 81 | 82 | // Returns true if the `this` value is resizable `ArrayBuffer`, 83 | // false otherwise. 84 | // 85 | // No setter. 86 | get resizable(); 87 | 88 | // If resizable, returns the maximum byte length passed in during construction. 89 | // If not resizable, returns the byte length. 90 | // 91 | // No setter. 92 | get maxByteLength(); 93 | 94 | // No setter. 95 | get byteLength(); 96 | } 97 | ``` 98 | 99 | ### `SharedArrayBuffer` 100 | 101 | ```javascript 102 | class SharedArrayBuffer { 103 | // If the options parameter is not an object with a "maxByteLength" 104 | // property, the SharedArrayBuffer cannot grow (status quo). 105 | // Otherwise it is growable. 106 | // 107 | // A growable SharedArrayBuffer can only grow up to the provided 108 | // options.maxByteLength. 109 | // 110 | // If options is an object with a "maxByteLength" property, 111 | // - Throws a RangeError if options.maxByteLength is not finite. 112 | // - Throws a RangeError if byteLength > options.maxByteLength. 113 | constructor(byteLength [, options ]); 114 | 115 | // Grows the buffer. 116 | // 117 | // Grows are designed to be implemented in-place, i.e. address space is 118 | // reserved up front but the pages are not committed until grown. 119 | // 120 | // Growable SharedArrayBuffers cannot shrink because it is real scary to 121 | // allow for shared memory. 122 | // 123 | // Throws a TypeError if the `this` value is not a growable 124 | // SharedArrayBuffer. 125 | // Throws a RangeError unless 126 | // this.byteLength <= newByteLength <= this.maxByteLength. 127 | // 128 | // Can throw OOM. 129 | grow(newByteLength); 130 | 131 | // Returns a *non*-growable SharedArrayBuffer. 132 | slice(start, end); 133 | 134 | // Returns true if the `this` value is a growable SharedArrayBuffer, 135 | // false otherwise. 136 | // 137 | // No setter. 138 | get growable(); 139 | 140 | // If resizable, returns the maximum byte length passed in during construction. 141 | // If not resizable, returns the byte length. 142 | // 143 | // No setter. 144 | get maxByteLength(); 145 | 146 | // No setter. 147 | get byteLength(); 148 | } 149 | ``` 150 | 151 | ### Modifications to _TypedArray_ 152 | 153 | TypedArrays are extended to make use of these buffers. When a TypedArray is backed by a resizable buffer, its byte offset length may automatically change if the backing buffer is resized. 154 | 155 | The _TypedArray_ (_buffer_, [, _byteOffset_ [, _length_ ] ] ) constructor is modified as follows: 156 | 157 | - If _buffer_ is a resizable `ArrayBuffer` or a growable `SharedArrayBuffer`, if the _length_ is `undefined`, then the constructed TA automatically tracks the length of the backing buffer. 158 | 159 | The length getter on _TypedArray_.prototype is modified as follows: 160 | 161 | - If this TA is backed by a resizable `ArrayBuffer` or growable `SharedArrayBuffer` and is automatically tracking the length of the backing buffer, then return floor((buffer byte length - byte offset) / element size). 162 | - If this TA is backed by a resizable `ArrayBuffer` and the length is out of bounds, then return 0. 163 | 164 | All methods and internal methods that access indexed properties on TypedArrays are modified as follow: 165 | 166 | - If this TA is backed by a resizable `ArrayBuffer` and the translated byte index on the backing buffer is out of bounds, return undefined. 167 | - If this TA is backed by a resizable `ArrayBuffer` and the translated byte length is out of bounds, return 0. 168 | 169 | This change generalizes the detachment check: if a fixed-length window on a backing buffer becomes out of bounds, either in whole or in part, due to resizing, treat it like a detached buffer. 170 | 171 | This generalized bounds check is performed on every index access on TypedArrays backed by resizable `ArrayBuffer`. 172 | 173 | Growable `SharedArrayBuffer`s can only grow, so TAs backed by growable `SharedArrayBuffer`s cannot go out of bounds. 174 | 175 | An example: 176 | 177 | ```javascript 178 | let rab = new ArrayBuffer(1024, { maxByteLength: 1024 ** 2 }); 179 | // 0 offset, auto length 180 | let U32a = new Uint32Array(rab); 181 | assert(U32a.length === 256); // (1024 - 0) / 4 182 | rab.resize(1024 * 2); 183 | assert(U32a.length === 512); // (2048 - 0) / 4 184 | 185 | // Non-0 offset, auto length 186 | let U32b = new Uint32Array(rab, 256); 187 | assert(U32b.length === 448); // (2048 - 256) / 4 188 | rab.resize(1024); 189 | assert(U32b.length === 192); // (1024 - 256) / 4 190 | 191 | // Non-0 offset, fixed length 192 | let U32c = new Uint32Array(rab, 128, 4); 193 | assert(U32c.length === 4); 194 | rab.resize(1024 * 2); 195 | assert(U32c.length === 4); 196 | 197 | // If a resize makes any accessible part of a TA OOB, the TA acts like 198 | // it's been detached. 199 | rab.resize(256); 200 | assertThrows(() => U32b[0]); 201 | assert(U32b.length === 0); 202 | rab.resize(132); 203 | // U32c can address rab[128] to rab[144]. Being partially OOB still makes 204 | // it act like it's been detached. 205 | assertThrows(() => U32c[0]); 206 | assert(U32c.length === 0); 207 | // Resizing the underlying buffer can bring a TA back into bounds. 208 | // New memory is zeroed. 209 | rab.resize(1024); 210 | assert(U32b[0] === 0); 211 | assert(U32b.length === 192); 212 | ``` 213 | 214 | ## Implementation 215 | 216 | - Both resizable `ArrayBuffer` and growable `SharedArrayBuffer` are designed to be direct buffers where the virtual memory is reserved for the address range but not backed by physical memory until needed. 217 | 218 | - TypedArrays that are backed by resizable and growable buffers have more complex, but similar-in-kind, logic to detachment checks. The performance expectation is that these TypedArrays will be slower than TypedArrays backed by fixed-size buffers. In tight loops, however, this generalized check is hoistable in the same way that the current detach check is hoistable. 219 | 220 | - TypedArrays that are backed by resizable and growable buffers are recommended to have a distinct hidden class from TypedArrays backed by fixed-size buffers for maintainability of security-sensitive fast paths. This unfortunately makes use sites polymorphic. The slowdown from the polymorphism needs to be benchmarked. 221 | 222 | ## Security 223 | 224 | `ArrayBuffer`s and TypedArrays are one of the most common attack vectors for web browsers. Resizability adds non-zero security risk to the platform in that bugs in bounds checking code for resizable buffers may be easily exploited. 225 | 226 | This security risk is intrinsic to the proposal and is not entirely eliminable. This proposal tries to mitigate with the following design choices: 227 | 228 | - Existing uses of `ArrayBuffer` and `SharedArrayBuffer` constructors remain fixed-length and are not retrofitted to be resizable. Internally the resizable buffer types may have different hidden classes so existing code paths can be kept separate 229 | - Make partially OOB TypedArrays act like their buffers are detached instead of auto-updating the length 230 | - Make in-place implementation always possible to limit data pointer moves 231 | 232 | ## FAQ and design rationale tradeoffs 233 | 234 | ### What happened to `transfer()`? It used to be here. 235 | 236 | It has been separated into its [own proposal](https://github.com/syg/proposal-arraybuffer-transfer) to further explore the design space. 237 | 238 | ### Why not retrofit all `ArrayBuffer`s to be resizable? 239 | 240 | Retrofitting the single-parameter `ArrayBuffer` instead of adding an explicit opt-in overload is hard because of both language and implementation concerns: 241 | 242 | 1. TypedArray views have particular offsets and lengths that would need to be updated. It is messy to determine what TAs' lengths should be updated. If growing, it seems like user intention needs to be taken into account, and those with explicitly provided lengths should not be updated. If shrinking, it seems like all views need to be updated. This would not only require tracking all created views but is not clean to reason about. 243 | 1. Browsers and VMs have battle-hardened code paths around existing TypedArrays and `ArrayBuffer`s, as they are the most popular way to attack browsers. By introducing new types, we hope to leave those existing paths alone. Otherwise we'd need to audit all existing paths, of which there are many because of web APIs' use of buffers, to ensure they handle the possibility of growth and shrinking. This is scary and is likely a security bug farm. 244 | 245 | ### Why require maximum length? 246 | 247 | The API is designed to be implementable as an in-place growth. Non in-place growth (i.e. realloc) semantics presents more challenges for implementation as well as a bigger attack surface. In-place growth has the guarantee that the data pointer of the backing store does not move. 248 | 249 | Under the hood, this means the backing store pointer can be made immovable. Note that this immovability of the data pointer is unobservable from within JS. For resizable `ArrayBuffer`s, it would be conformant, but possibly undesirable, to implement growth and shrinking as realloc. For growable `SharedArrayBuffer`s, due to memory model constraints, it is unlikely that a realloc implementation is possible. 250 | 251 | ### Why can't growable `SharedArrayBuffer` shrink? 252 | 253 | Shrinking shared memory is scary and seems like a bad time. 254 | 255 | ### How would growable `SharedArrayBuffer` growth work with the memory model? 256 | 257 | Growing a growable `SharedArrayBuffer` performs a SeqCst access on the buffer length. Explicit accesses to length, such as to the `byteLength` accessor, and built-in functions, such as `slice`, perform a SeqCst access on the buffer length. Bounds checks as part of indexed access, such as via `ta[idx]` and `Atomics.load(ta, idx)`, perform an Unordered access on the buffer length. 258 | 259 | This aligns with WebAssembly as well as enable more optimization opportunities for bounds checking codegen. It also means that other threads are not guaranteed to see the grown length without synchronizing on an explicit length access, such as by reading the `byteLength` accessor. 260 | 261 | ## Open questions 262 | 263 | ### Should `resize(0)` be allowed? 264 | 265 | ~~Currently a length of 0 always denotes a detached buffer. Are there use cases for `resize(0)`? Should it mean detach if allowed? Or should the buffer be allowed to grow again afterwards?~~ 266 | 267 | https://github.com/tc39/proposal-resizablearraybuffer/issues/22 points out that `ArrayBuffer(0)` is already a thing. This proposal thus allows `resize(0)`. 268 | 269 | ## History and acknowledgment 270 | 271 | Thanks to: 272 | - @lukewagner for the original straw-person https://gist.github.com/lukewagner/2735af7eea411e18cf20 273 | - @domenic for https://github.com/domenic/proposal-arraybuffer-transfer 274 | - @ulan for discussion and guidance of the implementation of ArrayBuffers in V8 and Chrome 275 | - @kainino0x for discussion of the WebGPU use case 276 | - @binji and @azakai for discussion of the WebAssembly use case 277 | -------------------------------------------------------------------------------- /docs/ecmarkup.css: -------------------------------------------------------------------------------- 1 | body { 2 | display: flex; 3 | word-wrap: break-word; 4 | font-size: 18px; 5 | line-height: 1.5; 6 | font-family: Cambria, Palatino Linotype, Palatino, Liberation Serif, serif; 7 | padding: 0; 8 | margin: 0; 9 | color: #111; 10 | } 11 | 12 | #spec-container { 13 | padding: 0 20px; 14 | flex-grow: 1; 15 | flex-basis: 66%; 16 | box-sizing: border-box; 17 | overflow: hidden; 18 | padding-bottom: 1em; 19 | } 20 | 21 | body.oldtoc { 22 | margin: 0 auto; 23 | } 24 | 25 | a { 26 | text-decoration: none; 27 | color: #206ca7; 28 | } 29 | 30 | a:visited { 31 | color: #206ca7; 32 | } 33 | 34 | a:hover { 35 | text-decoration: underline; 36 | color: #239dee; 37 | } 38 | 39 | a.e-user-code, 40 | span.e-user-code { 41 | white-space: nowrap; 42 | } 43 | 44 | a.e-user-code::before, 45 | span.e-user-code::before { 46 | display: none; 47 | color: brown; 48 | background-color: white; 49 | border: 2pt solid brown; 50 | padding: 0.3ex; 51 | margin: 0 0.25em 0 0; 52 | line-height: 1; 53 | vertical-align: text-top; 54 | text-transform: uppercase; 55 | font-family: 'Comic Code', sans-serif; 56 | font-weight: 900; 57 | font-size: x-small; 58 | } 59 | 60 | a.e-user-code:hover::before, 61 | span.e-user-code:hover::before { 62 | background-color: brown; 63 | color: white; 64 | } 65 | 66 | html.show-ao-annotations a.e-user-code::before, 67 | html.show-ao-annotations span.e-user-code::before { 68 | display: inline-block; 69 | } 70 | 71 | a.e-user-code::before, 72 | span.e-user-code::before { 73 | content: 'UC'; 74 | } 75 | 76 | code { 77 | font-weight: bold; 78 | font-family: Consolas, Monaco, monospace; 79 | white-space: pre; 80 | } 81 | 82 | pre code { 83 | font-weight: inherit; 84 | } 85 | 86 | pre code.hljs { 87 | background-color: #fff; 88 | margin: 0; 89 | padding: 0; 90 | } 91 | 92 | ol.toc { 93 | list-style: none; 94 | padding-left: 0; 95 | } 96 | 97 | ol.toc ol.toc { 98 | padding-left: 2ex; 99 | list-style: none; 100 | } 101 | 102 | var { 103 | color: #2aa198; 104 | transition: background-color 0.25s ease; 105 | cursor: pointer; 106 | } 107 | 108 | var.referenced0 { 109 | color: inherit; 110 | background-color: #ffff33; 111 | box-shadow: 0 0 0 2px #ffff33; 112 | } 113 | var.referenced1 { 114 | color: inherit; 115 | background-color: #ff87a2; 116 | box-shadow: 0 0 0 2px #ff87a2; 117 | } 118 | var.referenced2 { 119 | color: inherit; 120 | background-color: #96e885; 121 | box-shadow: 0 0 0 2px #96e885; 122 | } 123 | var.referenced3 { 124 | color: inherit; 125 | background-color: #3eeed2; 126 | box-shadow: 0 0 0 2px #3eeed2; 127 | } 128 | var.referenced4 { 129 | color: inherit; 130 | background-color: #eacfb6; 131 | box-shadow: 0 0 0 2px #eacfb6; 132 | } 133 | var.referenced5 { 134 | color: inherit; 135 | background-color: #82ddff; 136 | box-shadow: 0 0 0 2px #82ddff; 137 | } 138 | var.referenced6 { 139 | color: inherit; 140 | background-color: #ffbcf2; 141 | box-shadow: 0 0 0 2px #ffbcf2; 142 | } 143 | 144 | emu-const { 145 | font-family: sans-serif; 146 | } 147 | 148 | emu-val { 149 | font-weight: bold; 150 | } 151 | 152 | /* depth 1 */ 153 | emu-alg ol, 154 | /* depth 4 */ 155 | emu-alg ol ol ol ol, 156 | emu-alg ol.nested-thrice, 157 | emu-alg ol.nested-twice ol, 158 | emu-alg ol.nested-once ol ol { 159 | list-style-type: decimal; 160 | } 161 | 162 | /* depth 2 */ 163 | emu-alg ol ol, 164 | emu-alg ol.nested-once, 165 | /* depth 5 */ 166 | emu-alg ol ol ol ol ol, 167 | emu-alg ol.nested-four-times, 168 | emu-alg ol.nested-thrice ol, 169 | emu-alg ol.nested-twice ol ol, 170 | emu-alg ol.nested-once ol ol ol { 171 | list-style-type: lower-alpha; 172 | } 173 | 174 | /* depth 3 */ 175 | emu-alg ol ol ol, 176 | emu-alg ol.nested-twice, 177 | emu-alg ol.nested-once ol, 178 | /* depth 6 */ 179 | emu-alg ol ol ol ol ol ol, 180 | emu-alg ol.nested-lots, 181 | emu-alg ol.nested-four-times ol, 182 | emu-alg ol.nested-thrice ol ol, 183 | emu-alg ol.nested-twice ol ol ol, 184 | emu-alg ol.nested-once ol ol ol ol, 185 | /* depth 7+ */ 186 | emu-alg ol.nested-lots ol { 187 | list-style-type: lower-roman; 188 | } 189 | 190 | emu-eqn { 191 | display: block; 192 | margin-left: 4em; 193 | } 194 | 195 | emu-eqn.inline { 196 | display: inline; 197 | margin: 0; 198 | } 199 | 200 | emu-eqn div:first-child { 201 | margin-left: -2em; 202 | } 203 | 204 | emu-note { 205 | margin: 1em 0; 206 | display: flex; 207 | flex-direction: row; 208 | color: inherit; 209 | border-left: 5px solid #52e052; 210 | background: #e9fbe9; 211 | padding: 10px 10px 10px 0; 212 | } 213 | 214 | emu-note > span.note { 215 | flex-basis: 100px; 216 | min-width: 100px; 217 | flex-grow: 0; 218 | flex-shrink: 1; 219 | text-transform: uppercase; 220 | padding-left: 5px; 221 | } 222 | 223 | emu-note[type='editor'] { 224 | border-left-color: #faa; 225 | } 226 | 227 | emu-note > div.note-contents { 228 | flex-grow: 1; 229 | flex-shrink: 1; 230 | overflow: auto; 231 | } 232 | 233 | emu-note > div.note-contents > p:first-of-type { 234 | margin-top: 0; 235 | } 236 | 237 | emu-note > div.note-contents > p:last-of-type { 238 | margin-bottom: 0; 239 | } 240 | 241 | emu-table td code { 242 | white-space: normal; 243 | } 244 | 245 | emu-figure { 246 | display: block; 247 | } 248 | 249 | emu-example { 250 | display: block; 251 | margin: 1em 3em; 252 | } 253 | 254 | emu-example figure figcaption { 255 | margin-top: 0.5em; 256 | text-align: left; 257 | } 258 | 259 | emu-figure figure, 260 | emu-example figure, 261 | emu-table figure { 262 | display: flex; 263 | flex-direction: column; 264 | align-items: center; 265 | } 266 | 267 | emu-production { 268 | display: block; 269 | } 270 | 271 | emu-grammar[type='example'] emu-production, 272 | emu-grammar[type='definition'] emu-production { 273 | margin-top: 1em; 274 | margin-bottom: 1em; 275 | margin-left: 5ex; 276 | } 277 | 278 | emu-grammar.inline, 279 | emu-production.inline, 280 | emu-grammar.inline emu-production emu-rhs, 281 | emu-production.inline emu-rhs, 282 | emu-grammar[collapsed] emu-production emu-rhs { 283 | display: inline; 284 | padding-left: 1ex; 285 | margin-left: 0; 286 | } 287 | 288 | emu-production[collapsed] emu-rhs { 289 | display: inline; 290 | padding-left: 0.5ex; 291 | margin-left: 0; 292 | } 293 | 294 | emu-grammar[collapsed] emu-production, 295 | emu-production[collapsed] { 296 | margin: 0; 297 | } 298 | 299 | emu-constraints { 300 | font-size: 0.75em; 301 | margin-right: 0.5ex; 302 | } 303 | 304 | emu-gann { 305 | margin-right: 0.5ex; 306 | } 307 | 308 | emu-gann emu-t:last-child, 309 | emu-gann emu-gprose:last-child, 310 | emu-gann emu-nt:last-child { 311 | margin-right: 0; 312 | } 313 | 314 | emu-geq { 315 | margin-left: 0.5ex; 316 | font-weight: bold; 317 | } 318 | 319 | emu-oneof { 320 | font-weight: bold; 321 | margin-left: 0.5ex; 322 | } 323 | 324 | emu-nt { 325 | display: inline-block; 326 | font-style: italic; 327 | white-space: nowrap; 328 | text-indent: 0; 329 | } 330 | 331 | emu-nt a, 332 | emu-nt a:visited { 333 | color: #333; 334 | } 335 | 336 | emu-rhs emu-nt { 337 | margin-right: 0.5ex; 338 | } 339 | 340 | emu-t { 341 | display: inline-block; 342 | font-family: monospace; 343 | font-weight: bold; 344 | white-space: nowrap; 345 | text-indent: 0; 346 | } 347 | 348 | emu-production emu-t { 349 | margin-right: 0.5ex; 350 | } 351 | 352 | emu-rhs { 353 | display: block; 354 | padding-left: 75px; 355 | text-indent: -25px; 356 | } 357 | 358 | emu-production:not([collapsed]) emu-rhs { 359 | border: 0.2ex dashed transparent; 360 | } 361 | 362 | emu-production:not([collapsed]) emu-rhs:hover { 363 | border-color: #888; 364 | background-color: #f0f0f0; 365 | } 366 | 367 | emu-mods { 368 | font-size: 0.85em; 369 | vertical-align: sub; 370 | font-style: normal; 371 | font-weight: normal; 372 | } 373 | 374 | emu-params, 375 | emu-opt { 376 | margin-right: 1ex; 377 | font-family: monospace; 378 | } 379 | 380 | emu-params, 381 | emu-constraints { 382 | color: #2aa198; 383 | } 384 | 385 | emu-opt { 386 | color: #b58900; 387 | } 388 | 389 | emu-gprose { 390 | font-size: 0.9em; 391 | font-family: Helvetica, Arial, sans-serif; 392 | } 393 | 394 | emu-production emu-gprose { 395 | margin-right: 1ex; 396 | } 397 | 398 | h1.shortname { 399 | color: #f60; 400 | font-size: 1.5em; 401 | margin: 0; 402 | } 403 | 404 | h1.version { 405 | color: #f60; 406 | font-size: 1.5em; 407 | } 408 | 409 | h1.title { 410 | color: #f60; 411 | } 412 | 413 | h1, 414 | h2, 415 | h3, 416 | h4, 417 | h5, 418 | h6 { 419 | position: relative; 420 | } 421 | 422 | h1 .secnum { 423 | text-decoration: none; 424 | margin-right: 5px; 425 | } 426 | 427 | h1 span.title { 428 | order: 2; 429 | } 430 | 431 | h1 { 432 | font-size: 2.67em; 433 | margin-bottom: 0; 434 | line-height: 1em; 435 | } 436 | h2 { 437 | font-size: 2em; 438 | } 439 | h3 { 440 | font-size: 1.56em; 441 | } 442 | h4 { 443 | font-size: 1.25em; 444 | } 445 | h5 { 446 | font-size: 1.11em; 447 | } 448 | h6 { 449 | font-size: 1em; 450 | } 451 | 452 | pre code.hljs { 453 | background: transparent; 454 | } 455 | 456 | emu-clause[id], 457 | emu-annex[id], 458 | emu-intro[id] { 459 | scroll-margin-top: 2ex; 460 | } 461 | 462 | emu-intro h1, 463 | emu-clause h1, 464 | emu-annex h1 { 465 | font-size: 2em; 466 | } 467 | emu-intro h2, 468 | emu-clause h2, 469 | emu-annex h2 { 470 | font-size: 1.56em; 471 | } 472 | emu-intro h3, 473 | emu-clause h3, 474 | emu-annex h3 { 475 | font-size: 1.25em; 476 | } 477 | emu-intro h4, 478 | emu-clause h4, 479 | emu-annex h4 { 480 | font-size: 1.11em; 481 | } 482 | emu-intro h5, 483 | emu-clause h5, 484 | emu-annex h5 { 485 | font-size: 1em; 486 | } 487 | emu-intro h6, 488 | emu-clause h6, 489 | emu-annex h6 { 490 | font-size: 0.9em; 491 | } 492 | emu-intro emu-intro h1, 493 | emu-clause emu-clause h1, 494 | emu-annex emu-annex h1 { 495 | font-size: 1.56em; 496 | } 497 | emu-intro emu-intro h2, 498 | emu-clause emu-clause h2, 499 | emu-annex emu-annex h2 { 500 | font-size: 1.25em; 501 | } 502 | emu-intro emu-intro h3, 503 | emu-clause emu-clause h3, 504 | emu-annex emu-annex h3 { 505 | font-size: 1.11em; 506 | } 507 | emu-intro emu-intro h4, 508 | emu-clause emu-clause h4, 509 | emu-annex emu-annex h4 { 510 | font-size: 1em; 511 | } 512 | emu-intro emu-intro h5, 513 | emu-clause emu-clause h5, 514 | emu-annex emu-annex h5 { 515 | font-size: 0.9em; 516 | } 517 | emu-intro emu-intro emu-intro h1, 518 | emu-clause emu-clause emu-clause h1, 519 | emu-annex emu-annex emu-annex h1 { 520 | font-size: 1.25em; 521 | } 522 | emu-intro emu-intro emu-intro h2, 523 | emu-clause emu-clause emu-clause h2, 524 | emu-annex emu-annex emu-annex h2 { 525 | font-size: 1.11em; 526 | } 527 | emu-intro emu-intro emu-intro h3, 528 | emu-clause emu-clause emu-clause h3, 529 | emu-annex emu-annex emu-annex h3 { 530 | font-size: 1em; 531 | } 532 | emu-intro emu-intro emu-intro h4, 533 | emu-clause emu-clause emu-clause h4, 534 | emu-annex emu-annex emu-annex h4 { 535 | font-size: 0.9em; 536 | } 537 | emu-intro emu-intro emu-intro emu-intro h1, 538 | emu-clause emu-clause emu-clause emu-clause h1, 539 | emu-annex emu-annex emu-annex emu-annex h1 { 540 | font-size: 1.11em; 541 | } 542 | emu-intro emu-intro emu-intro emu-intro h2, 543 | emu-clause emu-clause emu-clause emu-clause h2, 544 | emu-annex emu-annex emu-annex emu-annex h2 { 545 | font-size: 1em; 546 | } 547 | emu-intro emu-intro emu-intro emu-intro h3, 548 | emu-clause emu-clause emu-clause emu-clause h3, 549 | emu-annex emu-annex emu-annex emu-annex h3 { 550 | font-size: 0.9em; 551 | } 552 | emu-intro emu-intro emu-intro emu-intro emu-intro h1, 553 | emu-clause emu-clause emu-clause emu-clause emu-clause h1, 554 | emu-annex emu-annex emu-annex emu-annex emu-annex h1 { 555 | font-size: 1em; 556 | } 557 | emu-intro emu-intro emu-intro emu-intro emu-intro h2, 558 | emu-clause emu-clause emu-clause emu-clause emu-clause h2, 559 | emu-annex emu-annex emu-annex emu-annex emu-annex h2 { 560 | font-size: 0.9em; 561 | } 562 | emu-intro emu-intro emu-intro emu-intro emu-intro emu-intro h1, 563 | emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause h1, 564 | emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex h1 { 565 | font-size: 0.9em; 566 | } 567 | 568 | emu-clause, 569 | emu-intro, 570 | emu-annex { 571 | display: block; 572 | } 573 | 574 | /* these values are twice the font-size for the

titles for clauses */ 575 | emu-intro, 576 | emu-clause, 577 | emu-annex { 578 | margin-top: 4em; 579 | } 580 | emu-intro emu-intro, 581 | emu-clause emu-clause, 582 | emu-annex emu-annex { 583 | margin-top: 3.12em; 584 | } 585 | emu-intro emu-intro emu-intro, 586 | emu-clause emu-clause emu-clause, 587 | emu-annex emu-annex emu-annex { 588 | margin-top: 2.5em; 589 | } 590 | emu-intro emu-intro emu-intro emu-intro, 591 | emu-clause emu-clause emu-clause emu-clause, 592 | emu-annex emu-annex emu-annex emu-annex { 593 | margin-top: 2.22em; 594 | } 595 | emu-intro emu-intro emu-intro emu-intro emu-intro, 596 | emu-clause emu-clause emu-clause emu-clause emu-clause, 597 | emu-annex emu-annex emu-annex emu-annex emu-annex { 598 | margin-top: 2em; 599 | } 600 | emu-intro emu-intro emu-intro emu-intro emu-intro emu-intro, 601 | emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause, 602 | emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex { 603 | margin-top: 1.8em; 604 | } 605 | 606 | #spec-container > emu-intro:first-of-type, 607 | #spec-container > emu-clause:first-of-type, 608 | #spec-container > emu-annex:first-of-type { 609 | margin-top: 0; 610 | } 611 | 612 | /* Figures and tables */ 613 | figure { 614 | display: block; 615 | margin: 1em 0 3em 0; 616 | } 617 | figure object { 618 | display: block; 619 | margin: 0 auto; 620 | } 621 | figure table.real-table { 622 | margin: 0 auto; 623 | } 624 | figure figcaption { 625 | display: block; 626 | color: #555555; 627 | font-weight: bold; 628 | text-align: center; 629 | } 630 | 631 | emu-table table { 632 | margin: 0 auto; 633 | } 634 | 635 | emu-table table, 636 | table.real-table { 637 | border-collapse: collapse; 638 | } 639 | 640 | emu-table td, 641 | emu-table th, 642 | table.real-table td, 643 | table.real-table th { 644 | border: 1px solid black; 645 | padding: 0.4em; 646 | vertical-align: baseline; 647 | } 648 | emu-table th, 649 | emu-table thead td, 650 | table.real-table th { 651 | background-color: #eeeeee; 652 | } 653 | 654 | emu-table td { 655 | background: #fff; 656 | } 657 | 658 | /* Note: the left content edges of table.lightweight-table >tbody >tr >td 659 | and div.display line up. */ 660 | table.lightweight-table { 661 | border-collapse: collapse; 662 | margin: 0 0 0 1.5em; 663 | } 664 | table.lightweight-table td, 665 | table.lightweight-table th { 666 | border: none; 667 | padding: 0 0.5em; 668 | vertical-align: baseline; 669 | } 670 | 671 | /* diff styles */ 672 | ins { 673 | background-color: #e0f8e0; 674 | text-decoration: none; 675 | border-bottom: 1px solid #396; 676 | } 677 | 678 | del { 679 | background-color: #fee; 680 | } 681 | 682 | ins.block, 683 | del.block, 684 | emu-production > ins, 685 | emu-production > del, 686 | emu-grammar > ins, 687 | emu-grammar > del { 688 | display: block; 689 | } 690 | emu-rhs > ins, 691 | emu-rhs > del { 692 | display: inline; 693 | } 694 | 695 | tr.ins > td > ins { 696 | border-bottom: none; 697 | } 698 | 699 | tr.ins > td { 700 | background-color: #e0f8e0; 701 | } 702 | 703 | tr.del > td { 704 | background-color: #fee; 705 | } 706 | 707 | /* Menu Styles */ 708 | #menu-toggle { 709 | font-size: 2em; 710 | 711 | position: fixed; 712 | top: 0; 713 | left: 0; 714 | width: 1.5em; 715 | height: 1.5em; 716 | z-index: 3; 717 | visibility: hidden; 718 | color: #1567a2; 719 | background-color: #fff; 720 | 721 | line-height: 1.5em; 722 | text-align: center; 723 | -webkit-touch-callout: none; 724 | -webkit-user-select: none; 725 | -khtml-user-select: none; 726 | -moz-user-select: none; 727 | -ms-user-select: none; 728 | user-select: none; 729 | 730 | cursor: pointer; 731 | } 732 | 733 | #menu { 734 | display: flex; 735 | flex-direction: column; 736 | width: 33%; 737 | height: 100vh; 738 | max-width: 500px; 739 | box-sizing: border-box; 740 | background-color: #ddd; 741 | overflow: hidden; 742 | transition: opacity 0.1s linear; 743 | padding: 0 5px; 744 | position: fixed; 745 | left: 0; 746 | top: 0; 747 | border-right: 2px solid #bbb; 748 | 749 | z-index: 2; 750 | } 751 | 752 | .menu-spacer { 753 | flex-basis: 33%; 754 | max-width: 500px; 755 | flex-grow: 0; 756 | flex-shrink: 0; 757 | } 758 | 759 | #menu a { 760 | color: #1567a2; 761 | } 762 | 763 | #menu.active { 764 | display: flex; 765 | opacity: 1; 766 | z-index: 2; 767 | } 768 | 769 | #menu-pins { 770 | flex-grow: 1; 771 | display: none; 772 | } 773 | 774 | #menu-pins.active { 775 | display: block; 776 | } 777 | 778 | #menu-pins-list { 779 | margin: 0; 780 | padding: 0; 781 | counter-reset: pins-counter; 782 | } 783 | 784 | #menu-pins-list > li:before { 785 | content: counter(pins-counter); 786 | counter-increment: pins-counter; 787 | display: inline-block; 788 | width: 25px; 789 | text-align: center; 790 | border: 1px solid #bbb; 791 | padding: 2px; 792 | margin: 4px; 793 | box-sizing: border-box; 794 | line-height: 1em; 795 | background-color: #ccc; 796 | border-radius: 4px; 797 | } 798 | #menu-toc > ol { 799 | padding: 0; 800 | flex-grow: 1; 801 | } 802 | 803 | #menu-toc > ol li { 804 | padding: 0; 805 | } 806 | 807 | #menu-toc > ol, 808 | #menu-toc > ol ol { 809 | list-style-type: none; 810 | margin: 0; 811 | padding: 0; 812 | } 813 | 814 | #menu-toc > ol ol { 815 | padding-left: 0.75em; 816 | } 817 | 818 | #menu-toc li { 819 | text-overflow: ellipsis; 820 | overflow: hidden; 821 | white-space: nowrap; 822 | } 823 | 824 | #menu-toc .item-toggle { 825 | display: inline-block; 826 | transform: rotate(-45deg) translate(-5px, -5px); 827 | transition: transform 0.1s ease; 828 | text-align: center; 829 | width: 20px; 830 | 831 | color: #aab; 832 | 833 | -webkit-touch-callout: none; 834 | -webkit-user-select: none; 835 | -khtml-user-select: none; 836 | -moz-user-select: none; 837 | -ms-user-select: none; 838 | user-select: none; 839 | 840 | cursor: pointer; 841 | } 842 | 843 | #menu-toc .item-toggle-none { 844 | display: inline-block; 845 | width: 20px; 846 | } 847 | 848 | #menu-toc li.active > .item-toggle { 849 | transform: rotate(45deg) translate(-5px, -5px); 850 | } 851 | 852 | #menu-toc li > ol { 853 | display: none; 854 | } 855 | 856 | #menu-toc li.active > ol { 857 | display: block; 858 | } 859 | 860 | #menu-toc li.revealed > a { 861 | background-color: #bbb; 862 | font-weight: bold; 863 | /* 864 | background-color: #222; 865 | color: #c6d8e4; 866 | */ 867 | } 868 | 869 | #menu-toc li.revealed-leaf > a { 870 | color: #206ca7; 871 | /* 872 | background-color: #222; 873 | color: #c6d8e4; 874 | */ 875 | } 876 | 877 | #menu-toc li.revealed > .item-toggle { 878 | transform: rotate(45deg) translate(-5px, -5px); 879 | } 880 | 881 | #menu-toc li.revealed > ol { 882 | display: block; 883 | } 884 | 885 | #menu-toc li > a { 886 | padding: 2px 5px; 887 | } 888 | 889 | #menu > * { 890 | margin-bottom: 5px; 891 | } 892 | 893 | .menu-pane-header { 894 | padding: 0 5px; 895 | text-transform: uppercase; 896 | background-color: #aaa; 897 | color: #335; 898 | font-weight: bold; 899 | letter-spacing: 2px; 900 | flex-grow: 0; 901 | flex-shrink: 0; 902 | font-size: 0.8em; 903 | } 904 | 905 | .menu-pane-header emu-opt, 906 | .menu-pane-header emu-t, 907 | .menu-pane-header emu-nt { 908 | margin-right: 0px; 909 | display: inline; 910 | color: inherit; 911 | } 912 | 913 | .menu-pane-header emu-rhs { 914 | display: inline; 915 | padding-left: 0px; 916 | text-indent: 0px; 917 | } 918 | 919 | .menu-pane-header emu-geq { 920 | margin-left: 0px; 921 | } 922 | 923 | a.menu-pane-header-production { 924 | color: inherit; 925 | } 926 | 927 | .menu-pane-header-production { 928 | text-transform: none; 929 | letter-spacing: 1.5px; 930 | padding-left: 0.5em; 931 | } 932 | 933 | #menu-toc { 934 | display: flex; 935 | flex-direction: column; 936 | width: 100%; 937 | overflow: hidden; 938 | flex-grow: 1; 939 | } 940 | 941 | #menu-toc ol.toc { 942 | overflow-x: hidden; 943 | overflow-y: auto; 944 | } 945 | 946 | #menu-search { 947 | position: relative; 948 | flex-grow: 0; 949 | flex-shrink: 0; 950 | width: 100%; 951 | 952 | display: flex; 953 | flex-direction: column; 954 | 955 | max-height: 300px; 956 | } 957 | 958 | #menu-trace-list { 959 | display: none; 960 | } 961 | 962 | #menu-search-box { 963 | box-sizing: border-box; 964 | display: block; 965 | width: 100%; 966 | margin: 5px 0 0 0; 967 | font-size: 1em; 968 | padding: 2px; 969 | background-color: #bbb; 970 | border: 1px solid #999; 971 | } 972 | 973 | #menu-search-results { 974 | overflow-x: hidden; 975 | overflow-y: auto; 976 | } 977 | 978 | li.menu-search-result-clause:before { 979 | content: 'clause'; 980 | width: 40px; 981 | display: inline-block; 982 | text-align: right; 983 | padding-right: 1ex; 984 | color: #666; 985 | font-size: 75%; 986 | } 987 | li.menu-search-result-op:before { 988 | content: 'op'; 989 | width: 40px; 990 | display: inline-block; 991 | text-align: right; 992 | padding-right: 1ex; 993 | color: #666; 994 | font-size: 75%; 995 | } 996 | 997 | li.menu-search-result-prod:before { 998 | content: 'prod'; 999 | width: 40px; 1000 | display: inline-block; 1001 | text-align: right; 1002 | padding-right: 1ex; 1003 | color: #666; 1004 | font-size: 75%; 1005 | } 1006 | 1007 | li.menu-search-result-term:before { 1008 | content: 'term'; 1009 | width: 40px; 1010 | display: inline-block; 1011 | text-align: right; 1012 | padding-right: 1ex; 1013 | color: #666; 1014 | font-size: 75%; 1015 | } 1016 | 1017 | #menu-search-results ul { 1018 | padding: 0 5px; 1019 | margin: 0; 1020 | } 1021 | 1022 | #menu-search-results li { 1023 | white-space: nowrap; 1024 | text-overflow: ellipsis; 1025 | } 1026 | 1027 | #menu-trace-list { 1028 | counter-reset: item; 1029 | margin: 0 0 0 20px; 1030 | padding: 0; 1031 | } 1032 | #menu-trace-list li { 1033 | display: block; 1034 | white-space: nowrap; 1035 | } 1036 | 1037 | #menu-trace-list li .secnum:after { 1038 | content: ' '; 1039 | } 1040 | #menu-trace-list li:before { 1041 | content: counter(item) ' '; 1042 | background-color: #222; 1043 | counter-increment: item; 1044 | color: #999; 1045 | width: 20px; 1046 | height: 20px; 1047 | line-height: 20px; 1048 | display: inline-block; 1049 | text-align: center; 1050 | margin: 2px 4px 2px 0; 1051 | } 1052 | 1053 | @media (max-width: 1000px) { 1054 | body { 1055 | margin: 0; 1056 | display: block; 1057 | } 1058 | 1059 | #menu { 1060 | display: none; 1061 | padding-top: 3em; 1062 | width: 450px; 1063 | } 1064 | 1065 | #menu.active { 1066 | position: fixed; 1067 | height: 100%; 1068 | left: 0; 1069 | top: 0; 1070 | right: 300px; 1071 | } 1072 | 1073 | #menu-toggle { 1074 | visibility: visible; 1075 | } 1076 | 1077 | #spec-container { 1078 | padding: 0 5px; 1079 | } 1080 | 1081 | #references-pane-spacer { 1082 | display: none; 1083 | } 1084 | } 1085 | 1086 | @media only screen and (max-width: 800px) { 1087 | #menu { 1088 | width: 100%; 1089 | } 1090 | 1091 | h1 .secnum:empty { 1092 | margin: 0; 1093 | padding: 0; 1094 | } 1095 | } 1096 | 1097 | /* Toolbox */ 1098 | .toolbox-container { 1099 | position: absolute; 1100 | display: none; 1101 | padding-bottom: 7px; 1102 | } 1103 | 1104 | .toolbox-container.active { 1105 | display: inline-block; 1106 | } 1107 | 1108 | .toolbox { 1109 | position: relative; 1110 | background: #ddd; 1111 | border: 1px solid #aaa; 1112 | color: #eee; 1113 | padding: 5px; 1114 | border-radius: 3px; 1115 | } 1116 | 1117 | .toolbox a { 1118 | text-decoration: none; 1119 | padding: 0 5px; 1120 | } 1121 | 1122 | .toolbox a:hover { 1123 | text-decoration: underline; 1124 | } 1125 | 1126 | .toolbox:after, 1127 | .toolbox:before { 1128 | top: 100%; 1129 | left: 15px; 1130 | border: solid transparent; 1131 | content: ' '; 1132 | height: 0; 1133 | width: 0; 1134 | position: absolute; 1135 | pointer-events: none; 1136 | } 1137 | 1138 | .toolbox:after { 1139 | border-color: rgba(0, 0, 0, 0); 1140 | border-top-color: #ddd; 1141 | border-width: 10px; 1142 | margin-left: -10px; 1143 | } 1144 | .toolbox:before { 1145 | border-color: rgba(204, 204, 204, 0); 1146 | border-top-color: #aaa; 1147 | border-width: 12px; 1148 | margin-left: -12px; 1149 | } 1150 | 1151 | #references-pane-container { 1152 | position: fixed; 1153 | bottom: 0; 1154 | left: 0; 1155 | right: 0; 1156 | height: 250px; 1157 | display: none; 1158 | background-color: #ddd; 1159 | z-index: 1; 1160 | } 1161 | 1162 | #references-pane-table-container { 1163 | overflow-x: hidden; 1164 | overflow-y: auto; 1165 | } 1166 | 1167 | #references-pane { 1168 | flex-grow: 1; 1169 | overflow: hidden; 1170 | display: flex; 1171 | flex-direction: column; 1172 | } 1173 | 1174 | #references-pane-container.active { 1175 | display: flex; 1176 | } 1177 | 1178 | #references-pane-close:after { 1179 | content: '✖'; 1180 | float: right; 1181 | cursor: pointer; 1182 | } 1183 | 1184 | #references-pane table tbody { 1185 | vertical-align: baseline; 1186 | } 1187 | 1188 | #references-pane table tr td:first-child { 1189 | text-align: right; 1190 | padding-right: 5px; 1191 | } 1192 | 1193 | @media print { 1194 | #menu-toggle { 1195 | display: none; 1196 | } 1197 | } 1198 | 1199 | [normative-optional], 1200 | [legacy] { 1201 | border-left: 5px solid #ff6600; 1202 | padding: 0.5em; 1203 | display: block; 1204 | background: #ffeedd; 1205 | } 1206 | 1207 | .clause-attributes-tag { 1208 | text-transform: uppercase; 1209 | color: #884400; 1210 | } 1211 | 1212 | .clause-attributes-tag a { 1213 | color: #884400; 1214 | } 1215 | 1216 | /* Shortcuts help dialog */ 1217 | 1218 | #shortcuts-help { 1219 | position: fixed; 1220 | left: 5%; 1221 | margin: 0 auto; 1222 | right: 5%; 1223 | z-index: 10; 1224 | top: 10%; 1225 | top: calc(5vw + 5vh); 1226 | padding: 30px 90px; 1227 | max-width: 500px; 1228 | outline: solid 10000px rgba(255, 255, 255, 0.6); 1229 | border-radius: 5px; 1230 | border-width: 1px 1px 0 1px; 1231 | background-color: #ddd; 1232 | display: none; 1233 | } 1234 | 1235 | #shortcuts-help.active { 1236 | display: block; 1237 | } 1238 | 1239 | #shortcuts-help ul { 1240 | padding: 0; 1241 | } 1242 | 1243 | #shortcuts-help li { 1244 | display: flex; 1245 | justify-content: space-between; 1246 | } 1247 | 1248 | #shortcuts-help code { 1249 | padding: 3px 10px; 1250 | border-radius: 3px; 1251 | border-width: 1px 1px 0 1px; 1252 | border-color: #bbb; 1253 | background-color: #eee; 1254 | box-shadow: inset 0 -1px 0 #ccc; 1255 | } 1256 | -------------------------------------------------------------------------------- /docs/ecmarkup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | let sdoBox = { 3 | init() { 4 | this.$alternativeId = null; 5 | this.$outer = document.createElement('div'); 6 | this.$outer.classList.add('toolbox-container'); 7 | this.$container = document.createElement('div'); 8 | this.$container.classList.add('toolbox'); 9 | this.$displayLink = document.createElement('a'); 10 | this.$displayLink.setAttribute('href', '#'); 11 | this.$displayLink.textContent = 'Syntax-Directed Operations'; 12 | this.$displayLink.addEventListener('click', e => { 13 | e.preventDefault(); 14 | e.stopPropagation(); 15 | referencePane.showSDOs(sdoMap[this.$alternativeId] || {}, this.$alternativeId); 16 | }); 17 | this.$container.appendChild(this.$displayLink); 18 | this.$outer.appendChild(this.$container); 19 | document.body.appendChild(this.$outer); 20 | }, 21 | 22 | activate(el) { 23 | clearTimeout(this.deactiveTimeout); 24 | Toolbox.deactivate(); 25 | this.$alternativeId = el.id; 26 | let numSdos = Object.keys(sdoMap[this.$alternativeId] || {}).length; 27 | this.$displayLink.textContent = 'Syntax-Directed Operations (' + numSdos + ')'; 28 | this.$outer.classList.add('active'); 29 | let top = el.offsetTop - this.$outer.offsetHeight; 30 | let left = el.offsetLeft + 50 - 10; // 50px = padding-left(=75px) + text-indent(=-25px) 31 | this.$outer.setAttribute('style', 'left: ' + left + 'px; top: ' + top + 'px'); 32 | if (top < document.body.scrollTop) { 33 | this.$container.scrollIntoView(); 34 | } 35 | }, 36 | 37 | deactivate() { 38 | clearTimeout(this.deactiveTimeout); 39 | this.$outer.classList.remove('active'); 40 | }, 41 | }; 42 | 43 | document.addEventListener('DOMContentLoaded', () => { 44 | if (typeof sdoMap == 'undefined') { 45 | console.error('could not find sdo map'); 46 | return; 47 | } 48 | sdoBox.init(); 49 | 50 | let insideTooltip = false; 51 | sdoBox.$outer.addEventListener('pointerenter', () => { 52 | insideTooltip = true; 53 | }); 54 | sdoBox.$outer.addEventListener('pointerleave', () => { 55 | insideTooltip = false; 56 | sdoBox.deactivate(); 57 | }); 58 | 59 | sdoBox.deactiveTimeout = null; 60 | [].forEach.call(document.querySelectorAll('emu-grammar[type=definition] emu-rhs'), node => { 61 | node.addEventListener('pointerenter', function () { 62 | sdoBox.activate(this); 63 | }); 64 | 65 | node.addEventListener('pointerleave', () => { 66 | sdoBox.deactiveTimeout = setTimeout(() => { 67 | if (!insideTooltip) { 68 | sdoBox.deactivate(); 69 | } 70 | }, 500); 71 | }); 72 | }); 73 | 74 | document.addEventListener( 75 | 'keydown', 76 | debounce(e => { 77 | if (e.code === 'Escape') { 78 | sdoBox.deactivate(); 79 | } 80 | }) 81 | ); 82 | }); 83 | 84 | 'use strict'; 85 | function Search(menu) { 86 | this.menu = menu; 87 | this.$search = document.getElementById('menu-search'); 88 | this.$searchBox = document.getElementById('menu-search-box'); 89 | this.$searchResults = document.getElementById('menu-search-results'); 90 | 91 | this.loadBiblio(); 92 | 93 | document.addEventListener('keydown', this.documentKeydown.bind(this)); 94 | 95 | this.$searchBox.addEventListener( 96 | 'keydown', 97 | debounce(this.searchBoxKeydown.bind(this), { stopPropagation: true }) 98 | ); 99 | this.$searchBox.addEventListener( 100 | 'keyup', 101 | debounce(this.searchBoxKeyup.bind(this), { stopPropagation: true }) 102 | ); 103 | 104 | // Perform an initial search if the box is not empty. 105 | if (this.$searchBox.value) { 106 | this.search(this.$searchBox.value); 107 | } 108 | } 109 | 110 | Search.prototype.loadBiblio = function () { 111 | if (typeof biblio === 'undefined') { 112 | console.error('could not find biblio'); 113 | this.biblio = { refToClause: {}, entries: [] }; 114 | } else { 115 | this.biblio = biblio; 116 | this.biblio.clauses = this.biblio.entries.filter(e => e.type === 'clause'); 117 | this.biblio.byId = this.biblio.entries.reduce((map, entry) => { 118 | map[entry.id] = entry; 119 | return map; 120 | }, {}); 121 | let refParentClause = Object.create(null); 122 | this.biblio.refParentClause = refParentClause; 123 | let refsByClause = this.biblio.refsByClause; 124 | Object.keys(refsByClause).forEach(clause => { 125 | refsByClause[clause].forEach(ref => { 126 | refParentClause[ref] = clause; 127 | }); 128 | }); 129 | } 130 | }; 131 | 132 | Search.prototype.documentKeydown = function (e) { 133 | if (e.key === '/') { 134 | e.preventDefault(); 135 | e.stopPropagation(); 136 | this.triggerSearch(); 137 | } 138 | }; 139 | 140 | Search.prototype.searchBoxKeydown = function (e) { 141 | e.stopPropagation(); 142 | e.preventDefault(); 143 | if (e.keyCode === 191 && e.target.value.length === 0) { 144 | e.preventDefault(); 145 | } else if (e.keyCode === 13) { 146 | e.preventDefault(); 147 | this.selectResult(); 148 | } 149 | }; 150 | 151 | Search.prototype.searchBoxKeyup = function (e) { 152 | if (e.keyCode === 13 || e.keyCode === 9) { 153 | return; 154 | } 155 | 156 | this.search(e.target.value); 157 | }; 158 | 159 | Search.prototype.triggerSearch = function () { 160 | if (this.menu.isVisible()) { 161 | this._closeAfterSearch = false; 162 | } else { 163 | this._closeAfterSearch = true; 164 | this.menu.show(); 165 | } 166 | 167 | this.$searchBox.focus(); 168 | this.$searchBox.select(); 169 | }; 170 | // bit 12 - Set if the result starts with searchString 171 | // bits 8-11: 8 - number of chunks multiplied by 2 if cases match, otherwise 1. 172 | // bits 1-7: 127 - length of the entry 173 | // General scheme: prefer case sensitive matches with fewer chunks, and otherwise 174 | // prefer shorter matches. 175 | function relevance(result) { 176 | let relevance = 0; 177 | 178 | relevance = Math.max(0, 8 - result.match.chunks) << 7; 179 | 180 | if (result.match.caseMatch) { 181 | relevance *= 2; 182 | } 183 | 184 | if (result.match.prefix) { 185 | relevance += 2048; 186 | } 187 | 188 | relevance += Math.max(0, 255 - result.key.length); 189 | 190 | return relevance; 191 | } 192 | 193 | Search.prototype.search = function (searchString) { 194 | if (searchString === '') { 195 | this.displayResults([]); 196 | this.hideSearch(); 197 | return; 198 | } else { 199 | this.showSearch(); 200 | } 201 | 202 | if (searchString.length === 1) { 203 | this.displayResults([]); 204 | return; 205 | } 206 | 207 | let results; 208 | 209 | if (/^[\d.]*$/.test(searchString)) { 210 | results = this.biblio.clauses 211 | .filter(clause => clause.number.substring(0, searchString.length) === searchString) 212 | .map(clause => ({ key: getKey(clause), entry: clause })); 213 | } else { 214 | results = []; 215 | 216 | for (let i = 0; i < this.biblio.entries.length; i++) { 217 | let entry = this.biblio.entries[i]; 218 | let key = getKey(entry); 219 | if (!key) { 220 | // biblio entries without a key aren't searchable 221 | continue; 222 | } 223 | 224 | let match = fuzzysearch(searchString, key); 225 | if (match) { 226 | results.push({ key, entry, match }); 227 | } 228 | } 229 | 230 | results.forEach(result => { 231 | result.relevance = relevance(result, searchString); 232 | }); 233 | 234 | results = results.sort((a, b) => b.relevance - a.relevance); 235 | } 236 | 237 | if (results.length > 50) { 238 | results = results.slice(0, 50); 239 | } 240 | 241 | this.displayResults(results); 242 | }; 243 | Search.prototype.hideSearch = function () { 244 | this.$search.classList.remove('active'); 245 | }; 246 | 247 | Search.prototype.showSearch = function () { 248 | this.$search.classList.add('active'); 249 | }; 250 | 251 | Search.prototype.selectResult = function () { 252 | let $first = this.$searchResults.querySelector('li:first-child a'); 253 | 254 | if ($first) { 255 | document.location = $first.getAttribute('href'); 256 | } 257 | 258 | this.$searchBox.value = ''; 259 | this.$searchBox.blur(); 260 | this.displayResults([]); 261 | this.hideSearch(); 262 | 263 | if (this._closeAfterSearch) { 264 | this.menu.hide(); 265 | } 266 | }; 267 | 268 | Search.prototype.displayResults = function (results) { 269 | if (results.length > 0) { 270 | this.$searchResults.classList.remove('no-results'); 271 | 272 | let html = ''; 307 | 308 | this.$searchResults.innerHTML = html; 309 | } else { 310 | this.$searchResults.innerHTML = ''; 311 | this.$searchResults.classList.add('no-results'); 312 | } 313 | }; 314 | 315 | function getKey(item) { 316 | if (item.key) { 317 | return item.key; 318 | } 319 | switch (item.type) { 320 | case 'clause': 321 | return item.title || item.titleHTML; 322 | case 'production': 323 | return item.name; 324 | case 'op': 325 | return item.aoid; 326 | case 'term': 327 | return item.term; 328 | case 'table': 329 | case 'figure': 330 | case 'example': 331 | case 'note': 332 | return item.caption; 333 | case 'step': 334 | return item.id; 335 | default: 336 | throw new Error("Can't get key for " + item.type); 337 | } 338 | } 339 | 340 | function Menu() { 341 | this.$toggle = document.getElementById('menu-toggle'); 342 | this.$menu = document.getElementById('menu'); 343 | this.$toc = document.querySelector('menu-toc > ol'); 344 | this.$pins = document.querySelector('#menu-pins'); 345 | this.$pinList = document.getElementById('menu-pins-list'); 346 | this.$toc = document.querySelector('#menu-toc > ol'); 347 | this.$specContainer = document.getElementById('spec-container'); 348 | this.search = new Search(this); 349 | 350 | this._pinnedIds = {}; 351 | this.loadPinEntries(); 352 | 353 | // toggle menu 354 | this.$toggle.addEventListener('click', this.toggle.bind(this)); 355 | 356 | // keydown events for pinned clauses 357 | document.addEventListener('keydown', this.documentKeydown.bind(this)); 358 | 359 | // toc expansion 360 | let tocItems = this.$menu.querySelectorAll('#menu-toc li'); 361 | for (let i = 0; i < tocItems.length; i++) { 362 | let $item = tocItems[i]; 363 | $item.addEventListener('click', event => { 364 | $item.classList.toggle('active'); 365 | event.stopPropagation(); 366 | }); 367 | } 368 | 369 | // close toc on toc item selection 370 | let tocLinks = this.$menu.querySelectorAll('#menu-toc li > a'); 371 | for (let i = 0; i < tocLinks.length; i++) { 372 | let $link = tocLinks[i]; 373 | $link.addEventListener('click', event => { 374 | this.toggle(); 375 | event.stopPropagation(); 376 | }); 377 | } 378 | 379 | // update active clause on scroll 380 | window.addEventListener('scroll', debounce(this.updateActiveClause.bind(this))); 381 | this.updateActiveClause(); 382 | 383 | // prevent menu scrolling from scrolling the body 384 | this.$toc.addEventListener('wheel', e => { 385 | let target = e.currentTarget; 386 | let offTop = e.deltaY < 0 && target.scrollTop === 0; 387 | if (offTop) { 388 | e.preventDefault(); 389 | } 390 | let offBottom = e.deltaY > 0 && target.offsetHeight + target.scrollTop >= target.scrollHeight; 391 | 392 | if (offBottom) { 393 | e.preventDefault(); 394 | } 395 | }); 396 | } 397 | 398 | Menu.prototype.documentKeydown = function (e) { 399 | e.stopPropagation(); 400 | if (e.keyCode === 80) { 401 | this.togglePinEntry(); 402 | } else if (e.keyCode > 48 && e.keyCode < 58) { 403 | this.selectPin(e.keyCode - 49); 404 | } 405 | }; 406 | 407 | Menu.prototype.updateActiveClause = function () { 408 | this.setActiveClause(findActiveClause(this.$specContainer)); 409 | }; 410 | 411 | Menu.prototype.setActiveClause = function (clause) { 412 | this.$activeClause = clause; 413 | this.revealInToc(this.$activeClause); 414 | }; 415 | 416 | Menu.prototype.revealInToc = function (path) { 417 | let current = this.$toc.querySelectorAll('li.revealed'); 418 | for (let i = 0; i < current.length; i++) { 419 | current[i].classList.remove('revealed'); 420 | current[i].classList.remove('revealed-leaf'); 421 | } 422 | 423 | current = this.$toc; 424 | let index = 0; 425 | outer: while (index < path.length) { 426 | let children = current.children; 427 | for (let i = 0; i < children.length; i++) { 428 | if ('#' + path[index].id === children[i].children[1].hash) { 429 | children[i].classList.add('revealed'); 430 | if (index === path.length - 1) { 431 | children[i].classList.add('revealed-leaf'); 432 | let rect = children[i].getBoundingClientRect(); 433 | // this.$toc.getBoundingClientRect().top; 434 | let tocRect = this.$toc.getBoundingClientRect(); 435 | if (rect.top + 10 > tocRect.bottom) { 436 | this.$toc.scrollTop = 437 | this.$toc.scrollTop + (rect.top - tocRect.bottom) + (rect.bottom - rect.top); 438 | } else if (rect.top < tocRect.top) { 439 | this.$toc.scrollTop = this.$toc.scrollTop - (tocRect.top - rect.top); 440 | } 441 | } 442 | current = children[i].querySelector('ol'); 443 | index++; 444 | continue outer; 445 | } 446 | } 447 | console.log('could not find location in table of contents', path); 448 | break; 449 | } 450 | }; 451 | 452 | function findActiveClause(root, path) { 453 | let clauses = getChildClauses(root); 454 | path = path || []; 455 | 456 | for (let $clause of clauses) { 457 | let rect = $clause.getBoundingClientRect(); 458 | let $header = $clause.querySelector('h1'); 459 | let marginTop = Math.max( 460 | parseInt(getComputedStyle($clause)['margin-top']), 461 | parseInt(getComputedStyle($header)['margin-top']) 462 | ); 463 | 464 | if (rect.top - marginTop <= 1 && rect.bottom > 0) { 465 | return findActiveClause($clause, path.concat($clause)) || path; 466 | } 467 | } 468 | 469 | return path; 470 | } 471 | 472 | function* getChildClauses(root) { 473 | for (let el of root.children) { 474 | switch (el.nodeName) { 475 | // descend into 476 | case 'EMU-IMPORT': 477 | yield* getChildClauses(el); 478 | break; 479 | 480 | // accept , , and 481 | case 'EMU-CLAUSE': 482 | case 'EMU-INTRO': 483 | case 'EMU-ANNEX': 484 | yield el; 485 | } 486 | } 487 | } 488 | 489 | Menu.prototype.toggle = function () { 490 | this.$menu.classList.toggle('active'); 491 | }; 492 | 493 | Menu.prototype.show = function () { 494 | this.$menu.classList.add('active'); 495 | }; 496 | 497 | Menu.prototype.hide = function () { 498 | this.$menu.classList.remove('active'); 499 | }; 500 | 501 | Menu.prototype.isVisible = function () { 502 | return this.$menu.classList.contains('active'); 503 | }; 504 | 505 | Menu.prototype.showPins = function () { 506 | this.$pins.classList.add('active'); 507 | }; 508 | 509 | Menu.prototype.hidePins = function () { 510 | this.$pins.classList.remove('active'); 511 | }; 512 | 513 | Menu.prototype.addPinEntry = function (id) { 514 | let entry = this.search.biblio.byId[id]; 515 | if (!entry) { 516 | // id was deleted after pin (or something) so remove it 517 | delete this._pinnedIds[id]; 518 | this.persistPinEntries(); 519 | return; 520 | } 521 | 522 | if (entry.type === 'clause') { 523 | let prefix; 524 | if (entry.number) { 525 | prefix = entry.number + ' '; 526 | } else { 527 | prefix = ''; 528 | } 529 | // prettier-ignore 530 | this.$pinList.innerHTML += `
  • ${prefix}${entry.titleHTML}
  • `; 531 | } else { 532 | this.$pinList.innerHTML += `
  • ${getKey(entry)}
  • `; 533 | } 534 | 535 | if (Object.keys(this._pinnedIds).length === 0) { 536 | this.showPins(); 537 | } 538 | this._pinnedIds[id] = true; 539 | this.persistPinEntries(); 540 | }; 541 | 542 | Menu.prototype.removePinEntry = function (id) { 543 | let item = this.$pinList.querySelector(`a[href="${makeLinkToId(id)}"]`).parentNode; 544 | this.$pinList.removeChild(item); 545 | delete this._pinnedIds[id]; 546 | if (Object.keys(this._pinnedIds).length === 0) { 547 | this.hidePins(); 548 | } 549 | 550 | this.persistPinEntries(); 551 | }; 552 | 553 | Menu.prototype.persistPinEntries = function () { 554 | try { 555 | if (!window.localStorage) return; 556 | } catch (e) { 557 | return; 558 | } 559 | 560 | localStorage.pinEntries = JSON.stringify(Object.keys(this._pinnedIds)); 561 | }; 562 | 563 | Menu.prototype.loadPinEntries = function () { 564 | try { 565 | if (!window.localStorage) return; 566 | } catch (e) { 567 | return; 568 | } 569 | 570 | let pinsString = window.localStorage.pinEntries; 571 | if (!pinsString) return; 572 | let pins = JSON.parse(pinsString); 573 | for (let i = 0; i < pins.length; i++) { 574 | this.addPinEntry(pins[i]); 575 | } 576 | }; 577 | 578 | Menu.prototype.togglePinEntry = function (id) { 579 | if (!id) { 580 | id = this.$activeClause[this.$activeClause.length - 1].id; 581 | } 582 | 583 | if (this._pinnedIds[id]) { 584 | this.removePinEntry(id); 585 | } else { 586 | this.addPinEntry(id); 587 | } 588 | }; 589 | 590 | Menu.prototype.selectPin = function (num) { 591 | document.location = this.$pinList.children[num].children[0].href; 592 | }; 593 | 594 | let menu; 595 | 596 | document.addEventListener('DOMContentLoaded', init); 597 | 598 | function debounce(fn, opts) { 599 | opts = opts || {}; 600 | let timeout; 601 | return function (e) { 602 | if (opts.stopPropagation) { 603 | e.stopPropagation(); 604 | } 605 | let args = arguments; 606 | if (timeout) { 607 | clearTimeout(timeout); 608 | } 609 | timeout = setTimeout(() => { 610 | timeout = null; 611 | fn.apply(this, args); 612 | }, 150); 613 | }; 614 | } 615 | 616 | let CLAUSE_NODES = ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX']; 617 | function findContainer($elem) { 618 | let parentClause = $elem.parentNode; 619 | while (parentClause && CLAUSE_NODES.indexOf(parentClause.nodeName) === -1) { 620 | parentClause = parentClause.parentNode; 621 | } 622 | return parentClause; 623 | } 624 | 625 | function findLocalReferences(parentClause, name) { 626 | let vars = parentClause.querySelectorAll('var'); 627 | let references = []; 628 | 629 | for (let i = 0; i < vars.length; i++) { 630 | let $var = vars[i]; 631 | 632 | if ($var.innerHTML === name) { 633 | references.push($var); 634 | } 635 | } 636 | 637 | return references; 638 | } 639 | 640 | let REFERENCED_CLASSES = Array.from({ length: 7 }, (x, i) => `referenced${i}`); 641 | function chooseHighlightIndex(parentClause) { 642 | let counts = REFERENCED_CLASSES.map($class => parentClause.getElementsByClassName($class).length); 643 | // Find the earliest index with the lowest count. 644 | let minCount = Infinity; 645 | let index = null; 646 | for (let i = 0; i < counts.length; i++) { 647 | if (counts[i] < minCount) { 648 | minCount = counts[i]; 649 | index = i; 650 | } 651 | } 652 | return index; 653 | } 654 | 655 | function toggleFindLocalReferences($elem) { 656 | let parentClause = findContainer($elem); 657 | let references = findLocalReferences(parentClause, $elem.innerHTML); 658 | if ($elem.classList.contains('referenced')) { 659 | references.forEach($reference => { 660 | $reference.classList.remove('referenced', ...REFERENCED_CLASSES); 661 | }); 662 | } else { 663 | let index = chooseHighlightIndex(parentClause); 664 | references.forEach($reference => { 665 | $reference.classList.add('referenced', `referenced${index}`); 666 | }); 667 | } 668 | } 669 | 670 | function installFindLocalReferences() { 671 | document.addEventListener('click', e => { 672 | if (e.target.nodeName === 'VAR') { 673 | toggleFindLocalReferences(e.target); 674 | } 675 | }); 676 | } 677 | 678 | document.addEventListener('DOMContentLoaded', installFindLocalReferences); 679 | 680 | // The following license applies to the fuzzysearch function 681 | // The MIT License (MIT) 682 | // Copyright © 2015 Nicolas Bevacqua 683 | // Copyright © 2016 Brian Terlson 684 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 685 | // this software and associated documentation files (the "Software"), to deal in 686 | // the Software without restriction, including without limitation the rights to 687 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 688 | // the Software, and to permit persons to whom the Software is furnished to do so, 689 | // subject to the following conditions: 690 | 691 | // The above copyright notice and this permission notice shall be included in all 692 | // copies or substantial portions of the Software. 693 | 694 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 695 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 696 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 697 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 698 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 699 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 700 | function fuzzysearch(searchString, haystack, caseInsensitive) { 701 | let tlen = haystack.length; 702 | let qlen = searchString.length; 703 | let chunks = 1; 704 | let finding = false; 705 | 706 | if (qlen > tlen) { 707 | return false; 708 | } 709 | 710 | if (qlen === tlen) { 711 | if (searchString === haystack) { 712 | return { caseMatch: true, chunks: 1, prefix: true }; 713 | } else if (searchString.toLowerCase() === haystack.toLowerCase()) { 714 | return { caseMatch: false, chunks: 1, prefix: true }; 715 | } else { 716 | return false; 717 | } 718 | } 719 | 720 | let j = 0; 721 | outer: for (let i = 0; i < qlen; i++) { 722 | let nch = searchString[i]; 723 | while (j < tlen) { 724 | let targetChar = haystack[j++]; 725 | if (targetChar === nch) { 726 | finding = true; 727 | continue outer; 728 | } 729 | if (finding) { 730 | chunks++; 731 | finding = false; 732 | } 733 | } 734 | 735 | if (caseInsensitive) { 736 | return false; 737 | } 738 | 739 | return fuzzysearch(searchString.toLowerCase(), haystack.toLowerCase(), true); 740 | } 741 | 742 | return { caseMatch: !caseInsensitive, chunks, prefix: j <= qlen }; 743 | } 744 | 745 | let referencePane = { 746 | init() { 747 | this.$container = document.createElement('div'); 748 | this.$container.setAttribute('id', 'references-pane-container'); 749 | 750 | let $spacer = document.createElement('div'); 751 | $spacer.setAttribute('id', 'references-pane-spacer'); 752 | $spacer.classList.add('menu-spacer'); 753 | 754 | this.$pane = document.createElement('div'); 755 | this.$pane.setAttribute('id', 'references-pane'); 756 | 757 | this.$container.appendChild($spacer); 758 | this.$container.appendChild(this.$pane); 759 | 760 | this.$header = document.createElement('div'); 761 | this.$header.classList.add('menu-pane-header'); 762 | this.$headerText = document.createElement('span'); 763 | this.$header.appendChild(this.$headerText); 764 | this.$headerRefId = document.createElement('a'); 765 | this.$header.appendChild(this.$headerRefId); 766 | this.$closeButton = document.createElement('span'); 767 | this.$closeButton.setAttribute('id', 'references-pane-close'); 768 | this.$closeButton.addEventListener('click', () => { 769 | this.deactivate(); 770 | }); 771 | this.$header.appendChild(this.$closeButton); 772 | 773 | this.$pane.appendChild(this.$header); 774 | let tableContainer = document.createElement('div'); 775 | tableContainer.setAttribute('id', 'references-pane-table-container'); 776 | 777 | this.$table = document.createElement('table'); 778 | this.$table.setAttribute('id', 'references-pane-table'); 779 | 780 | this.$tableBody = this.$table.createTBody(); 781 | 782 | tableContainer.appendChild(this.$table); 783 | this.$pane.appendChild(tableContainer); 784 | 785 | menu.$specContainer.appendChild(this.$container); 786 | }, 787 | 788 | activate() { 789 | this.$container.classList.add('active'); 790 | }, 791 | 792 | deactivate() { 793 | this.$container.classList.remove('active'); 794 | this.state = null; 795 | }, 796 | 797 | showReferencesFor(entry) { 798 | this.activate(); 799 | this.state = { type: 'ref', id: entry.id }; 800 | this.$headerText.textContent = 'References to '; 801 | let newBody = document.createElement('tbody'); 802 | let previousId; 803 | let previousCell; 804 | let dupCount = 0; 805 | this.$headerRefId.textContent = '#' + entry.id; 806 | this.$headerRefId.setAttribute('href', makeLinkToId(entry.id)); 807 | this.$headerRefId.style.display = 'inline'; 808 | (entry.referencingIds || []) 809 | .map(id => { 810 | let cid = menu.search.biblio.refParentClause[id]; 811 | let clause = menu.search.biblio.byId[cid]; 812 | if (clause == null) { 813 | throw new Error('could not find clause for id ' + cid); 814 | } 815 | return { id, clause }; 816 | }) 817 | .sort((a, b) => sortByClauseNumber(a.clause, b.clause)) 818 | .forEach(record => { 819 | if (previousId === record.clause.id) { 820 | previousCell.innerHTML += ` (${dupCount + 2})`; 821 | dupCount++; 822 | } else { 823 | let row = newBody.insertRow(); 824 | let cell = row.insertCell(); 825 | cell.innerHTML = record.clause.number; 826 | cell = row.insertCell(); 827 | cell.innerHTML = `${record.clause.titleHTML}`; 828 | previousCell = cell; 829 | previousId = record.clause.id; 830 | dupCount = 0; 831 | } 832 | }, this); 833 | this.$table.removeChild(this.$tableBody); 834 | this.$tableBody = newBody; 835 | this.$table.appendChild(this.$tableBody); 836 | }, 837 | 838 | showSDOs(sdos, alternativeId) { 839 | let rhs = document.getElementById(alternativeId); 840 | let parentName = rhs.parentNode.getAttribute('name'); 841 | let colons = rhs.parentNode.querySelector('emu-geq'); 842 | rhs = rhs.cloneNode(true); 843 | rhs.querySelectorAll('emu-params,emu-constraints').forEach(e => { 844 | e.remove(); 845 | }); 846 | rhs.querySelectorAll('[id]').forEach(e => { 847 | e.removeAttribute('id'); 848 | }); 849 | rhs.querySelectorAll('a').forEach(e => { 850 | e.parentNode.replaceChild(document.createTextNode(e.textContent), e); 851 | }); 852 | 853 | // prettier-ignore 854 | this.$headerText.innerHTML = `Syntax-Directed Operations for
    ${parentName} ${colons.outerHTML} `; 855 | this.$headerText.querySelector('a').append(rhs); 856 | this.showSDOsBody(sdos, alternativeId); 857 | }, 858 | 859 | showSDOsBody(sdos, alternativeId) { 860 | this.activate(); 861 | this.state = { type: 'sdo', id: alternativeId, html: this.$headerText.innerHTML }; 862 | this.$headerRefId.style.display = 'none'; 863 | let newBody = document.createElement('tbody'); 864 | Object.keys(sdos).forEach(sdoName => { 865 | let pair = sdos[sdoName]; 866 | let clause = pair.clause; 867 | let ids = pair.ids; 868 | let first = ids[0]; 869 | let row = newBody.insertRow(); 870 | let cell = row.insertCell(); 871 | cell.innerHTML = clause; 872 | cell = row.insertCell(); 873 | let html = '' + sdoName + ''; 874 | for (let i = 1; i < ids.length; ++i) { 875 | html += ' (' + (i + 1) + ')'; 876 | } 877 | cell.innerHTML = html; 878 | }); 879 | this.$table.removeChild(this.$tableBody); 880 | this.$tableBody = newBody; 881 | this.$table.appendChild(this.$tableBody); 882 | }, 883 | }; 884 | 885 | let Toolbox = { 886 | init() { 887 | this.$outer = document.createElement('div'); 888 | this.$outer.classList.add('toolbox-container'); 889 | this.$container = document.createElement('div'); 890 | this.$container.classList.add('toolbox'); 891 | this.$outer.appendChild(this.$container); 892 | this.$permalink = document.createElement('a'); 893 | this.$permalink.textContent = 'Permalink'; 894 | this.$pinLink = document.createElement('a'); 895 | this.$pinLink.textContent = 'Pin'; 896 | this.$pinLink.setAttribute('href', '#'); 897 | this.$pinLink.addEventListener('click', e => { 898 | e.preventDefault(); 899 | e.stopPropagation(); 900 | menu.togglePinEntry(this.entry.id); 901 | this.$pinLink.textContent = menu._pinnedIds[this.entry.id] ? 'Unpin' : 'Pin'; 902 | }); 903 | 904 | this.$refsLink = document.createElement('a'); 905 | this.$refsLink.setAttribute('href', '#'); 906 | this.$refsLink.addEventListener('click', e => { 907 | e.preventDefault(); 908 | e.stopPropagation(); 909 | referencePane.showReferencesFor(this.entry); 910 | }); 911 | this.$container.appendChild(this.$permalink); 912 | this.$container.appendChild(this.$pinLink); 913 | this.$container.appendChild(this.$refsLink); 914 | document.body.appendChild(this.$outer); 915 | }, 916 | 917 | activate(el, entry, target) { 918 | if (el === this._activeEl) return; 919 | sdoBox.deactivate(); 920 | this.active = true; 921 | this.entry = entry; 922 | this.$pinLink.textContent = menu._pinnedIds[entry.id] ? 'Unpin' : 'Pin'; 923 | this.$outer.classList.add('active'); 924 | this.top = el.offsetTop - this.$outer.offsetHeight; 925 | this.left = el.offsetLeft - 10; 926 | this.$outer.setAttribute('style', 'left: ' + this.left + 'px; top: ' + this.top + 'px'); 927 | this.updatePermalink(); 928 | this.updateReferences(); 929 | this._activeEl = el; 930 | if (this.top < document.body.scrollTop && el === target) { 931 | // don't scroll unless it's a small thing (< 200px) 932 | this.$outer.scrollIntoView(); 933 | } 934 | }, 935 | 936 | updatePermalink() { 937 | this.$permalink.setAttribute('href', makeLinkToId(this.entry.id)); 938 | }, 939 | 940 | updateReferences() { 941 | this.$refsLink.textContent = `References (${(this.entry.referencingIds || []).length})`; 942 | }, 943 | 944 | activateIfMouseOver(e) { 945 | let ref = this.findReferenceUnder(e.target); 946 | if (ref && (!this.active || e.pageY > this._activeEl.offsetTop)) { 947 | let entry = menu.search.biblio.byId[ref.id]; 948 | this.activate(ref.element, entry, e.target); 949 | } else if ( 950 | this.active && 951 | (e.pageY < this.top || e.pageY > this._activeEl.offsetTop + this._activeEl.offsetHeight) 952 | ) { 953 | this.deactivate(); 954 | } 955 | }, 956 | 957 | findReferenceUnder(el) { 958 | while (el) { 959 | let parent = el.parentNode; 960 | if (el.nodeName === 'EMU-RHS' || el.nodeName === 'EMU-PRODUCTION') { 961 | return null; 962 | } 963 | if ( 964 | el.nodeName === 'H1' && 965 | parent.nodeName.match(/EMU-CLAUSE|EMU-ANNEX|EMU-INTRO/) && 966 | parent.id 967 | ) { 968 | return { element: el, id: parent.id }; 969 | } else if (el.nodeName === 'EMU-NT') { 970 | if ( 971 | parent.nodeName === 'EMU-PRODUCTION' && 972 | parent.id && 973 | parent.id[0] !== '_' && 974 | parent.firstElementChild === el 975 | ) { 976 | // return the LHS non-terminal element 977 | return { element: el, id: parent.id }; 978 | } 979 | return null; 980 | } else if ( 981 | el.nodeName.match(/EMU-(?!CLAUSE|XREF|ANNEX|INTRO)|DFN/) && 982 | el.id && 983 | el.id[0] !== '_' 984 | ) { 985 | if ( 986 | el.nodeName === 'EMU-FIGURE' || 987 | el.nodeName === 'EMU-TABLE' || 988 | el.nodeName === 'EMU-EXAMPLE' 989 | ) { 990 | // return the figcaption element 991 | return { element: el.children[0].children[0], id: el.id }; 992 | } else { 993 | return { element: el, id: el.id }; 994 | } 995 | } 996 | el = parent; 997 | } 998 | }, 999 | 1000 | deactivate() { 1001 | this.$outer.classList.remove('active'); 1002 | this._activeEl = null; 1003 | this.active = false; 1004 | }, 1005 | }; 1006 | 1007 | function sortByClauseNumber(clause1, clause2) { 1008 | let c1c = clause1.number.split('.'); 1009 | let c2c = clause2.number.split('.'); 1010 | 1011 | for (let i = 0; i < c1c.length; i++) { 1012 | if (i >= c2c.length) { 1013 | return 1; 1014 | } 1015 | 1016 | let c1 = c1c[i]; 1017 | let c2 = c2c[i]; 1018 | let c1cn = Number(c1); 1019 | let c2cn = Number(c2); 1020 | 1021 | if (Number.isNaN(c1cn) && Number.isNaN(c2cn)) { 1022 | if (c1 > c2) { 1023 | return 1; 1024 | } else if (c1 < c2) { 1025 | return -1; 1026 | } 1027 | } else if (!Number.isNaN(c1cn) && Number.isNaN(c2cn)) { 1028 | return -1; 1029 | } else if (Number.isNaN(c1cn) && !Number.isNaN(c2cn)) { 1030 | return 1; 1031 | } else if (c1cn > c2cn) { 1032 | return 1; 1033 | } else if (c1cn < c2cn) { 1034 | return -1; 1035 | } 1036 | } 1037 | 1038 | if (c1c.length === c2c.length) { 1039 | return 0; 1040 | } 1041 | return -1; 1042 | } 1043 | 1044 | function makeLinkToId(id) { 1045 | let hash = '#' + id; 1046 | if (typeof idToSection === 'undefined' || !idToSection[id]) { 1047 | return hash; 1048 | } 1049 | let targetSec = idToSection[id]; 1050 | return (targetSec === 'index' ? './' : targetSec + '.html') + hash; 1051 | } 1052 | 1053 | function doShortcut(e) { 1054 | if (!(e.target instanceof HTMLElement)) { 1055 | return; 1056 | } 1057 | let target = e.target; 1058 | let name = target.nodeName.toLowerCase(); 1059 | if (name === 'textarea' || name === 'input' || name === 'select' || target.isContentEditable) { 1060 | return; 1061 | } 1062 | if (e.altKey || e.ctrlKey || e.metaKey) { 1063 | return; 1064 | } 1065 | if (e.key === 'm' && usesMultipage) { 1066 | let pathParts = location.pathname.split('/'); 1067 | let hash = location.hash; 1068 | if (pathParts[pathParts.length - 2] === 'multipage') { 1069 | if (hash === '') { 1070 | let sectionName = pathParts[pathParts.length - 1]; 1071 | if (sectionName.endsWith('.html')) { 1072 | sectionName = sectionName.slice(0, -5); 1073 | } 1074 | if (idToSection['sec-' + sectionName] !== undefined) { 1075 | hash = '#sec-' + sectionName; 1076 | } 1077 | } 1078 | location = pathParts.slice(0, -2).join('/') + '/' + hash; 1079 | } else { 1080 | location = 'multipage/' + hash; 1081 | } 1082 | } else if (e.key === 'u') { 1083 | document.documentElement.classList.toggle('show-ao-annotations'); 1084 | } else if (e.key === '?') { 1085 | document.getElementById('shortcuts-help').classList.toggle('active'); 1086 | } 1087 | } 1088 | 1089 | function init() { 1090 | menu = new Menu(); 1091 | let $container = document.getElementById('spec-container'); 1092 | $container.addEventListener( 1093 | 'mouseover', 1094 | debounce(e => { 1095 | Toolbox.activateIfMouseOver(e); 1096 | }) 1097 | ); 1098 | document.addEventListener( 1099 | 'keydown', 1100 | debounce(e => { 1101 | if (e.code === 'Escape') { 1102 | if (Toolbox.active) { 1103 | Toolbox.deactivate(); 1104 | } 1105 | document.getElementById('shortcuts-help').classList.remove('active'); 1106 | } 1107 | }) 1108 | ); 1109 | } 1110 | 1111 | document.addEventListener('keypress', doShortcut); 1112 | 1113 | document.addEventListener('DOMContentLoaded', () => { 1114 | Toolbox.init(); 1115 | referencePane.init(); 1116 | }); 1117 | 1118 | // preserve state during navigation 1119 | 1120 | function getTocPath(li) { 1121 | let path = []; 1122 | let pointer = li; 1123 | while (true) { 1124 | let parent = pointer.parentElement; 1125 | if (parent == null) { 1126 | return null; 1127 | } 1128 | let index = [].indexOf.call(parent.children, pointer); 1129 | if (index == -1) { 1130 | return null; 1131 | } 1132 | path.unshift(index); 1133 | pointer = parent.parentElement; 1134 | if (pointer == null) { 1135 | return null; 1136 | } 1137 | if (pointer.id === 'menu-toc') { 1138 | break; 1139 | } 1140 | if (pointer.tagName !== 'LI') { 1141 | return null; 1142 | } 1143 | } 1144 | return path; 1145 | } 1146 | 1147 | function activateTocPath(path) { 1148 | try { 1149 | let pointer = document.getElementById('menu-toc'); 1150 | for (let index of path) { 1151 | pointer = pointer.querySelector('ol').children[index]; 1152 | } 1153 | pointer.classList.add('active'); 1154 | } catch (e) { 1155 | // pass 1156 | } 1157 | } 1158 | 1159 | function getActiveTocPaths() { 1160 | return [...menu.$menu.querySelectorAll('.active')].map(getTocPath).filter(p => p != null); 1161 | } 1162 | 1163 | function loadStateFromSessionStorage() { 1164 | if (!window.sessionStorage || typeof menu === 'undefined' || window.navigating) { 1165 | return; 1166 | } 1167 | if (sessionStorage.referencePaneState != null) { 1168 | let state = JSON.parse(sessionStorage.referencePaneState); 1169 | if (state != null) { 1170 | if (state.type === 'ref') { 1171 | let entry = menu.search.biblio.byId[state.id]; 1172 | if (entry != null) { 1173 | referencePane.showReferencesFor(entry); 1174 | } 1175 | } else if (state.type === 'sdo') { 1176 | let sdos = sdoMap[state.id]; 1177 | if (sdos != null) { 1178 | referencePane.$headerText.innerHTML = state.html; 1179 | referencePane.showSDOsBody(sdos, state.id); 1180 | } 1181 | } 1182 | delete sessionStorage.referencePaneState; 1183 | } 1184 | } 1185 | 1186 | if (sessionStorage.activeTocPaths != null) { 1187 | document 1188 | .getElementById('menu-toc') 1189 | .querySelectorAll('.active') 1190 | .forEach(e => { 1191 | e.classList.remove('active'); 1192 | }); 1193 | let active = JSON.parse(sessionStorage.activeTocPaths); 1194 | active.forEach(activateTocPath); 1195 | delete sessionStorage.activeTocPaths; 1196 | } 1197 | 1198 | if (sessionStorage.searchValue != null) { 1199 | let value = JSON.parse(sessionStorage.searchValue); 1200 | menu.search.$searchBox.value = value; 1201 | menu.search.search(value); 1202 | delete sessionStorage.searchValue; 1203 | } 1204 | 1205 | if (sessionStorage.tocScroll != null) { 1206 | let tocScroll = JSON.parse(sessionStorage.tocScroll); 1207 | menu.$toc.scrollTop = tocScroll; 1208 | delete sessionStorage.tocScroll; 1209 | } 1210 | } 1211 | 1212 | document.addEventListener('DOMContentLoaded', loadStateFromSessionStorage); 1213 | 1214 | window.addEventListener('pageshow', loadStateFromSessionStorage); 1215 | 1216 | window.addEventListener('beforeunload', () => { 1217 | if (!window.sessionStorage || typeof menu === 'undefined') { 1218 | return; 1219 | } 1220 | sessionStorage.referencePaneState = JSON.stringify(referencePane.state || null); 1221 | sessionStorage.activeTocPaths = JSON.stringify(getActiveTocPaths()); 1222 | sessionStorage.searchValue = JSON.stringify(menu.search.$searchBox.value); 1223 | sessionStorage.tocScroll = JSON.stringify(menu.$toc.scrollTop); 1224 | }); 1225 | 1226 | 'use strict'; 1227 | let decimalBullet = Array.from({ length: 100 }, (a, i) => '' + (i + 1)); 1228 | let alphaBullet = Array.from({ length: 26 }, (a, i) => String.fromCharCode('a'.charCodeAt(0) + i)); 1229 | 1230 | // prettier-ignore 1231 | let romanBullet = ['i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix', 'x', 'xi', 'xii', 'xiii', 'xiv', 'xv', 'xvi', 'xvii', 'xviii', 'xix', 'xx', 'xxi', 'xxii', 'xxiii', 'xxiv', 'xxv']; 1232 | // prettier-ignore 1233 | let bullets = [decimalBullet, alphaBullet, romanBullet, decimalBullet, alphaBullet, romanBullet]; 1234 | 1235 | function addStepNumberText(ol, parentIndex) { 1236 | for (let i = 0; i < ol.children.length; ++i) { 1237 | let child = ol.children[i]; 1238 | let index = parentIndex.concat([i]); 1239 | let applicable = bullets[Math.min(index.length - 1, 5)]; 1240 | let span = document.createElement('span'); 1241 | span.textContent = (applicable[i] || '?') + '. '; 1242 | span.style.fontSize = '0'; 1243 | span.setAttribute('aria-hidden', 'true'); 1244 | child.prepend(span); 1245 | let sublist = child.querySelector('ol'); 1246 | if (sublist != null) { 1247 | addStepNumberText(sublist, index); 1248 | } 1249 | } 1250 | } 1251 | document.addEventListener('DOMContentLoaded', () => { 1252 | document.querySelectorAll('emu-alg > ol').forEach(ol => { 1253 | addStepNumberText(ol, []); 1254 | }); 1255 | }); 1256 | 1257 | let sdoMap = JSON.parse(`{}`); 1258 | let biblio = JSON.parse(`{"refsByClause":{"sec-get-arraybuffer-@@species":["_ref_0"],"sec-makeidempotentarraybufferbytelengthgetter":["_ref_1"],"sec-arraybuffer-length":["_ref_2","_ref_3"],"sec-get-arraybuffer.prototype.maxbytelength":["_ref_4"],"sec-get-arraybuffer.prototype.resizable":["_ref_5"],"sec-arraybuffer.prototype.slice":["_ref_6"],"sec-arraybuffer.prototype.resize":["_ref_7"],"sec-hostgrowsharedarraybuffer":["_ref_8"],"sec-sharedarraybuffer-length":["_ref_9","_ref_10"],"sec-get-sharedarraybuffer.prototype.bytelength":["_ref_11"],"sec-get-sharedarraybuffer.prototype.growable":["_ref_12"],"sec-get-sharedarraybuffer.prototype.maxbytelength":["_ref_13"],"sec-sharedarraybuffer.prototype.grow":["_ref_14"],"sec-sharedarraybuffer.prototype.slice":["_ref_15","_ref_16"],"sec-integer-indexed-exotic-objects-ownpropertykeys":["_ref_17","_ref_18"],"sec-isvalidintegerindex":["_ref_19","_ref_20"],"sec-integerindexedobjectbytelength":["_ref_21"],"sec-integerindexedobjectlength":["_ref_22","_ref_23"],"sec-isarraybufferviewoutofbounds":["_ref_24","_ref_25","_ref_26","_ref_27","_ref_28"],"sec-validatetypedarray":["_ref_29","_ref_30"],"sec-get-%typedarray%.prototype.bytelength":["_ref_31","_ref_32"],"sec-get-%typedarray%.prototype.byteoffset":["_ref_33","_ref_34"],"sec-get-%typedarray%.prototype.length":["_ref_35","_ref_36"],"sec-%typedarray%.prototype.copywithin":["_ref_37","_ref_38","_ref_39","_ref_40","_ref_41"],"sec-%typedarray%.prototype.fill":["_ref_42","_ref_43","_ref_44","_ref_45","_ref_46"],"sec-%typedarray%.prototype.slice":["_ref_47","_ref_48","_ref_49","_ref_50","_ref_51"],"sec-%typedarray%.prototype.subarray":["_ref_52","_ref_53"],"sec-settypedarrayfromtypedarray":["_ref_54","_ref_55","_ref_56","_ref_57","_ref_58"],"sec-initializetypedarrayfromtypedarray":["_ref_59","_ref_60","_ref_61","_ref_62"],"sec-initializetypedarrayfromarraybuffer":["_ref_63","_ref_64"],"sec-getviewbytelength":["_ref_65","_ref_66"],"sec-getviewvalue":["_ref_67","_ref_68"],"sec-setviewvalue":["_ref_69","_ref_70"],"sec-dataview-buffer-byteoffset-bytelength":["_ref_71","_ref_72","_ref_73"],"sec-get-dataview.prototype.bytelength":["_ref_74","_ref_75","_ref_76"],"sec-get-dataview.prototype.byteoffset":["_ref_77","_ref_78"],"sec-atomics.compareexchange":["_ref_79","_ref_80","_ref_81","_ref_82"],"sec-atomics.store":["_ref_83","_ref_84","_ref_85"],"sec-atomicreadmodifywrite":["_ref_86","_ref_87","_ref_88","_ref_89"],"sec-validateatomicaccess":["_ref_90","_ref_91"],"omitted-for-brevity":["_ref_92","_ref_93"]},"entries":[{"type":"clause","id":"intro","titleHTML":"Resizable ArrayBuffer and growable SharedArrayBuffer","number":""},{"type":"op","aoid":"DetachArrayBuffer","refId":"sec-detacharraybuffer"},{"type":"clause","id":"sec-detacharraybuffer","title":"DetachArrayBuffer ( arrayBuffer [ , key ] )","titleHTML":"DetachArrayBuffer ( arrayBuffer [ , key ] )","number":"1.1.1"},{"type":"op","aoid":"AllocateArrayBuffer","refId":"sec-allocatearraybuffer"},{"type":"clause","id":"sec-allocatearraybuffer","title":"AllocateArrayBuffer ( constructor, byteLength [ , maxByteLength ] )","titleHTML":"AllocateArrayBuffer ( constructor, byteLength [ , maxByteLength ] )","number":"1.1.2","referencingIds":["_ref_3","_ref_61"]},{"type":"op","aoid":"ArrayBufferByteLength","refId":"sec-arraybufferlength"},{"type":"clause","id":"sec-arraybufferlength","title":"ArrayBufferByteLength ( arrayBuffer, order )","titleHTML":"ArrayBufferByteLength ( arrayBuffer, order )","number":"1.1.3","referencingIds":["_ref_1","_ref_11","_ref_15","_ref_64","_ref_71","_ref_93"]},{"type":"op","aoid":"MakeIdempotentArrayBufferByteLengthGetter","refId":"sec-makeidempotentarraybufferbytelengthgetter"},{"type":"clause","id":"sec-makeidempotentarraybufferbytelengthgetter","title":"MakeIdempotentArrayBufferByteLengthGetter ( order )","titleHTML":"MakeIdempotentArrayBufferByteLengthGetter ( order )","number":"1.1.4","referencingIds":["_ref_17","_ref_19","_ref_24","_ref_29","_ref_31","_ref_33","_ref_35","_ref_38","_ref_40","_ref_43","_ref_45","_ref_48","_ref_50","_ref_52","_ref_54","_ref_56","_ref_59","_ref_67","_ref_69","_ref_73","_ref_74","_ref_77","_ref_80","_ref_84","_ref_87","_ref_90"]},{"type":"op","aoid":"IsResizableArrayBuffer","refId":"sec-isresizablearraybuffer"},{"type":"clause","id":"sec-isresizablearraybuffer","title":"IsResizableArrayBuffer ( arrayBuffer )","titleHTML":"IsResizableArrayBuffer ( arrayBuffer )","number":"1.1.5","referencingIds":["_ref_4","_ref_5","_ref_12","_ref_13","_ref_23","_ref_63","_ref_66","_ref_72"]},{"type":"op","aoid":"GetArrayBufferMaxByteLengthOption","refId":"sec-getarraybuffermaxbytelengthoption"},{"type":"clause","id":"sec-getarraybuffermaxbytelengthoption","title":"GetArrayBufferMaxByteLengthOption ( options )","titleHTML":"GetArrayBufferMaxByteLengthOption ( options )","number":"1.1.6","referencingIds":["_ref_2","_ref_9"]},{"type":"op","aoid":"HostResizeArrayBuffer","refId":"sec-hostresizearraybuffer"},{"type":"clause","id":"sec-hostresizearraybuffer","title":"HostResizeArrayBuffer ( buffer, newByteLength )","titleHTML":"HostResizeArrayBuffer ( buffer, newByteLength )","number":"1.1.7","referencingIds":["_ref_7","_ref_8"]},{"type":"clause","id":"sec-abstract-operations-for-arraybuffer-objects-mods","titleHTML":"Modifications to Abstract Operations for ArrayBuffer Objects","number":"1.1"},{"type":"clause","id":"sec-arraybuffer-length","title":"ArrayBuffer ( length [ , options ] )","titleHTML":"ArrayBuffer ( length [ , options ] )","number":"1.2.1"},{"type":"clause","id":"sec-arraybuffer-constructor","titleHTML":"The ArrayBuffer Constructor","number":"1.2","referencingIds":["_ref_6","_ref_62"]},{"type":"clause","id":"sec-get-arraybuffer-@@species","titleHTML":"get ArrayBuffer [ @@species ]","number":"1.3.1"},{"type":"clause","id":"sec-get-arraybuffer.prototype.maxbytelength","titleHTML":"get ArrayBuffer.prototype.maxByteLength","number":"1.3.2"},{"type":"clause","id":"sec-get-arraybuffer.prototype.resizable","titleHTML":"get ArrayBuffer.prototype.resizable","number":"1.3.3"},{"type":"clause","id":"sec-arraybuffer.prototype.slice","title":"ArrayBuffer.prototype.slice ( start, end )","titleHTML":"ArrayBuffer.prototype.slice ( start, end )","number":"1.3.4","referencingIds":["_ref_0"]},{"type":"clause","id":"sec-arraybuffer.prototype.resize","title":"ArrayBuffer.prototype.resize ( newLength )","titleHTML":"ArrayBuffer.prototype.resize ( newLength )","number":"1.3.5"},{"type":"clause","id":"sec-properties-of-the-arraybuffer-prototype-object-mods","titleHTML":"Modifications to the Properties of the ArrayBuffer Prototype Object","number":"1.3"},{"type":"clause","id":"sec-arraybuffer-objects-mods","titleHTML":"Modifications to ArrayBuffer Objects","number":"1"},{"type":"op","aoid":"AllocateSharedArrayBuffer","refId":"sec-allocatesharedarraybuffer"},{"type":"clause","id":"sec-allocatesharedarraybuffer","title":"AllocateSharedArrayBuffer ( constructor, byteLength [ , maxByteLength ] )","titleHTML":"AllocateSharedArrayBuffer ( constructor, byteLength [ , maxByteLength ] )","number":"2.1.1","referencingIds":["_ref_10"]},{"type":"op","aoid":"HostGrowSharedArrayBuffer","refId":"sec-hostgrowsharedarraybuffer"},{"type":"clause","id":"sec-hostgrowsharedarraybuffer","title":"HostGrowSharedArrayBuffer ( buffer, newByteLength )","titleHTML":"HostGrowSharedArrayBuffer ( buffer, newByteLength )","number":"2.1.2","referencingIds":["_ref_14"]},{"type":"clause","id":"sec-abstract-operations-for-sharedarraybuffer-objects-mods","titleHTML":"Modifications to Abstract Operations for SharedArrayBuffer Objects","number":"2.1"},{"type":"clause","id":"sec-sharedarraybuffer-length","title":"SharedArrayBuffer ( length [ , options ] )","titleHTML":"SharedArrayBuffer ( length [ , options ] )","number":"2.2.1"},{"type":"clause","id":"sec-sharedarraybuffer-constructor","titleHTML":"The SharedArrayBuffer Constructor","number":"2.2","referencingIds":["_ref_16"]},{"type":"clause","id":"sec-get-sharedarraybuffer.prototype.bytelength","titleHTML":"get SharedArrayBuffer.prototype.byteLength","number":"2.3.1"},{"type":"clause","id":"sec-get-sharedarraybuffer.prototype.growable","titleHTML":"get SharedArrayBuffer.prototype.growable","number":"2.3.2"},{"type":"clause","id":"sec-get-sharedarraybuffer.prototype.maxbytelength","titleHTML":"get SharedArrayBuffer.prototype.maxByteLength","number":"2.3.3"},{"type":"clause","id":"sec-sharedarraybuffer.prototype.grow","title":"SharedArrayBuffer.prototype.grow ( newLength )","titleHTML":"SharedArrayBuffer.prototype.grow ( newLength )","number":"2.3.4"},{"type":"clause","id":"sec-sharedarraybuffer.prototype.slice","title":"SharedArrayBuffer.prototype.slice ( start, end )","titleHTML":"SharedArrayBuffer.prototype.slice ( start, end )","number":"2.3.5"},{"type":"clause","id":"sec-properties-of-the-sharedarraybuffer-prototype-object-mods","titleHTML":"Modifications to the Properties of the SharedArrayBuffer Prototype Object","number":"2.3"},{"type":"clause","id":"sec-sharedarraybuffer-objects-mods","titleHTML":"Modifications to SharedArrayBuffer Objects","number":"2"},{"type":"clause","id":"sec-integer-indexed-exotic-objects-ownpropertykeys","titleHTML":"[[OwnPropertyKeys]] ( )","number":"3.1"},{"type":"op","aoid":"IsValidIntegerIndex","refId":"sec-isvalidintegerindex"},{"type":"clause","id":"sec-isvalidintegerindex","title":"IsValidIntegerIndex ( O, index )","titleHTML":"IsValidIntegerIndex ( O, index )","number":"3.2"},{"type":"op","aoid":"IntegerIndexedObjectByteLength","refId":"sec-integerindexedobjectbytelength"},{"type":"clause","id":"sec-integerindexedobjectbytelength","title":"IntegerIndexedObjectByteLength ( O, getBufferByteLength )","titleHTML":"IntegerIndexedObjectByteLength ( O, getBufferByteLength )","number":"3.3","referencingIds":["_ref_32","_ref_58"]},{"type":"op","aoid":"IntegerIndexedObjectLength","refId":"sec-integerindexedobjectlength"},{"type":"clause","id":"sec-integerindexedobjectlength","title":"IntegerIndexedObjectLength ( O, getBufferByteLength )","titleHTML":"IntegerIndexedObjectLength ( O, getBufferByteLength )","number":"3.4","referencingIds":["_ref_18","_ref_20","_ref_21","_ref_36","_ref_39","_ref_41","_ref_44","_ref_46","_ref_49","_ref_51","_ref_53","_ref_55","_ref_57","_ref_60","_ref_81","_ref_85","_ref_88","_ref_91","_ref_92"]},{"type":"op","aoid":"IsIntegerIndexedObjectOutOfBounds","refId":"sec-isintegerindexedobjectoutofbounds"},{"type":"clause","id":"sec-isintegerindexedobjectoutofbounds","title":"IsIntegerIndexedObjectOutOfBounds ( O, getBufferByteLength )","titleHTML":"IsIntegerIndexedObjectOutOfBounds ( O, getBufferByteLength )","number":"3.5","referencingIds":["_ref_22","_ref_26","_ref_28","_ref_30","_ref_34"]},{"type":"op","aoid":"IsArrayBufferViewOutOfBounds","refId":"sec-isarraybufferviewoutofbounds"},{"type":"clause","id":"sec-isarraybufferviewoutofbounds","title":"IsArrayBufferViewOutOfBounds ( O )","titleHTML":"IsArrayBufferViewOutOfBounds ( O )","number":"3.6"},{"type":"clause","id":"sec-integer-indexed-exotic-objects-mods","titleHTML":"Modifications to Integer-Indexed Exotic Objects","number":"3"},{"type":"op","aoid":"ValidateTypedArray","refId":"sec-validatetypedarray"},{"type":"clause","id":"sec-validatetypedarray","title":"ValidateTypedArray ( O )","titleHTML":"ValidateTypedArray ( O )","number":"4.1.1","referencingIds":["_ref_37","_ref_42","_ref_47"]},{"type":"clause","id":"sec-get-%typedarray%.prototype.bytelength","titleHTML":"get %TypedArray%.prototype.byteLength","number":"4.1.2"},{"type":"clause","id":"sec-get-%typedarray%.prototype.byteoffset","titleHTML":"get %TypedArray%.prototype.byteOffset","number":"4.1.3"},{"type":"clause","id":"sec-get-%typedarray%.prototype.length","titleHTML":"get %TypedArray%.prototype.length","number":"4.1.4"},{"type":"clause","id":"sec-%typedarray%.prototype.copywithin","title":"%TypedArray%.prototype.copyWithin ( target, start [ , end ] )","titleHTML":"%TypedArray%.prototype.copyWithin ( target, start [ , end ] )","number":"4.1.5"},{"type":"clause","id":"sec-%typedarray%.prototype.fill","title":"%TypedArray%.prototype.fill ( value [ , start [ , end ] ] )","titleHTML":"%TypedArray%.prototype.fill ( value [ , start [ , end ] ] )","number":"4.1.6"},{"type":"clause","id":"sec-%typedarray%.prototype.slice","title":"%TypedArray%.prototype.slice ( start, end )","titleHTML":"%TypedArray%.prototype.slice ( start, end )","number":"4.1.7"},{"type":"clause","id":"sec-%typedarray%.prototype.subarray","title":"%TypedArray%.prototype.subarray ( begin, end )","titleHTML":"%TypedArray%.prototype.subarray ( begin, end )","number":"4.1.8"},{"type":"op","aoid":"SetTypedArrayFromTypedArray","refId":"sec-settypedarrayfromtypedarray"},{"type":"clause","id":"sec-settypedarrayfromtypedarray","title":"SetTypedArrayFromTypedArray ( target, targetOffset, source )","titleHTML":"SetTypedArrayFromTypedArray ( target, targetOffset, source )","number":"4.1.9"},{"type":"clause","id":"sec-properties-of-the-%typedarrayprototype%-object-mods","titleHTML":"Modifications to Properties of the %TypedArray.prototype% Object","number":"4.1"},{"type":"op","aoid":"InitializeTypedArrayFromTypedArray","refId":"sec-initializetypedarrayfromtypedarray"},{"type":"clause","id":"sec-initializetypedarrayfromtypedarray","title":"InitializeTypedArrayFromTypedArray ( O, srcArray )","titleHTML":"InitializeTypedArrayFromTypedArray ( O, srcArray )","number":"4.2.1"},{"type":"op","aoid":"InitializeTypedArrayFromArrayBuffer","refId":"sec-initializetypedarrayfromarraybuffer"},{"type":"clause","id":"sec-initializetypedarrayfromarraybuffer","title":"InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset, length )","titleHTML":"InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset, length )","number":"4.2.2"},{"type":"clause","id":"sec-typedarray-constructors-mods","title":"Modifications to the TypedArray Constructors","titleHTML":"Modifications to the TypedArray Constructors","number":"4.2"},{"type":"clause","id":"sec-typedarray-objects-mods","titleHTML":"Modifications to TypedArray Objects","number":"4"},{"type":"op","aoid":"GetViewByteLength","refId":"sec-getviewbytelength"},{"type":"clause","id":"sec-getviewbytelength","title":"GetViewByteLength ( view, getBufferByteLength )","titleHTML":"GetViewByteLength ( view, getBufferByteLength )","number":"5.1.1","referencingIds":["_ref_68","_ref_70","_ref_76"]},{"type":"op","aoid":"IsViewOutOfBounds","refId":"sec-isviewoutofbounds"},{"type":"clause","id":"sec-isviewoutofbounds","title":"IsViewOutOfBounds ( view, getBufferByteLength )","titleHTML":"IsViewOutOfBounds ( view, getBufferByteLength )","number":"5.1.2","referencingIds":["_ref_25","_ref_27","_ref_65","_ref_75","_ref_78"]},{"type":"op","aoid":"GetViewValue","refId":"sec-getviewvalue"},{"type":"clause","id":"sec-getviewvalue","title":"GetViewValue ( view, requestIndex, isLittleEndian, type )","titleHTML":"GetViewValue ( view, requestIndex, isLittleEndian, type )","number":"5.1.3"},{"type":"op","aoid":"SetViewValue","refId":"sec-setviewvalue"},{"type":"clause","id":"sec-setviewvalue","title":"SetViewValue ( view, requestIndex, isLittleEndian, type, value )","titleHTML":"SetViewValue ( view, requestIndex, isLittleEndian, type, value )","number":"5.1.4"},{"type":"clause","id":"sec-abstract-operations-for-dataview-objects-mods","titleHTML":"Modifications to Abstract Operations For DataView Objects","number":"5.1"},{"type":"clause","id":"sec-dataview-buffer-byteoffset-bytelength","title":"DataView ( buffer [ , byteOffset [ , byteLength ] ] )","titleHTML":"DataView ( buffer [ , byteOffset [ , byteLength ] ] )","number":"5.2.1"},{"type":"clause","id":"sec-dataview-constructor-mods","titleHTML":"Modifications to the DataView Constructor","number":"5.2"},{"type":"clause","id":"sec-get-dataview.prototype.bytelength","titleHTML":"get DataView.prototype.byteLength","number":"5.3.1"},{"type":"clause","id":"sec-get-dataview.prototype.byteoffset","titleHTML":"get DataView.prototype.byteOffset","number":"5.3.2"},{"type":"clause","id":"sec-properties-of-the-dataview-prototype-object-mods","titleHTML":"Modifications to Properties of the DataView Prototype Object","number":"5.3"},{"type":"clause","id":"sec-dataview-objects-mods","titleHTML":"Modifications to DataView Objects","number":"5"},{"type":"clause","id":"sec-atomics.compareexchange","title":"Atomics.compareExchange ( typedArray, index, expectedValue, replacementValue )","titleHTML":"Atomics.compareExchange ( typedArray, index, expectedValue, replacementValue )","number":"6.1.1"},{"type":"clause","id":"sec-atomics.store","title":"Atomics.store ( typedArray, index, value )","titleHTML":"Atomics.store ( typedArray, index, value )","number":"6.1.2"},{"type":"clause","id":"sec-properties-of-the-atomics-object-mods","titleHTML":"Modifications to Properties of the Atomics Object","number":"6.1"},{"type":"op","aoid":"AtomicReadModifyWrite","refId":"sec-atomicreadmodifywrite"},{"type":"clause","id":"sec-atomicreadmodifywrite","title":"AtomicReadModifyWrite ( typedArray, index, value, op )","titleHTML":"AtomicReadModifyWrite ( typedArray, index, value, op )","number":"6.2.1"},{"type":"op","aoid":"ValidateAtomicAccess","refId":"sec-validateatomicaccess"},{"type":"clause","id":"sec-validateatomicaccess","title":"ValidateAtomicAccess ( typedArray, requestIndex )","titleHTML":"ValidateAtomicAccess ( typedArray, requestIndex )","number":"6.2.2","referencingIds":["_ref_79","_ref_82","_ref_83","_ref_86","_ref_89"]},{"type":"clause","id":"sec-abstract-operations-for-atomics-mods","titleHTML":"Modifications to Abstract Operations for Atomics","number":"6.2"},{"type":"clause","id":"sec-atomics-mods","titleHTML":"Modifications to Atomics","number":"6"},{"type":"clause","id":"sec-maxbytelength-guidelines","titleHTML":"Resizable ArrayBuffer and growable SharedArrayBuffer Guidelines","number":"7"},{"type":"clause","id":"omitted-for-brevity","titleHTML":"Mechanical Changes Omitted for Brevity","number":"8"}]}`); 1259 | ;let usesMultipage = false -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Resizable ArrayBuffer and growable SharedArrayBuffer 4 | 5 | 6 |

    In preparation of Stage 4, please use ecma262#3116.

    7 | 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "npm run build-loose -- --watch", 5 | "build": "npm run build-loose -- --strict", 6 | "build-loose": "ecmarkup --verbose --load-biblio @tc39/ecma262-biblio --lint-spec spec.html docs/index.html --js-out docs/ecmarkup.js --css-out docs/ecmarkup.css" 7 | }, 8 | "homepage": "https://github.com/tc39/proposal-resizablearraybuffer#readme", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/tc39/proposal-resizablearraybuffer.git" 12 | }, 13 | "license": "MIT", 14 | "devDependencies": { 15 | "ecmarkup": "^16.0.0", 16 | "@tc39/ecma262-biblio": "2.1.2478" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spec.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Resizable ArrayBuffer and growable SharedArrayBuffer 6 | 15 | 16 |

    Resizable ArrayBuffer and growable SharedArrayBuffer

    17 |

    We extend the `ArrayBuffer` and `SharedArrayBuffer` constructors to take an additional maximum byte length, which would construct dynamically resizable and growable array buffers, respectively.

    18 |
    19 | 20 | 21 |

    Modifications to ArrayBuffer Objects

    22 | 23 | 24 |

    Modifications to Abstract Operations for ArrayBuffer Objects

    25 | 26 | 27 |

    28 | DetachArrayBuffer ( 29 | _arrayBuffer_: an ArrayBuffer, 30 | optional _key_: anything, 31 | ): either a normal completion containing ~unused~ or a throw completion 32 |

    33 |
    34 |
    35 | 36 | 1. Assert: IsSharedArrayBuffer(_arrayBuffer_) is *false*. 37 | 1. If _key_ is not present, set _key_ to *undefined*. 38 | 1. If _arrayBuffer_.[[ArrayBufferDetachKey]] is not _key_, throw a *TypeError* exception. 39 | 1. Set _arrayBuffer_.[[ArrayBufferData]] to *null*. 40 | 1. Set _arrayBuffer_.[[ArrayBufferByteLength]] to 0. 41 | 1. Return ~unused~. 42 | 43 | 44 |

    Detaching an ArrayBuffer instance disassociates the Data Block used as its backing store from the instance and sets the byte length of the buffer to 0. No operations defined by this specification use the DetachArrayBuffer abstract operation. However, an ECMAScript host or implementation may define such operations.

    45 |
    46 |
    47 | 48 | 49 |

    50 | AllocateArrayBuffer ( 51 | _constructor_: a constructor, 52 | _byteLength_: a non-negative integer, 53 | optional _maxByteLength_: a non-negative integer or ~empty~, 54 | ): either a normal completion containing an ArrayBuffer or a throw completion 55 |

    56 |
    57 |
    description
    58 |
    It is used to create an ArrayBuffer.
    59 |
    60 | 61 | 1. Let _slots_ be « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] ». 62 | 1. If _maxByteLength_ is present and not ~empty~, then 63 | 1. If _byteLength_ > _maxByteLength_, throw a *RangeError* exception. 64 | 1. Append [[ArrayBufferMaxByteLength]] to _slots_. 65 | 1. Let _obj_ be ? OrdinaryCreateFromConstructor(_constructor_, *"%ArrayBuffer.prototype%"*, « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »_slots_). 66 | 1. Let _block_ be ? CreateByteDataBlock(_byteLength_). 67 | 1. Set _obj_.[[ArrayBufferData]] to _block_. 68 | 1. Set _obj_.[[ArrayBufferByteLength]] to _byteLength_. 69 | 1. If _maxByteLength_ is present and not ~empty~, then 70 | 1. If it is not possible to create a Data Block _block_ consisting of _maxByteLength_ bytes, throw a *RangeError* exception. 71 | 1. NOTE: Resizable ArrayBuffers are designed to be implementable with in-place growth. Implementations reserve the right to throw if, for example, virtual memory cannot be reserved up front. 72 | 1. Set _obj_.[[ArrayBufferMaxByteLength]] to _maxByteLength_. 73 | 1. Return _obj_. 74 | 75 |
    76 | 77 | 78 | 79 |

    80 | ArrayBufferByteLength ( 81 | _arrayBuffer_: an ArrayBuffer or SharedArrayBuffer, 82 | _order_: ~SeqCst~ or ~Unordered~, 83 | ): a non-negative integer 84 |

    85 |
    86 |
    87 | 88 | 1. If IsSharedArrayBuffer(_arrayBuffer_) is *true* and _arrayBuffer_ has an [[ArrayBufferByteLengthData]] internal slot, then 89 | 1. Let _bufferByteLengthBlock_ be _arrayBuffer_.[[ArrayBufferByteLengthData]]. 90 | 1. Return ℝ(GetValueFromBuffer(_bufferByteLengthBlock_, 0, ~BigUint64~, *true*, _order_)). 91 | 1. Assert: IsDetachedBuffer(_arrayBuffer_) is *false*. 92 | 1. Return _arrayBuffer_.[[ArrayBufferByteLength]]. 93 | 94 |
    95 | 96 | 97 |

    98 | MakeIdempotentArrayBufferByteLengthGetter ( 99 | _order_: ~SeqCst~ or ~Unordered~, 100 | ): an Abstract Closure with one parameter 101 |

    102 |
    103 |
    description
    104 |
    The returned Abstract Closure helps ensure that there there is a single shared memory read event of the byte length data block in the calling operation.
    105 |
    106 | 107 | 1. NOTE: The [[ArrayBuffer]] slot is used for editorial clarity only, that a getter should only be used with a single ArrayBuffer. 108 | 1. Let _lengthStorage_ be { [[ArrayBuffer]]: ~empty~, [[ByteLength]]: ~empty~ }. 109 | 1. Let _getter_ be a new Abstract Closure with parameters (_buffer_) that captures _lengthStorage_ and _order_ and performs the following steps when called: 110 | 1. If _lengthStorage_.[[ByteLength]] is ~empty~, then 111 | 1. Assert: _lengthStorage_.[[ArrayBuffer]] is ~empty~. 112 | 1. Set _lengthStorage_.[[ArrayBuffer]] to _buffer_. 113 | 1. Set _lengthStorage_.[[ByteLength]] to ArrayBufferByteLength(_buffer_, _order_). 114 | 1. Assert: SameValue(_lengthStorage_.[[ArrayBuffer]], _buffer_) is *true*. 115 | 1. Return _lengthStorage_.[[ByteLength]]. 116 | 1. Return _getter_. 117 | 118 |
    119 | 120 | 121 |

    122 | IsResizableArrayBuffer ( 123 | _arrayBuffer_: an ArrayBuffer or a SharedArrayBuffer, 124 | ): a Boolean 125 |

    126 |
    127 |
    128 | 129 | 1. If _arrayBuffer_ has an [[ArrayBufferMaxByteLength]] internal slot, return *true*. 130 | 1. Return *false*. 131 | 132 |
    133 | 134 | 135 |

    136 | GetArrayBufferMaxByteLengthOption ( 137 | _options_: an ECMAScript language value, 138 | ): a normal completion containing either a non-negative integer or ~empty~ 139 |

    140 |
    141 |
    142 | 143 | 1. If Type(_options_) is not Object, return ~empty~. 144 | 1. Let _maxByteLength_ be ? Get(_options_, `"maxByteLength"`). 145 | 1. If _maxByteLength_ is *undefined*, return ~empty~. 146 | 1. Return ? ToIndex(_maxByteLength_). 147 | 148 |
    149 | 150 | 151 |

    152 | HostResizeArrayBuffer ( 153 | _buffer_: an ArrayBuffer, 154 | _newByteLength_: a non-negative integer, 155 | ): either a normal completion containing either ~handled~ or ~unhandled~, or a throw completion 156 |

    157 |
    158 |
    description
    159 |
    The host-defined abstract operation HostResizeArrayBuffer takes arguments _buffer_ (an ArrayBuffer object) and _newByteLength_. It gives the host an opportunity to perform implementation-defined resizing of _buffer_. If the host chooses not to handle resizing of _buffer_, it may return ~unhandled~ for the default behaviour.
    160 |
    161 | 162 |

    The implementation of HostResizeArrayBuffer must conform to the following requirements:

    163 |
      164 |
    • The abstract operation must return either NormalCompletion(~handled~), NormalCompletion(~unhandled~), or an abrupt throw completion.
    • 165 |
    • The abstract operation does not detach _buffer_.
    • 166 |
    • If the abstract operation completes normally with ~handled~, _buffer_.[[ArrayBufferByteLength]] is _newByteLength_.
    • 167 |
    168 | 169 |

    The default implementation of HostResizeArrayBuffer is to return ~unhandled~.

    170 |
    171 |
    172 |
    173 | 174 | 175 |

    The ArrayBuffer Constructor

    176 | 177 | 178 |

    ArrayBuffer ( _length_ [ , _options_ ] )

    179 |

    When the `ArrayBuffer` function is called with argument _length_ and optional argument _options_, the following steps are taken:

    180 | 181 | 1. If NewTarget is *undefined*, throw a *TypeError* exception. 182 | 1. Let _byteLength_ be ? ToIndex(_length_). 183 | 1. Let _requestedMaxByteLength_ be ? GetArrayBufferMaxByteLengthOption(_options_). 184 | 1. Return ? AllocateArrayBuffer(NewTarget, _byteLength_, _requestedMaxByteLength_). 185 | 186 |
    187 |
    188 | 189 | 190 |

    Modifications to the Properties of the ArrayBuffer Prototype Object

    191 | 192 | 193 |

    get ArrayBuffer [ @@species ]

    194 |

    `ArrayBuffer[@@species]` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    195 | 196 | 1. Return the *this* value. 197 | 198 |

    The value of the *"name"* property of this function is *"get [Symbol.species]"*.

    199 | 200 |

    ArrayBuffer prototype methods normally use their *this* value's constructor to create a derived object. However, a subclass constructor may over-ride that default behaviour for the method by redefining its @@species property.

    201 |
    202 |
    203 | 204 | 205 | 206 |

    get ArrayBuffer.prototype.maxByteLength

    207 |

    `ArrayBuffer.prototype.maxByteLength` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    208 | 209 | 1. Let _O_ be the *this* value. 210 | 1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferData]]). 211 | 1. If IsSharedArrayBuffer(_O_) is *true*, throw a *TypeError* exception. 212 | 1. If IsDetachedBuffer(_O_) is *true*, return *+0*𝔽. 213 | 1. If IsResizableArrayBuffer(_O_) is *true*, then 214 | 1. Let _length_ be _O_.[[ArrayBufferMaxByteLength]]. 215 | 1. Else, 216 | 1. Let _length_ be _O_.[[ArrayBufferByteLength]]. 217 | 1. Return 𝔽(_length_). 218 | 219 |
    220 | 221 | 222 |

    get ArrayBuffer.prototype.resizable

    223 |

    `ArrayBuffer.prototype.resizable` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    224 | 225 | 1. Let _O_ be the *this* value. 226 | 1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferData]]). 227 | 1. If IsSharedArrayBuffer(_O_) is *true*, throw a *TypeError* exception. 228 | 1. Return IsResizableArrayBuffer(_O_). 229 | 230 |
    231 |
    232 | 233 | 234 |

    ArrayBuffer.prototype.slice ( _start_, _end_ )

    235 |

    This method performs the following steps when called:

    236 | 237 | 1. Let _O_ be the *this* value. 238 | 1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferData]]). 239 | 1. If IsSharedArrayBuffer(_O_) is *true*, throw a *TypeError* exception. 240 | 1. If IsDetachedBuffer(_O_) is *true*, throw a *TypeError* exception. 241 | 1. Let _len_ be _O_.[[ArrayBufferByteLength]]. 242 | 1. Let _relativeStart_ be ? ToIntegerOrInfinity(_start_). 243 | 1. If _relativeStart_ is -∞, let _first_ be 0. 244 | 1. Else if _relativeStart_ < 0, let _first_ be max(_len_ + _relativeStart_, 0). 245 | 1. Else, let _first_ be min(_relativeStart_, _len_). 246 | 1. If _end_ is *undefined*, let _relativeEnd_ be _len_; else let _relativeEnd_ be ? ToIntegerOrInfinity(_end_). 247 | 1. If _relativeEnd_ is -∞, let _final_ be 0. 248 | 1. Else if _relativeEnd_ < 0, let _final_ be max(_len_ + _relativeEnd_, 0). 249 | 1. Else, let _final_ be min(_relativeEnd_, _len_). 250 | 1. Let _newLen_ be max(_final_ - _first_, 0). 251 | 1. Let _ctor_ be ? SpeciesConstructor(_O_, %ArrayBuffer%). 252 | 1. Let _new_ be ? Construct(_ctor_, « 𝔽(_newLen_) »). 253 | 1. Perform ? RequireInternalSlot(_new_, [[ArrayBufferData]]). 254 | 1. If IsSharedArrayBuffer(_new_) is *true*, throw a *TypeError* exception. 255 | 1. If IsDetachedBuffer(_new_) is *true*, throw a *TypeError* exception. 256 | 1. If SameValue(_new_, _O_) is *true*, throw a *TypeError* exception. 257 | 1. If _new_.[[ArrayBufferByteLength]] < _newLen_, throw a *TypeError* exception. 258 | 1. NOTE: Side-effects of the above steps may have detached or resized _O_. 259 | 1. If IsDetachedBuffer(_O_) is *true*, throw a *TypeError* exception. 260 | 1. Let _fromBuf_ be _O_.[[ArrayBufferData]]. 261 | 1. Let _toBuf_ be _new_.[[ArrayBufferData]]. 262 | 1. If _first_ < _O_.[[ArrayBufferByteLength]], then 263 | 1. Perform CopyDataBlockBytes(_toBuf_, 0, _fromBuf_, _first_, min(_O_.[[ArrayBufferByteLength]], _newLen_)). 264 | 1. Return _new_. 265 | 266 |
    267 | 268 | 269 | 270 |

    ArrayBuffer.prototype.resize ( _newLength_ )

    271 |

    The following steps are taken:

    272 | 273 | 1. Let _O_ be the *this* value. 274 | 1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferMaxByteLength]]). 275 | 1. If IsSharedArrayBuffer(_O_) is *true*, throw a *TypeError* exception. 276 | 1. If IsDetachedBuffer(_O_) is *true*, throw a *TypeError* exception. 277 | 1. Let _newByteLength_ be ? ToIntegerOrInfinity(_newLength_). 278 | 1. If _newByteLength_ < 0 or _newByteLength_ > _O_.[[ArrayBufferMaxByteLength]], throw a *RangeError* exception. 279 | 1. Let _hostHandled_ be ? HostResizeArrayBuffer(_O_, _newByteLength_). 280 | 1. If _hostHandled_ is ~handled~, return *undefined*. 281 | 1. Let _oldBlock_ be _O_.[[ArrayBufferData]]. 282 | 1. Let _newBlock_ be ? CreateByteDataBlock(_newByteLength_). 283 | 1. Let _copyLength_ be min(_newByteLength_, _O_.[[ArrayBufferByteLength]]). 284 | 1. Perform CopyDataBlockBytes(_newBlock_, 0, _oldBlock_, 0, _copyLength_). 285 | 1. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations reserve the right to implement this method as in-place growth or shrinkage. 286 | 1. Set _O_.[[ArrayBufferData]] to _newBlock_. 287 | 1. Set _O_.[[ArrayBufferByteLength]] to _newLength_. 288 | 1. Return *undefined*. 289 | 290 |
    291 |
    292 |
    293 |
    294 | 295 | 296 |

    Modifications to SharedArrayBuffer Objects

    297 | 298 | 299 |

    Modifications to Abstract Operations for SharedArrayBuffer Objects

    300 | 301 | 302 |

    303 | AllocateSharedArrayBuffer ( 304 | _constructor_: a constructor, 305 | _byteLength_: a non-negative integer, 306 | optional _maxByteLength_: a non-negative integer or ~empty~, 307 | ): either a normal completion containing a SharedArrayBuffer or a throw completion 308 |

    309 |
    310 |
    description
    311 |
    It is used to create a SharedArrayBuffer.
    312 |
    313 | 314 | 1. Let _slots_ be « [[ArrayBufferData]] ». 315 | 1. If _maxByteLength_ is present and not ~empty~, then 316 | 1. If _byteLength_ > _maxByteLength_, throw a *RangeError* exception. 317 | 1. Append [[ArrayBufferByteLengthData]] and [[ArrayBufferMaxByteLength]] to _slots_. 318 | 1. Else, append [[ArrayBufferByteLength]] to _slots_. 319 | 1. Let _obj_ be ? OrdinaryCreateFromConstructor(_constructor_, *"%SharedArrayBuffer.prototype%"*, « [[ArrayBufferData]], [[ArrayBufferByteLength]] »_slots_). 320 | 1. If _maxByteLength_ is present, let _allocLength_ be _maxByteLength_. 321 | 1. Else, let _allocLength_ be _byteLength_. 322 | 1. Let _block_ be ? CreateSharedByteDataBlock(_byteLength__allocLength_). 323 | 1. NOTE: Growable SharedArrayBuffers must be implemented as in-place growable. Creation of a _maxByteLength_ sized Data Block is a specification mechanism. It may be implemented as committing a _byteLength_ sized buffer while reserving _maxByteLength_ in virtual memory. 324 | 1. Set _obj_.[[ArrayBufferData]] to _block_. 325 | 1. If _maxByteLength_ is present and not ~empty~, then 326 | 1. Assert: _byteLength_ ≤ _maxByteLength_. 327 | 1. Let _byteLengthBlock_ be ? CreateSharedByteDataBlock(8). 328 | 1. Perform SetValueInBuffer(_byteLengthBlock_, 0, ~BigUint64~, ℤ(_byteLength_), *true*, ~SeqCst~). 329 | 1. Set _obj_.[[ArrayBufferByteLengthData]] to _byteLengthBlock_. 330 | 1. Set _obj_.[[ArrayBufferMaxByteLength]] to _maxByteLength_. 331 | 1. Else, 332 | 1. Set _obj_.[[ArrayBufferByteLength]] to _byteLength_. 333 | 1. Return _obj_. 334 | 335 |
    336 | 337 | 338 | 339 |

    340 | HostGrowSharedArrayBuffer ( 341 | _buffer_: a SharedArrayBuffer, 342 | _newByteLength_: a non-negative integer 343 | ): either a normal completion containing either ~handled~ or ~unhandled~, or a throw completion 344 |

    345 |
    346 |
    description
    347 |
    It gives the host an opportunity to perform implementation-defined growing of _buffer_. If the host chooses not to handle resizing of _buffer_, it may return ~unhandled~ for the default behaviour.
    348 |
    349 |

    The implementation of HostGrowSharedArrayBuffer must conform to the following requirements:

    350 |
      351 |
    • The abstract operation must return either NormalCompletion(~handled~), NormalCompletion(~unhandled~), or an abrupt throw completion.
    • 352 |
    • If the abstract operation does not complete normally with ~unhandled~, and _newByteLength_ < the current byte length of the _buffer_ or _newByteLength_ > _buffer_.[[ArrayBufferMaxByteLength]], throw a *RangeError* exception.
    • 353 |
    • Let _isLittleEndian_ be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. If the abstract operation completes normally with ~handled~, a WriteSharedMemory or ReadModifyWriteSharedMemory event whose [[Order]] is ~SeqCst~, [[Payload]] is NumericToRawBytes(~BigUint64~, _newByteLength_, _isLittleEndian_), [[Block]] is _buffer_.[[ArrayBufferByteLengthData]], [[ByteIndex]] is 0, and [[ElementSize]] is 8 is added to the surrounding agent's candidate execution such that racing calls to `SharedArrayBuffer.prototype.grow` are not "lost", i.e. silently do nothing.
    • 354 |
    355 | 356 |

    The default implementation of HostGrowSharedArrayBuffer is to return ~unhandled~.

    357 | 358 | 359 |

    The second requirement above is intentionally vague about how or when the current byte length of _buffer_ is read. Because the byte length must be updated via an atomic read-modify-write operation on the underlying hardware, architectures that use load-link/store-conditional or load-exclusive/store-exclusive instruction pairs may wish to keep the paired instructions close in the instruction stream. As such, SharedArrayBuffer.prototype.grow itself does not perform bounds checking on _newByteLength_ before calling HostGrowSharedArrayBuffer, nor is there a requirement on when the current byte length is read.

    360 |

    This is in contrast with HostResizeArrayBuffer, which is guaranteed that the value of _newByteLength_ is ≥ 0 and ≤ _buffer_.[[ArrayBufferMaxByteLength]].

    361 |
    362 |
    363 |
    364 |
    365 | 366 | 367 |

    The SharedArrayBuffer Constructor

    368 | 369 | 370 |

    SharedArrayBuffer ( _length_ [ , _options_ ] )

    371 |

    When the `SharedArrayBuffer` function is called with argument _length_ and optional argument _options_, the following steps are taken:

    372 | 373 | 1. If NewTarget is *undefined*, throw a *TypeError* exception. 374 | 1. Let _byteLength_ be ? ToIndex(_length_). 375 | 1. Let _requestedMaxByteLength_ be ? GetArrayBufferMaxByteLengthOption(_options_). 376 | 1. Return ? AllocateSharedArrayBuffer(NewTarget, _byteLength_, _requestedMaxByteLength_). 377 | 378 |
    379 |
    380 | 381 | 382 |

    Modifications to the Properties of the SharedArrayBuffer Prototype Object

    383 | 384 | 385 |

    get SharedArrayBuffer.prototype.byteLength

    386 |

    `SharedArrayBuffer.prototype.byteLength` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    387 | 388 | 1. Let _O_ be the *this* value. 389 | 1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferData]]). 390 | 1. If IsSharedArrayBuffer(_O_) is *false*, throw a *TypeError* exception. 391 | 1. Let _length_ be _O_.[[ArrayBufferByteLength]]ArrayBufferByteLength(_O_, ~SeqCst~). 392 | 1. Return 𝔽(_length_). 393 | 394 |
    395 | 396 | 397 | 398 |

    get SharedArrayBuffer.prototype.growable

    399 |

    `SharedArrayBuffer.prototype.growable` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    400 | 401 | 1. Let _O_ be the *this* value. 402 | 1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferData]]). 403 | 1. If IsSharedArrayBuffer(_O_) is *false*, throw a *TypeError* exception. 404 | 1. Return IsResizableArrayBuffer(_O_). 405 | 406 |
    407 | 408 | 409 |

    get SharedArrayBuffer.prototype.maxByteLength

    410 |

    `SharedArrayBuffer.prototype.maxByteLength` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    411 | 412 | 1. Let _O_ be the *this* value. 413 | 1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferData]]). 414 | 1. If IsSharedArrayBuffer(_O_) is *false*, throw a *TypeError* exception. 415 | 1. If IsResizableArrayBuffer(_O_) is *true*, then 416 | 1. Let _length_ be _O_.[[ArrayBufferMaxByteLength]]. 417 | 1. Else, 418 | 1. Let _length_ be _O_.[[ArrayBufferByteLength]]. 419 | 1. Return 𝔽(_length_). 420 | 421 |
    422 | 423 | 424 |

    SharedArrayBuffer.prototype.grow ( _newLength_ )

    425 |

    The following steps are taken:

    426 | 427 | 1. Let _O_ be the *this* value. 428 | 1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferMaxByteLength]]). 429 | 1. If IsSharedArrayBuffer(_O_) is *false*, throw a *TypeError* exception. 430 | 1. Let _newByteLength_ be ? ToIntegerOrInfinity(_newLength_). 431 | 1. Let _hostHandled_ be ? HostGrowSharedArrayBuffer(_O_, _newByteLength_). 432 | 1. If _hostHandled_ is ~handled~, return *undefined*. 433 | 1. Let _rawCurrentByteLengthBytesRead_ be a List of length 8 whose elements are nondeterministically chosen byte values. 434 | 1. NOTE: In implementations, _rawCurrentByteLengthBytesRead_ is the result of a load-link, of a load-exclusive, or of an operand of a read-modify-write instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency. 435 | 1. Let _byteLengthBlock_ be _O_.[[ArrayBufferByteLengthData]]. 436 | 1. Let _isLittleEndian_ be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. 437 | 1. Let _currentByteLength_ be RawBytesToNumeric(~BigUint64~, _rawCurrentByteLengthBytesRead_, _isLittleEndian_). 438 | 1. Let _growFailed_ be *false*. 439 | 1. If _newByteLength_ < _currentByteLength_ or _newByteLength_ > _O_.[[ArrayBufferMaxByteLength]], set _growFailed_ to *true*. 440 | 1. Let _byteLengthDelta_ be _newByteLength_ - _currentByteLength_. 441 | 1. If it is impossible to create a new Shared Data Block value consisting of _byteLengthDelta_ bytes, set _growFailed_ to *true*. 442 | 1. NOTE: No new Shared Data Block is constructed and used here. The observable behaviour of growable SharedArrayBuffers is specified by allocating a max-sized Shared Data Block at construction time, and this step is intended to capture the requirement that implementations that run out of memory must throw a *RangeError*. 443 | 1. NOTE: The above checks help ensure that concurrent calls to SharedArrayBuffer.prototype.grow are totally ordered. For example, consider two racing calls: `sab.grow(10)` and `sab.grow(20)`. One of the two calls is guaranteed to win the race. The call to `sab.grow(10)` will never shrink `sab` even if `sab.grow(20)` happened first. 444 | 1. If _growFailed_ is *false* and _newByteLength_ ≠ _currentByteLength_, then 445 | 1. NOTE: Resizes to the same length explicitly do nothing to avoid gratuitous synchronization. 446 | 1. Let _second_ be a new read-modify-write modification function with parameters (_oldBytes_, _newBytes_) that captures nothing and performs the following steps atomically when called: 447 | 1. Return _newBytes_. 448 | 1. Let _newByteLengthBytes_ be NumericToRawBytes(~BigUint64~, ℤ(_newByteLength_), _isLittleEndian_). 449 | 1. Let _event_ be ReadModifyWriteSharedMemory { [[Order]]: ~SeqCst~, [[NoTear]]: *true*, [[Block]]: _byteLengthBlock_, [[ByteIndex]]: 0, [[ElementSize]]: 8, [[Payload]]: _newByteLengthBytes_, [[ModifyOp]]: _second_ }. 450 | 1. NOTE: The new memory is already zeroed, as a _O_.[[ArrayBufferMaxByteLength]] sized Data Block is already allocated. This is a specification mechanism; an implementation is not required to reserve _O_.[[ArrayBufferMaxByteLength]] bytes of physical memory. 451 | 1. Else, 452 | 1. Let _event_ be ReadSharedMemory { [[Order]]: ~SeqCst~, [[NoTear]]: *true*, [[Block]]: _byteLengthBlock_, [[ByteIndex]]: 0, [[ElementSize]]: 8 }. 453 | 1. Let _execution_ be the [[CandidateExecution]] field of the surrounding agent's Agent Record. 454 | 1. Let _eventList_ be the [[EventList]] field of the element in _execution_.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier(). 455 | 1. Append _event_ to _eventList_. 456 | 1. Append Chosen Value Record { [[Event]]: _event_, [[ChosenValue]]: _rawCurrentByteLengthBytesRead_ } to _execution_.[[ChosenValues]]. 457 | 1. If _growFailed_ is *true*, throw a *RangeError* exception. 458 | 1. Return *undefined*. 459 | 460 | 461 |

    Spurious failures of the compare-exchange to update the length are prohibited. If the bounds checking for the new length passes and the implementation is not out of memory, a ReadModifyWriteSharedMemory event (i.e. a successful compare-exchange) is always added into the candidate execution.

    462 |

    Many of the above steps are shared with the algorithm steps of Atomics.compareExchange and should be refactored when merged into the full specification.

    463 |
    464 |
    465 |
    466 | 467 | 468 |

    SharedArrayBuffer.prototype.slice ( _start_, _end_ )

    469 |

    This method performs the following steps when called:

    470 | 471 | 1. Let _O_ be the *this* value. 472 | 1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferData]]). 473 | 1. If IsSharedArrayBuffer(_O_) is *false*, throw a *TypeError* exception. 474 | 1. Let _len_ be _O_.[[ArrayBufferByteLength]]ArrayBufferByteLength(_O_, ~SeqCst~). 475 | 1. Let _relativeStart_ be ? ToIntegerOrInfinity(_start_). 476 | 1. If _relativeStart_ is -∞, let _first_ be 0. 477 | 1. Else if _relativeStart_ < 0, let _first_ be max(_len_ + _relativeStart_, 0). 478 | 1. Else, let _first_ be min(_relativeStart_, _len_). 479 | 1. If _end_ is *undefined*, let _relativeEnd_ be _len_; else let _relativeEnd_ be ? ToIntegerOrInfinity(_end_). 480 | 1. If _relativeEnd_ is -∞, let _final_ be 0. 481 | 1. Else if _relativeEnd_ < 0, let _final_ be max(_len_ + _relativeEnd_, 0). 482 | 1. Else, let _final_ be min(_relativeEnd_, _len_). 483 | 1. Let _newLen_ be max(_final_ - _first_, 0). 484 | 1. Let _ctor_ be ? SpeciesConstructor(_O_, %SharedArrayBuffer%). 485 | 1. Let _new_ be ? Construct(_ctor_, « 𝔽(_newLen_) »). 486 | 1. Perform ? RequireInternalSlot(_new_, [[ArrayBufferData]]). 487 | 1. NOTE: Unlike ArrayBuffers, SharedArrayBuffers cannot shrink, so the length does not need to be reloaded. 488 | 1. If IsSharedArrayBuffer(_new_) is *false*, throw a *TypeError* exception. 489 | 1. If _new_.[[ArrayBufferData]] and _O_.[[ArrayBufferData]] are the same Shared Data Block values, throw a *TypeError* exception. 490 | 1. If _new_.[[ArrayBufferByteLength]] < _newLen_, throw a *TypeError* exception. 491 | 1. Let _fromBuf_ be _O_.[[ArrayBufferData]]. 492 | 1. Let _toBuf_ be _new_.[[ArrayBufferData]]. 493 | 1. Perform CopyDataBlockBytes(_toBuf_, 0, _fromBuf_, _first_, _newLen_). 494 | 1. Return _new_. 495 | 496 |
    497 |
    498 |
    499 | 500 | 501 |

    Modifications to Integer-Indexed Exotic Objects

    502 | 503 | 504 |

    [[OwnPropertyKeys]] ( ): a normal completion containing a List of property keys

    505 |
    506 |
    for
    507 |
    an Integer-Indexed exotic object _O_
    508 |
    509 | 510 | 1. Let _keys_ be a new empty List. 511 | 1. Assert: _O_ is an Integer-Indexed exotic object. 512 | 1. If IsDetachedBuffer(_O_.[[ViewedArrayBuffer]]) is *false*, then 513 | 1. For each integer _i_ starting with 0 such that _i_ < _O_.[[ArrayLength]], in ascending order, do 514 | 1. Add ! ToString(𝔽(_i_)) as the last element of _keys_. 515 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 516 | 1. Let _len_ be IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 517 | 1. If _len_ is not ~out-of-bounds~, then 518 | 1. For each integer _i_ starting with 0 such that _i_ < _len_, in ascending order, do 519 | 1. Add ! ToString(𝔽(_i_)) as the last element of _keys_. 520 | 1. For each own property key _P_ of _O_ such that Type(_P_) is String and _P_ is not an integer index, in ascending chronological order of property creation, do 521 | 1. Add _P_ as the last element of _keys_. 522 | 1. For each own property key _P_ of _O_ such that Type(_P_) is Symbol, in ascending chronological order of property creation, do 523 | 1. Add _P_ as the last element of _keys_. 524 | 1. Return _keys_. 525 | 526 |
    527 | 528 | 529 |

    530 | IsValidIntegerIndex ( 531 | _O_: an Integer-Indexed exotic object, 532 | _index_: a Number, 533 | ): a Boolean 534 |

    535 |
    536 |
    537 | 538 | 1. If IsDetachedBuffer(_O_.[[ViewedArrayBuffer]]) is *true*, return *false*. 539 | 1. If IsIntegralNumber(_index_) is *false*, return *false*. 540 | 1. If _index_ is *-0*𝔽, return *false*. 541 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~Unordered~). 542 | 1. NOTE: Bounds checking is not a synchronizing operation when _O_'s backing buffer is a growable SharedArrayBuffer. 543 | 1. Let _length_ be IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 544 | 1. If _length_ is ~out-of-bounds~ or ℝ(_index_) < 0 or ℝ(_index_) ≥ _O_.[[ArrayLength]]_length_, return *false*. 545 | 1. Return *true*. 546 | 547 |
    548 | 549 | 550 | 551 |

    552 | IntegerIndexedObjectByteLength ( 553 | _O_: an Integer-Indexed exotic object, 554 | _getBufferByteLength_: an Abstract Closure, 555 | ): a non-negative integer 556 |

    557 |
    558 |
    559 | 560 | 1. Let _length_ be IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 561 | 1. If _length_ is ~out-of-bounds~ or _length_ = 0, return 0. 562 | 1. If _O_.[[ByteLength]] is not ~auto~, return _O_.[[ByteLength]]. 563 | 1. Let _elementSize_ be TypedArrayElementSize(_O_). 564 | 1. Return _length_ × _elementSize_. 565 | 566 |
    567 | 568 | 569 |

    570 | IntegerIndexedObjectLength ( 571 | _O_: an Integer-Indexed exotic object, 572 | _getBufferByteLength_: an Abstract Closure, 573 | ): a non-negative integer or ~out-of-bounds~ 574 |

    575 |
    576 |
    577 | 578 | 1. If IsIntegerIndexedObjectOutOfBounds(_O_, _getBufferByteLength_) is *true*, return ~out-of-bounds~. 579 | 1. If _O_.[[ArrayLength]] is not ~auto~, return _O_.[[ArrayLength]]. 580 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 581 | 1. Let _bufferByteLength_ be _getBufferByteLength_(_buffer_). 582 | 1. Assert: IsResizableArrayBuffer(_buffer_) is *true*. 583 | 1. Let _byteOffset_ be _O_.[[ByteOffset]]. 584 | 1. Let _elementSize_ be TypedArrayElementSize(_O_). 585 | 1. Return floor((_bufferByteLength_ - _byteOffset_) / _elementSize_). 586 | 587 |
    588 | 589 | 590 |

    591 | IsIntegerIndexedObjectOutOfBounds ( 592 | _O_: an Integer-Indexed exotic object, 593 | _getBufferByteLength_: an Abstract Closure, 594 | ): a Boolean 595 |

    596 |
    597 |
    description
    598 |
    It checks if any of the object's numeric properties reference a value at an index not contained within the underlying data block's bounds.
    599 |
    600 | 601 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 602 | 1. If IsDetachedBuffer(_O_.[[ViewedArrayBuffer]]) is *true*, return *true*. 603 | 1. Let _bufferByteLength_ be _getBufferByteLength_(_buffer_). 604 | 1. Let _byteOffsetStart_ be _O_.[[ByteOffset]]. 605 | 1. If _O_.[[ArrayLength]] is ~auto~, then 606 | 1. Let _byteOffsetEnd_ be _bufferByteLength_. 607 | 1. Else, 608 | 1. Let _elementSize_ be TypedArrayElementSize(_O_). 609 | 1. Let _byteOffsetEnd_ be _byteOffsetStart_ + _O_.[[ArrayLength]] × _elementSize_. 610 | 1. If _byteOffsetStart_ > _bufferByteLength_ or _byteOffsetEnd_ > _bufferByteLength_, return *true*. 611 | 1. NOTE: 0-length TypedArrays are not considered out-of-bounds. 612 | 1. Return *false*. 613 | 614 |
    615 | 616 | 617 |

    618 | IsArrayBufferViewOutOfBounds( 619 | _O_: an Integer-Indexed exotic object or a DataView, 620 | ): a Boolean 621 |

    622 |
    623 |
    description
    624 |
    It checks if either any of a TypedArray's numeric properties or a DataView object's methods can reference a value at an index not contained within the underlying data block's bounds. This abstract operation exists as a convenience for upstream specifications.
    625 |
    626 | 627 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 628 | 1. If IsDetachedBuffer(_buffer_) is *true*, return *true*. 629 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 630 | 1. If IsSharedArrayBuffer(_buffer_) is *true*, then 631 | 1. Assert: If _O_ has a [[DataView]] internal slot, IsViewOutOfBounds(_O_, _getBufferByteLength_) is *false*. Else, IsIntegerIndexedObjectOutOfBounds(_O_, _getBufferByteLength_) is *false*. 632 | 1. NOTE: SharedArrayBuffers can only grow, and views on it cannot go out of bounds after construction. This is special-cased in this operation to avoid shared memory loads of the buffer's byte length, which are not necessary for this check. 633 | 1. Return *false*. 634 | 1. If _O_ has a [[DataView]] internal slot, return IsViewOutOfBounds(_O_, _getBufferByteLength_). 635 | 1. Return IsIntegerIndexedObjectOutOfBounds(_O_, _getBufferByteLength_). 636 | 637 |
    638 |
    639 |
    640 | 641 | 642 |

    Modifications to TypedArray Objects

    643 | 644 | 645 |

    Modifications to Properties of the %TypedArray.prototype% Object

    646 | 647 | 648 |

    649 | ValidateTypedArray ( 650 | _O_: an ECMAScript language value, 651 | ): either a normal completion containing ~unused~ or a throw completion 652 |

    653 |
    654 |
    655 | 656 | 1. Perform ? RequireInternalSlot(_O_, [[TypedArrayName]]). 657 | 1. Assert: _O_ has a [[ViewedArrayBuffer]] internal slot. 658 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 659 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 660 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 661 | 1. If IsIntegerIndexedObjectOutOfBounds(_O_, _getBufferByteLength_) is *true*, throw a *TypeError* exception. 662 | 1. Return _buffer_. 663 | 664 |
    665 | 666 | 667 |

    get %TypedArray%.prototype.byteLength

    668 |

    %TypedArray%`.prototype.byteLength` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    669 | 670 | 1. Let _O_ be the *this* value. 671 | 1. Perform ? RequireInternalSlot(_O_, [[TypedArrayName]]). 672 | 1. Assert: _O_ has a [[ViewedArrayBuffer]] internal slot. 673 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 674 | 1. If IsDetachedBuffer(_buffer_) is *true*, return *+0*𝔽. 675 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 676 | 1. Let _size_ be _O_.[[ByteLength]]IntegerIndexedObjectByteLength(_O_, _getBufferByteLength_). 677 | 1. Return 𝔽(_size_). 678 | 679 |
    680 | 681 | 682 |

    get %TypedArray%.prototype.byteOffset

    683 |

    %TypedArray%`.prototype.byteOffset` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    684 | 685 | 1. Let _O_ be the *this* value. 686 | 1. Perform ? RequireInternalSlot(_O_, [[TypedArrayName]]). 687 | 1. Assert: _O_ has a [[ViewedArrayBuffer]] internal slot. 688 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 689 | 1. If IsDetachedBuffer(_buffer_) is *true*, return *+0*𝔽. 690 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 691 | 1. If IsIntegerIndexedObjectOutOfBounds(_O_, _getBufferByteLength_) is *true*, return *+0*𝔽. 692 | 1. Let _offset_ be _O_.[[ByteOffset]]. 693 | 1. Return 𝔽(_offset_). 694 | 695 |
    696 | 697 | 698 |

    get %TypedArray%.prototype.length

    699 |

    %TypedArray%`.prototype.length` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    700 | 701 | 1. Let _O_ be the *this* value. 702 | 1. Perform ? RequireInternalSlot(_O_, [[TypedArrayName]]). 703 | 1. Assert: _O_ has [[ViewedArrayBuffer]] and [[ArrayLength]] internal slots. 704 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 705 | 1. If IsDetachedBuffer(_buffer_) is *true*, return *+0*𝔽. 706 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 707 | 1. Let _length_ be _O_.[[ArrayLength]]IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 708 | 1. If _length_ is ~out-of-bounds~, set _length_ to 0. 709 | 1. Return 𝔽(_length_). 710 | 711 |

    This function is not generic. The *this* value must be an object with a [[TypedArrayName]] internal slot.

    712 |
    713 | 714 | 715 |

    %TypedArray%.prototype.copyWithin ( _target_, _start_ [ , _end_ ] )

    716 |

    The interpretation and use of the arguments of %TypedArray%`.prototype.copyWithin` are the same as for `Array.prototype.copyWithin` as defined in .

    717 |

    This method performs the following steps when called:

    718 | 719 | 1. Let _O_ be the *this* value. 720 | 1. Perform ? ValidateTypedArray(_O_). 721 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 722 | 1. Let _len_ be _O_.[[ArrayLength]]IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 723 | 1. Assert: _len_ is not ~out-of-bounds~. 724 | 1. Let _relativeTarget_ be ? ToIntegerOrInfinity(_target_). 725 | 1. If _relativeTarget_ is -∞, let _to_ be 0. 726 | 1. Else if _relativeTarget_ < 0, let _to_ be max(_len_ + _relativeTarget_, 0). 727 | 1. Else, let _to_ be min(_relativeTarget_, _len_). 728 | 1. Let _relativeStart_ be ? ToIntegerOrInfinity(_start_). 729 | 1. If _relativeStart_ is -∞, let _from_ be 0. 730 | 1. Else if _relativeStart_ < 0, let _from_ be max(_len_ + _relativeStart_, 0). 731 | 1. Else, let _from_ be min(_relativeStart_, _len_). 732 | 1. If _end_ is *undefined*, let _relativeEnd_ be _len_; else let _relativeEnd_ be ? ToIntegerOrInfinity(_end_). 733 | 1. If _relativeEnd_ is -∞, let _final_ be 0. 734 | 1. Else if _relativeEnd_ < 0, let _final_ be max(_len_ + _relativeEnd_, 0). 735 | 1. Else, let _final_ be min(_relativeEnd_, _len_). 736 | 1. Let _count_ be min(_final_ - _from_, _len_ - _to_). 737 | 1. If _count_ > 0, then 738 | 1. NOTE: The copying must be performed in a manner that preserves the bit-level encoding of the source data. 739 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 740 | 1. Set _getBufferByteLength_ to MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 741 | 1. Set _len_ to IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 742 | 1. If IsDetachedBuffer(_buffer_) is *true*_len_ is ~out-of-bounds~, throw a *TypeError* exception. 743 | 1. Let _typedArrayName_ be the String value of _O_.[[TypedArrayName]]. 744 | 1. Let _elementSize_ be the Element Size value specified in for _typedArrayName_. 745 | 1. Let _byteOffset_ be _O_.[[ByteOffset]]. 746 | 1. Let _bufferByteLimit_ be _len_ × _elementSize_ + _byteOffset_. 747 | 1. Let _toByteIndex_ be _to_ × _elementSize_ + _byteOffset_. 748 | 1. Let _fromByteIndex_ be _from_ × _elementSize_ + _byteOffset_. 749 | 1. Let _countBytes_ be _count_ × _elementSize_. 750 | 1. If _fromByteIndex_ < _toByteIndex_ and _toByteIndex_ < _fromByteIndex_ + _countBytes_, then 751 | 1. Let _direction_ be -1. 752 | 1. Set _fromByteIndex_ to _fromByteIndex_ + _countBytes_ - 1. 753 | 1. Set _toByteIndex_ to _toByteIndex_ + _countBytes_ - 1. 754 | 1. Else, 755 | 1. Let _direction_ be 1. 756 | 1. Repeat, while _countBytes_ > 0, 757 | 1. If _fromByteIndex_ < _bufferByteLimit_ and _toByteIndex_ < _bufferByteLimit_, then 758 | 1. Let _value_ be GetValueFromBuffer(_buffer_, _fromByteIndex_, ~Uint8~, *true*, ~Unordered~). 759 | 1. Perform SetValueInBuffer(_buffer_, _toByteIndex_, ~Uint8~, _value_, *true*, ~Unordered~). 760 | 1. Set _fromByteIndex_ to _fromByteIndex_ + _direction_. 761 | 1. Set _toByteIndex_ to _toByteIndex_ + _direction_. 762 | 1. Set _countBytes_ to _countBytes_ - 1. 763 | 1. Return _O_. 764 | 765 |
    766 | 767 | 768 |

    %TypedArray%.prototype.fill ( _value_ [ , _start_ [ , _end_ ] ] )

    769 |

    The interpretation and use of the arguments of %TypedArray%`.prototype.fill` are the same as for `Array.prototype.fill` as defined in .

    770 |

    This method performs the following steps when called:

    771 | 772 | 1. Let _O_ be the *this* value. 773 | 1. Perform ? ValidateTypedArray(_O_). 774 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 775 | 1. Let _len_ be _O_.[[ArrayLength]]IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 776 | 1. Assert: _len_ is not ~out-of-bounds~. 777 | 1. If _O_.[[ContentType]] is ~BigInt~, set _value_ to ? ToBigInt(_value_). 778 | 1. Otherwise, set _value_ to ? ToNumber(_value_). 779 | 1. Let _relativeStart_ be ? ToIntegerOrInfinity(_start_). 780 | 1. If _relativeStart_ is -∞, let _k_ be 0. 781 | 1. Else if _relativeStart_ < 0, let _k_ be max(_len_ + _relativeStart_, 0). 782 | 1. Else, let _k_ be min(_relativeStart_, _len_). 783 | 1. If _end_ is *undefined*, let _relativeEnd_ be _len_; else let _relativeEnd_ be ? ToIntegerOrInfinity(_end_). 784 | 1. If _relativeEnd_ is -∞, let _final_ be 0. 785 | 1. Else if _relativeEnd_ < 0, let _final_ be max(_len_ + _relativeEnd_, 0). 786 | 1. Else, let _final_ be min(_relativeEnd_, _len_). 787 | 1. If IsDetachedBuffer(_O_.[[ViewedArrayBuffer]]) is *true*, throw a *TypeError* exception. 788 | 1. Set _getBufferByteLength_ to MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 789 | 1. Set _len_ to IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 790 | 1. If _len_ is ~out-of-bounds~, throw a *TypeError* exception. 791 | 1. Set _final_ to min(_final_, _len_). 792 | 1. Repeat, while _k_ < _final_, 793 | 1. Let _Pk_ be ! ToString(𝔽(_k_)). 794 | 1. Perform ! Set(_O_, _Pk_, _value_, *true*). 795 | 1. Set _k_ to _k_ + 1. 796 | 1. Return _O_. 797 | 798 |
    799 | 800 | 801 |

    %TypedArray%.prototype.slice ( _start_, _end_ )

    802 |

    The interpretation and use of the arguments of %TypedArray%`.prototype.slice` are the same as for `Array.prototype.slice` as defined in . The following steps are taken:

    803 |

    This method performs the following steps when called:

    804 | 805 | 1. Let _O_ be the *this* value. 806 | 1. Perform ? ValidateTypedArray(_O_). 807 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 808 | 1. Let _len_ be _O_.[[ArrayLength]]IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 809 | 1. Assert: _len_ is not ~out-of-bounds~. 810 | 1. Let _relativeStart_ be ? ToIntegerOrInfinity(_start_). 811 | 1. If _relativeStart_ is -∞, let _k_ be 0. 812 | 1. Else if _relativeStart_ < 0, let _k_ be max(_len_ + _relativeStart_, 0). 813 | 1. Else, let _k_ be min(_relativeStart_, _len_). 814 | 1. If _end_ is *undefined*, let _relativeEnd_ be _len_; else let _relativeEnd_ be ? ToIntegerOrInfinity(_end_). 815 | 1. If _relativeEnd_ is -∞, let _final_ be 0. 816 | 1. Else if _relativeEnd_ < 0, let _final_ be max(_len_ + _relativeEnd_, 0). 817 | 1. Else, let _final_ be min(_relativeEnd_, _len_). 818 | 1. Let _count_ be max(_final_ - _k_, 0). 819 | 1. Let _A_ be ? TypedArraySpeciesCreate(_O_, « 𝔽(_count_) »). 820 | 1. If _count_ > 0, then 821 | 1. If IsDetachedBuffer(_O_.[[ViewedArrayBuffer]]) is *true*, throw a *TypeError* exception. 822 | 1. Set _getBufferByteLength_ to MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 823 | 1. Set _len_ to IntegerIndexedObjectLength(_O_, _getBufferByteLength_). 824 | 1. If _len_ is ~out-of-bounds~, throw a *TypeError* exception. 825 | 1. Set _final_ to min(_final_, _len_). 826 | 1. Let _srcName_ be the String value of _O_.[[TypedArrayName]]. 827 | 1. Let _srcType_ be the Element Type value in for _srcName_. 828 | 1. Let _targetName_ be the String value of _A_.[[TypedArrayName]]. 829 | 1. Let _targetType_ be the Element Type value in for _targetName_. 830 | 1. If _srcType_ is different from _targetType_, then 831 | 1. Let _n_ be 0. 832 | 1. Repeat, while _k_ < _final_, 833 | 1. Let _Pk_ be ! ToString(𝔽(_k_)). 834 | 1. Let _kValue_ be ! Get(_O_, _Pk_). 835 | 1. Perform ! Set(_A_, ! ToString(𝔽(_n_)), _kValue_, *true*). 836 | 1. Set _k_ to _k_ + 1. 837 | 1. Set _n_ to _n_ + 1. 838 | 1. Else, 839 | 1. Let _srcBuffer_ be _O_.[[ViewedArrayBuffer]]. 840 | 1. Let _targetBuffer_ be _A_.[[ViewedArrayBuffer]]. 841 | 1. Let _elementSize_ be the Element Size value specified in for Element Type _srcType_. 842 | 1. NOTE: If _srcType_ and _targetType_ are the same, the transfer must be performed in a manner that preserves the bit-level encoding of the source data. 843 | 1. Let _srcByteOffset_ be _O_.[[ByteOffset]]. 844 | 1. Let _targetByteIndex_ be _A_.[[ByteOffset]]. 845 | 1. Let _srcByteIndex_ be (_k_ × _elementSize_) + _srcByteOffset_. 846 | 1. Let _limit_ be _targetByteIndex_ + min(_count_, _len_) × _elementSize_. 847 | 1. Repeat, while _targetByteIndex_ < _limit_, 848 | 1. Let _value_ be GetValueFromBuffer(_srcBuffer_, _srcByteIndex_, ~Uint8~, *true*, ~Unordered~). 849 | 1. Perform SetValueInBuffer(_targetBuffer_, _targetByteIndex_, ~Uint8~, _value_, *true*, ~Unordered~). 850 | 1. Set _srcByteIndex_ to _srcByteIndex_ + 1. 851 | 1. Set _targetByteIndex_ to _targetByteIndex_ + 1. 852 | 1. Return _A_. 853 | 854 |

    This function is not generic. The *this* value must be an object with a [[TypedArrayName]] internal slot.

    855 |
    856 | 857 | 858 |

    %TypedArray%.prototype.subarray ( _begin_, _end_ )

    859 |

    Returns a new _TypedArray_ whose element type is the same as this _TypedArray_ and whose ArrayBuffer is the same as the ArrayBuffer of this _TypedArray_, referencing the elements at _begin_, inclusive, up to _end_, exclusive. If either _begin_ or _end_ is negative, it refers to an index from the end of the array, as opposed to from the beginning.

    860 |

    This method performs the following steps when called:

    861 | 862 | 1. Let _O_ be the *this* value. 863 | 1. Perform ? RequireInternalSlot(_O_, [[TypedArrayName]]). 864 | 1. Assert: _O_ has a [[ViewedArrayBuffer]] internal slot. 865 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 866 | 1. Let _getSrcBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 867 | 1. Let _srcLength_ be _O_.[[ArrayLength]]IntegerIndexedObjectLength(_O_, _getSrcBufferByteLength_). 868 | 1. If _srcLength_ is ~out-of-bounds~, set _srcLength_ to 0. 869 | 1. Let _relativeBegin_ be ? ToIntegerOrInfinity(_begin_). 870 | 1. If _relativeBegin_ is -∞, let _beginIndex_ be 0. 871 | 1. Else if _relativeBegin_ < 0, let _beginIndex_ be max(_srcLength_ + _relativeBegin_, 0). 872 | 1. Else, let _beginIndex_ be min(_relativeBegin_, _srcLength_). 873 | 1. If _O_.[[ArrayLength]] is ~auto~ and _end_ is *undefined*, then 874 | 1. Let _newLength_ be *undefined*. 875 | 1. Else, 876 | 1. If _end_ is *undefined*, let _relativeEnd_ be _srcLength_; else let _relativeEnd_ be ? ToIntegerOrInfinity(_end_). 877 | 1. If _relativeEnd_ is -∞, let _endIndex_ be 0. 878 | 1. Else if _relativeEnd_ < 0, let _endIndex_ be max(_srcLength_ + _relativeEnd_, 0). 879 | 1. Else, let _endIndex_ be min(_relativeEnd_, _srcLength_). 880 | 1. Let _newLength_ be max(_endIndex_ - _beginIndex_, 0). 881 | 1. Let _constructorName_ be the String value of _O_.[[TypedArrayName]]. 882 | 1. Let _elementSize_ be the Element Size value specified in for _constructorName_. 883 | 1. Let _srcByteOffset_ be _O_.[[ByteOffset]]. 884 | 1. Let _beginByteOffset_ be _srcByteOffset_ + _beginIndex_ × _elementSize_. 885 | 1. If _newLength_ is *undefined*, then 886 | 1. Let _argumentsList_ be « _buffer_, 𝔽(_beginByteOffset_) ». 887 | 1. Else, 888 | 1. Let _argumentsList_ be « _buffer_, 𝔽(_beginByteOffset_), 𝔽(_newLength_) ». 889 | 1. Return ? TypedArraySpeciesCreate(_O_, _argumentsList_). 890 | 891 |

    This function is not generic. The *this* value must be an object with a [[TypedArrayName]] internal slot.

    892 |
    893 | 894 | 895 |

    896 | SetTypedArrayFromTypedArray ( 897 | _target_: a TypedArray, 898 | _targetOffset_: a non-negative integer or +∞, 899 | _source_: a TypedArray, 900 | ): either a normal completion containing ~unused~ or a throw completion 901 |

    902 |
    903 |
    description
    904 |
    It sets multiple values in _target_, starting at index _targetOffset_, reading the values from _source_.
    905 |
    906 | 907 | 1. Assert: _source_ is an Object that has a [[TypedArrayName]] internal slot. 908 | 1. Let _targetBuffer_ be _target_.[[ViewedArrayBuffer]]. 909 | 1. If IsDetachedBuffer(_targetBuffer_) is *true*, throw a *TypeError* exception. 910 | 1. Let _getTargetBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 911 | 1. Let _targetLength_ be _target_.[[ArrayLength]]IntegerIndexedObjectLength(_target_, _getTargetBufferByteLength_). 912 | 1. If _targetLength_ is ~out-of-bounds~, throw a *TypeError* exception. 913 | 1. Let _srcBuffer_ be _source_.[[ViewedArrayBuffer]]. 914 | 1. If IsDetachedBuffer(_srcBuffer_) is *true*, throw a *TypeError* exception. 915 | 1. Let _targetType_ be TypedArrayElementType(_target_). 916 | 1. Let _targetElementSize_ be TypedArrayElementSize(_target_). 917 | 1. Let _targetByteOffset_ be _target_.[[ByteOffset]]. 918 | 1. Let _srcType_ be TypedArrayElementType(_source_). 919 | 1. Let _srcElementSize_ be TypedArrayElementSize(_source_). 920 | 1. Let _getSrcBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 921 | 1. Let _srcLength_ be _source_.[[ArrayLength]]IntegerIndexedObjectLength(_source_, _getSrcBufferByteLength_). 922 | 1. If _srcLength_ is ~out-of-bounds~, throw a *TypeError* exception. 923 | 1. Let _srcByteOffset_ be _source_.[[ByteOffset]]. 924 | 1. If _targetOffset_ is +∞, throw a *RangeError* exception. 925 | 1. If _srcLength_ + _targetOffset_ > _targetLength_, throw a *RangeError* exception. 926 | 1. If _target_.[[ContentType]] ≠ _source_.[[ContentType]], throw a *TypeError* exception. 927 | 1. If both IsSharedArrayBuffer(_srcBuffer_) and IsSharedArrayBuffer(_targetBuffer_) are *true*, then 928 | 1. If _srcBuffer_.[[ArrayBufferData]] and _targetBuffer_.[[ArrayBufferData]] are the same Shared Data Block values, let _same_ be *true*; else let _same_ be *false*. 929 | 1. Else, let _same_ be SameValue(_srcBuffer_, _targetBuffer_). 930 | 1. If _same_ is *true*, then 931 | 1. Let _srcByteLength_ be _source_.[[ByteLength]]IntegerIndexedObjectByteLength(_source_, _getSrcBufferByteLength_). 932 | 1. Set _srcBuffer_ to ? CloneArrayBuffer(_srcBuffer_, _srcByteOffset_, _srcByteLength_). 933 | 1. Let _srcByteIndex_ be 0. 934 | 1. Else, let _srcByteIndex_ be _srcByteOffset_. 935 | 1. Let _targetByteIndex_ be _targetOffset_ × _targetElementSize_ + _targetByteOffset_. 936 | 1. Let _limit_ be _targetByteIndex_ + _targetElementSize_ × _srcLength_. 937 | 1. If _srcType_ is the same as _targetType_, then 938 | 1. NOTE: If _srcType_ and _targetType_ are the same, the transfer must be performed in a manner that preserves the bit-level encoding of the source data. 939 | 1. Repeat, while _targetByteIndex_ < _limit_, 940 | 1. Let _value_ be GetValueFromBuffer(_srcBuffer_, _srcByteIndex_, ~Uint8~, *true*, ~Unordered~). 941 | 1. Perform SetValueInBuffer(_targetBuffer_, _targetByteIndex_, ~Uint8~, _value_, *true*, ~Unordered~). 942 | 1. Set _srcByteIndex_ to _srcByteIndex_ + 1. 943 | 1. Set _targetByteIndex_ to _targetByteIndex_ + 1. 944 | 1. Else, 945 | 1. Repeat, while _targetByteIndex_ < _limit_, 946 | 1. Let _value_ be GetValueFromBuffer(_srcBuffer_, _srcByteIndex_, _srcType_, *true*, ~Unordered~). 947 | 1. Perform SetValueInBuffer(_targetBuffer_, _targetByteIndex_, _targetType_, _value_, *true*, ~Unordered~). 948 | 1. Set _srcByteIndex_ to _srcByteIndex_ + _srcElementSize_. 949 | 1. Set _targetByteIndex_ to _targetByteIndex_ + _targetElementSize_. 950 | 951 |
    952 |
    953 | 954 | 955 |

    Modifications to the _TypedArray_ Constructors

    956 | 957 | 958 |

    959 | InitializeTypedArrayFromTypedArray ( 960 | _O_: a TypedArray, 961 | _srcArray_: a TypedArray, 962 | ): either a normal completion containing ~unused~ or a throw completion 963 |

    964 |
    965 |
    966 | 967 | 1. Let _srcData_ be _srcArray_.[[ViewedArrayBuffer]]. 968 | 1. If IsDetachedBuffer(_srcData_) is *true*, throw a *TypeError* exception. 969 | 1. Let _elementType_ be TypedArrayElementType(_O_). 970 | 1. Let _elementSize_ be TypedArrayElementSize(_O_). 971 | 1. Let _getSrcBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 972 | 1. Let _elementLength_ be _srcArray_.[[ArrayLength]]IntegerIndexedObjectLength(_srcArray_, _getSrcBufferByteLength_). 973 | 1. If _elementLength_ is ~out-of-bounds~, throw a *TypeError* exception. 974 | 1. Let _srcType_ be TypedArrayElementType(_srcArray_). 975 | 1. Let _srcElementSize_ be TypedArrayElementSize(_srcArray_). 976 | 1. Let _srcByteOffset_ be _srcArray_.[[ByteOffset]]. 977 | 1. Let _byteLength_ be _elementSize_ × _elementLength_. 978 | 1. If _elementType_ is the same as _srcType_, then 979 | 1. Let _data_ be ? CloneArrayBuffer(_srcData_, _srcByteOffset_, _byteLength_). 980 | 1. Else, 981 | 1. Let _data_ be ? AllocateArrayBuffer(%ArrayBuffer%, _byteLength_). 982 | 1. If _srcArray_.[[ContentType]] ≠ _O_.[[ContentType]], throw a *TypeError* exception. 983 | 1. Let _srcByteIndex_ be _srcByteOffset_. 984 | 1. Let _targetByteIndex_ be 0. 985 | 1. Let _count_ be _elementLength_. 986 | 1. Repeat, while _count_ > 0, 987 | 1. Let _value_ be GetValueFromBuffer(_srcData_, _srcByteIndex_, _srcType_, *true*, ~Unordered~). 988 | 1. Perform SetValueInBuffer(_data_, _targetByteIndex_, _elementType_, _value_, *true*, ~Unordered~). 989 | 1. Set _srcByteIndex_ to _srcByteIndex_ + _srcElementSize_. 990 | 1. Set _targetByteIndex_ to _targetByteIndex_ + _elementSize_. 991 | 1. Set _count_ to _count_ - 1. 992 | 1. Set _O_.[[ViewedArrayBuffer]] to _data_. 993 | 1. Set _O_.[[ByteLength]] to _byteLength_. 994 | 1. Set _O_.[[ByteOffset]] to 0. 995 | 1. Set _O_.[[ArrayLength]] to _elementLength_. 996 | 1. Return ~unused~. 997 | 998 |
    999 | 1000 | 1001 |

    1002 | InitializeTypedArrayFromArrayBuffer ( 1003 | _O_: a TypedArray, 1004 | _buffer_: an ArrayBuffer or a SharedArrayBuffer, 1005 | _byteOffset_: an ECMAScript language value, 1006 | _length_: an ECMAScript language value, 1007 | ): either a normal completion containing ~unused~ or a throw completion 1008 |

    1009 |
    1010 |
    1011 | 1012 | 1. Let _elementSize_ be TypedArrayElementSize(_O_). 1013 | 1. Let _offset_ be ? ToIndex(_byteOffset_). 1014 | 1. If _offset_ modulo _elementSize_ ≠ 0, throw a *RangeError* exception. 1015 | 1. Let _bufferIsResizable_ be IsResizableArrayBuffer(_buffer_). 1016 | 1. If _length_ is not *undefined*, then 1017 | 1. Let _newLength_ be ? ToIndex(_length_). 1018 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1019 | 1. Let _bufferByteLength_ be _buffer_.[[ArrayBufferByteLength]]ArrayBufferByteLength(_buffer_, ~SeqCst~). 1020 | 1. If _length_ is *undefined* and _bufferIsResizable_ is *true*, then 1021 | 1. If _offset_ > _bufferByteLength_, throw a *RangeError* exception. 1022 | 1. Set _O_.[[ByteLength]] to ~auto~. 1023 | 1. Set _O_.[[ArrayLength]] to ~auto~. 1024 | 1. Else, 1025 | 1. If _length_ is *undefined*, then 1026 | 1. If _bufferByteLength_ modulo _elementSize_ ≠ 0, throw a *RangeError* exception. 1027 | 1. Let _newByteLength_ be _bufferByteLength_ - _offset_. 1028 | 1. If _newByteLength_ < 0, throw a *RangeError* exception. 1029 | 1. Else, 1030 | 1. Let _newByteLength_ be _newLength_ × _elementSize_. 1031 | 1. If _offset_ + _newByteLength_ > _bufferByteLength_, throw a *RangeError* exception. 1032 | 1. Set _O_.[[ViewedArrayBuffer]] to _buffer_. 1033 | 1. Set _O_.[[ByteLength]] to _newByteLength_. 1034 | 1. Set _O_.[[ByteOffset]] to _offset_. 1035 | 1. Set _O_.[[ArrayLength]] to _newByteLength_ / _elementSize_. 1036 | 1. Set _O_.[[ViewedArrayBuffer]] to _buffer_. 1037 | 1. Set _O_.[[ByteOffset]] to _offset_. 1038 | 1. Return ~unused~. 1039 | 1040 |
    1041 |
    1042 |
    1043 | 1044 | 1045 |

    Modifications to DataView Objects

    1046 | 1047 | 1048 |

    Modifications to Abstract Operations For DataView Objects

    1049 | 1050 | 1051 | 1052 |

    1053 | GetViewByteLength ( 1054 | _view_: a DataView, 1055 | _getBufferByteLength_: an Abstract Closure, 1056 | ): a non-negative integer or ~out-of-bounds~ 1057 |

    1058 |
    1059 |
    1060 | 1061 | 1. If IsViewOutOfBounds(_view_, _getBufferByteLength_) is *true*, return ~out-of-bounds~. 1062 | 1. If _view_.[[ByteLength]] is not ~auto~, return _view_.[[ByteLength]]. 1063 | 1. Let _buffer_ be _view_.[[ViewedArrayBuffer]]. 1064 | 1. Let _bufferByteLength_ be _getBufferByteLength_(_buffer_). 1065 | 1. Assert: IsResizableArrayBuffer(_buffer_) is *true*. 1066 | 1. Let _byteOffset_ be _view_.[[ByteOffset]]. 1067 | 1. Return _bufferByteLength_ - _byteOffset_. 1068 | 1069 |
    1070 | 1071 | 1072 |

    1073 | IsViewOutOfBounds ( 1074 | _view_: a DataView, 1075 | _getBufferByteLength_: an Abstract Closure, 1076 | ): a Boolean 1077 |

    1078 |
    1079 |
    1080 | 1081 | 1. Let _buffer_ be _view_.[[ViewedArrayBuffer]]. 1082 | 1. If IsDetachedBuffer(_buffer_) is *true*, return *true*. 1083 | 1. Let _bufferByteLength_ be _getBufferByteLength_(_buffer_). 1084 | 1. Let _byteOffsetStart_ be _view_.[[ByteOffset]]. 1085 | 1. If _view_.[[ByteLength]] is ~auto~, then 1086 | 1. Let _byteOffsetEnd_ be _bufferByteLength_. 1087 | 1. Else, 1088 | 1. Let _byteOffsetEnd_ be _byteOffsetStart_ + _view_.[[ByteLength]]. 1089 | 1. If _byteOffsetStart_ > _bufferByteLength_ or _byteOffsetEnd_ > _bufferByteLength_, return *true*. 1090 | 1. NOTE: 0-length DataViews are not considered out-of-bounds. 1091 | 1. Return *false*. 1092 | 1093 |
    1094 |
    1095 | 1096 | 1097 |

    1098 | GetViewValue ( 1099 | _view_: an ECMAScript language value, 1100 | _requestIndex_: an ECMAScript language value, 1101 | _isLittleEndian_: an ECMAScript language value, 1102 | _type_: a TypedArray element type, 1103 | ): either a normal completion containing either a Number or a BigInt, or a throw completion 1104 |

    1105 |
    1106 |
    description
    1107 |
    It is used by functions on DataView instances to retrieve values from the view's buffer.
    1108 |
    1109 | 1110 | 1. Perform ? RequireInternalSlot(_view_, [[DataView]]). 1111 | 1. Assert: _view_ has a [[ViewedArrayBuffer]] internal slot. 1112 | 1. Let _getIndex_ be ? ToIndex(_requestIndex_). 1113 | 1. Set _isLittleEndian_ to ToBoolean(_isLittleEndian_). 1114 | 1. Let _buffer_ be _view_.[[ViewedArrayBuffer]]. 1115 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1116 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~Unordered~). 1117 | 1. NOTE: Bounds checking is not a synchronizing operation when _view_'s backing buffer is a growable SharedArrayBuffer. 1118 | 1. Let _viewOffset_ be _view_.[[ByteOffset]]. 1119 | 1. Let _viewSize_ be _view_.[[ByteLength]]GetViewByteLength(_view_, _getBufferByteLength_). 1120 | 1. If _viewSize_ is ~out-of-bounds~, throw a *TypeError* exception. 1121 | 1. Let _elementSize_ be the Element Size value specified in for Element Type _type_. 1122 | 1. If _getIndex_ + _elementSize_ > _viewSize_, throw a *RangeError* exception. 1123 | 1. Let _bufferIndex_ be _getIndex_ + _viewOffset_. 1124 | 1. Return GetValueFromBuffer(_buffer_, _bufferIndex_, _type_, *false*, ~Unordered~, _isLittleEndian_). 1125 | 1126 |
    1127 | 1128 | 1129 |

    1130 | SetViewValue ( 1131 | _view_: an ECMAScript language value, 1132 | _requestIndex_: an ECMAScript language value, 1133 | _isLittleEndian_: an ECMAScript language value, 1134 | _type_: a TypedArray element type, 1135 | _value_: an ECMAScript language value, 1136 | ): either a normal completion containing *undefined* or a throw completion 1137 |

    1138 |
    1139 |
    description
    1140 |
    It is used by functions on DataView instances to store values into the view's buffer.
    1141 |
    1142 | 1143 | 1. Perform ? RequireInternalSlot(_view_, [[DataView]]). 1144 | 1. Assert: _view_ has a [[ViewedArrayBuffer]] internal slot. 1145 | 1. Let _getIndex_ be ? ToIndex(_requestIndex_). 1146 | 1. If IsBigIntElementType(_type_) is *true*, let _numberValue_ be ? ToBigInt(_value_). 1147 | 1. Otherwise, let _numberValue_ be ? ToNumber(_value_). 1148 | 1. Set _isLittleEndian_ to ToBoolean(_isLittleEndian_). 1149 | 1. Let _buffer_ be _view_.[[ViewedArrayBuffer]]. 1150 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1151 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~Unordered~). 1152 | 1. NOTE: Bounds checking is not a synchronizing operation when _view_'s backing buffer is a growable SharedArrayBuffer. 1153 | 1. Let _viewOffset_ be _view_.[[ByteOffset]]. 1154 | 1. Let _viewSize_ be _view_.[[ByteLength]]GetViewByteLength(_view_, _getBufferByteLength_). 1155 | 1. If _viewSize_ is ~out-of-bounds~, throw a *TypeError* exception. 1156 | 1. Let _elementSize_ be the Element Size value specified in for Element Type _type_. 1157 | 1. If _getIndex_ + _elementSize_ > _viewSize_, throw a *RangeError* exception. 1158 | 1. Let _bufferIndex_ be _getIndex_ + _viewOffset_. 1159 | 1. Perform SetValueInBuffer(_buffer_, _bufferIndex_, _type_, _numberValue_, *false*, ~Unordered~, _isLittleEndian_). 1160 | 1161 |
    1162 |
    1163 | 1164 | 1165 |

    Modifications to the DataView Constructor

    1166 | 1167 | 1168 |

    DataView ( _buffer_ [ , _byteOffset_ [ , _byteLength_ ] ] )

    1169 |

    When the `DataView` function is called with at least one argument _buffer_, the following steps are taken:

    1170 | 1171 | 1. If NewTarget is *undefined*, throw a *TypeError* exception. 1172 | 1. Perform ? RequireInternalSlot(_buffer_, [[ArrayBufferData]]). 1173 | 1. Let _offset_ be ? ToIndex(_byteOffset_). 1174 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1175 | 1. Let _bufferByteLength_ be _buffer_.[[ArrayBufferByteLength]]ArrayBufferByteLength(_buffer_, ~SeqCst~). 1176 | 1. If _offset_ > _bufferByteLength_, throw a *RangeError* exception. 1177 | 1. Let _bufferIsResizable_ be IsResizableArrayBuffer(_buffer_). 1178 | 1. Let _byteLengthChecked_ be ~empty~. 1179 | 1. If _bufferIsResizable_ is *true* and _byteLength_ is *undefined*, then 1180 | 1. Let _viewByteLength_ be ~auto~. 1181 | 1. IfElse if _byteLength_ is *undefined*, then 1182 | 1. Let _viewByteLength_ be _bufferByteLength_ - _offset_. 1183 | 1. Else, 1184 | 1. Set _byteLengthChecked_ to ? ToIndex(_byteLength_). 1185 | 1. Let _viewByteLength_ be ? ToIndex(_byteLength_)_byteLengthChecked_. 1186 | 1. If _offset_ + _viewByteLength_ > _bufferByteLength_, throw a *RangeError* exception. 1187 | 1. Let _O_ be ? OrdinaryCreateFromConstructor(NewTarget, *"%DataView.prototype%"*, « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »). 1188 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1189 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 1190 | 1. Set _bufferByteLength_ be _getBufferByteLength_(_buffer_). 1191 | 1. If _offset_ > _bufferByteLength_, throw a *RangeError* exception. 1192 | 1. If _byteLengthChecked_ is not ~empty~, then 1193 | 1. If _offset_ + _viewByteLength_ > _bufferByteLength_, throw a *RangeError* exception. 1194 | 1. Set _O_.[[ViewedArrayBuffer]] to _buffer_. 1195 | 1. Set _O_.[[ByteLength]] to _viewByteLength_. 1196 | 1. Set _O_.[[ByteOffset]] to _offset_. 1197 | 1. Return _O_. 1198 | 1199 |
    1200 |
    1201 | 1202 | 1203 |

    Modifications to Properties of the DataView Prototype Object

    1204 | 1205 | 1206 |

    get DataView.prototype.byteLength

    1207 |

    `DataView.prototype.byteLength` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    1208 | 1209 | 1. Let _O_ be the *this* value. 1210 | 1. Perform ? RequireInternalSlot(_O_, [[DataView]]). 1211 | 1. Assert: _O_ has a [[ViewedArrayBuffer]] internal slot. 1212 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 1213 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1214 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 1215 | 1. If IsViewOutOfBounds(_O_, _getBufferByteLength_) is *true*, throw a *TypeError* exception. 1216 | 1. Let _size_ be _O_.[[ByteLength]]GetViewByteLength(_O_, _getBufferByteLength_). 1217 | 1. Return 𝔽(_size_). 1218 | 1219 |
    1220 | 1221 | 1222 |

    get DataView.prototype.byteOffset

    1223 |

    `DataView.prototype.byteOffset` is an accessor property whose set accessor function is *undefined*. Its get accessor function performs the following steps:

    1224 | 1225 | 1. Let _O_ be the *this* value. 1226 | 1. Perform ? RequireInternalSlot(_O_, [[DataView]]). 1227 | 1. Assert: _O_ has a [[ViewedArrayBuffer]] internal slot. 1228 | 1. Let _buffer_ be _O_.[[ViewedArrayBuffer]]. 1229 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1230 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~SeqCst~). 1231 | 1. If IsViewOutOfBounds(_O_, _getBufferByteLength_) is *true*, throw a *TypeError* exception. 1232 | 1. Let _offset_ be _O_.[[ByteOffset]]. 1233 | 1. Return 𝔽(_offset_). 1234 | 1235 |
    1236 |
    1237 |
    1238 | 1239 | 1240 |

    Modifications to Atomics

    1241 | 1242 | 1243 |

    Modifications to Properties of the Atomics Object

    1244 | 1245 | 1246 |

    Atomics.compareExchange ( _typedArray_, _index_, _expectedValue_, _replacementValue_ )

    1247 |

    This function performs the following steps when called:

    1248 | 1249 | 1. Let _buffer_ be ? ValidateIntegerTypedArray(_typedArray_). 1250 | 1. Let _block_ be _buffer_.[[ArrayBufferData]]. 1251 | 1. Let _indexedPosition_ be ? ValidateAtomicAccess(_typedArray_, _index_). 1252 | 1. If _typedArray_.[[ContentType]] is ~BigInt~, then 1253 | 1. Let _expected_ be ? ToBigInt(_expectedValue_). 1254 | 1. Let _replacement_ be ? ToBigInt(_replacementValue_). 1255 | 1. Else, 1256 | 1. Let _expected_ be 𝔽(? ToIntegerOrInfinity(_expectedValue_)). 1257 | 1. Let _replacement_ be 𝔽(? ToIntegerOrInfinity(_replacementValue_)). 1258 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1259 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~Unordered~). 1260 | 1. NOTE: Bounds checking is not a synchronizing operation when _typedArray_'s backing buffer is a growable SharedArrayBuffer. 1261 | 1. Let _length_ be IntegerIndexedObjectLength(_typedArray_, _getBufferByteLength_). 1262 | 1. If ~length~ is ~out-of-bounds~, throw a *TypeError* exception. 1263 | 1. If _indexedPosition_ ≥ _length_, throw a *RangeError* exception. 1264 | 1. NOTE: The above checks isare not redundant with the checks in ValidateIntegerTypedArray and ValidateAtomicAccess because the call to ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached. These checks are only needed when _typedArray_'s backing buffer is an ArrayBuffer. 1265 | 1. Let _elementType_ be TypedArrayElementType(_typedArray_). 1266 | 1. Let _elementSize_ be TypedArrayElementSize(_typedArray_). 1267 | 1. Let _isLittleEndian_ be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. 1268 | 1. Let _expectedBytes_ be NumericToRawBytes(_elementType_, _expected_, _isLittleEndian_). 1269 | 1. Let _replacementBytes_ be NumericToRawBytes(_elementType_, _replacement_, _isLittleEndian_). 1270 | 1. If IsSharedArrayBuffer(_buffer_) is *true*, then 1271 | 1. Let _execution_ be the [[CandidateExecution]] field of the surrounding agent's Agent Record. 1272 | 1. Let _eventList_ be the [[EventList]] field of the element in _execution_.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier(). 1273 | 1. Let _rawBytesRead_ be a List of length _elementSize_ whose elements are nondeterministically chosen byte values. 1274 | 1. NOTE: In implementations, _rawBytesRead_ is the result of a load-link, of a load-exclusive, or of an operand of a read-modify-write instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency. 1275 | 1. NOTE: The comparison of the expected value and the read value is performed outside of the read-modify-write modification function to avoid needlessly strong synchronization when the expected value is not equal to the read value. 1276 | 1. If ByteListEqual(_rawBytesRead_, _expectedBytes_) is *true*, then 1277 | 1. Let _second_ be a new read-modify-write modification function with parameters (_oldBytes_, _newBytes_) that captures nothing and performs the following steps atomically when called: 1278 | 1. Return _newBytes_. 1279 | 1. Let _event_ be ReadModifyWriteSharedMemory { [[Order]]: ~SeqCst~, [[NoTear]]: *true*, [[Block]]: _block_, [[ByteIndex]]: _indexedPosition_, [[ElementSize]]: _elementSize_, [[Payload]]: _replacementBytes_, [[ModifyOp]]: _second_ }. 1280 | 1. Else, 1281 | 1. Let _event_ be ReadSharedMemory { [[Order]]: ~SeqCst~, [[NoTear]]: *true*, [[Block]]: _block_, [[ByteIndex]]: _indexedPosition_, [[ElementSize]]: _elementSize_ }. 1282 | 1. Append _event_ to _eventList_. 1283 | 1. Append Chosen Value Record { [[Event]]: _event_, [[ChosenValue]]: _rawBytesRead_ } to _execution_.[[ChosenValues]]. 1284 | 1. Else, 1285 | 1. Let _rawBytesRead_ be a List of length _elementSize_ whose elements are the sequence of _elementSize_ bytes starting with _block_[_indexedPosition_]. 1286 | 1. If ByteListEqual(_rawBytesRead_, _expectedBytes_) is *true*, then 1287 | 1. Store the individual bytes of _replacementBytes_ into _block_, starting at _block_[_indexedPosition_]. 1288 | 1. Return RawBytesToNumeric(_elementType_, _rawBytesRead_, _isLittleEndian_). 1289 | 1290 |
    1291 | 1292 | 1293 |

    Atomics.store ( _typedArray_, _index_, _value_ )

    1294 |

    This function performs the following steps when called:

    1295 | 1296 | 1. Let _buffer_ be ? ValidateIntegerTypedArray(_typedArray_). 1297 | 1. Let _indexedPosition_ be ? ValidateAtomicAccess(_typedArray_, _index_). 1298 | 1. If _typedArray_.[[ContentType]] is ~BigInt~, let _v_ be ? ToBigInt(_value_). 1299 | 1. Otherwise, let _v_ be 𝔽(? ToIntegerOrInfinity(_value_)). 1300 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1301 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~Unordered~). 1302 | 1. NOTE: Bounds checking is not a synchronizing operation when _typedArray_'s backing buffer is a growable SharedArrayBuffer. 1303 | 1. Let _length_ be IntegerIndexedObjectLength(_typedArray_, _getBufferByteLength_). 1304 | 1. If ~length~ is ~out-of-bounds~, throw a *TypeError* exception. 1305 | 1. If _indexedPosition_ ≥ _length_, throw a *RangeError* exception. 1306 | 1. Let _elementType_ be TypedArrayElementType(_typedArray_). 1307 | 1. Perform SetValueInBuffer(_buffer_, _indexedPosition_, _elementType_, _v_, *true*, ~SeqCst~). 1308 | 1. Return _v_. 1309 | 1310 |
    1311 |
    1312 | 1313 | 1314 |

    Modifications to Abstract Operations for Atomics

    1315 | 1316 | 1317 |

    1318 | AtomicReadModifyWrite ( 1319 | _typedArray_: an ECMAScript language value, 1320 | _index_: an ECMAScript language value, 1321 | _value_: an ECMAScript language value, 1322 | _op_: a read-modify-write modification function, 1323 | ): either a normal completion containing either a Number or a BigInt, or a throw completion 1324 |

    1325 |
    1326 |
    description
    1327 |
    _op_ takes two List of byte values arguments and returns a List of byte values. This operation atomically loads a value, combines it with another value, and stores the result of the combination. It returns the loaded value.
    1328 |
    1329 | 1330 | 1. Let _buffer_ be ? ValidateIntegerTypedArray(_typedArray_). 1331 | 1. Let _indexedPosition_ be ? ValidateAtomicAccess(_typedArray_, _index_). 1332 | 1. If _typedArray_.[[ContentType]] is ~BigInt~, let _v_ be ? ToBigInt(_value_). 1333 | 1. Otherwise, let _v_ be 𝔽(? ToIntegerOrInfinity(_value_)). 1334 | 1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. 1335 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~Unordered~). 1336 | 1. NOTE: Bounds checking is not a synchronizing operation when _typedArray_'s backing buffer is a growable SharedArrayBuffer. 1337 | 1. Let _length_ be IntegerIndexedObjectLength(_typedArray_, _getBufferByteLength_). 1338 | 1. If ~length~ is ~out-of-bounds~, throw a *TypeError* exception. 1339 | 1. If _indexedPosition_ ≥ _length_, throw a *RangeError* exception. 1340 | 1. NOTE: The above checks isare not redundant with the checks in ValidateIntegerTypedArray and ValidateAtomicAccess because the call to ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached. These checks are only needed when _typedArray_'s backing buffer is an ArrayBuffer. 1341 | 1. Let _elementType_ be TypedArrayElementType(_typedArray_). 1342 | 1. Return GetModifySetValueInBuffer(_buffer_, _indexedPosition_, _elementType_, _v_, _op_). 1343 | 1344 |
    1345 | 1346 | 1347 |

    1348 | ValidateAtomicAccess ( 1349 | _typedArray_: a TypedArray, 1350 | _requestIndex_: an ECMAScript language value, 1351 | ): either a normal completion containing an integer or a throw completion 1352 |

    1353 |
    1354 |
    1355 | 1356 | 1. Let _getBufferByteLength_ be MakeIdempotentArrayBufferByteLengthGetter(~Unordered~). 1357 | 1. Let _length_ be _typedArray_.[[ArrayLength]]IntegerIndexedObjectLength(_typedArray_, _getBufferByteLength_). 1358 | 1. Assert: _length_ is not ~out-of-bounds~. 1359 | 1. Let _accessIndex_ be ? ToIndex(_requestIndex_). 1360 | 1. Assert: _accessIndex_ ≥ 0. 1361 | 1. If _accessIndex_ ≥ _length_, throw a *RangeError* exception. 1362 | 1. Let _arrayTypeName_ be _typedArray_.[[TypedArrayName]]. 1363 | 1. Let _elementSize_ be the Element Size value specified in for _arrayTypeName_. 1364 | 1. Let _offset_ be _typedArray_.[[ByteOffset]]. 1365 | 1. Return (_accessIndex_ × _elementSize_) + _offset_. 1366 | 1367 |
    1368 |
    1369 |
    1370 | 1371 | 1372 |

    Resizable ArrayBuffer and growable SharedArrayBuffer Guidelines

    1373 | 1374 |

    The following are guidelines for ECMAScript programmers working with resizable ArrayBuffer and growable SharedArrayBuffer.

    1375 |

    We recommend that programs be tested in their deployment environments where possible. The amount of available physical memory differ greatly between hardware devices. Similarly, virtual memory subsystems also differ greatly between hardware devices as well as operating systems. An application that runs without out-of-memory errors on a 64-bit desktop web browser could run out of memory on a 32-bit mobile web browser.

    1376 |

    When choosing a value for the `"maxByteLength"` option for resizable ArrayBuffer and growable SharedArrayBuffer, we recommend that the smallest possible size for the application be chosen. We recommend that `"maxByteLength"` does not exceed 1073741824, or 1GiB.

    1377 |

    Please note that successfully constructing a ResizableArrayBuffer or a growable SharedArrayBuffer for a particular maximum size does not guarantee that future resizes and grows will succeed.

    1378 |
    1379 | 1380 | 1381 |

    The following are guidelines for ECMAScript implementers implementing resizable ArrayBuffer.

    1382 |

    Resizable ArrayBuffer can be implemented as copying upon resize, as in-place growth via reserving virtual memory up front, or as a combination of both for different values of the constructor's `"maxByteLength"` option.

    1383 |

    If a host is multi-tenanted (i.e. it runs many JavaScript applications simultaneously), such as a web browser, and its implementations choose to implement in-place growth by reserving virtual memory, we recommend that both 32-bit and 64-bit implementations throw for values of `"maxByteLength"` ≥ 1GiB to 1.5GiB. This is to reduce the likelihood a single application can exhaust the virtual memory address space and to reduce interoperability risks.

    1384 |

    If a host does not have virtual memory, such as those running on embedded devices without an MMU, or if a host only implements resizing by copying, it may accept any Number value for the `"maxByteLength"` option. However, we recommend a *RangeError* be thrown if a memory block of the requested size can never be allocated. For example, if the requested size is greater than the maximium amount of usable memory on the device.

    1385 |
    1386 | 1387 | 1388 |

    The following are guidelines for ECMAScript implementers implementing growable SharedArrayBuffer.

    1389 |

    We recommend growable SharedArrayBuffer be implemented as in-place growth via reserving virtual memory up front.

    1390 |

    Because grow operations can be concurrent with memory accesses on a growable SharedArrayBuffer, the constraints of the memory model require that even unordered accesses do not "tear" (bits of their values will not be mixed). In practice, this means the underlying data block of a growable SharedArrayBuffer cannot be grown by being copied without stopping the world. We do not recommend stopping the world as an implementation strategy because it introduces a serialization point and is slow.

    1391 |

    Grown memory must appear zeroed from the moment of its creation, including to any racy concurrent accesses. This can be accomplished via zero-filled-on-demand virtual memory pages, or careful synchronization if manually zeroing memory.

    1392 |

    In practice it is difficult to implement growable SharedArrayBuffer by copying on hosts that do not have virtual memory, such as those running on embedded devices without an MMU. Memory usage behaviour of growable SharedArrayBuffers on such hosts may significantly differ from that of hosts with virtual memory. Such hosts should clearly communicate memory usage expectations to users.

    1393 |
    1394 |
    1395 | 1396 | 1397 |

    Mechanical Changes Omitted for Brevity

    1398 |
      1399 |
    • Uses of [[ArrayLength]] on Integer-Indexed exotic objects are replaced with calls to IntegerIndexedObjectLength.

    • 1400 |
    • Uses of [[ByteLength]] on Integer-Indexed exotic objects are replaced with calls to IntegerIndexedByteLength.

    • 1401 |
    • Uses of [[ArrayBufferByteLength]] on buffer objects that may be growable SharedArrayBuffers are replaced with calls to ArrayBufferByteLength.

    • 1402 |
    1403 |
    1404 | --------------------------------------------------------------------------------