@swf2es/runtime 0.1.20 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/avm2/amf.d.ts +1 -1
  2. package/dist/avm2/amf.d.ts.map +1 -1
  3. package/dist/avm2/amf.js +1 -1
  4. package/dist/avm2/amf.js.map +1 -1
  5. package/dist/avm2/enumeration.d.ts +1 -19
  6. package/dist/avm2/enumeration.d.ts.map +1 -1
  7. package/dist/avm2/enumeration.js +1 -51
  8. package/dist/avm2/enumeration.js.map +1 -1
  9. package/dist/avm2/errors.d.ts +2 -0
  10. package/dist/avm2/errors.d.ts.map +1 -1
  11. package/dist/avm2/errors.js +40 -2
  12. package/dist/avm2/errors.js.map +1 -1
  13. package/dist/avm2/index.d.ts +3 -1
  14. package/dist/avm2/index.d.ts.map +1 -1
  15. package/dist/avm2/index.js +3 -1
  16. package/dist/avm2/index.js.map +1 -1
  17. package/dist/avm2/natives/aliases.js +1 -1
  18. package/dist/avm2/natives/aliases.js.map +1 -1
  19. package/dist/avm2/natives/bytearray.d.ts +0 -160
  20. package/dist/avm2/natives/bytearray.d.ts.map +1 -1
  21. package/dist/avm2/natives/bytearray.js +1 -506
  22. package/dist/avm2/natives/bytearray.js.map +1 -1
  23. package/dist/avm2/natives/bytes.d.ts +141 -0
  24. package/dist/avm2/natives/bytes.d.ts.map +1 -0
  25. package/dist/avm2/natives/bytes.js +458 -0
  26. package/dist/avm2/natives/bytes.js.map +1 -0
  27. package/dist/avm2/natives/concurrent.js +1 -1
  28. package/dist/avm2/natives/concurrent.js.map +1 -1
  29. package/dist/avm2/natives/shared-bytes.d.ts +45 -0
  30. package/dist/avm2/natives/shared-bytes.d.ts.map +1 -0
  31. package/dist/avm2/natives/shared-bytes.js +52 -0
  32. package/dist/avm2/natives/shared-bytes.js.map +1 -0
  33. package/dist/avm2/natives/shell.d.ts.map +1 -1
  34. package/dist/avm2/natives/shell.js +3 -1
  35. package/dist/avm2/natives/shell.js.map +1 -1
  36. package/dist/avm2/options.d.ts +1 -1
  37. package/dist/avm2/options.d.ts.map +1 -1
  38. package/dist/avm2/runtime-xml.d.ts +11 -0
  39. package/dist/avm2/runtime-xml.d.ts.map +1 -0
  40. package/dist/avm2/runtime-xml.js +44 -0
  41. package/dist/avm2/runtime-xml.js.map +1 -0
  42. package/dist/avm2/runtime.d.ts +5 -13
  43. package/dist/avm2/runtime.d.ts.map +1 -1
  44. package/dist/avm2/runtime.js +17 -67
  45. package/dist/avm2/runtime.js.map +1 -1
  46. package/dist/avm2/sparse-indices.d.ts +34 -0
  47. package/dist/avm2/sparse-indices.d.ts.map +1 -0
  48. package/dist/avm2/sparse-indices.js +54 -0
  49. package/dist/avm2/sparse-indices.js.map +1 -0
  50. package/package.json +2 -2
  51. package/src/avm2/amf.ts +1 -1
  52. package/src/avm2/enumeration.ts +1 -76
  53. package/src/avm2/errors.ts +48 -3
  54. package/src/avm2/index.ts +9 -12
  55. package/src/avm2/natives/aliases.ts +1 -1
  56. package/src/avm2/natives/bytearray.ts +1 -616
  57. package/src/avm2/natives/bytes.ts +543 -0
  58. package/src/avm2/natives/concurrent.ts +1 -1
  59. package/src/avm2/natives/shared-bytes.ts +99 -0
  60. package/src/avm2/natives/shell.ts +3 -8
  61. package/src/avm2/options.ts +1 -1
  62. package/src/avm2/runtime-xml.ts +54 -0
  63. package/src/avm2/runtime.ts +31 -78
  64. package/src/avm2/sparse-indices.ts +80 -0
