2024-01-01 14:58:21 -05:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2022-09-04 23:26:52 -04:00
|
|
|
|
2023-12-07 08:21:01 -05:00
|
|
|
import { core, primordials } from "ext:core/mod.js";
|
2024-01-10 17:37:25 -05:00
|
|
|
const {
|
|
|
|
isArrayBuffer,
|
|
|
|
isDataView,
|
|
|
|
isTypedArray,
|
|
|
|
} = core;
|
2024-01-29 16:02:26 -05:00
|
|
|
import {
|
2024-01-10 17:37:25 -05:00
|
|
|
op_ffi_buf_copy_into,
|
|
|
|
op_ffi_call_nonblocking,
|
|
|
|
op_ffi_call_ptr,
|
|
|
|
op_ffi_call_ptr_nonblocking,
|
|
|
|
op_ffi_cstr_read,
|
|
|
|
op_ffi_get_buf,
|
|
|
|
op_ffi_get_static,
|
|
|
|
op_ffi_load,
|
|
|
|
op_ffi_ptr_create,
|
|
|
|
op_ffi_ptr_equals,
|
|
|
|
op_ffi_ptr_of,
|
|
|
|
op_ffi_ptr_of_exact,
|
|
|
|
op_ffi_ptr_offset,
|
|
|
|
op_ffi_ptr_value,
|
|
|
|
op_ffi_read_bool,
|
|
|
|
op_ffi_read_f32,
|
|
|
|
op_ffi_read_f64,
|
|
|
|
op_ffi_read_i16,
|
|
|
|
op_ffi_read_i32,
|
|
|
|
op_ffi_read_i64,
|
|
|
|
op_ffi_read_i8,
|
|
|
|
op_ffi_read_ptr,
|
|
|
|
op_ffi_read_u16,
|
|
|
|
op_ffi_read_u32,
|
|
|
|
op_ffi_read_u64,
|
|
|
|
op_ffi_read_u8,
|
|
|
|
op_ffi_unsafe_callback_close,
|
|
|
|
op_ffi_unsafe_callback_create,
|
|
|
|
op_ffi_unsafe_callback_ref,
|
2024-01-29 16:02:26 -05:00
|
|
|
} from "ext:core/ops";
|
2023-02-07 14:22:46 -05:00
|
|
|
const {
|
2023-04-02 13:41:41 -04:00
|
|
|
ArrayBufferIsView,
|
|
|
|
ArrayBufferPrototypeGetByteLength,
|
2023-02-07 14:22:46 -05:00
|
|
|
ArrayPrototypeMap,
|
|
|
|
ArrayPrototypeJoin,
|
2023-04-02 13:41:41 -04:00
|
|
|
DataViewPrototypeGetByteLength,
|
2023-02-07 14:22:46 -05:00
|
|
|
ObjectDefineProperty,
|
2023-05-02 06:15:45 -04:00
|
|
|
ObjectHasOwn,
|
2023-02-07 14:22:46 -05:00
|
|
|
ObjectPrototypeIsPrototypeOf,
|
|
|
|
NumberIsSafeInteger,
|
2023-04-02 13:41:41 -04:00
|
|
|
TypedArrayPrototypeGetBuffer,
|
|
|
|
TypedArrayPrototypeGetByteLength,
|
2023-02-07 14:22:46 -05:00
|
|
|
TypeError,
|
|
|
|
Uint8Array,
|
|
|
|
Int32Array,
|
|
|
|
Uint32Array,
|
|
|
|
BigInt64Array,
|
|
|
|
BigUint64Array,
|
|
|
|
Function,
|
|
|
|
ReflectHas,
|
|
|
|
PromisePrototypeThen,
|
|
|
|
MathMax,
|
|
|
|
MathCeil,
|
|
|
|
SafeMap,
|
|
|
|
SafeArrayIterator,
|
2023-04-14 16:23:28 -04:00
|
|
|
SafeWeakMap,
|
2023-02-07 14:22:46 -05:00
|
|
|
} = primordials;
|
2024-01-10 17:37:25 -05:00
|
|
|
|
2023-03-08 06:44:54 -05:00
|
|
|
import { pathFromURL } from "ext:deno_web/00_infra.js";
|
2023-02-07 14:22:46 -05:00
|
|
|
|
2023-04-02 13:41:41 -04:00
|
|
|
/**
|
|
|
|
* @param {BufferSource} source
|
|
|
|
* @returns {number}
|
|
|
|
*/
|
|
|
|
function getBufferSourceByteLength(source) {
|
2024-01-03 23:12:38 -05:00
|
|
|
if (isTypedArray(source)) {
|
|
|
|
return TypedArrayPrototypeGetByteLength(source);
|
|
|
|
} else if (isDataView(source)) {
|
|
|
|
return DataViewPrototypeGetByteLength(source);
|
2023-04-02 13:41:41 -04:00
|
|
|
}
|
|
|
|
return ArrayBufferPrototypeGetByteLength(source);
|
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
const U32_BUFFER = new Uint32Array(2);
|
2023-04-02 13:41:41 -04:00
|
|
|
const U64_BUFFER = new BigUint64Array(TypedArrayPrototypeGetBuffer(U32_BUFFER));
|
|
|
|
const I64_BUFFER = new BigInt64Array(TypedArrayPrototypeGetBuffer(U32_BUFFER));
|
2023-02-07 14:22:46 -05:00
|
|
|
class UnsafePointerView {
|
|
|
|
pointer;
|
|
|
|
|
|
|
|
constructor(pointer) {
|
|
|
|
this.pointer = pointer;
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getBool(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_bool(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getUint8(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_u8(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getInt8(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_i8(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getUint16(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_u16(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getInt16(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_i16(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getUint32(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_u32(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getInt32(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_i32(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getBigUint64(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
op_ffi_read_u64(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
U32_BUFFER,
|
|
|
|
);
|
|
|
|
return U64_BUFFER[0];
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getBigInt64(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
op_ffi_read_i64(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
U32_BUFFER,
|
|
|
|
);
|
|
|
|
return I64_BUFFER[0];
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getFloat32(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_f32(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getFloat64(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_f64(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2022-08-05 12:27:12 -04:00
|
|
|
|
2023-02-22 12:32:38 -05:00
|
|
|
getPointer(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_read_ptr(
|
2023-02-22 12:32:38 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getCString(offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_cstr_read(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2022-08-05 12:27:12 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
static getCString(pointer, offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_cstr_read(
|
2023-02-07 14:22:46 -05:00
|
|
|
pointer,
|
|
|
|
offset,
|
|
|
|
);
|
|
|
|
}
|
2021-12-15 09:41:49 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
getArrayBuffer(byteLength, offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_get_buf(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
byteLength,
|
|
|
|
);
|
|
|
|
}
|
2022-08-05 12:27:12 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
static getArrayBuffer(pointer, byteLength, offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_get_buf(
|
2023-02-07 14:22:46 -05:00
|
|
|
pointer,
|
|
|
|
offset,
|
|
|
|
byteLength,
|
|
|
|
);
|
2021-12-15 09:41:49 -05:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
copyInto(destination, offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
op_ffi_buf_copy_into(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
offset,
|
|
|
|
destination,
|
2023-04-02 13:41:41 -04:00
|
|
|
getBufferSourceByteLength(destination),
|
2023-02-07 14:22:46 -05:00
|
|
|
);
|
2021-12-15 09:41:49 -05:00
|
|
|
}
|
2022-01-12 06:38:26 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
static copyInto(pointer, destination, offset = 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
op_ffi_buf_copy_into(
|
2023-02-07 14:22:46 -05:00
|
|
|
pointer,
|
|
|
|
offset,
|
|
|
|
destination,
|
2023-04-02 13:41:41 -04:00
|
|
|
getBufferSourceByteLength(destination),
|
2023-02-07 14:22:46 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const OUT_BUFFER = new Uint32Array(2);
|
2023-04-02 13:41:41 -04:00
|
|
|
const OUT_BUFFER_64 = new BigInt64Array(
|
|
|
|
TypedArrayPrototypeGetBuffer(OUT_BUFFER),
|
|
|
|
);
|
2023-04-14 16:23:28 -04:00
|
|
|
const POINTER_TO_BUFFER_WEAK_MAP = new SafeWeakMap();
|
2023-02-07 14:22:46 -05:00
|
|
|
class UnsafePointer {
|
2023-02-22 12:32:38 -05:00
|
|
|
static create(value) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_ptr_create(value);
|
2023-02-22 12:32:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static equals(a, b) {
|
|
|
|
if (a === null || b === null) {
|
|
|
|
return a === b;
|
|
|
|
}
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_ptr_equals(a, b);
|
2023-02-22 12:32:38 -05:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
static of(value) {
|
|
|
|
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) {
|
|
|
|
return value.pointer;
|
2022-01-12 06:38:26 -05:00
|
|
|
}
|
2023-10-05 09:35:21 -04:00
|
|
|
let pointer;
|
|
|
|
if (ArrayBufferIsView(value)) {
|
|
|
|
if (value.length === 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
pointer = op_ffi_ptr_of_exact(value);
|
2023-10-05 09:35:21 -04:00
|
|
|
} else {
|
2024-01-10 17:37:25 -05:00
|
|
|
pointer = op_ffi_ptr_of(value);
|
2023-10-05 09:35:21 -04:00
|
|
|
}
|
2024-01-03 23:12:38 -05:00
|
|
|
} else if (isArrayBuffer(value)) {
|
2023-10-05 09:35:21 -04:00
|
|
|
if (value.length === 0) {
|
2024-01-10 17:37:25 -05:00
|
|
|
pointer = op_ffi_ptr_of_exact(new Uint8Array(value));
|
2023-10-05 09:35:21 -04:00
|
|
|
} else {
|
2024-01-10 17:37:25 -05:00
|
|
|
pointer = op_ffi_ptr_of(new Uint8Array(value));
|
2023-10-05 09:35:21 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new TypeError(
|
|
|
|
"Expected ArrayBuffer, ArrayBufferView or UnsafeCallbackPrototype",
|
|
|
|
);
|
|
|
|
}
|
2023-03-05 03:01:23 -05:00
|
|
|
if (pointer) {
|
|
|
|
POINTER_TO_BUFFER_WEAK_MAP.set(pointer, value);
|
|
|
|
}
|
|
|
|
return pointer;
|
2023-02-22 12:32:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static offset(value, offset) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_ptr_offset(value, offset);
|
2023-02-22 12:32:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static value(value) {
|
|
|
|
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) {
|
|
|
|
value = value.pointer;
|
|
|
|
}
|
2024-01-10 17:37:25 -05:00
|
|
|
op_ffi_ptr_value(value, OUT_BUFFER);
|
2023-02-07 14:22:46 -05:00
|
|
|
const result = OUT_BUFFER[0] + 2 ** 32 * OUT_BUFFER[1];
|
|
|
|
if (NumberIsSafeInteger(result)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return OUT_BUFFER_64[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class UnsafeFnPointer {
|
|
|
|
pointer;
|
|
|
|
definition;
|
|
|
|
#structSize;
|
|
|
|
|
|
|
|
constructor(pointer, definition) {
|
|
|
|
this.pointer = pointer;
|
|
|
|
this.definition = definition;
|
|
|
|
this.#structSize = isStruct(definition.result)
|
|
|
|
? getTypeSizeAndAlignment(definition.result)[0]
|
|
|
|
: null;
|
|
|
|
}
|
2022-01-12 06:38:26 -05:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
call(...parameters) {
|
|
|
|
if (this.definition.nonblocking) {
|
|
|
|
if (this.#structSize === null) {
|
2023-12-26 20:30:26 -05:00
|
|
|
return op_ffi_call_ptr_nonblocking(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
this.definition,
|
|
|
|
parameters,
|
|
|
|
);
|
2022-01-12 06:38:26 -05:00
|
|
|
} else {
|
2023-02-07 14:22:46 -05:00
|
|
|
const buffer = new Uint8Array(this.#structSize);
|
|
|
|
return PromisePrototypeThen(
|
2023-12-26 20:30:26 -05:00
|
|
|
op_ffi_call_ptr_nonblocking(
|
2023-01-07 22:58:10 -05:00
|
|
|
this.pointer,
|
|
|
|
this.definition,
|
|
|
|
parameters,
|
|
|
|
buffer,
|
2023-02-07 14:22:46 -05:00
|
|
|
),
|
|
|
|
() => buffer,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (this.#structSize === null) {
|
2024-01-10 17:37:25 -05:00
|
|
|
return op_ffi_call_ptr(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
this.definition,
|
|
|
|
parameters,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
const buffer = new Uint8Array(this.#structSize);
|
2024-01-10 17:37:25 -05:00
|
|
|
op_ffi_call_ptr(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.pointer,
|
|
|
|
this.definition,
|
|
|
|
parameters,
|
|
|
|
buffer,
|
|
|
|
);
|
|
|
|
return buffer;
|
2022-01-12 06:38:26 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function isReturnedAsBigInt(type) {
|
2023-02-22 12:32:38 -05:00
|
|
|
return type === "u64" || type === "i64" ||
|
2023-02-07 14:22:46 -05:00
|
|
|
type === "usize" || type === "isize";
|
|
|
|
}
|
|
|
|
|
|
|
|
function isStruct(type) {
|
|
|
|
return typeof type === "object" && type !== null &&
|
|
|
|
typeof type.struct === "object";
|
|
|
|
}
|
|
|
|
|
|
|
|
function getTypeSizeAndAlignment(type, cache = new SafeMap()) {
|
|
|
|
if (isStruct(type)) {
|
|
|
|
const cached = cache.get(type);
|
|
|
|
if (cached !== undefined) {
|
|
|
|
if (cached === null) {
|
|
|
|
throw new TypeError("Recursive struct definition");
|
2023-01-07 22:58:10 -05:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
return cached;
|
2023-01-07 22:58:10 -05:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
cache.set(type, null);
|
|
|
|
let size = 0;
|
|
|
|
let alignment = 1;
|
|
|
|
for (const field of new SafeArrayIterator(type.struct)) {
|
|
|
|
const { 0: fieldSize, 1: fieldAlign } = getTypeSizeAndAlignment(
|
|
|
|
field,
|
|
|
|
cache,
|
|
|
|
);
|
|
|
|
alignment = MathMax(alignment, fieldAlign);
|
|
|
|
size = MathCeil(size / fieldAlign) * fieldAlign;
|
|
|
|
size += fieldSize;
|
2023-01-07 22:58:10 -05:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
size = MathCeil(size / alignment) * alignment;
|
2023-03-31 23:56:02 -04:00
|
|
|
const result = [size, alignment];
|
|
|
|
cache.set(type, result);
|
|
|
|
return result;
|
2023-01-07 22:58:10 -05:00
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
switch (type) {
|
|
|
|
case "bool":
|
|
|
|
case "u8":
|
|
|
|
case "i8":
|
|
|
|
return [1, 1];
|
|
|
|
case "u16":
|
|
|
|
case "i16":
|
|
|
|
return [2, 2];
|
|
|
|
case "u32":
|
|
|
|
case "i32":
|
|
|
|
case "f32":
|
|
|
|
return [4, 4];
|
|
|
|
case "u64":
|
|
|
|
case "i64":
|
|
|
|
case "f64":
|
|
|
|
case "pointer":
|
|
|
|
case "buffer":
|
|
|
|
case "function":
|
|
|
|
case "usize":
|
|
|
|
case "isize":
|
|
|
|
return [8, 8];
|
|
|
|
default:
|
|
|
|
throw new TypeError(`Unsupported type: ${type}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class UnsafeCallback {
|
|
|
|
#refcount;
|
|
|
|
// Internal promise only meant to keep Deno from exiting
|
|
|
|
#refpromise;
|
|
|
|
#rid;
|
|
|
|
definition;
|
|
|
|
callback;
|
|
|
|
pointer;
|
|
|
|
|
|
|
|
constructor(definition, callback) {
|
|
|
|
if (definition.nonblocking) {
|
|
|
|
throw new TypeError(
|
|
|
|
"Invalid UnsafeCallback, cannot be nonblocking",
|
2022-06-20 07:06:04 -04:00
|
|
|
);
|
|
|
|
}
|
2024-01-10 17:37:25 -05:00
|
|
|
const { 0: rid, 1: pointer } = op_ffi_unsafe_callback_create(
|
2023-02-07 14:22:46 -05:00
|
|
|
definition,
|
|
|
|
callback,
|
|
|
|
);
|
|
|
|
this.#refcount = 0;
|
|
|
|
this.#rid = rid;
|
|
|
|
this.pointer = pointer;
|
|
|
|
this.definition = definition;
|
|
|
|
this.callback = callback;
|
|
|
|
}
|
2022-06-20 07:06:04 -04:00
|
|
|
|
2023-02-22 14:09:59 -05:00
|
|
|
static threadSafe(definition, callback) {
|
|
|
|
const unsafeCallback = new UnsafeCallback(definition, callback);
|
|
|
|
unsafeCallback.ref();
|
|
|
|
return unsafeCallback;
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
ref() {
|
|
|
|
if (this.#refcount++ === 0) {
|
2023-02-22 14:09:59 -05:00
|
|
|
if (this.#refpromise) {
|
|
|
|
// Re-refing
|
2023-11-09 15:57:26 -05:00
|
|
|
core.refOpPromise(this.#refpromise);
|
2023-02-22 14:09:59 -05:00
|
|
|
} else {
|
2023-12-26 20:30:26 -05:00
|
|
|
this.#refpromise = op_ffi_unsafe_callback_ref(
|
2023-02-22 14:09:59 -05:00
|
|
|
this.#rid,
|
|
|
|
);
|
|
|
|
}
|
2022-06-28 05:23:36 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
return this.#refcount;
|
|
|
|
}
|
2022-06-28 05:23:36 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
unref() {
|
|
|
|
// Only decrement refcount if it is positive, and only
|
|
|
|
// unref the callback if refcount reaches zero.
|
|
|
|
if (this.#refcount > 0 && --this.#refcount === 0) {
|
2023-11-09 15:57:26 -05:00
|
|
|
core.unrefOpPromise(this.#refpromise);
|
2022-06-28 05:23:36 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
return this.#refcount;
|
|
|
|
}
|
2022-06-28 05:23:36 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
close() {
|
|
|
|
this.#refcount = 0;
|
2024-01-10 17:37:25 -05:00
|
|
|
op_ffi_unsafe_callback_close(this.#rid);
|
2022-06-20 07:06:04 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
}
|
2022-06-20 07:06:04 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
const UnsafeCallbackPrototype = UnsafeCallback.prototype;
|
2022-06-20 07:06:04 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
class DynamicLibrary {
|
|
|
|
#rid;
|
2024-05-22 18:03:35 -04:00
|
|
|
symbols = { __proto__: null };
|
2021-08-06 17:28:10 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
constructor(path, symbols) {
|
2024-01-10 17:37:25 -05:00
|
|
|
({ 0: this.#rid, 1: this.symbols } = op_ffi_load({ path, symbols }));
|
2023-02-07 14:22:46 -05:00
|
|
|
for (const symbol in symbols) {
|
2023-05-02 06:15:45 -04:00
|
|
|
if (!ObjectHasOwn(symbols, symbol)) {
|
2023-02-07 14:22:46 -05:00
|
|
|
continue;
|
|
|
|
}
|
2022-12-19 21:37:50 -05:00
|
|
|
|
2023-04-03 14:32:21 -04:00
|
|
|
// Symbol was marked as optional, and not found.
|
|
|
|
// In that case, we set its value to null in Rust-side.
|
|
|
|
if (symbols[symbol] === null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
if (ReflectHas(symbols[symbol], "type")) {
|
|
|
|
const type = symbols[symbol].type;
|
|
|
|
if (type === "void") {
|
|
|
|
throw new TypeError(
|
|
|
|
"Foreign symbol of type 'void' is not supported.",
|
2022-02-18 07:21:19 -05:00
|
|
|
);
|
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
|
|
|
|
const name = symbols[symbol].name || symbol;
|
2024-01-10 17:37:25 -05:00
|
|
|
const value = op_ffi_get_static(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.#rid,
|
|
|
|
name,
|
|
|
|
type,
|
2023-04-03 14:32:21 -04:00
|
|
|
symbols[symbol].optional,
|
2023-02-07 14:22:46 -05:00
|
|
|
);
|
|
|
|
ObjectDefineProperty(
|
|
|
|
this.symbols,
|
|
|
|
symbol,
|
|
|
|
{
|
|
|
|
configurable: false,
|
|
|
|
enumerable: true,
|
|
|
|
value,
|
|
|
|
writable: false,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const resultType = symbols[symbol].result;
|
|
|
|
const isStructResult = isStruct(resultType);
|
|
|
|
const structSize = isStructResult
|
|
|
|
? getTypeSizeAndAlignment(resultType)[0]
|
|
|
|
: 0;
|
|
|
|
const needsUnpacking = isReturnedAsBigInt(resultType);
|
|
|
|
|
|
|
|
const isNonBlocking = symbols[symbol].nonblocking;
|
|
|
|
if (isNonBlocking) {
|
|
|
|
ObjectDefineProperty(
|
|
|
|
this.symbols,
|
|
|
|
symbol,
|
|
|
|
{
|
|
|
|
configurable: false,
|
|
|
|
enumerable: true,
|
|
|
|
value: (...parameters) => {
|
|
|
|
if (isStructResult) {
|
|
|
|
const buffer = new Uint8Array(structSize);
|
2023-12-26 20:30:26 -05:00
|
|
|
const ret = op_ffi_call_nonblocking(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.#rid,
|
|
|
|
symbol,
|
|
|
|
parameters,
|
|
|
|
buffer,
|
|
|
|
);
|
|
|
|
return PromisePrototypeThen(
|
|
|
|
ret,
|
|
|
|
() => buffer,
|
|
|
|
);
|
|
|
|
} else {
|
2023-12-26 20:30:26 -05:00
|
|
|
return op_ffi_call_nonblocking(
|
2023-02-07 14:22:46 -05:00
|
|
|
this.#rid,
|
|
|
|
symbol,
|
|
|
|
parameters,
|
|
|
|
);
|
|
|
|
}
|
2022-06-29 04:13:33 -04:00
|
|
|
},
|
2023-02-07 14:22:46 -05:00
|
|
|
writable: false,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2022-07-28 08:38:22 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
if (needsUnpacking && !isNonBlocking) {
|
|
|
|
const call = this.symbols[symbol];
|
|
|
|
const parameters = symbols[symbol].parameters;
|
|
|
|
const vi = new Int32Array(2);
|
2023-04-02 13:41:41 -04:00
|
|
|
const b = new BigInt64Array(TypedArrayPrototypeGetBuffer(vi));
|
2022-07-28 08:38:22 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
const params = ArrayPrototypeJoin(
|
|
|
|
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
|
|
|
", ",
|
|
|
|
);
|
|
|
|
// Make sure V8 has no excuse to not optimize this function.
|
|
|
|
this.symbols[symbol] = new Function(
|
|
|
|
"vi",
|
|
|
|
"b",
|
|
|
|
"call",
|
|
|
|
`return function (${params}) {
|
2022-07-28 08:38:22 -04:00
|
|
|
call(${params}${parameters.length > 0 ? ", " : ""}vi);
|
|
|
|
return b[0];
|
|
|
|
}`,
|
BREAKING(ffi/unstable): always return u64 as bigint (#23981)
The mixed `number | bigint` representation was useful optimization for
pointers. Now, pointers are represented as V8 externals. As part of the
FFI stabilization effort we want to make `bigint` the only
representation for `u64` and `i64`.
BigInt representation performance is almost on par with mixed
representation with the added benefit that its less confusing and users
don't need manual checks and conversions for doing operations on the
value.
```
cpu: AMD Ryzen 5 7530U with Radeon Graphics
runtime: deno 1.43.6+92a8d09 (x86_64-unknown-linux-gnu)
file:///home/divy/gh/ffi/main.ts
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------- -----------------------------
nop 4.01 ns/iter 249,533,690.5 (3.97 ns … 10.8 ns) 3.97 ns 4.36 ns 9.03 ns
ret bigint 7.74 ns/iter 129,127,186.8 (7.72 ns … 10.46 ns) 7.72 ns 8.11 ns 8.82 ns
ret i32 7.81 ns/iter 128,087,100.5 (7.77 ns … 12.72 ns) 7.78 ns 8.57 ns 9.75 ns
ret bigint (add op) 15.02 ns/iter 66,588,253.2 (14.64 ns … 24.99 ns) 14.76 ns 19.13 ns 19.44 ns
ret i32 (add op) 12.02 ns/iter 83,209,131.8 (11.95 ns … 18.18 ns) 11.98 ns 13.11 ns 14.5 ns
```
2024-05-28 00:01:09 -04:00
|
|
|
)(vi, b, call);
|
2023-02-07 14:22:46 -05:00
|
|
|
} else if (isStructResult && !isNonBlocking) {
|
|
|
|
const call = this.symbols[symbol];
|
|
|
|
const parameters = symbols[symbol].parameters;
|
|
|
|
const params = ArrayPrototypeJoin(
|
|
|
|
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
|
|
|
", ",
|
|
|
|
);
|
|
|
|
this.symbols[symbol] = new Function(
|
|
|
|
"call",
|
|
|
|
`return function (${params}) {
|
2023-01-07 22:58:10 -05:00
|
|
|
const buffer = new Uint8Array(${structSize});
|
|
|
|
call(${params}${parameters.length > 0 ? ", " : ""}buffer);
|
|
|
|
return buffer;
|
|
|
|
}`,
|
2023-02-07 14:22:46 -05:00
|
|
|
)(call);
|
2021-08-06 17:28:10 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
close() {
|
|
|
|
core.close(this.#rid);
|
2021-08-06 17:28:10 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function dlopen(path, symbols) {
|
|
|
|
return new DynamicLibrary(pathFromURL(path), symbols);
|
|
|
|
}
|
|
|
|
|
|
|
|
export {
|
|
|
|
dlopen,
|
|
|
|
UnsafeCallback,
|
|
|
|
UnsafeFnPointer,
|
|
|
|
UnsafePointer,
|
|
|
|
UnsafePointerView,
|
|
|
|
};
|