aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile18
-rw-r--r--src/javascript/jsc/api/FFI.h56
-rw-r--r--src/javascript/jsc/api/ffi.zig101
3 files changed, 139 insertions, 36 deletions
diff --git a/Makefile b/Makefile
index ceee511f7..c8199addd 100644
--- a/Makefile
+++ b/Makefile
@@ -304,7 +304,7 @@ ARCHIVE_FILES_WITHOUT_LIBCRYPTO = $(MIMALLOC_FILE_PATH) \
-lssl \
-lbase64 \
-ltcc \
- -L/Users/jarred/Build/tinycc
+ -L$(TINYCC_DIR)
ARCHIVE_FILES = $(ARCHIVE_FILES_WITHOUT_LIBCRYPTO) -lcrypto
@@ -359,6 +359,15 @@ base64:
$(CXX) $(CXXFLAGS) $(CFLAGS) -c neonbase64.cc -g -fPIC -emit-llvm && \
$(AR) rcvs $(BUN_DEPS_OUT_DIR)/libbase64.a ./*.bc
+# Prevent dependency on libtcc1 so it doesn't do filesystem lookups
+TINYCC_CFLAGS= -DTCC_LIBTCC1=\"\0\"
+
+tinycc:
+ cd $(TINYCC_DIR) && \
+ make clean && \
+ AR=$(AR) CC=$(CC) CFLAGS='$(CFLAGS) -emit-llvm $(TINYCC_CFLAGS)' ./configure --enable-static --cc=$(CC) --ar=$(AR) --config-predefs=yes && \
+ make -j10
+
generate-builtins:
$(shell which python || which python2) $(realpath $(WEBKIT_DIR)/Source/JavaScriptCore/Scripts/generate-js-builtins.py) -i $(realpath src/javascript/jsc/bindings/builtins/js) -o $(realpath src/javascript/jsc/bindings) --framework WebCore
@@ -781,11 +790,16 @@ ifeq ($(OS_NAME),linux)
release-bin-push-dsym:
endif
+TINYCC_DIR ?= $(realpath $(HOME)/Build/tinycc)
release-bin-push: release-bin-push-bin release-bin-push-dsym
-release-bin-without-push: test-all release-bin-check release-bin-generate release-bin-codesign
+generate-release-bin-as-zip: release-bin-generate release-bin-codesign
+release-bin-without-push: test-all release-bin-check generate-release-bin-as-zip
+
release-bin: release-bin-without-push release-bin-push
+
+
release-bin-dir:
echo $(PACKAGE_DIR)
diff --git a/src/javascript/jsc/api/FFI.h b/src/javascript/jsc/api/FFI.h
index 44259eb3b..48e4db48d 100644
--- a/src/javascript/jsc/api/FFI.h
+++ b/src/javascript/jsc/api/FFI.h
@@ -7,38 +7,58 @@
// It must be kept in sync with JSCJSValue.h
// https://github.com/Jarred-Sumner/WebKit/blob/72c2052b781cbfd4af867ae79ac9de460e392fba/Source/JavaScriptCore/runtime/JSCJSValue.h#L455-L458
#ifdef IS_CALLBACK
-#define INJECT_BEFORE printf("bun_call %p cachedJSContext %p cachedCallbackFunction %p\n", &bun_call, cachedJSContext, cachedCallbackFunction);
-#endif
-
-#ifdef USES_FLOAT
-#include <math.h>
+// #define INJECT_BEFORE printf("bun_call %p cachedJSContext %p cachedCallbackFunction %p\n", &bun_call, cachedJSContext, cachedCallbackFunction);
#endif
#define IS_BIG_ENDIAN 0
#define USE_JSVALUE64 1
#define USE_JSVALUE32_64 0
-#include <stdint.h>
-#include <stdio.h>
+// #include <stdint.h>
+#ifdef INJECT_BEFORE
+// #include <stdio.h>
+#endif
// #include <tcclib.h>
// // /* 7.18.1.1 Exact-width integer types */
-// typedef unsigned char uint8_t;
-// typedef signed char int8_t;
-// typedef short int16_t;
-// typedef unsigned short uint16_t;
-// typedef int int32_t;
-// typedef unsigned int uint32_t;
-// typedef long long int64_t;
-// typedef unsigned long long uint64_t;
-// typedef uint64_t size_t;
-// typedef long intptr_t;
-// typedef uint64_t uintptr_t;
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+typedef uint64_t size_t;
+typedef long intptr_t;
+typedef uint64_t uintptr_t;
typedef _Bool bool;
#define true 1
#define false 0
+#ifdef USES_FLOAT
+// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c
+double trunc(double x);
+double trunc(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ int e = (int)(u.i >> 52 & 0x7ff) - 0x3ff + 12;
+ uint64_t m;
+
+ if (e >= 52 + 12)
+ return x;
+ if (e < 12)
+ e = 1;
+ m = -1ULL >> e;
+ if ((u.i & m) == 0)
+ return x;
+ x + 0x1p120f;
+ u.i &= ~m;
+ return u.f;
+}
+#endif
+
// This value is 2^49, used to encode doubles such that the encoded value will
// begin with a 15-bit pattern within the range 0x0002..0xFFFC.
diff --git a/src/javascript/jsc/api/ffi.zig b/src/javascript/jsc/api/ffi.zig
index 124b0f533..ac8f615d1 100644
--- a/src/javascript/jsc/api/ffi.zig
+++ b/src/javascript/jsc/api/ffi.zig
@@ -133,12 +133,15 @@ pub const FFI = struct {
// TODO: WeakRefHandle that automatically frees it?
JSC.C.JSValueProtect(globalThis.ref(), js_callback.asObjectRef());
func.base_name = "";
+
func.compileCallback(allocator, globalThis, js_callback.asObjectRef().?) catch return ZigString.init("Out of memory").toErrorInstance(globalThis);
switch (func.step) {
.failed => |err| {
JSC.C.JSValueUnprotect(globalThis.ref(), js_callback.asObjectRef());
- const message = ZigString.init(err).toErrorInstance(globalThis);
+ const message = ZigString.init(err.msg).toErrorInstance(globalThis);
+
func.deinit(allocator);
+
return message;
},
.pending => {
@@ -310,6 +313,7 @@ pub const FFI = struct {
var obj = JSC.JSValue.c(JSC.C.JSObjectMake(global.ref(), null, null));
JSC.C.JSValueProtect(global.ref(), obj.asObjectRef());
+ // var has_checked = false;
defer JSC.C.JSValueUnprotect(global.ref(), obj.asObjectRef());
for (symbols.values()) |*function| {
var resolved_symbol = dylib.lookup(*anyopaque, function.base_name) orelse {
@@ -324,8 +328,19 @@ pub const FFI = struct {
};
function.symbol_from_dynamic_library = resolved_symbol;
- function.compile(allocator) catch {
- const ret = JSC.toInvalidArguments("Failed to compile symbol \"{s}\" in \"{s}\"", .{ std.mem.span(function.base_name), name_slice.slice() }, global.ref());
+ // if (!has_checked and VirtualMachine.vm.rareData().needs_to_copy_libtcc1 orelse true) {
+ // has_checked = true;
+ // VirtualMachine.vm.rareData().needs_to_copy_libtcc1 = false;
+ // if (VirtualMachine.vm.bundler.env.get("BUN_INSTALL")) |bun_install_dir| {
+ // maybeCopyLibtcc1(bun_install_dir);
+ // }
+ // }
+ function.compile(allocator) catch |err| {
+ const ret = JSC.toInvalidArguments("{s} when compiling symbol \"{s}\" in \"{s}\"", .{
+ std.mem.span(@errorName(err)),
+ std.mem.span(function.base_name),
+ name_slice.slice(),
+ }, global.ref());
for (symbols.values()) |*value| {
allocator.free(bun.constStrToU8(std.mem.span(value.base_name)));
value.arg_types.clearAndFree(allocator);
@@ -342,7 +357,9 @@ pub const FFI = struct {
}
symbols.clearAndFree(allocator);
dylib.close();
- return ZigString.init(err).toErrorInstance(global);
+ const res = ZigString.init(err.msg).toErrorInstance(global);
+ function.deinit(allocator);
+ return res;
},
.pending => {
for (symbols.values()) |*value| {
@@ -491,6 +508,8 @@ pub const FFI = struct {
arg_types: std.ArrayListUnmanaged(ABIType) = .{},
step: Step = Step{ .pending = {} },
+ pub var lib_dirZ: [*:0]const u8 = "";
+
pub fn deinit(val: *Function, allocator: std.mem.Allocator) void {
if (std.mem.span(val.base_name).len > 0) allocator.free(bun.constStrToU8(std.mem.span(val.base_name)));
@@ -508,8 +527,8 @@ pub const FFI = struct {
}
}
- if (val.step == .failed) {
- allocator.free(val.step.failed);
+ if (val.step == .failed and val.step.failed.allocated) {
+ allocator.free(val.step.failed.msg);
}
}
@@ -521,7 +540,10 @@ pub const FFI = struct {
js_function: ?*anyopaque = null,
js_context: ?*anyopaque = null,
},
- failed: []const u8,
+ failed: struct {
+ msg: []const u8,
+ allocated: bool = false,
+ },
};
const FFI_HEADER: string = @embedFile("./FFI.h");
@@ -548,12 +570,23 @@ pub const FFI = struct {
pub fn handleTCCError(ctx: ?*anyopaque, message: [*c]const u8) callconv(.C) void {
var this = bun.cast(*Function, ctx.?);
- this.step = .{ .failed = std.mem.span(message) };
+ var msg = std.mem.span(message);
+ if (msg.len > 0) {
+ var offset: usize = 0;
+ // the message we get from TCC sometimes has garbage in it
+ // i think because we're doing in-memory compilation
+ while (offset < msg.len) : (offset += 1) {
+ if (msg[offset] > 0x20 and msg[offset] < 0x7f) break;
+ }
+ msg = msg[offset..];
+ }
+
+ this.step = .{ .failed = .{ .msg = VirtualMachine.vm.allocator.dupe(u8, msg) catch unreachable, .allocated = true } };
}
extern fn pthread_jit_write_protect_np(enable: bool) callconv(.C) void;
- const tcc_options = "-std=c11 -Wl,--export-all-symbols";
+ const tcc_options = "-std=c11 -nostdlib -Wl,--export-all-symbols";
pub fn compile(
this: *Function,
@@ -565,8 +598,10 @@ pub const FFI = struct {
try source_code.append(0);
defer source_code.deinit();
+
var state = TCC.tcc_new() orelse return error.TCCMissing;
TCC.tcc_set_options(state, tcc_options);
+ // addSharedLibPaths(state);
TCC.tcc_set_error_func(state, this, handleTCCError);
this.state = state;
defer {
@@ -589,14 +624,25 @@ pub const FFI = struct {
// did tcc report failure but never called the error callback?
if (compilation_result == -1) {
- this.step = .{ .failed = "tcc returned -1, which means it failed" };
+ this.step = .{ .failed = .{ .msg = "tcc returned -1, which means it failed" } };
return;
}
-
+ CompilerRT.inject(state);
_ = TCC.tcc_add_symbol(state, this.base_name, this.symbol_from_dynamic_library.?);
+ if (this.step == .failed) {
+ return;
+ }
var relocation_size = TCC.tcc_relocate(state, null);
- if (relocation_size == 0) return;
+ if (this.step == .failed) {
+ return;
+ }
+
+ if (relocation_size < 0) {
+ this.step = .{ .failed = .{ .msg = "tcc_relocate returned a negative value" } };
+ return;
+ }
+
var bytes: []u8 = try allocator.rawAlloc(@intCast(usize, relocation_size), 16, 16, 0);
defer {
if (this.step == .failed) {
@@ -615,7 +661,7 @@ pub const FFI = struct {
var formatted_symbol_name = try std.fmt.allocPrintZ(allocator, "bun_gen_{s}", .{std.mem.span(this.base_name)});
defer allocator.free(formatted_symbol_name);
var symbol = TCC.tcc_get_symbol(state, formatted_symbol_name) orelse {
- this.step = .{ .failed = "missing generated symbol in source code" };
+ this.step = .{ .failed = .{ .msg = "missing generated symbol in source code" } };
return;
};
@@ -629,6 +675,29 @@ pub const FFI = struct {
return;
}
+ const CompilerRT = struct {
+ noinline fn memset(
+ dest: [*]u8,
+ c: u8,
+ byte_count: usize,
+ ) callconv(.C) void {
+ @memset(dest, c, byte_count);
+ }
+
+ noinline fn memcpy(
+ noalias dest: [*]u8,
+ noalias source: [*]const u8,
+ byte_count: usize,
+ ) callconv(.C) void {
+ @memcpy(dest, source, byte_count);
+ }
+
+ pub fn inject(state: *TCC.TCCState) void {
+ _ = TCC.tcc_add_symbol(state, "memset", &memset);
+ _ = TCC.tcc_add_symbol(state, "memcpy", &memcpy);
+ }
+ };
+
pub fn compileCallback(
this: *Function,
allocator: std.mem.Allocator,
@@ -643,7 +712,6 @@ pub const FFI = struct {
try source_code.append(0);
// defer source_code.deinit();
var state = TCC.tcc_new() orelse return error.TCCMissing;
-
TCC.tcc_set_options(state, tcc_options);
TCC.tcc_set_error_func(state, this, handleTCCError);
this.state = state;
@@ -655,6 +723,7 @@ pub const FFI = struct {
}
_ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY);
+ CompilerRT.inject(state);
const compilation_result = TCC.tcc_compile_string(
state,
@@ -668,7 +737,7 @@ pub const FFI = struct {
// did tcc report failure but never called the error callback?
if (compilation_result == -1) {
- this.step = .{ .failed = "tcc returned -1, which means it failed" };
+ this.step = .{ .failed = .{ .msg = "tcc returned -1, which means it failed" } };
return;
}
@@ -696,7 +765,7 @@ pub const FFI = struct {
}
var symbol = TCC.tcc_get_symbol(state, "my_callback_function") orelse {
- this.step = .{ .failed = "missing generated symbol in source code" };
+ this.step = .{ .failed = .{ .msg = "missing generated symbol in source code" } };
return;
};