@@ -24,590 +24,12 @@ import type { IndexHook } from "../hooks.js";
24
24
  import type { Runtime } from "../runtime.js";
25
25
  import type { Traits } from "../traits.js";
26
26
  import { fromCodes, fromUtf8, utf8 } from "../utf8.js";
27
+ import { Bytes, bytesOf, copyBytes, GLOBAL_MEMORY_MIN_SIZE } from "./bytes.js";
27
28
  import { type Natives, registerNativeClass } from "./define.js";
28
29
 
29
- const kGrowthIncr = 4096;
30
- const kHugeGrowthThreshold = 24 * 1024 * 1024;
31
- const kHugeGrowthIncr = 24 * 1024 * 1024;
32
- /**
33
- * The most a ByteArray holds: what a 32-bit avmshell, as the oracle's, can
34
- * allocate, beyond which it fails with MemoryError 1000, and less than a
35
- * browser's ArrayBuffer may be refused at.
36
- */
37
- const kMaxCapacity = 0x80000000;
38
- /** DomainEnv::GLOBAL_MEMORY_MIN_SIZE: domain memory's least length. */
39
- export const GLOBAL_MEMORY_MIN_SIZE = 1024;
40
30
  const kAMF0 = 0;
41
31
  const kAMF3 = 3;
42
32
 
