Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ext/ffi): Implement UnsafePointer and UnsafePointerView API #12828

Merged
merged 26 commits into from Dec 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
196aa7e
feat(ext/ffi): returning and passing pointers transmuted as f64
eliassjogreen Nov 20, 2021
bca9d3a
fix: integration test
eliassjogreen Nov 20, 2021
41a0f34
fix: tests? make buffer static
eliassjogreen Nov 20, 2021
1196b9c
feat: allow null pointers and make sure passed buffer index doesnt in…
eliassjogreen Nov 20, 2021
4a157c8
fix: lint
eliassjogreen Nov 20, 2021
c95ae2b
Merge branch 'main' into buffer-pointers
eliassjogreen Nov 22, 2021
7b7722f
feat: UnsafePointer api
eliassjogreen Nov 22, 2021
dfec684
fix: use primordials
eliassjogreen Nov 22, 2021
73d0516
fix: update integration test
eliassjogreen Nov 22, 2021
b4fb77c
fix: dj's comments
eliassjogreen Nov 23, 2021
1d6eff4
Merge branch 'main' into buffer-pointers
eliassjogreen Nov 23, 2021
7e1ec5c
Merge branch 'main' into buffer-pointers
eliassjogreen Nov 23, 2021
dfc3c90
feat: implement a simple `UnsafePointerView`
eliassjogreen Nov 29, 2021
0d933c5
Merge branch 'main' into buffer-pointers
eliassjogreen Nov 29, 2021
0188cbc
fix: lint
eliassjogreen Nov 30, 2021
1713eb9
Merge branch 'main' into buffer-pointers
eliassjogreen Dec 1, 2021
6916e69
Merge branch 'main' into buffer-pointers
eliassjogreen Dec 11, 2021
45fb0f4
feat: typings & rename `buffer` type to `pointer`
eliassjogreen Dec 13, 2021
b78150f
Merge branch 'main' into buffer-pointers
eliassjogreen Dec 13, 2021
6a4dd96
fix: test
eliassjogreen Dec 13, 2021
bae224c
fix: test
eliassjogreen Dec 13, 2021
fb3a193
feat: add all permission checks
eliassjogreen Dec 13, 2021
5080d8b
Merge branch 'main' into buffer-pointers
eliassjogreen Dec 13, 2021
8f88c1b
Merge branch 'main' into buffer-pointers
eliassjogreen Dec 13, 2021
b45925e
Merge branch 'main' into buffer-pointers
eliassjogreen Dec 14, 2021
5fc0cfa
feat: bnoordhuis feedback
eliassjogreen Dec 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 77 additions & 3 deletions cli/dts/lib.deno.unstable.d.ts
Expand Up @@ -117,16 +117,90 @@ declare namespace Deno {
| "usize"
| "isize"
| "f32"
| "f64";
| "f64"
| "pointer";

/** A foreign function as defined by its parameter and result types */
export interface ForeignFunction {
parameters: (NativeType | "buffer")[];
parameters: NativeType[];
result: NativeType;
/** When true, function calls will run on a dedicated blocking thread and will return a Promise resolving to the `result`. */
nonblocking?: boolean;
}

type TypedArray =
| Int8Array
| Uint8Array
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| Uint8ClampedArray
| Float32Array
| Float64Array
| BigInt64Array
| BigUint64Array;

/** **UNSTABLE**: Unsafe and new API, beware!
*
* An unsafe pointer to a memory location for passing and returning pointers to and from the ffi
*/
export class UnsafePointer {
constructor(value: bigint);

value: bigint;

/**
* Return the direct memory pointer to the typed array in memory
*/
static of(typedArray: TypedArray): UnsafePointer;

/**
* Returns the value of the pointer which is useful in certain scenarios.
*/
valueOf(): bigint;
}

/** **UNSTABLE**: Unsafe and new API, beware!
*
* An unsafe pointer view to a memory location as specified by the `pointer`
* value. The `UnsafePointerView` API mimics the standard built in interface
* `DataView` for accessing the underlying types at an memory location
* (numbers, strings and raw bytes).
*/
export class UnsafePointerView {
constructor(pointer: UnsafePointer);

pointer: UnsafePointer;

/** Gets an unsigned 8-bit integer at the specified byte offset from the pointer. */
getUint8(offset?: number): number;
/** Gets a signed 8-bit integer at the specified byte offset from the pointer. */
getInt8(offset?: number): number;
/** Gets an unsigned 16-bit integer at the specified byte offset from the pointer. */
getUint16(offset?: number): number;
/** Gets a signed 16-bit integer at the specified byte offset from the pointer. */
getInt16(offset?: number): number;
/** Gets an unsigned 32-bit integer at the specified byte offset from the pointer. */
getUint32(offset?: number): number;
/** Gets a signed 32-bit integer at the specified byte offset from the pointer. */
getInt32(offset?: number): number;
/** Gets an unsigned 64-bit integer at the specified byte offset from the pointer. */
getBigUint64(offset?: number): bigint;
/** Gets a signed 64-bit integer at the specified byte offset from the pointer. */
getBigInt64(offset?: number): bigint;
/** Gets a signed 32-bit float at the specified byte offset from the pointer. */
getFloat32(offset?: number): number;
/** Gets a signed 64-bit float at the specified byte offset from the pointer. */
getFloat64(offset?: number): number;
/** Gets a C string (null terminated string) at the specified byte offset from the pointer. */
getCString(offset?: number): string;
/** Gets an ArrayBuffer of length `byteLength` at the specified byte offset from the pointer. */
getArrayBuffer(byteLength: number, offset?: number): ArrayBuffer;
/** Copies the memory of the pointer into a typed array. Length is determined from the typed array's `byteLength`. Also takes optional offset from the pointer. */
copyInto(destination: TypedArray, offset?: number): void;
}

/** A dynamic library resource */
export interface DynamicLibrary<S extends Record<string, ForeignFunction>> {
/** All of the registered symbols along with functions for calling them */
Expand All @@ -135,7 +209,7 @@ declare namespace Deno {
close(): void;
}

/** **UNSTABLE**: new API
/** **UNSTABLE**: Unsafe and new API, beware!
*
* Opens a dynamic library and registers symbols
*/
Expand Down
185 changes: 175 additions & 10 deletions ext/ffi/00_ffi.js
Expand Up @@ -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 = {};
Expand All @@ -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;
}
};
}
Expand All @@ -63,5 +228,5 @@
return new DynamicLibrary(pathFromURL(path), symbols);
}

window.__bootstrap.ffi = { dlopen };
window.__bootstrap.ffi = { dlopen, UnsafePointer, UnsafePointerView };
})(this);