mirror of
https://github.com/oven-sh/bun
synced 2026-02-08 18:08:50 +00:00
Compare commits
3 Commits
dylan/pyth
...
zack/maybe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
516253e128 | ||
|
|
f2494f8718 | ||
|
|
9dd74598f0 |
@@ -319,6 +319,7 @@ src/bundler/ServerComponentParseTask.zig
|
||||
src/bundler/ThreadPool.zig
|
||||
src/bunfig.zig
|
||||
src/cache.zig
|
||||
src/CheckedUninit.zig
|
||||
src/ci_info.zig
|
||||
src/cli.zig
|
||||
src/cli/add_command.zig
|
||||
|
||||
88
src/CheckedField.zig
Normal file
88
src/CheckedField.zig
Normal file
@@ -0,0 +1,88 @@
|
||||
/// Q: When to use this type?
|
||||
/// A: When you have to default initialize a field to `undefined` because you
|
||||
/// can't initialize it right away. (For example the `jsc: *VM` field in
|
||||
/// `VirtualMachine.zig`)
|
||||
///
|
||||
/// This wrapper type inserts checks in debug builds that ensure we're not
|
||||
/// accidentally forgetting to set it and causing subtle and time-wasting
|
||||
/// bugs!
|
||||
///
|
||||
/// Q: Why though, can't I just remember to initialize it?
|
||||
/// A: *You* might remember to initialize it, but someone else using the API may
|
||||
/// not, or a refactoring may forget it, and as we all know `undefined` in Zig
|
||||
/// causes subtle and extremely time-consuming btle bugs.
|
||||
///
|
||||
/// Fun fact: I wasted 30 minutes fixing a bug that turned out to be a field
|
||||
/// defaulted to `undefined` that didn't get set! So please, use this wrapper
|
||||
/// type to save everyone's time and patience :)
|
||||
///
|
||||
/// Q: Why not just use an optional? (e.g. `my_field: ?T = null`)
|
||||
/// A: A lot of the fields that get default set to undefined are only
|
||||
/// *temporarily* unset during initialization. It is annoying to have to unwrap
|
||||
/// optionals when 99% of the program will have the field initialized.
|
||||
///
|
||||
/// Q: Okay, how do I use it?
|
||||
/// A: Read on:
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Take a field that was previously default initialized to `undefined`:
|
||||
/// ```zig
|
||||
/// const VirtualMachine = struct {
|
||||
/// jsc: *VM = undefined,
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// And use `CheckedField(T)` instead!
|
||||
/// ```zig
|
||||
/// const VirtualMachine = struct {
|
||||
/// jsc: CheckedField(*VM) = .{},
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// You can then call `this.jsc.set(value)` to initialize it and
|
||||
/// `this.jsc.get()` to get the value.
|
||||
///
|
||||
/// Congratulations! You've just saved everyone's time!
|
||||
pub fn CheckedField(comptime T: type) type {
|
||||
const enabled = bun.Environment.isDebug;
|
||||
return struct {
|
||||
__value: T = undefined,
|
||||
__is_init: if (enabled) bool else void = if (enabled) false else {},
|
||||
|
||||
const This = @This();
|
||||
|
||||
pub inline fn get(this: *const This) T {
|
||||
this.assertInitialized();
|
||||
return this.__value;
|
||||
}
|
||||
|
||||
pub inline fn getPtr(this: *const This) *const T {
|
||||
this.assertInitialized();
|
||||
return &this.__value;
|
||||
}
|
||||
|
||||
pub inline fn mut(this: *This) *T {
|
||||
this.assertInitialized();
|
||||
return &this.__value;
|
||||
}
|
||||
|
||||
pub inline fn set(this: *This, value: T) void {
|
||||
this.__value = value;
|
||||
if (comptime enabled) {
|
||||
this.__is_init = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn assertInitialized(this: *const This) void {
|
||||
if (comptime enabled) {
|
||||
if (!this.__is_init) {
|
||||
@panic("CheckedField: Not initialized");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const bun = @import("bun");
|
||||
@@ -198,7 +198,7 @@ pub fn init(
|
||||
|
||||
const loaded_result = try vm.loadMacroEntryPoint(input_specifier, function_name, specifier, hash);
|
||||
|
||||
switch (loaded_result.unwrap(vm.jsc, .leave_unhandled)) {
|
||||
switch (loaded_result.unwrap(vm.jsc.get(), .leave_unhandled)) {
|
||||
.rejected => |result| {
|
||||
vm.unhandledRejection(vm.global, result, loaded_result.asValue());
|
||||
vm.disableMacroMode();
|
||||
@@ -502,8 +502,8 @@ pub const Runner = struct {
|
||||
|
||||
this.macro.vm.waitForPromise(promise);
|
||||
|
||||
const promise_result = promise.result(this.macro.vm.jsc);
|
||||
const rejected = promise.status(this.macro.vm.jsc) == .rejected;
|
||||
const promise_result = promise.result(this.macro.vm.jsc.get());
|
||||
const rejected = promise.status(this.macro.vm.jsc.get()) == .rejected;
|
||||
|
||||
if (promise_result.isUndefined() and this.is_top_level) {
|
||||
this.is_top_level = false;
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn buildCommand(ctx: bun.CLI.Command.Context) !void {
|
||||
// that bypass Bun's normal module resolver and plugin system.
|
||||
vm.global = BakeCreateProdGlobal(vm.console);
|
||||
vm.regular_event_loop.global = vm.global;
|
||||
vm.jsc = vm.global.vm();
|
||||
vm.jsc.set(vm.global.vm());
|
||||
vm.event_loop.ensureWaker();
|
||||
const b = &vm.transpiler;
|
||||
vm.preload = ctx.preloads;
|
||||
@@ -80,7 +80,7 @@ pub fn buildCommand(ctx: bun.CLI.Command.Context) !void {
|
||||
vm.is_main_thread = true;
|
||||
JSC.VirtualMachine.is_main_thread_vm = true;
|
||||
|
||||
const api_lock = vm.jsc.getAPILock();
|
||||
const api_lock = vm.jsc.get().getAPILock();
|
||||
defer api_lock.release();
|
||||
buildWithVm(ctx, cwd, vm) catch |err| switch (err) {
|
||||
error.JSError => |e| {
|
||||
@@ -136,9 +136,9 @@ pub fn buildWithVm(ctx: bun.CLI.Command.Context, cwd: []const u8, vm: *VirtualMa
|
||||
return error.JSError;
|
||||
};
|
||||
|
||||
config_promise.setHandled(vm.jsc);
|
||||
config_promise.setHandled(vm.jsc.get());
|
||||
vm.waitForPromise(.{ .internal = config_promise });
|
||||
var options = switch (config_promise.unwrap(vm.jsc, .mark_handled)) {
|
||||
var options = switch (config_promise.unwrap(vm.jsc.get(), .mark_handled)) {
|
||||
.pending => unreachable,
|
||||
.fulfilled => |resolved| config: {
|
||||
bun.assert(resolved.isUndefined());
|
||||
@@ -549,9 +549,9 @@ pub fn buildWithVm(ctx: bun.CLI.Command.Context, cwd: []const u8, vm: *VirtualMa
|
||||
route_param_info,
|
||||
route_style_references,
|
||||
);
|
||||
render_promise.setHandled(vm.jsc);
|
||||
render_promise.setHandled(vm.jsc.get());
|
||||
vm.waitForPromise(.{ .normal = render_promise });
|
||||
switch (render_promise.unwrap(vm.jsc, .mark_handled)) {
|
||||
switch (render_promise.unwrap(vm.jsc.get(), .mark_handled)) {
|
||||
.pending => unreachable,
|
||||
.fulfilled => {
|
||||
Output.prettyln("done", .{});
|
||||
@@ -568,9 +568,9 @@ pub fn buildWithVm(ctx: bun.CLI.Command.Context, cwd: []const u8, vm: *VirtualMa
|
||||
/// quits the process on exception
|
||||
fn loadModule(vm: *VirtualMachine, global: *JSC.JSGlobalObject, key: JSValue) !JSValue {
|
||||
const promise = BakeLoadModuleByKey(global, key).asAnyPromise().?.internal;
|
||||
promise.setHandled(vm.jsc);
|
||||
promise.setHandled(vm.jsc.get());
|
||||
vm.waitForPromise(.{ .internal = promise });
|
||||
switch (promise.unwrap(vm.jsc, .mark_handled)) {
|
||||
switch (promise.unwrap(vm.jsc.get(), .mark_handled)) {
|
||||
.pending => unreachable,
|
||||
.fulfilled => |val| {
|
||||
bun.assert(val.isUndefined());
|
||||
|
||||
@@ -2144,7 +2144,7 @@ pub const RuntimeTranspilerStore = struct {
|
||||
|
||||
pub fn runFromJSThread(this: *RuntimeTranspilerStore, event_loop: *JSC.EventLoop, global: *JSC.JSGlobalObject, vm: *JSC.VirtualMachine) void {
|
||||
var batch = this.queue.popBatch();
|
||||
const jsc_vm = vm.jsc;
|
||||
const jsc_vm = vm.jsc.get();
|
||||
var iter = batch.iterator();
|
||||
if (iter.next()) |job| {
|
||||
// we run just one job first to see if there are more
|
||||
|
||||
@@ -46,7 +46,7 @@ dns_result_order: DNSResolver.Order = .verbatim,
|
||||
counters: Counters = .{},
|
||||
|
||||
hot_reload: bun.CLI.Command.HotReload = .none,
|
||||
jsc: *VM = undefined,
|
||||
jsc: CheckedField(*VM) = .{},
|
||||
|
||||
/// hide bun:wrap from stack traces
|
||||
/// bun:wrap is very noisy
|
||||
@@ -1018,8 +1018,8 @@ pub fn initWithModuleGraph(
|
||||
null,
|
||||
);
|
||||
vm.regular_event_loop.global = vm.global;
|
||||
vm.jsc = vm.global.vm();
|
||||
uws.Loop.get().internal_loop_data.jsc_vm = vm.jsc;
|
||||
vm.jsc.set(vm.global.vm());
|
||||
uws.Loop.get().internal_loop_data.jsc_vm = vm.jsc.get();
|
||||
|
||||
vm.configureDebugger(opts.debugger);
|
||||
vm.body_value_hive_allocator = Body.Value.HiveAllocator.init(bun.typedAllocator(JSC.WebCore.Body.Value));
|
||||
@@ -1127,17 +1127,22 @@ pub fn init(opts: Options) !*VirtualMachine {
|
||||
|
||||
vm.transpiler.macro_context = js_ast.Macro.MacroContext.init(&vm.transpiler);
|
||||
|
||||
vm.global = JSGlobalObject.create(
|
||||
vm.global = JSGlobalObject.createEnsureWaker(
|
||||
vm,
|
||||
vm.console,
|
||||
if (opts.is_main_thread) 1 else std.math.maxInt(i32),
|
||||
opts.smol,
|
||||
opts.eval,
|
||||
null,
|
||||
// DO NOT call `.ensureWaker()` as this accesses `vm.jsc` which is not
|
||||
// initialized yet.
|
||||
false,
|
||||
);
|
||||
vm.regular_event_loop.global = vm.global;
|
||||
vm.jsc = vm.global.vm();
|
||||
uws.Loop.get().internal_loop_data.jsc_vm = vm.jsc;
|
||||
vm.jsc.set(vm.global.vm());
|
||||
uws.Loop.get().internal_loop_data.jsc_vm = vm.jsc.get();
|
||||
// NOW call ensure waker since everything is set
|
||||
vm.eventLoop().ensureWaker();
|
||||
vm.smol = opts.smol;
|
||||
vm.dns_result_order = opts.dns_result_order;
|
||||
|
||||
@@ -1299,8 +1304,8 @@ pub fn initWorker(
|
||||
worker.cpp_worker,
|
||||
);
|
||||
vm.regular_event_loop.global = vm.global;
|
||||
vm.jsc = vm.global.vm();
|
||||
uws.Loop.get().internal_loop_data.jsc_vm = vm.jsc;
|
||||
vm.jsc.set(vm.global.vm());
|
||||
uws.Loop.get().internal_loop_data.jsc_vm = vm.jsc.get();
|
||||
vm.transpiler.setAllocator(allocator);
|
||||
vm.body_value_hive_allocator = Body.Value.HiveAllocator.init(bun.typedAllocator(JSC.WebCore.Body.Value));
|
||||
|
||||
@@ -1945,7 +1950,7 @@ pub noinline fn runErrorHandler(this: *VirtualMachine, result: JSValue, exceptio
|
||||
|
||||
const writer = buffered_writer.writer();
|
||||
|
||||
if (result.asException(this.jsc)) |exception| {
|
||||
if (result.asException(this.jsc.get())) |exception| {
|
||||
this.printException(
|
||||
exception,
|
||||
exception_list,
|
||||
@@ -3680,3 +3685,4 @@ const DotEnv = bun.DotEnv;
|
||||
const HotReloader = JSC.hot_reloader.HotReloader;
|
||||
const Body = webcore.Body;
|
||||
const Counters = @import("./Counters.zig");
|
||||
const CheckedField = bun.CheckedField;
|
||||
|
||||
@@ -2250,7 +2250,7 @@ pub fn spawnMaybeSync(
|
||||
!stdio[2].isPiped() and
|
||||
extra_fds.items.len == 0 and
|
||||
!jsc_vm.auto_killer.enabled and
|
||||
!jsc_vm.jsc.hasExecutionTimeLimit() and
|
||||
!jsc_vm.jsc.get().hasExecutionTimeLimit() and
|
||||
!jsc_vm.isInspectorEnabled() and
|
||||
!bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_DISABLE_SPAWNSYNC_FAST_PATH);
|
||||
|
||||
|
||||
@@ -2239,7 +2239,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d
|
||||
|
||||
const ctx = this.request_pool_allocator.tryGet() catch bun.outOfMemory();
|
||||
ctx.create(this, req, resp, should_deinit_context, method);
|
||||
this.vm.jsc.reportExtraMemory(@sizeOf(RequestContext));
|
||||
this.vm.jsc.get().reportExtraMemory(@sizeOf(RequestContext));
|
||||
const body = this.vm.initRequestBodyValue(.{ .Null = {} }) catch unreachable;
|
||||
|
||||
ctx.request_body = body;
|
||||
|
||||
@@ -773,11 +773,22 @@ pub const JSGlobalObject = opaque {
|
||||
mini_mode: bool,
|
||||
eval_mode: bool,
|
||||
worker_ptr: ?*anyopaque,
|
||||
) *JSGlobalObject {
|
||||
return createEnsureWaker(v, console, context_id, mini_mode, eval_mode, worker_ptr, true);
|
||||
}
|
||||
pub fn createEnsureWaker(
|
||||
v: *JSC.VirtualMachine,
|
||||
console: *anyopaque,
|
||||
context_id: i32,
|
||||
mini_mode: bool,
|
||||
eval_mode: bool,
|
||||
worker_ptr: ?*anyopaque,
|
||||
ensure_waker: bool,
|
||||
) *JSGlobalObject {
|
||||
const trace = bun.perf.trace("JSGlobalObject.create");
|
||||
defer trace.end();
|
||||
|
||||
v.eventLoop().ensureWaker();
|
||||
if (ensure_waker) v.eventLoop().ensureWaker();
|
||||
const global = Zig__GlobalObject__create(console, context_id, mini_mode, eval_mode, worker_ptr);
|
||||
|
||||
// JSC might mess with the stack size.
|
||||
|
||||
@@ -70,7 +70,7 @@ pub fn exit(this: *EventLoop) void {
|
||||
defer this.debug.exit();
|
||||
|
||||
if (count == 1 and !this.virtual_machine.is_inside_deferred_task_queue) {
|
||||
this.drainMicrotasksWithGlobal(this.global, this.virtual_machine.jsc) catch {};
|
||||
this.drainMicrotasksWithGlobal(this.global, this.virtual_machine.jsc.get()) catch {};
|
||||
}
|
||||
|
||||
this.entered_event_loop_count -= 1;
|
||||
@@ -83,7 +83,7 @@ pub fn exitMaybeDrainMicrotasks(this: *EventLoop, allow_drain_microtask: bool) b
|
||||
defer this.debug.exit();
|
||||
|
||||
if (allow_drain_microtask and count == 1 and !this.virtual_machine.is_inside_deferred_task_queue) {
|
||||
try this.drainMicrotasksWithGlobal(this.global, this.virtual_machine.jsc);
|
||||
try this.drainMicrotasksWithGlobal(this.global, this.virtual_machine.jsc.get());
|
||||
}
|
||||
|
||||
this.entered_event_loop_count -= 1;
|
||||
@@ -127,13 +127,13 @@ pub fn drainMicrotasksWithGlobal(this: *EventLoop, globalObject: *JSC.JSGlobalOb
|
||||
}
|
||||
|
||||
pub fn drainMicrotasks(this: *EventLoop) bun.JSExecutionTerminated!void {
|
||||
try this.drainMicrotasksWithGlobal(this.global, this.virtual_machine.jsc);
|
||||
try this.drainMicrotasksWithGlobal(this.global, this.virtual_machine.jsc.get());
|
||||
}
|
||||
|
||||
// should be called after exit()
|
||||
pub fn maybeDrainMicrotasks(this: *EventLoop) void {
|
||||
if (this.entered_event_loop_count == 0 and !this.virtual_machine.is_inside_deferred_task_queue) {
|
||||
this.drainMicrotasksWithGlobal(this.global, this.virtual_machine.jsc) catch {};
|
||||
this.drainMicrotasksWithGlobal(this.global, this.virtual_machine.jsc.get()) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ pub fn tick(this: *EventLoop) void {
|
||||
this.processGCTimer();
|
||||
|
||||
const global = ctx.global;
|
||||
const global_vm = ctx.jsc;
|
||||
const global_vm = ctx.jsc.get();
|
||||
|
||||
while (true) {
|
||||
while (this.tickWithCount(ctx) > 0) : (this.global.handleRejectedPromises()) {
|
||||
@@ -485,7 +485,7 @@ pub fn tick(this: *EventLoop) void {
|
||||
}
|
||||
|
||||
pub fn waitForPromise(this: *EventLoop, promise: JSC.AnyPromise) void {
|
||||
const jsc_vm = this.virtual_machine.jsc;
|
||||
const jsc_vm = this.virtual_machine.jsc.get();
|
||||
switch (promise.status(jsc_vm)) {
|
||||
.pending => {
|
||||
while (promise.status(jsc_vm) == .pending) {
|
||||
@@ -502,7 +502,7 @@ pub fn waitForPromise(this: *EventLoop, promise: JSC.AnyPromise) void {
|
||||
|
||||
pub fn waitForPromiseWithTermination(this: *EventLoop, promise: JSC.AnyPromise) void {
|
||||
const worker = this.virtual_machine.worker orelse @panic("EventLoop.waitForPromiseWithTermination: worker is not initialized");
|
||||
const jsc_vm = this.virtual_machine.jsc;
|
||||
const jsc_vm = this.virtual_machine.jsc.get();
|
||||
switch (promise.status(jsc_vm)) {
|
||||
.pending => {
|
||||
while (!worker.hasRequestedTerminate() and promise.status(jsc_vm) == .pending) {
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn init(this: *GarbageCollectionController, vm: *VirtualMachine) void {
|
||||
const actual = uws.Loop.get();
|
||||
this.gc_timer = uws.Timer.createFallthrough(actual, this);
|
||||
this.gc_repeating_timer = uws.Timer.createFallthrough(actual, this);
|
||||
actual.internal_loop_data.jsc_vm = vm.jsc;
|
||||
actual.internal_loop_data.jsc_vm = vm.jsc.get();
|
||||
|
||||
if (comptime Environment.isDebug) {
|
||||
if (bun.getenvZ("BUN_TRACK_LAST_FN_NAME") != null) {
|
||||
@@ -114,7 +114,7 @@ pub fn onGCRepeatingTimer(timer: *uws.Timer) callconv(.C) void {
|
||||
|
||||
pub fn processGCTimer(this: *GarbageCollectionController) void {
|
||||
if (this.disabled) return;
|
||||
var vm = this.bunVM().jsc;
|
||||
var vm = this.bunVM().jsc.get();
|
||||
this.processGCTimerWithHeapSize(vm, vm.blockBytesAllocated());
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ fn processGCTimerWithHeapSize(this: *GarbageCollectionController, vm: *JSC.VM, t
|
||||
|
||||
pub fn performGC(this: *GarbageCollectionController) void {
|
||||
if (this.disabled) return;
|
||||
var vm = this.bunVM().jsc;
|
||||
var vm = this.bunVM().jsc.get();
|
||||
vm.collectAsync();
|
||||
this.gc_last_heap_size = vm.blockBytesAllocated();
|
||||
}
|
||||
|
||||
@@ -3767,3 +3767,5 @@ pub fn contains(item: anytype, list: *const std.ArrayListUnmanaged(@TypeOf(item)
|
||||
else => std.mem.indexOfScalar(T, list.items, item) != null,
|
||||
};
|
||||
}
|
||||
|
||||
pub const CheckedField = @import("./CheckedField.zig").CheckedField;
|
||||
|
||||
@@ -437,7 +437,7 @@ pub const Run = struct {
|
||||
const to_print = brk: {
|
||||
const result: JSC.JSValue = vm.entry_point_result.value.get() orelse .js_undefined;
|
||||
if (result.asAnyPromise()) |promise| {
|
||||
switch (promise.status(vm.jsc)) {
|
||||
switch (promise.status(vm.jsc.get())) {
|
||||
.pending => {
|
||||
result._then2(vm.global, .js_undefined, Bun__onResolveEntryPointResult, Bun__onRejectEntryPointResult);
|
||||
|
||||
@@ -451,7 +451,7 @@ pub const Run = struct {
|
||||
|
||||
break :brk result;
|
||||
},
|
||||
else => break :brk promise.result(vm.jsc),
|
||||
else => break :brk promise.result(vm.jsc.get()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user