43
- /**
44
- * A shareable ByteArray's memory once it has crossed to another worker,
45
- * which every worker's ByteArray for it uses, as avmplus' ByteArrays share
46
- * one Buffer: its bytes grow in place, and its length is one for all.
47
- * Plain data, which a structured clone carries to another thread.
48
- */
49
- export interface SharedBytesMemory {
50
- /** The same in every worker, by which each VM keeps one ByteArray for it, as avmplus interns it. */
51
- id: number;
52
- /** Its words: the length, as a uint32. */
53
- meta: SharedArrayBuffer;
54
- /** Its bytes, as long as its capacity: growable, up to the most a ByteArray holds where it can. */
55
- data: SharedArrayBuffer;
56
- }
57
-
58
- /** The growable SharedArrayBuffer of ES2024, which the runtime's ES2022 library does not type. */
59
- interface GrowableShared {
60
- readonly byteLength: number;
61
- readonly maxByteLength: number;
62
- grow(length: number): void;
63
- }
64
-
65
- type GrowableSharedConstructor = new (
66
- length: number,
67
- options: { maxByteLength: number },
68
- ) => SharedArrayBuffer;
69
-
70
- /** The ways a browser serves a page cross-origin isolated, for the error a page that is not gets. */
71
- const ISOLATION =
72
- "a cross-origin isolated page (served with Cross-Origin-Opener-Policy: same-origin and " +
73
- "Cross-Origin-Embedder-Policy: credentialless or require-corp)";
74
-
75
- /**
76
- * Whether this thread can share memory with others: SharedArrayBuffer is
77
- * there, as node always has it, and a browser only for a page that is
78
- * cross-origin isolated.
79
- */
80
- export function canShareMemory(): boolean {
81
- const g = globalThis as { crossOriginIsolated?: boolean };
82
- return typeof SharedArrayBuffer === "function" && g.crossOriginIsolated !== false;
83
- }
84
-
85
- /** What a page that is not cross-origin isolated gets for sharing `what` with another worker. */
86
- export function notIsolated(what: string): Error {
87
- return new Error(
88
- `swf2es: sharing ${what} with another worker needs shared memory, which a browser gives only ${ISOLATION}`,
89
- );
90
- }
91
-
92
- /** Each runtime's ByteArray for each shared memory, by its id. */
93
- const sharedObjects = new WeakMap<Runtime, Map<number, WeakRef<AsObject>>>();
94
-
95
- function sharedObjectsOf(rt: Runtime): Map<number, WeakRef<AsObject>> {
96
- let objects = sharedObjects.get(rt);
97
- if (!objects) {
98
- objects = new Map();
99
- sharedObjects.set(rt, objects);
100
- }
101
-
102
- return objects;
103
- }
104
-
105
- /** A count of bytes, which a ByteArray and its runtime share. */
106
- interface Count {
107
- bytes: number;
108
- }
109
-
110
- /** The capacity of each runtime's ByteArrays, live or not yet collected. */
111
- const runtimeCapacity = new WeakMap<Runtime, Count>();
112
- /** A ByteArray collected: its capacity no longer counted, by its runtime's count and its own. */
113
- const collected = new FinalizationRegistry<{ runtime: Count; own: Count }>(({ runtime, own }) => {
114
- runtime.bytes -= own.bytes;
115
- });
116
-
117
- /** The capacity of `rt`'s ByteArrays, which avmshell's System counts in its memory. */
118
- export function byteArrayCapacity(rt: Runtime): number {
119
- return runtimeCapacity.get(rt)?.bytes ?? 0;
120
- }
121
-
122
- /**
123
- * A ByteArray's state: its buffer, as long as its capacity, and its length
124
- * and position. A read or write takes its offset first: growing replaces
125
- * the buffer and its view.
126
- *
127
- * A length or position is stored through `>>> 0`, as a uint32: an int32
128
- * while below 2^31, and a double only from there.
129
- * JavaScriptCore keeps a number its JIT once made a double a double, and
130
- * an addition that has seen one makes doubles from then on: a double
131
- * offset fails the int32 check of a DataView or typed array access, and
132
- * after enough failures JavaScriptCore calls DataView's methods instead
133
- * of inlining them, a quarter slower on every read and write. V8 makes
134
- * small integers of them itself.
135
- */
136
- export class Bytes {
137
- buffer: Uint8Array = new Uint8Array(0);
138
- view = new DataView(this.buffer.buffer);
139
- length = 0;
140
- position = 0;
141
- littleEndian = false;
142
- /** As ByteArray's constructor: ByteArray.defaultObjectEncoding when it is made. */
143
- objectEncoding: number;
144
- /** Whether it is the domain memory, which it then tells when its buffer or length changes. */
145
- subscribed = false;
146
- /**
147
- * Whether `shareable` was set true, which cannot be undone: the
148
- * ByteArray then crosses to another worker as its memory, not a copy.
149
- */
150
- shareable = false;
151
- /**
152
- * Its memory once it has crossed to another worker (share), which its
153
- * buffer and view are then over, tracking its length as it grows; null
154
- * until then, when it is a ByteArray like any other.
155
- */
156
- shared: SharedBytesMemory | null = null;
157
- /** The shared memory's words, the length first, and its bytes as words for the atomics. */
158
- private sharedWords: Int32Array | null = null;
159
- private sharedInts: Int32Array | null = null;
160
- /** Its runtime's capacity count, and its own part of it. */
161
- private readonly capacity: { runtime: Count; own: Count };
162
-
163
- constructor(
164
- readonly rt: Runtime,
165
- readonly owner: AsObject,
166
- ) {
167
- this.objectEncoding = rt.defaultObjectEncoding;
168
- let runtime = runtimeCapacity.get(rt);
169
- if (!runtime) {
170
- runtime = { bytes: 0 };
171
- runtimeCapacity.set(rt, runtime);
172
- }
173
-
174
- this.capacity = { runtime, own: { bytes: 0 } };
175
- collected.register(this, this.capacity);
176
- }
177
-
178
- /** Its buffer replaced by `next`, its runtime's capacity count with it. */
179
- setBuffer(next: Uint8Array<ArrayBuffer>): void {
180
- const { runtime, own } = this.capacity;
181
- runtime.bytes += next.length - own.bytes;
182
- own.bytes = next.length;
183
- this.buffer = next;
184
- this.view = new DataView(next.buffer);
185
- }
186
-
187
- get available(): number {
188
- return this.length > this.position ? this.length - this.position : 0;
189
- }
190
-
191
- /** As Grower::ReallocBackingStore: a new buffer, the old length's bytes copied and the rest zero. */
192
- private realloc(capacityIn: number, minimum: number, fromSetter: boolean): void {
193
- let capacity = capacityIn;
194
- if (capacity < minimum) {
195
- capacity = minimum;
196
- }
197
-
198
- if (!(this.buffer.length === 0 && fromSetter) && capacity < kGrowthIncr) {
199
- capacity = kGrowthIncr;
200
- }
201
-
202
- if (minimum > kMaxCapacity) {
203
- throw this.rt.error("flash.errors::MemoryError", 1000);
204
- }
205
-
206
- capacity = Math.min(capacity, kMaxCapacity);
207
- if (capacity === this.buffer.length) {
208
- return;
209
- }
210
-
211
- if (this.shared !== null) {
212
- this.reshare(capacity);
213
- return;
214
- }
215
-
216
- let next: Uint8Array<ArrayBuffer>;
217
- try {
218
- next = new Uint8Array(capacity);
219
- } catch {
220
- // The host would not give it the memory, as the system would not give avmplus.
221
- throw this.rt.error("flash.errors::MemoryError", 1000);
222
- }
223
-
224
- next.set(this.buffer.subarray(0, Math.min(capacity, this.length)));
225
- this.setBuffer(next);
226
- }
227
-
228
- /** As Grower::EnsureWritableCapacity: double, at least `minimum`, and 4096 unless the setter sizes an empty one. */
229
- private ensure(minimum: number, fromSetter: boolean): void {
230
- if (minimum > 0xffffffff - 2 * 4096) {
231
- throw this.rt.error("flash.errors::MemoryError", 1000);
232
- }
233
-
234
- if (minimum > this.buffer.length) {
235
- this.realloc(this.buffer.length * 2, minimum, fromSetter);
236
- }
237
- }
238
-
239
- /** As ByteArray::SetLengthCommon. */
240
- setLength(length: number, fromSetter = false): void {
241
- if (this.subscribed && length < GLOBAL_MEMORY_MIN_SIZE) {
242
- throw this.rt.error("RangeError", 1506);
243
- }
244
-
245
- this.reserve(length, fromSetter);
246
- this.length = length >>> 0;
247
- if (this.position > length) {
248
- this.position = length >>> 0;
249
- }
250
-
251
- this.notify();
252
- }
253
-
254
- /**
255
- * The length made at least `length`, as a write past the end makes it:
256
- * shared, by a compare and swap that only ever raises it, so that
257
- * workers appending at once never lower what another made it.
258
- */
259
- extend(length: number): void {
260
- if (this.shared === null) {
261
- this.setLength(length);
262
- return;
263
- }
264
-
265
- this.reserve(length, false);
266
- this.raise(length);
267
- this.notify();
268
- }
269
-
270
- /** A shared length raised to `length` unless another worker made it as long. */
271
- private raise(length: number): void {
272
- const words = this.sharedWords as Int32Array;
273
- for (;;) {
274
- const now = Atomics.load(words, 0) >>> 0;
275
- if (now >= length || Atomics.compareExchange(words, 0, now | 0, length | 0) >>> 0 === now) {
276
- return;
277
- }
278
- }
279
- }
280
-
281
- /** As Grower::SetLengthCommon, before the length is set: the capacity for `length`. */
282
- private reserve(length: number, fromSetter: boolean): void {
283
- if (!fromSetter || (length < kHugeGrowthThreshold && this.length < kHugeGrowthThreshold)) {
284
- if (length > this.buffer.length) {
285
- this.ensure(length, fromSetter);
286
- }
287
- } else {
288
- const rounded = Math.ceil(length / kHugeGrowthIncr) * kHugeGrowthIncr;
289
- const capacity = rounded <= 0xffffffff - 2 * 4096 ? rounded : length;
290
- if (capacity !== this.buffer.length) {
291
- this.realloc(capacity, length, true);
292
- }
293
- }
294
- }
295
-
296
- /** Tell the domain memory, if this is it, where the bytes are now: a new view only if they moved or their length changed. */
297
- notify(): void {
298
- const rt = this.rt;
299
- if (
300
- this.subscribed &&
301
- (rt.memory.buffer !== this.buffer.buffer || rt.memoryLength !== this.length)
302
- ) {
303
- rt.memory = new DataView(this.buffer.buffer, 0, this.length);
304
- }
305
- }
306
-
307
- /** As ByteArray::Clear: no buffer at all; shared, every worker's ByteArray for it empty. */
308
- clear(): void {
309
- if (this.subscribed) {
310
- throw this.rt.error("RangeError", 1506);
311
- }
312
-
313
- this.position = 0;
314
- if (this.shared !== null) {
315
- // The memory cannot shrink: what it held is zeroed, as a new buffer
316
- // would be once it grows again, before the length is told, so that a
317
- // worker that grows it once it is empty writes into zeroes already.
318
- this.buffer.fill(0);
319
- this.length = 0;
320
- return;
321
- }
322
-
323
- this.setBuffer(new Uint8Array(0));
324
- this.length = 0;
325
- }
326
-
327
- /**
328
- * Its memory, shared from now on: a growable SharedArrayBuffer its bytes
329
- * move to once, and that this ByteArray, and every worker's ByteArray for
330
- * it, uses from then on. Where memory cannot be shared, the error that
331
- * names cross-origin isolation.
332
- */
333
- share(): SharedBytesMemory {
334
- if (this.shared !== null) {
335
- return this.shared;
336
- }
337
-
338
- if (!canShareMemory()) {
339
- throw notIsolated("a shareable ByteArray");
340
- }
341
-
342
- const capacity = Math.max(this.buffer.length, this.length);
343
- const Shared = SharedArrayBuffer as unknown as GrowableSharedConstructor;
344
- let data: SharedArrayBuffer | null = null;
345
- // As much room to grow as a ByteArray has, or as much as the host reserves.
346
- for (let max = kMaxCapacity; data === null; max /= 2) {
347
- try {
348
- data = new Shared(capacity, { maxByteLength: Math.max(max, capacity) });
349
- } catch {
350
- if (max <= capacity) {
351
- throw this.rt.error("flash.errors::MemoryError", 1000);
352
- }
353
- }
354
- }
355
-
356
- new Uint8Array(data).set(this.buffer);
357
- const memory: SharedBytesMemory = {
358
- id: Math.floor(Math.random() * 2 ** 52),
359
- meta: new SharedArrayBuffer(8),
360
- data,
361
- };
362
- new Int32Array(memory.meta)[0] = this.length | 0;
363
- this.adopt(memory);
364
- sharedObjectsOf(this.rt).set(memory.id, new WeakRef(this.owner));
365
- return memory;
366
- }
367
-
368
- /**
369
- * Use `memory`, shared, as its bytes and length from now on. Its length
370
- * becomes an accessor of the memory's word on this object alone, so that
371
- * every read sees another worker's change and every write is seen, and
372
- * a ByteArray that shares nothing reads a plain field as before.
373
- */
374
- adopt(memory: SharedBytesMemory): void {
375
- this.shared = memory;
376
- this.shareable = true;
377
- const words = new Int32Array(memory.meta);
378
- this.sharedWords = words;
379
- Object.defineProperty(this, "length", {
380
- get: () => Atomics.load(words, 0) >>> 0,
381
- set: (length: number) => Atomics.store(words, 0, length | 0),
382
- configurable: true,
383
- });
384
- // Views that track the memory's length as any worker grows it.
385
- this.buffer = new Uint8Array(memory.data);
386
- this.view = new DataView(memory.data);
387
- this.sharedInts = new Int32Array(memory.data);
388
- const { runtime, own } = this.capacity;
389
- runtime.bytes += memory.data.byteLength - own.bytes;
390
- own.bytes = memory.data.byteLength;
391
- if (this.subscribed) {
392
- this.rt.refreshMemory = () => this.notify();
393
- }
394
-
395
- this.notify();
396
- }
397
-
398
- /**
399
- * A shared memory's capacity made at least `capacity`: it grows in place,
400
- * which every worker sees, and never shrinks, as another worker may have
401
- * grown it past what this one asks for since. Nor is what a smaller
402
- * buffer would drop zeroed: without avmplus' safepoint, another worker
403
- * may have grown the length again and written there meanwhile, so bytes
404
- * past a lowered length may come back when it grows again.
405
- */
406
- private reshare(capacity: number): void {
407
- const data = (this.shared as SharedBytesMemory).data as unknown as GrowableShared;
408
- if (capacity <= data.byteLength) {
409
- return;
410
- }
411
-
412
- if (capacity > data.maxByteLength) {
413
- throw this.rt.error("flash.errors::MemoryError", 1000);
414
- }
415
-
416
- try {
417
- data.grow(capacity);
418
- } catch {
419
- // Another worker grew it meanwhile, to as much or more, or the host refused.
420
- if (data.byteLength < capacity) {
421
- throw this.rt.error("flash.errors::MemoryError", 1000);
422
- }
423
- }
424
-
425
- const { runtime, own } = this.capacity;
426
- runtime.bytes += data.byteLength - own.bytes;
427
- own.bytes = data.byteLength;
428
- }
429
-
430
- /** As ByteArray::CAS, once the index is checked: atomic where the memory is shared. */
431
- compareAndSwapInt(at: number, expected: number, next: number): number {
432
- if (this.sharedInts !== null) {
433
- return Atomics.compareExchange(this.sharedInts, at >> 2, expected, next);
434
- }
435
-
436
- const previous = this.view.getInt32(at, true);
437
- if (previous === expected) {
438
- this.view.setInt32(at, next, true);
439
- }
440
-
441
- return previous;
442
- }
443
-
444
- /**
445
- * As UnprotectedAtomicCompareAndSwapLength: the length set to `next`, as
446
- * its setter sets it, if it is `expected`; the length before. Shared,
447
- * the capacity is made first and the length swapped atomically, so that
448
- * of workers growing it at once one wins.
449
- */
450
- compareAndSwapLength(expected: number, next: number): number {
451
- if (this.shared === null) {
452
- const previous = this.length;
453
- if (previous === expected) {
454
- this.setLength(next, true);
455
- }
456
-
457
- return previous;
458
- }
459
-
460
- // Read once: another worker may change it between two reads.
461
- const now = this.length;
462
- if (now !== expected) {
463
- return now;
464
- }
465
-
466
- // Only grows the memory, so it does nothing another worker would undo if the swap fails.
467
- this.reserve(next, true);
468
- const previous =
469
- Atomics.compareExchange(this.sharedWords as Int32Array, 0, expected | 0, next | 0) >>> 0;
470
- if (previous === expected && this.position > next) {
471
- this.position = next;
472
- }
473
-
474
- this.notify();
475
- return previous;
476
- }
477
-
478
- /** As requestBytesForShortRead: the offset of `n` bytes to read, or EOFError. */
479
- shortRead(n: number): number {
480
- const at = this.position;
481
- if (at >= this.length || at + n > this.length) {
482
- throw this.rt.error("flash.errors::EOFError", 2030);
483
- }
484
-
485
- this.position = (at + n) >>> 0;
486
- return at;
487
- }
488
-
489
- /** As requestBytesForShortWrite: the offset of `n` bytes to write, the length grown to hold them. */
490
- shortWrite(n: number): number {
491
- const at = this.position;
492
- if (at >= this.length || at + n > this.length) {
493
- if (at + n > 0xffffffff) {
494
- throw this.rt.error("flash.errors::MemoryError", 1000);
495
- }
496
-
497
- this.extend(at + n);
498
- }
499
-
500
- this.position = (at + n) >>> 0;
501
- return at;
502
- }
503
-
504
- /** As DataInput::CheckEOF. */
505
- checkEOF(count: number): void {
506
- if (this.available < count) {
507
- throw this.rt.error("flash.errors::EOFError", 2030);
508
- }
509
- }
510
-
511
- /** As ByteArray::Read: `count` bytes from the position. */
512
- read(count: number): Uint8Array {
513
- this.checkEOF(count);
514
- const bytes = this.buffer.slice(this.position, this.position + count);
515
- this.position = (this.position + count) >>> 0;
516
- return bytes;
517
- }
518
-
519
- /** As read, but the bytes themselves, not a copy: for a caller that only reads them, at once. */
520
- readView(count: number): Uint8Array {
521
- this.checkEOF(count);
522
- const bytes = this.buffer.subarray(this.position, this.position + count);
523
- this.position = (this.position + count) >>> 0;
524
- return bytes;
525
- }
526
-
527
- /**
528
- * As ByteArray::Write: `count` bytes of `source` from `offset`, all of
529
- * it by default, at the position; the length grows to the position after.
530
- */
531
- write(source: Uint8Array, offset = 0, count = source.length): void {
532
- // Before the buffer is read: making room may replace it.
533
- const at = this.claim(count);
534
- copyBytes(this.buffer, at, source, offset, count);
535
- }
536
-
537
- /**
538
- * As ByteArray::Write but for the bytes, which the caller writes itself:
539
- * room for `count` at the position, which moves past them, the length
540
- * growing to it. Where in the buffer they start.
541
- */
542
- claim(count: number): number {
543
- if (count > 0xffffffff - this.position) {
544
- throw this.rt.error("flash.errors::MemoryError", 1000);
545
- }
546
-
547
- const at = this.position;
548
- const end = (at + count) >>> 0;
549
- if (end > this.buffer.length) {
550
- this.ensure(end, false);
551
- }
552
-
553
- this.position = end;
554
- if (this.length < end) {
555
- if (this.shared === null) {
556
- this.length = end;
557
- } else {
558
- this.raise(end);
559
- }
560
- }
561
-
562
- this.notify();
563
- return at;
564
- }
565
-
566
- /** As ByteArray's operator[] for a write: the length grows to hold `index`. */
567
- setByte(index: number, value: number): void {
568
- if (index >= this.length) {
569
- if (index === 0xffffffff) {
570
- throw this.rt.error("flash.errors::MemoryError", 1000);
571
- }
572
-
573
- this.extend(index + 1);
574
- }
575
-
576
- this.buffer[index] = value;
577
- }
578
- }
579
-
580
- /** Below this many bytes, copyBytes copies one at a time. */
581
- const COPY_LOOP_LENGTH = 32;
582
-
583
- /**
584
- * `count` bytes of `source` from `offset` copied into `target` at `at`,
585
- * as memmove copies them where the two are one array. Part of `source`,
586
- * fewer than COPY_LOOP_LENGTH bytes, is copied one at a time: the
587
- * subarray set() would take costs more than they do, in JavaScriptCore up
588
- * to about 64 bytes and in V8 up to about 32. All of it set() copies
589
- * faster than a loop in V8, and as fast in JavaScriptCore.
590
- */
591
- function copyBytes(
592
- target: Uint8Array,
593
- at: number,
594
- source: Uint8Array,
595
- offset: number,
596
- count: number,
597
- ): void {
598
- if (source === target) {
599
- target.copyWithin(at, offset, offset + count);
600
- } else if (offset === 0 && count === source.length) {
601
- target.set(source, at);
602
- } else if (count < COPY_LOOP_LENGTH) {
603
- for (let i = 0; i < count; i++) {
604
- target[at + i] = source[offset + i];
605
- }
606
- } else {
607
- target.set(source.subarray(offset, offset + count), at);
608
- }
609
- }
610
-
611
33
  /** How a ByteArray's elements are indexed: its bytes; past its length undefined, and a write grows it. */
