Skip to content

Commit 0d6429c

Browse files
rhashimotoshoestringr
authored andcommitted
Replace Facade Proxy with handwritten proxy. (rhashimoto#285)
* Replace Proxy with handwritten proxy for jRead/jWrite buffers. * Replace Proxy with handwritten proxy for VFS return data. --------- Co-authored-by: Roy Hashimoto <[email protected]>
1 parent 5604b2b commit 0d6429c

File tree

2 files changed

+222
-55
lines changed

2 files changed

+222
-55
lines changed

src/FacadeVFS.js

Lines changed: 221 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -406,72 +406,30 @@ export class FacadeVFS extends VFS.Base {
406406

407407
/**
408408
* Wrapped DataView for pointer arguments.
409-
* Pointers to a single value are passed using DataView. A Proxy
410-
* wrapper prevents use of incorrect type or endianness.
409+
* Pointers to a single value are passed using a DataView-like class.
410+
* This wrapper class prevents use of incorrect type or endianness, and
411+
* reacquires the underlying buffer when the WebAssembly memory is resized.
411412
* @param {'Int32'|'BigInt64'} type
412413
* @param {number} byteOffset
413414
* @returns {DataView}
414415
*/
415416
#makeTypedDataView(type, byteOffset) {
416-
const byteLength = type === "Int32" ? 4 : 8
417-
const getter = `get${type}`
418-
const setter = `set${type}`
419-
const makeDataView = () =>
420-
new DataView(
421-
this._module.HEAPU8.buffer,
422-
this._module.HEAPU8.byteOffset + byteOffset,
423-
byteLength,
424-
)
425-
let dataView = makeDataView()
426-
return new Proxy(dataView, {
427-
get(_, prop) {
428-
if (dataView.buffer.byteLength === 0) {
429-
// WebAssembly memory resize detached the buffer.
430-
dataView = makeDataView()
431-
}
432-
if (prop === getter) {
433-
return function (byteOffset, littleEndian) {
434-
if (!littleEndian) throw new Error("must be little endian")
435-
return dataView[prop](byteOffset, littleEndian)
436-
}
437-
}
438-
if (prop === setter) {
439-
return function (byteOffset, value, littleEndian) {
440-
if (!littleEndian) throw new Error("must be little endian")
441-
return dataView[prop](byteOffset, value, littleEndian)
442-
}
443-
}
444-
if (typeof prop === "string" && prop.match(/^(get)|(set)/)) {
445-
throw new Error("invalid type")
446-
}
447-
const result = dataView[prop]
448-
return typeof result === "function" ? result.bind(dataView) : result
449-
},
450-
})
417+
// @ts-ignore
418+
return new DataViewProxy(this._module, byteOffset, type)
451419
}
452420

453421
/**
422+
* Wrapped Uint8Array for buffer arguments.
423+
* Memory blocks are passed as a Uint8Array-like class. This wrapper
424+
* class reacquires the underlying buffer when the WebAssembly memory
425+
* is resized.
454426
* @param {number} byteOffset
455427
* @param {number} byteLength
428+
* @returns {Uint8Array}
456429
*/
457430
#makeDataArray(byteOffset, byteLength) {
458-
let target = this._module.HEAPU8.subarray(
459-
byteOffset,
460-
byteOffset + byteLength,
461-
)
462-
return new Proxy(target, {
463-
get: (_, prop, receiver) => {
464-
if (target.buffer.byteLength === 0) {
465-
// WebAssembly memory resize detached the buffer.
466-
target = this._module.HEAPU8.subarray(
467-
byteOffset,
468-
byteOffset + byteLength,
469-
)
470-
}
471-
const result = target[prop]
472-
return typeof result === "function" ? result.bind(target) : result
473-
},
474-
})
431+
// @ts-ignore
432+
return new Uint8ArrayProxy(this._module, byteOffset, byteLength)
475433
}
476434

