FFI: provide napi_env explicitly (#15431)

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
This commit is contained in:
Kai Tamkun
2024-11-26 13:54:24 -08:00
committed by GitHub
parent 5e9563833d
commit 68f026b3cd
5 changed files with 180 additions and 20 deletions

View File

@@ -60,8 +60,9 @@ typedef enum {
napi_detachable_arraybuffer_expected,
napi_would_deadlock // unused
} napi_status;
void* NapiHandleScope__open(void* jsGlobalObject, bool detached);
void NapiHandleScope__close(void* jsGlobalObject, void* handleScope);
void* NapiHandleScope__open(void* napi_env, bool detached);
void NapiHandleScope__close(void* napi_env, void* handleScope);
extern struct napi_env__ Bun__thisFFIModuleNapiEnv;
#endif

View File

@@ -444,6 +444,13 @@ pub const FFI = struct {
return error.DeferredErrors;
}
for (this.symbols.map.values()) |*symbol| {
if (symbol.needsNapiEnv()) {
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", globalThis);
break;
}
}
for (this.define.items) |define| {
TCC.tcc_define_symbol(state, define[0], define[1]);
@@ -801,7 +808,7 @@ pub const FFI = struct {
const function_name = function.base_name.?;
const allocator = bun.default_allocator;
function.compile(allocator) catch |err| {
function.compile(allocator, globalThis) catch |err| {
if (!globalThis.hasException()) {
const ret = JSC.toInvalidArguments("{s} when translating symbol \"{s}\"", .{
@errorName(err),
@@ -1141,7 +1148,7 @@ pub const FFI = struct {
function.symbol_from_dynamic_library = resolved_symbol;
}
function.compile(allocator) catch |err| {
function.compile(allocator, global) catch |err| {
const ret = JSC.toInvalidArguments("{s} when compiling symbol \"{s}\" in \"{s}\"", .{
bun.asByteSlice(@errorName(err)),
bun.asByteSlice(function_name),
@@ -1246,7 +1253,7 @@ pub const FFI = struct {
return ret;
}
function.compile(allocator) catch |err| {
function.compile(allocator, global) catch |err| {
const ret = JSC.toInvalidArguments("{s} when compiling symbol \"{s}\"", .{
bun.asByteSlice(@errorName(err)),
bun.asByteSlice(function_name),
@@ -1555,6 +1562,7 @@ pub const FFI = struct {
pub fn compile(
this: *Function,
allocator: std.mem.Allocator,
globalObject: *JSC.JSGlobalObject,
) !void {
var source_code = std.ArrayList(u8).init(allocator);
var source_code_writer = source_code.writer();
@@ -1577,6 +1585,8 @@ pub const FFI = struct {
_ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY);
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", globalObject);
CompilerRT.define(state);
// TCC.tcc_define_symbol(
@@ -1684,6 +1694,8 @@ pub const FFI = struct {
_ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY);
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", js_context);
CompilerRT.define(state);
const compilation_result = TCC.tcc_compile_string(
@@ -1811,7 +1823,7 @@ pub const FFI = struct {
if (this.needsHandleScope()) {
try writer.writeAll(
\\ void* handleScope = NapiHandleScope__open(JS_GLOBAL_OBJECT, false);
\\ void* handleScope = NapiHandleScope__open(&Bun__thisFFIModuleNapiEnv, false);
\\
);
}
@@ -1824,7 +1836,7 @@ pub const FFI = struct {
for (this.arg_types.items, 0..) |arg, i| {
if (arg == .napi_env) {
try writer.print(
\\ napi_env arg{d} = (napi_env)JS_GLOBAL_OBJECT;
\\ napi_env arg{d} = (napi_env)&Bun__thisFFIModuleNapiEnv;
\\ argsPtr++;
\\
,
@@ -1924,7 +1936,7 @@ pub const FFI = struct {
if (this.needsHandleScope()) {
try writer.writeAll(
\\ NapiHandleScope__close(JS_GLOBAL_OBJECT, handleScope);
\\ NapiHandleScope__close(&Bun__thisFFIModuleNapiEnv, handleScope);
\\
);
}
@@ -2056,6 +2068,16 @@ pub const FFI = struct {
try writer.writeAll(";\n}\n\n");
}
fn needsNapiEnv(this: *const FFI.Function) bool {
for (this.arg_types.items) |arg| {
if (arg == .napi_env or arg == .napi_value) {
return true;
}
}
return false;
}
};
// Must be kept in sync with JSFFIFunction.h version
@@ -2240,7 +2262,7 @@ pub const FFI = struct {
try writer.writeAll("JSVALUE_TO_FLOAT(");
},
.napi_env => {
try writer.writeAll("(napi_env)JS_GLOBAL_OBJECT");
try writer.writeAll("((napi_env)&Bun__thisFFIModuleNapiEnv)");
return;
},
.napi_value => {
@@ -2301,7 +2323,7 @@ pub const FFI = struct {
try writer.print("FLOAT_TO_JSVALUE({s})", .{self.symbol});
},
.napi_env => {
try writer.writeAll("JS_GLOBAL_OBJECT");
try writer.writeAll("((napi_env)&Bun__thisFFIModuleNapiEnv)");
},
.napi_value => {
try writer.print("((EncodedJSValue) {{.asNapiValue = {s} }} )", .{self.symbol});

View File

@@ -2,8 +2,8 @@ import { expect, it } from "bun:test";
import { bunEnv, bunExe, isWindows } from "harness";
import path from "path";
// TODO: we need to install build-essential and apple SDK in CI.
// it can't find includes. It can on machiens with that enabled.
// TODO: we need to install build-essential and Apple SDK in CI.
// It can't find includes. It can on machines with that enabled.
it.todoIf(isWindows)("can run a .c file", () => {
const result = Bun.spawnSync({
cmd: [bunExe(), path.join(__dirname, "cc-fixture.js")],

View File

@@ -35,6 +35,38 @@ typedef _Bool bool;
#define true 1
#define false 0
#ifndef SRC_JS_NATIVE_API_TYPES_H_
typedef struct napi_env__ *napi_env;
typedef int64_t napi_value;
typedef enum {
napi_ok,
napi_invalid_arg,
napi_object_expected,
napi_string_expected,
napi_name_expected,
napi_function_expected,
napi_number_expected,
napi_boolean_expected,
napi_array_expected,
napi_generic_failure,
napi_pending_exception,
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
napi_callback_scope_mismatch,
napi_queue_full,
napi_closing,
napi_bigint_expected,
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock // unused
} napi_status;
void* NapiHandleScope__open(void* napi_env, bool detached);
void NapiHandleScope__close(void* napi_env, void* handleScope);
extern struct napi_env__ Bun__thisFFIModuleNapiEnv;
#endif
#ifdef INJECT_BEFORE
// #include <stdint.h>
@@ -45,14 +77,14 @@ typedef _Bool bool;
// begin with a 15-bit pattern within the range 0x0002..0xFFFC.
#define DoubleEncodeOffsetBit 49
#define DoubleEncodeOffset (1ll << DoubleEncodeOffsetBit)
#define OtherTag 0x2
#define BoolTag 0x4
#define UndefinedTag 0x8
#define OtherTag 0x2ll
#define BoolTag 0x4ll
#define UndefinedTag 0x8ll
#define TagValueFalse (OtherTag | BoolTag | false)
#define TagValueTrue (OtherTag | BoolTag | true)
#define TagValueUndefined (OtherTag | UndefinedTag)
#define TagValueNull (OtherTag)
#define NotCellMask NumberTag | OtherTag
#define NotCellMask (int64_t)(NumberTag | OtherTag)
#define MAX_INT32 2147483648
#define MAX_INT52 9007199254740991
@@ -70,6 +102,8 @@ typedef union EncodedJSValue {
JSCell *ptr;
#endif
napi_value asNapiValue;
#if IS_BIG_ENDIAN
struct {
int32_t tag;
@@ -140,6 +174,11 @@ static int32_t JSVALUE_TO_INT32(EncodedJSValue val) __attribute__((__always_inli
static float JSVALUE_TO_FLOAT(EncodedJSValue val) __attribute__((__always_inline__));
static double JSVALUE_TO_DOUBLE(EncodedJSValue val) __attribute__((__always_inline__));
static bool JSVALUE_TO_BOOL(EncodedJSValue val) __attribute__((__always_inline__));
static uint8_t GET_JSTYPE(EncodedJSValue val) __attribute__((__always_inline__));
static bool JSTYPE_IS_TYPED_ARRAY(uint8_t type) __attribute__((__always_inline__));
static bool JSCELL_IS_TYPED_ARRAY(EncodedJSValue val) __attribute__((__always_inline__));
static void* JSVALUE_TO_TYPED_ARRAY_VECTOR(EncodedJSValue val) __attribute__((__always_inline__));
static uint64_t JSVALUE_TO_TYPED_ARRAY_LENGTH(EncodedJSValue val) __attribute__((__always_inline__));
static bool JSVALUE_IS_CELL(EncodedJSValue val) {
return !(val.asInt64 & NotCellMask);
@@ -153,6 +192,25 @@ static bool JSVALUE_IS_NUMBER(EncodedJSValue val) {
return val.asInt64 & NumberTag;
}
static uint8_t GET_JSTYPE(EncodedJSValue val) {
return *(uint8_t*)((uint8_t*)val.asPtr + JSCell__offsetOfType);
}
static bool JSTYPE_IS_TYPED_ARRAY(uint8_t type) {
return type >= JSTypeArrayBufferViewMin && type <= JSTypeArrayBufferViewMax;
}
static bool JSCELL_IS_TYPED_ARRAY(EncodedJSValue val) {
return JSVALUE_IS_CELL(val) && JSTYPE_IS_TYPED_ARRAY(GET_JSTYPE(val));
}
static void* JSVALUE_TO_TYPED_ARRAY_VECTOR(EncodedJSValue val) {
return *(void**)((char*)val.asPtr + JSArrayBufferView__offsetOfVector);
}
static uint64_t JSVALUE_TO_TYPED_ARRAY_LENGTH(EncodedJSValue val) {
return *(uint64_t*)((char*)val.asPtr + JSArrayBufferView__offsetOfLength);
}
// JSValue numbers-as-pointers are represented as a 52-bit integer
// Previously, the pointer was stored at the end of the 64-bit value
@@ -162,6 +220,11 @@ static bool JSVALUE_IS_NUMBER(EncodedJSValue val) {
static void* JSVALUE_TO_PTR(EncodedJSValue val) {
if (val.asInt64 == TagValueNull)
return 0;
if (JSCELL_IS_TYPED_ARRAY(val)) {
return JSVALUE_TO_TYPED_ARRAY_VECTOR(val);
}
val.asInt64 -= DoubleEncodeOffset;
size_t ptr = (size_t)val.asDouble;
return (void*)ptr;
@@ -244,6 +307,10 @@ static uint64_t JSVALUE_TO_UINT64(EncodedJSValue value) {
return (uint64_t)JSVALUE_TO_DOUBLE(value);
}
if (JSCELL_IS_TYPED_ARRAY(value)) {
return (uint64_t)JSVALUE_TO_TYPED_ARRAY_LENGTH(value);
}
return JSVALUE_TO_UINT64_SLOW(value);
}
static int64_t JSVALUE_TO_INT64(EncodedJSValue value) {
@@ -293,6 +360,9 @@ ZIG_REPR_TYPE JSFunctionCall(void* jsGlobalObject, void* callFrame);
/* --- The Callback Function */
bool my_callback_function(void* arg0) {
#ifdef INJECT_BEFORE
INJECT_BEFORE;
#endif
ZIG_REPR_TYPE arguments[1];
arguments[0] = PTR_TO_JSVALUE(arg0).asZigRepr;
return (bool)JSVALUE_TO_BOOL(_FFI_Callback_call((void*)0x0000000000000000ULL, 1, arguments));

View File

@@ -35,6 +35,38 @@ typedef _Bool bool;
#define true 1
#define false 0
#ifndef SRC_JS_NATIVE_API_TYPES_H_
typedef struct napi_env__ *napi_env;
typedef int64_t napi_value;
typedef enum {
napi_ok,
napi_invalid_arg,
napi_object_expected,
napi_string_expected,
napi_name_expected,
napi_function_expected,
napi_number_expected,
napi_boolean_expected,
napi_array_expected,
napi_generic_failure,
napi_pending_exception,
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
napi_callback_scope_mismatch,
napi_queue_full,
napi_closing,
napi_bigint_expected,
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock // unused
} napi_status;
void* NapiHandleScope__open(void* napi_env, bool detached);
void NapiHandleScope__close(void* napi_env, void* handleScope);
extern struct napi_env__ Bun__thisFFIModuleNapiEnv;
#endif
#ifdef INJECT_BEFORE
// #include <stdint.h>
@@ -45,14 +77,14 @@ typedef _Bool bool;
// begin with a 15-bit pattern within the range 0x0002..0xFFFC.
#define DoubleEncodeOffsetBit 49
#define DoubleEncodeOffset (1ll << DoubleEncodeOffsetBit)
#define OtherTag 0x2
#define BoolTag 0x4
#define UndefinedTag 0x8
#define OtherTag 0x2ll
#define BoolTag 0x4ll
#define UndefinedTag 0x8ll
#define TagValueFalse (OtherTag | BoolTag | false)
#define TagValueTrue (OtherTag | BoolTag | true)
#define TagValueUndefined (OtherTag | UndefinedTag)
#define TagValueNull (OtherTag)
#define NotCellMask NumberTag | OtherTag
#define NotCellMask (int64_t)(NumberTag | OtherTag)
#define MAX_INT32 2147483648
#define MAX_INT52 9007199254740991
@@ -70,6 +102,8 @@ typedef union EncodedJSValue {
JSCell *ptr;
#endif
napi_value asNapiValue;
#if IS_BIG_ENDIAN
struct {
int32_t tag;
@@ -140,6 +174,11 @@ static int32_t JSVALUE_TO_INT32(EncodedJSValue val) __attribute__((__always_inli
static float JSVALUE_TO_FLOAT(EncodedJSValue val) __attribute__((__always_inline__));
static double JSVALUE_TO_DOUBLE(EncodedJSValue val) __attribute__((__always_inline__));
static bool JSVALUE_TO_BOOL(EncodedJSValue val) __attribute__((__always_inline__));
static uint8_t GET_JSTYPE(EncodedJSValue val) __attribute__((__always_inline__));
static bool JSTYPE_IS_TYPED_ARRAY(uint8_t type) __attribute__((__always_inline__));
static bool JSCELL_IS_TYPED_ARRAY(EncodedJSValue val) __attribute__((__always_inline__));
static void* JSVALUE_TO_TYPED_ARRAY_VECTOR(EncodedJSValue val) __attribute__((__always_inline__));
static uint64_t JSVALUE_TO_TYPED_ARRAY_LENGTH(EncodedJSValue val) __attribute__((__always_inline__));
static bool JSVALUE_IS_CELL(EncodedJSValue val) {
return !(val.asInt64 & NotCellMask);
@@ -153,6 +192,25 @@ static bool JSVALUE_IS_NUMBER(EncodedJSValue val) {
return val.asInt64 & NumberTag;
}
static uint8_t GET_JSTYPE(EncodedJSValue val) {
return *(uint8_t*)((uint8_t*)val.asPtr + JSCell__offsetOfType);
}
static bool JSTYPE_IS_TYPED_ARRAY(uint8_t type) {
return type >= JSTypeArrayBufferViewMin && type <= JSTypeArrayBufferViewMax;
}
static bool JSCELL_IS_TYPED_ARRAY(EncodedJSValue val) {
return JSVALUE_IS_CELL(val) && JSTYPE_IS_TYPED_ARRAY(GET_JSTYPE(val));
}
static void* JSVALUE_TO_TYPED_ARRAY_VECTOR(EncodedJSValue val) {
return *(void**)((char*)val.asPtr + JSArrayBufferView__offsetOfVector);
}
static uint64_t JSVALUE_TO_TYPED_ARRAY_LENGTH(EncodedJSValue val) {
return *(uint64_t*)((char*)val.asPtr + JSArrayBufferView__offsetOfLength);
}
// JSValue numbers-as-pointers are represented as a 52-bit integer
// Previously, the pointer was stored at the end of the 64-bit value
@@ -162,6 +220,11 @@ static bool JSVALUE_IS_NUMBER(EncodedJSValue val) {
static void* JSVALUE_TO_PTR(EncodedJSValue val) {
if (val.asInt64 == TagValueNull)
return 0;
if (JSCELL_IS_TYPED_ARRAY(val)) {
return JSVALUE_TO_TYPED_ARRAY_VECTOR(val);
}
val.asInt64 -= DoubleEncodeOffset;
size_t ptr = (size_t)val.asDouble;
return (void*)ptr;
@@ -244,6 +307,10 @@ static uint64_t JSVALUE_TO_UINT64(EncodedJSValue value) {
return (uint64_t)JSVALUE_TO_DOUBLE(value);
}
if (JSCELL_IS_TYPED_ARRAY(value)) {
return (uint64_t)JSVALUE_TO_TYPED_ARRAY_LENGTH(value);
}
return JSVALUE_TO_UINT64_SLOW(value);
}
static int64_t JSVALUE_TO_INT64(EncodedJSValue value) {