612
34
  export const byteArrayHook: IndexHook & { create: (traits: Traits, rt: Runtime) => AsObject } = {
613
35
  // Its state from the start: it takes defaultObjectEncoding as it is then.
@@ -629,43 +51,6 @@ export const byteArrayHook: IndexHook & { create: (traits: Traits, rt: Runtime)
629
51
  },
630
52
  };
631
53
 
632
- /** A ByteArray's state, made the first time a native needs it. */
633
- export function bytesOf(rt: Runtime, o: AsObject): Bytes {
634
- if (!o.$bytes) {
635
- o.$bytes = new Bytes(rt, o);
636
- }
637
-
638
- return o.$bytes;
639
- }
640
-
641
- /**
642
- * A shareable ByteArray's memory, shared from now on, as it crosses to
643
- * another worker; where memory cannot be shared, the error that names
644
- * cross-origin isolation.
645
- */
646
- export function shareBytes(rt: Runtime, o: AsObject): SharedBytesMemory {
647
- return bytesOf(rt, o).share();
648
- }
649
-
650
- /** This VM's one ByteArray for a shared memory, as avmplus interns one for its Buffer: shareable, at position 0. */
651
- export function sharedByteArray(rt: Runtime, memory: SharedBytesMemory): AsObject {
652
- const objects = sharedObjectsOf(rt);
653
- const known = objects.get(memory.id)?.deref();
654
- if (known) {
655
- return known;
656
- }
657
-
658
- const o = rt.construct(rt.classNamed("flash.utils::ByteArray")) as AsObject;
659
- bytesOf(rt, o).adopt(memory);
660
- objects.set(memory.id, new WeakRef(o));
661
- return o;
662
- }
663
-
664
- /** This VM's ByteArray for the shared memory `id`, if it has one. */
665
- export function knownSharedByteArray(rt: Runtime, id: number): AsObject | undefined {
666
- return sharedObjectsOf(rt).get(id)?.deref();
667
- }
668
-
669
54
  /** The longest string readUTFBytes reads a character at a time. */
670
55
  const SHORT_STRING = 24;
671
56