477435
#decodeFilename(zName, flags) {
@@ -515,3 +473,212 @@ export class FacadeVFS extends VFS.Base {
515473
function delegalize(lo32, hi32) {
516474
return hi32 * 0x100000000 + lo32 + (lo32 < 0 ? 2 ** 32 : 0)
517475
}
476+
477+
// This class provides a Uint8Array-like interface for a WebAssembly memory
478+
// buffer. It is used to access memory blocks passed as arguments to
479+
// xRead, xWrite, etc. The class reacquires the underlying buffer when the
480+
// WebAssembly memory is resized, which can happen when the memory is
481+
// detached and resized by the WebAssembly module.
482+
//
483+
// Note that although this class implements the same methods as Uint8Array,
484+
// it is not a real Uint8Array and passing it to functions that expect
485+
// a Uint8Array may not work. Use subarray() to get a real Uint8Array
486+
// if needed.
487+
class Uint8ArrayProxy {
488+
#module
489+
490+
#_array = new Uint8Array()
491+
get #array() {
492+
if (this.#_array.buffer.byteLength === 0) {
493+
// WebAssembly memory resize detached the buffer so re-create the
494+
// array with the new buffer.
495+
this.#_array = this.#module.HEAPU8.subarray(
496+
this.byteOffset,
497+
this.byteOffset + this.byteLength,
498+
)
499+
}
500+
return this.#_array
501+
}
502+
503+
/**
504+
* @param {*} module
505+
* @param {number} byteOffset
506+
* @param {number} byteLength
507+
*/
508+
constructor(module, byteOffset, byteLength) {
509+
this.#module = module
510+
this.byteOffset = byteOffset
511+
this.length = this.byteLength = byteLength
512+
}
513+
514+
get buffer() {
515+
return this.#array.buffer
516+
}
517+
518+
at(index) {
519+
return this.#array.at(index)
520+
}
521+
copyWithin(target, start, end) {
522+
this.#array.copyWithin(target, start, end)
523+
}
524+
entries() {
525+
return this.#array.entries()
526+
}
527+
every(predicate) {
528+
return this.#array.every(predicate)
529+
}
530+
fill(value, start, end) {
531+
this.#array.fill(value, start, end)
532+
}
533+
filter(predicate) {
534+
return this.#array.filter(predicate)
535+
}
536+
find(predicate) {
537+
return this.#array.find(predicate)
538+
}
539+
findIndex(predicate) {
540+
return this.#array.findIndex(predicate)
541+
}
542+
findLast(predicate) {
543+
return this.#array.findLast(predicate)
544+
}
545+
findLastIndex(predicate) {
546+
return this.#array.findLastIndex(predicate)
547+
}
548+
forEach(callback) {
549+
this.#array.forEach(callback)
550+
}
551+
includes(value, start) {
552+
return this.#array.includes(value, start)
553+
}
554+
indexOf(value, start) {
555+
return this.#array.indexOf(value, start)
556+
}
557+
join(separator) {
558+
return this.#array.join(separator)
559+
}
560+
keys() {
561+
return this.#array.keys()
562+
}
563+
lastIndexOf(value, start) {
564+
return this.#array.lastIndexOf(value, start)
565+
}
566+
map(callback) {
567+
return this.#array.map(callback)
568+
}
569+
reduce(callback, initialValue) {
570+
return this.#array.reduce(callback, initialValue)
571+
}
572+
reduceRight(callback, initialValue) {
573+
return this.#array.reduceRight(callback, initialValue)
574+
}
575+
reverse() {
576+
this.#array.reverse()
577+
}
578+
set(array, offset) {
579+
this.#array.set(array, offset)
580+
}
581+
slice(start, end) {
582+
return this.#array.slice(start, end)
583+
}
584+
some(predicate) {
585+
return this.#array.some(predicate)
586+
}
587+
sort(compareFn) {
588+
this.#array.sort(compareFn)
589+
}
590+
subarray(begin, end) {
591+
return this.#array.subarray(begin, end)
592+
}
593+
toLocaleString(locales, options) {
594+
// @ts-ignore
595+
return this.#array.toLocaleString(locales, options)
596+
}
597+
toReversed() {
598+
return this.#array.toReversed()
599+
}
600+
toSorted(compareFn) {
601+
return this.#array.toSorted(compareFn)
602+
}
603+
toString() {
604+
return this.#array.toString()
605+
}
606+
values() {
607+
return this.#array.values()
608+
}
609+
with(index, value) {
610+
return this.#array.with(index, value)
611+
}
612+
[Symbol.iterator]() {
613+
return this.#array[Symbol.iterator]()
614+
}
615+
}
616+
617+
// This class provides a DataView-like interface for a WebAssembly memory
618+
// buffer, restricted to either Int32 or BigInt64 types. It also reacquires
619+
// the underlying buffer when the WebAssembly memory is resized, which can
620+
// happen when the memory is detached and resized by the WebAssembly module.
621+
class DataViewProxy {
622+
#module
623+
#type
624+
625+
#_view = new DataView(new ArrayBuffer(0))
626+
get #view() {
627+
if (this.#_view.buffer.byteLength === 0) {
628+
// WebAssembly memory resize detached the buffer so re-create the
629+
// view with the new buffer.
630+
this.#_view = new DataView(
631+
this.#module.HEAPU8.buffer,
632+
this.#module.HEAPU8.byteOffset + this.byteOffset,
633+
)
634+
}
635+
return this.#_view
636+
}
637+
638+
/**
639+
* @param {*} module
640+
* @param {number} byteOffset
641+
* @param {'Int32'|'BigInt64'} type
642+
*/
643+
constructor(module, byteOffset, type) {
644+
this.#module = module
645+
this.byteOffset = byteOffset
646+
this.#type = type
647+
}
648+
649+
get buffer() {
650+
return this.#view.buffer
651+
}
652+
get byteLength() {
653+
return this.#type === "Int32" ? 4 : 8
654+
}
655+
656+
getInt32(byteOffset, littleEndian) {
657+
if (this.#type !== "Int32") {
658+
throw new Error("invalid type")
659+
}
660+
if (!littleEndian) throw new Error("must be little endian")
661+
return this.#view.getInt32(byteOffset, littleEndian)
662+
}
663+
setInt32(byteOffset, value, littleEndian) {
664+
if (this.#type !== "Int32") {
665+
throw new Error("invalid type")
666+
}
667+
if (!littleEndian) throw new Error("must be little endian")
668+
this.#view.setInt32(byteOffset, value, littleEndian)
669+
}
670+
getBigInt64(byteOffset, littleEndian) {
671+
if (this.#type !== "BigInt64") {
672+
throw new Error("invalid type")
673+
}
674+
if (!littleEndian) throw new Error("must be little endian")
675+
return this.#view.getBigInt64(byteOffset, littleEndian)
676+
}
677+
setBigInt64(byteOffset, value, littleEndian) {
678+
if (this.#type !== "BigInt64") {
679+
throw new Error("invalid type")
680+
}
681+
if (!littleEndian) throw new Error("must be little endian")
682+
this.#view.setBigInt64(byteOffset, value, littleEndian)
683+
}
684+
}

src/examples/MemoryVFS.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export class MemoryVFS extends FacadeVFS {
122122
}
123123

124124
// Copy data.
125-
new Uint8Array(file.data, iOffset, pData.byteLength).set(pData)
125+
new Uint8Array(file.data, iOffset, pData.byteLength).set(pData.subarray())
126126
file.size = Math.max(file.size, iOffset + pData.byteLength)
127127
return VFS.SQLITE_OK
128128
}

0 commit comments

Comments
 (0)