mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 07:44:48 -05:00
406 lines
10 KiB
JavaScript
406 lines
10 KiB
JavaScript
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
|
"use strict";
|
|
|
|
((window) => {
|
|
const core = window.Deno.core;
|
|
const ops = core.ops;
|
|
const __bootstrap = window.__bootstrap;
|
|
const {
|
|
BigInt,
|
|
ObjectDefineProperty,
|
|
ArrayPrototypeMap,
|
|
Number,
|
|
NumberIsSafeInteger,
|
|
ArrayPrototypeJoin,
|
|
ObjectPrototypeIsPrototypeOf,
|
|
PromisePrototypeThen,
|
|
TypeError,
|
|
Int32Array,
|
|
Uint32Array,
|
|
BigInt64Array,
|
|
Function,
|
|
} = window.__bootstrap.primordials;
|
|
|
|
function unpackU64(returnValue) {
|
|
if (typeof returnValue === "number") {
|
|
return returnValue;
|
|
}
|
|
const [hi, lo] = returnValue;
|
|
return BigInt(hi) << 32n | BigInt(lo);
|
|
}
|
|
|
|
function unpackI64(returnValue) {
|
|
if (typeof returnValue === "number") {
|
|
return returnValue;
|
|
}
|
|
const [hi, lo] = returnValue;
|
|
const u64 = unpackU64([hi, lo]);
|
|
return u64 >> 63n ? u64 - 0x10000000000000000n : u64;
|
|
}
|
|
|
|
class UnsafePointerView {
|
|
pointer;
|
|
|
|
constructor(pointer) {
|
|
this.pointer = pointer;
|
|
}
|
|
|
|
getBool(offset = 0) {
|
|
return ops.op_ffi_read_bool(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getUint8(offset = 0) {
|
|
return ops.op_ffi_read_u8(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getInt8(offset = 0) {
|
|
return ops.op_ffi_read_i8(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getUint16(offset = 0) {
|
|
return ops.op_ffi_read_u16(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getInt16(offset = 0) {
|
|
return ops.op_ffi_read_i16(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getUint32(offset = 0) {
|
|
return ops.op_ffi_read_u32(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getInt32(offset = 0) {
|
|
return ops.op_ffi_read_i32(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getBigUint64(offset = 0) {
|
|
return ops.op_ffi_read_u64(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getBigInt64(offset = 0) {
|
|
return ops.op_ffi_read_i64(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getFloat32(offset = 0) {
|
|
return ops.op_ffi_read_f32(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getFloat64(offset = 0) {
|
|
return ops.op_ffi_read_f64(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
getCString(offset = 0) {
|
|
return ops.op_ffi_cstr_read(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
);
|
|
}
|
|
|
|
static getCString(pointer, offset = 0) {
|
|
return ops.op_ffi_cstr_read(
|
|
offset ? BigInt(pointer) + BigInt(offset) : pointer,
|
|
);
|
|
}
|
|
|
|
getArrayBuffer(byteLength, offset = 0) {
|
|
return ops.op_ffi_get_buf(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
byteLength,
|
|
);
|
|
}
|
|
|
|
static getArrayBuffer(pointer, byteLength, offset = 0) {
|
|
return ops.op_ffi_get_buf(
|
|
offset ? BigInt(pointer) + BigInt(offset) : pointer,
|
|
byteLength,
|
|
);
|
|
}
|
|
|
|
copyInto(destination, offset = 0) {
|
|
ops.op_ffi_buf_copy_into(
|
|
offset ? BigInt(this.pointer) + BigInt(offset) : this.pointer,
|
|
destination,
|
|
destination.byteLength,
|
|
);
|
|
}
|
|
|
|
static copyInto(pointer, destination, offset = 0) {
|
|
ops.op_ffi_buf_copy_into(
|
|
offset ? BigInt(pointer) + BigInt(offset) : pointer,
|
|
destination,
|
|
destination.byteLength,
|
|
);
|
|
}
|
|
}
|
|
|
|
class UnsafePointer {
|
|
static of(value) {
|
|
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) {
|
|
return value.pointer;
|
|
}
|
|
return ops.op_ffi_ptr_of(value);
|
|
}
|
|
}
|
|
|
|
function unpackNonblockingReturnValue(type, result) {
|
|
if (
|
|
typeof type === "object" && type !== null && "function" in type ||
|
|
type === "pointer"
|
|
) {
|
|
return unpackU64(result);
|
|
}
|
|
switch (type) {
|
|
case "isize":
|
|
case "i64":
|
|
return unpackI64(result);
|
|
case "usize":
|
|
case "u64":
|
|
return unpackU64(result);
|
|
default:
|
|
return result;
|
|
}
|
|
}
|
|
|
|
class UnsafeFnPointer {
|
|
pointer;
|
|
definition;
|
|
|
|
constructor(pointer, definition) {
|
|
this.pointer = pointer;
|
|
this.definition = definition;
|
|
}
|
|
|
|
call(...parameters) {
|
|
const resultType = this.definition.result;
|
|
if (this.definition.nonblocking) {
|
|
const promise = core.opAsync(
|
|
"op_ffi_call_ptr_nonblocking",
|
|
this.pointer,
|
|
this.definition,
|
|
parameters,
|
|
);
|
|
|
|
if (
|
|
isReturnedAsBigInt(resultType)
|
|
) {
|
|
return PromisePrototypeThen(
|
|
promise,
|
|
(result) => unpackNonblockingReturnValue(resultType, result),
|
|
);
|
|
}
|
|
|
|
return promise;
|
|
} else {
|
|
return ops.op_ffi_call_ptr(
|
|
this.pointer,
|
|
this.definition,
|
|
parameters,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function isPointerType(type) {
|
|
return type === "buffer" || type === "pointer" ||
|
|
typeof type === "object" && type !== null && "function" in type;
|
|
}
|
|
|
|
function isReturnedAsBigInt(type) {
|
|
return isPointerType(type) || type === "u64" || type === "i64" ||
|
|
type === "usize" || type === "isize";
|
|
}
|
|
|
|
function isI64(type) {
|
|
return type === "i64" || type === "isize";
|
|
}
|
|
|
|
class UnsafeCallback {
|
|
#refcount;
|
|
#rid;
|
|
definition;
|
|
callback;
|
|
pointer;
|
|
|
|
constructor(definition, callback) {
|
|
if (definition.nonblocking) {
|
|
throw new TypeError(
|
|
"Invalid UnsafeCallback, cannot be nonblocking",
|
|
);
|
|
}
|
|
const [rid, pointer] = ops.op_ffi_unsafe_callback_create(
|
|
definition,
|
|
callback,
|
|
);
|
|
this.#refcount = 0;
|
|
this.#rid = rid;
|
|
this.pointer = pointer;
|
|
this.definition = definition;
|
|
this.callback = callback;
|
|
}
|
|
|
|
ref() {
|
|
if (this.#refcount++ === 0) {
|
|
ops.op_ffi_unsafe_callback_ref(true);
|
|
}
|
|
}
|
|
|
|
unref() {
|
|
// Only decrement refcount if it is positive, and only
|
|
// unref the callback if refcount reaches zero.
|
|
if (this.#refcount > 0 && --this.#refcount === 0) {
|
|
ops.op_ffi_unsafe_callback_ref(false);
|
|
}
|
|
}
|
|
|
|
close() {
|
|
if (this.#refcount) {
|
|
this.#refcount = 0;
|
|
ops.op_ffi_unsafe_callback_ref(false);
|
|
}
|
|
core.close(this.#rid);
|
|
}
|
|
}
|
|
|
|
const UnsafeCallbackPrototype = UnsafeCallback.prototype;
|
|
|
|
class DynamicLibrary {
|
|
#rid;
|
|
symbols = {};
|
|
|
|
constructor(path, symbols) {
|
|
[this.#rid, this.symbols] = ops.op_ffi_load({ path, symbols });
|
|
for (const symbol in symbols) {
|
|
if ("type" in symbols[symbol]) {
|
|
const type = symbols[symbol].type;
|
|
if (type === "void") {
|
|
throw new TypeError(
|
|
"Foreign symbol of type 'void' is not supported.",
|
|
);
|
|
}
|
|
|
|
const name = symbols[symbol].name || symbol;
|
|
const value = ops.op_ffi_get_static(
|
|
this.#rid,
|
|
name,
|
|
type,
|
|
);
|
|
ObjectDefineProperty(
|
|
this.symbols,
|
|
symbol,
|
|
{
|
|
configurable: false,
|
|
enumerable: true,
|
|
value,
|
|
writable: false,
|
|
},
|
|
);
|
|
continue;
|
|
}
|
|
const resultType = symbols[symbol].result;
|
|
const needsUnpacking = isReturnedAsBigInt(resultType);
|
|
|
|
const isNonBlocking = symbols[symbol].nonblocking;
|
|
if (isNonBlocking) {
|
|
ObjectDefineProperty(
|
|
this.symbols,
|
|
symbol,
|
|
{
|
|
configurable: false,
|
|
enumerable: true,
|
|
value: (...parameters) => {
|
|
const promise = core.opAsync(
|
|
"op_ffi_call_nonblocking",
|
|
this.#rid,
|
|
symbol,
|
|
parameters,
|
|
);
|
|
|
|
if (needsUnpacking) {
|
|
return PromisePrototypeThen(
|
|
promise,
|
|
(result) =>
|
|
unpackNonblockingReturnValue(resultType, result),
|
|
);
|
|
}
|
|
|
|
return promise;
|
|
},
|
|
writable: false,
|
|
},
|
|
);
|
|
}
|
|
|
|
if (needsUnpacking && !isNonBlocking) {
|
|
const call = this.symbols[symbol];
|
|
const parameters = symbols[symbol].parameters;
|
|
const vi = new Int32Array(2);
|
|
const vui = new Uint32Array(vi.buffer);
|
|
const b = new BigInt64Array(vi.buffer);
|
|
|
|
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",
|
|
"vui",
|
|
"b",
|
|
"call",
|
|
"NumberIsSafeInteger",
|
|
"Number",
|
|
`return function (${params}) {
|
|
call(${params}${parameters.length > 0 ? ", " : ""}vi);
|
|
${
|
|
isI64(resultType)
|
|
? `const n1 = Number(b[0])`
|
|
: `const n1 = vui[0] + 2 ** 32 * vui[1]` // Faster path for u64
|
|
};
|
|
if (NumberIsSafeInteger(n1)) return n1;
|
|
return b[0];
|
|
}`,
|
|
)(vi, vui, b, call, NumberIsSafeInteger, Number);
|
|
}
|
|
}
|
|
}
|
|
|
|
close() {
|
|
core.close(this.#rid);
|
|
}
|
|
}
|
|
|
|
function dlopen(path, symbols) {
|
|
// URL support is progressively enhanced by util in `runtime/js`.
|
|
const pathFromURL = __bootstrap.util.pathFromURL ?? ((p) => p);
|
|
return new DynamicLibrary(pathFromURL(path), symbols);
|
|
}
|
|
|
|
window.__bootstrap.ffi = {
|
|
dlopen,
|
|
UnsafeCallback,
|
|
UnsafePointer,
|
|
UnsafePointerView,
|
|
UnsafeFnPointer,
|
|
};
|
|
})(this);
|