summaryrefslogtreecommitdiff
path: root/tests/unit/ffi_test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit/ffi_test.ts')
-rw-r--r--tests/unit/ffi_test.ts137
1 files changed, 137 insertions, 0 deletions
diff --git a/tests/unit/ffi_test.ts b/tests/unit/ffi_test.ts
new file mode 100644
index 000000000..2b56a8db1
--- /dev/null
+++ b/tests/unit/ffi_test.ts
@@ -0,0 +1,137 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+import { assertEquals, assertRejects, assertThrows } from "./test_util.ts";
+
+Deno.test({ permissions: { ffi: true } }, function dlopenInvalidArguments() {
+ const filename = "/usr/lib/libc.so.6";
+ assertThrows(() => {
+ // @ts-expect-error: ForeignFunction cannot be null
+ Deno.dlopen(filename, { malloc: null });
+ }, TypeError);
+ assertThrows(() => {
+ Deno.dlopen(filename, {
+ // @ts-expect-error: invalid NativeType
+ malloc: { parameters: ["a"], result: "b" },
+ });
+ }, TypeError);
+ assertThrows(() => {
+ // @ts-expect-error: DynamicLibrary symbols cannot be null
+ Deno.dlopen(filename, null);
+ }, TypeError);
+ assertThrows(() => {
+ // @ts-expect-error: require 2 arguments
+ Deno.dlopen(filename);
+ }, TypeError);
+});
+
+Deno.test({ permissions: { ffi: false } }, function ffiPermissionDenied() {
+ assertThrows(() => {
+ Deno.dlopen("/usr/lib/libc.so.6", {});
+ }, Deno.errors.PermissionDenied);
+ const fnptr = new Deno.UnsafeFnPointer(
+ // @ts-expect-error: Not NonNullable but null check is after permissions check.
+ null,
+ {
+ parameters: ["u32", "pointer"],
+ result: "void",
+ } as const,
+ );
+ assertThrows(() => {
+ fnptr.call(123, null);
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ Deno.UnsafePointer.of(new Uint8Array(0));
+ }, Deno.errors.PermissionDenied);
+ const ptrView = new Deno.UnsafePointerView(
+ // @ts-expect-error: Not NonNullable but null check is after permissions check.
+ null,
+ );
+ assertThrows(() => {
+ ptrView.copyInto(new Uint8Array(0));
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ ptrView.getCString();
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ ptrView.getUint8();
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ ptrView.getInt8();
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ ptrView.getUint16();
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ ptrView.getInt16();
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ ptrView.getUint32();
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ ptrView.getInt32();
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ ptrView.getFloat32();
+ }, Deno.errors.PermissionDenied);
+ assertThrows(() => {
+ ptrView.getFloat64();
+ }, Deno.errors.PermissionDenied);
+});
+
+Deno.test({ permissions: { ffi: true } }, function pointerOf() {
+ const buffer = new ArrayBuffer(1024);
+ const baseAddress = Deno.UnsafePointer.value(Deno.UnsafePointer.of(buffer));
+ const uint8Address = Deno.UnsafePointer.value(
+ Deno.UnsafePointer.of(new Uint8Array(buffer)),
+ );
+ assertEquals(baseAddress, uint8Address);
+ const float64Address = Deno.UnsafePointer.value(
+ Deno.UnsafePointer.of(new Float64Array(buffer)),
+ );
+ assertEquals(baseAddress, float64Address);
+ const uint8AddressOffset = Deno.UnsafePointer.value(
+ Deno.UnsafePointer.of(new Uint8Array(buffer, 100)),
+ );
+ assertEquals(Number(baseAddress) + 100, uint8AddressOffset);
+ const float64AddressOffset = Deno.UnsafePointer.value(
+ Deno.UnsafePointer.of(new Float64Array(buffer, 80)),
+ );
+ assertEquals(Number(baseAddress) + 80, float64AddressOffset);
+});
+
+Deno.test({ permissions: { ffi: true } }, function callWithError() {
+ const throwCb = () => {
+ throw new Error("Error");
+ };
+ const cb = new Deno.UnsafeCallback({
+ parameters: [],
+ result: "void",
+ }, throwCb);
+ const fnPointer = new Deno.UnsafeFnPointer(cb.pointer, {
+ parameters: [],
+ result: "void",
+ });
+ assertThrows(() => fnPointer.call());
+ cb.close();
+});
+
+Deno.test(
+ { permissions: { ffi: true }, ignore: true },
+ async function callNonBlockingWithError() {
+ const throwCb = () => {
+ throw new Error("Error");
+ };
+ const cb = new Deno.UnsafeCallback({
+ parameters: [],
+ result: "void",
+ }, throwCb);
+ const fnPointer = new Deno.UnsafeFnPointer(cb.pointer, {
+ parameters: [],
+ result: "void",
+ nonblocking: true,
+ });
+ // TODO(mmastrac): currently ignored as we do not thread callback exceptions through nonblocking pointers
+ await assertRejects(async () => await fnPointer.call());
+ cb.close();
+ },
+);