diff options
Diffstat (limited to 'ext/ffi/00_ffi.js')
-rw-r--r-- | ext/ffi/00_ffi.js | 185 |
1 files changed, 175 insertions, 10 deletions
diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 25eba0233..48e2e4f92 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -6,7 +6,142 @@ const __bootstrap = window.__bootstrap; const { ArrayBuffer, + Uint8Array, + BigInt, + Number, + TypeError, } = window.__bootstrap.primordials; + + function unpackU64([hi, lo]) { + return BigInt(hi) << 32n | BigInt(lo); + } + + function packU64(value) { + return [Number(value >> 32n), Number(value & 0xFFFFFFFFn)]; + } + + function unpackI64([hi, lo]) { + const u64 = unpackU64([hi, lo]); + return u64 >> 63n ? u64 - 0x10000000000000000n : u64; + } + + class UnsafePointerView { + pointer; + + constructor(pointer) { + this.pointer = pointer; + } + + getUint8(offset = 0) { + return core.opSync( + "op_ffi_read_u8", + packU64(this.pointer.value + BigInt(offset)), + ); + } + + getInt8(offset = 0) { + return core.opSync( + "op_ffi_read_i8", + packU64(this.pointer.value + BigInt(offset)), + ); + } + + getUint16(offset = 0) { + return core.opSync( + "op_ffi_read_u16", + packU64(this.pointer.value + BigInt(offset)), + ); + } + + getInt16(offset = 0) { + return core.opSync( + "op_ffi_read_i16", + packU64(this.pointer.value + BigInt(offset)), + ); + } + + getUint32(offset = 0) { + return core.opSync( + "op_ffi_read_u32", + packU64(this.pointer.value + BigInt(offset)), + ); + } + + getInt32(offset = 0) { + return core.opSync( + "op_ffi_read_i32", + packU64(this.pointer.value + BigInt(offset)), + ); + } + + getBigUint64(offset = 0) { + return unpackU64(core.opSync( + "op_ffi_read_u64", + packU64(this.pointer.value + BigInt(offset)), + )); + } + + getBigInt64(offset = 0) { + return unpackI64(core.opSync( + "op_ffi_read_u64", + packU64(this.pointer.value + BigInt(offset)), + )); + } + + getFloat32(offset = 0) { + return core.opSync( + "op_ffi_read_f32", + packU64(this.pointer.value + BigInt(offset)), + ); + } + + getFloat64(offset = 0) { + return core.opSync( + "op_ffi_read_f64", + packU64(this.pointer.value + BigInt(offset)), + ); + } + + getCString(offset = 0) { + return core.opSync( + "op_ffi_cstr_read", + packU64(this.pointer.value + BigInt(offset)), + ); + } + + getArrayBuffer(byteLength, offset = 0) { + const uint8array = new Uint8Array(byteLength); + this.copyInto(uint8array, offset); + return uint8array.buffer; + } + + copyInto(destination, offset = 0) { + core.opSync("op_ffi_buf_copy_into", [ + packU64(this.pointer.value + BigInt(offset)), + destination, + destination.byteLength, + ]); + } + } + + class UnsafePointer { + value; + + constructor(value) { + this.value = value; + } + + static of(typedArray) { + return new UnsafePointer( + unpackU64(core.opSync("op_ffi_ptr_of", typedArray)), + ); + } + + valueOf() { + return this.value; + } + } + class DynamicLibrary { #rid; symbols = {}; @@ -16,37 +151,67 @@ for (const symbol in symbols) { const isNonBlocking = symbols[symbol].nonblocking; + const types = symbols[symbol].parameters; this.symbols[symbol] = (...args) => { const parameters = []; const buffers = []; - for (const arg of args) { - if ( - arg?.buffer instanceof ArrayBuffer && - arg.byteLength !== undefined - ) { - parameters.push(buffers.length); - buffers.push(arg); + for (let i = 0; i < types.length; i++) { + const type = types[i]; + const arg = args[i]; + + if (type === "pointer") { + if ( + arg?.buffer instanceof ArrayBuffer && + arg.byteLength !== undefined + ) { + parameters.push(buffers.length); + buffers.push(arg); + } else if (arg instanceof UnsafePointer) { + parameters.push(packU64(arg.value)); + buffers.push(undefined); + } else if (arg === null) { + parameters.push(null); + buffers.push(undefined); + } else { + throw new TypeError( + "Invalid ffi arg value, expected TypedArray, UnsafePointer or null", + ); + } } else { parameters.push(arg); } } if (isNonBlocking) { - return core.opAsync("op_ffi_call_nonblocking", { + const promise = core.opAsync("op_ffi_call_nonblocking", { rid: this.#rid, symbol, parameters, buffers, }); + + if (symbols[symbol].result === "pointer") { + return promise.then((value) => + new UnsafePointer(unpackU64(value)) + ); + } + + return promise; } else { - return core.opSync("op_ffi_call", { + const result = core.opSync("op_ffi_call", { rid: this.#rid, symbol, parameters, buffers, }); + + if (symbols[symbol].result === "pointer") { + return new UnsafePointer(unpackU64(result)); + } + + return result; } }; } @@ -63,5 +228,5 @@ return new DynamicLibrary(pathFromURL(path), symbols); } - window.__bootstrap.ffi = { dlopen }; + window.__bootstrap.ffi = { dlopen, UnsafePointer, UnsafePointerView }; })(this); |