mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 03:48:56 +00:00
Fix a couple memory leaks in bun dev
This commit is contained in:
@@ -37,11 +37,6 @@ query_string_map: ?QueryStringMap = null,
|
||||
param_map: ?QueryStringMap = null,
|
||||
params_list_holder: FilesystemRouter.Param.List = .{},
|
||||
|
||||
script_src: ?string = null,
|
||||
script_src_buf: [1024]u8 = undefined,
|
||||
|
||||
script_src_buf_writer: ScriptSrcStream = undefined,
|
||||
|
||||
pub fn importRoute(
|
||||
this: *Router,
|
||||
ctx: js.JSContextRef,
|
||||
@@ -70,15 +65,25 @@ pub fn match(
|
||||
return null;
|
||||
}
|
||||
|
||||
var arg: JSC.JSValue = undefined;
|
||||
|
||||
if (FetchEvent.Class.loaded and js.JSValueIsObjectOfClass(ctx, arguments[0], FetchEvent.Class.get().*)) {
|
||||
return matchFetchEvent(ctx, To.Zig.ptr(FetchEvent, arguments[0]), exception);
|
||||
var fetch_event = To.Zig.ptr(FetchEvent, arguments[0]);
|
||||
if (fetch_event.request_context != null) {
|
||||
return matchFetchEvent(ctx, fetch_event, exception);
|
||||
}
|
||||
|
||||
// When disconencted, we still have a copy of the request data in here
|
||||
arg = JSC.JSValue.fromRef(fetch_event.getRequest(ctx, null, null, null));
|
||||
} else {
|
||||
arg = JSC.JSValue.fromRef(arguments[0]);
|
||||
}
|
||||
|
||||
var router = JavaScript.VirtualMachine.vm.bundler.router orelse {
|
||||
JSError(getAllocator(ctx), "Bun.match needs a framework configured with routes", .{}, ctx, exception);
|
||||
return null;
|
||||
};
|
||||
var arg = JSC.JSValue.fromRef(arguments[0]);
|
||||
|
||||
var path_: ?ZigString.Slice = null;
|
||||
var pathname: string = "";
|
||||
defer {
|
||||
@@ -128,7 +133,6 @@ pub fn match(
|
||||
instance.params_list_holder = params_list;
|
||||
instance.route = &instance.route_holder;
|
||||
instance.route_holder.params = &instance.params_list_holder;
|
||||
instance.script_src_buf_writer = ScriptSrcStream{ .pos = 0, .buffer = std.mem.span(&instance.script_src_buf) };
|
||||
|
||||
return Instance.make(ctx, instance);
|
||||
}
|
||||
@@ -150,7 +154,7 @@ fn matchFetchEvent(
|
||||
fetch_event: *const FetchEvent,
|
||||
_: js.ExceptionRef,
|
||||
) js.JSObjectRef {
|
||||
return createRouteObject(ctx, fetch_event.request_context);
|
||||
return createRouteObject(ctx, fetch_event.request_context.?);
|
||||
}
|
||||
|
||||
fn createRouteObject(ctx: js.JSContextRef, req: *const http.RequestContext) js.JSValueRef {
|
||||
@@ -169,7 +173,6 @@ fn createRouteObjectFromMatch(
|
||||
router.* = Router{
|
||||
.route = route,
|
||||
};
|
||||
router.script_src_buf_writer = ScriptSrcStream{ .pos = 0, .buffer = std.mem.span(&router.script_src_buf) };
|
||||
|
||||
return Instance.make(ctx, router);
|
||||
}
|
||||
@@ -221,9 +224,7 @@ pub const Instance = NewClass(
|
||||
},
|
||||
},
|
||||
.{
|
||||
.finalize = .{
|
||||
.rfn = finalize,
|
||||
},
|
||||
.finalize = finalize,
|
||||
.import = .{
|
||||
.rfn = importRoute,
|
||||
.ts = d.ts{
|
||||
@@ -345,8 +346,9 @@ pub fn finalize(
|
||||
this.params_list_holder.deinit(bun.default_allocator);
|
||||
this.params_list_holder = .{};
|
||||
this.needs_deinit = false;
|
||||
bun.default_allocator.destroy(this);
|
||||
}
|
||||
|
||||
bun.default_allocator.destroy(this);
|
||||
}
|
||||
|
||||
pub fn getPathname(
|
||||
@@ -437,13 +439,13 @@ pub fn createQueryObject(ctx: js.JSContextRef, map: *QueryStringMap, _: js.Excep
|
||||
return value.asRef();
|
||||
}
|
||||
|
||||
threadlocal var entry_point_tempbuf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
pub fn getScriptSrcString(
|
||||
comptime Writer: type,
|
||||
writer: Writer,
|
||||
file_path: string,
|
||||
client_framework_enabled: bool,
|
||||
) void {
|
||||
var entry_point_tempbuf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
// We don't store the framework config including the client parts in the server
|
||||
// instead, we just store a boolean saying whether we should generate this whenever the script is requested
|
||||
// this is kind of bad. we should consider instead a way to inline the contents of the script.
|
||||
@@ -454,11 +456,11 @@ pub fn getScriptSrcString(
|
||||
Fs.PathName.init(file_path),
|
||||
),
|
||||
VirtualMachine.vm.origin,
|
||||
ScriptSrcStream.Writer,
|
||||
Writer,
|
||||
writer,
|
||||
);
|
||||
} else {
|
||||
JavaScript.Bun.getPublicPath(file_path, VirtualMachine.vm.origin, ScriptSrcStream.Writer, writer);
|
||||
JavaScript.Bun.getPublicPath(file_path, VirtualMachine.vm.origin, Writer, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,12 +471,12 @@ pub fn getScriptSrc(
|
||||
_: js.JSStringRef,
|
||||
_: js.ExceptionRef,
|
||||
) js.JSValueRef {
|
||||
const src = this.script_src orelse brk: {
|
||||
getScriptSrcString(ScriptSrcStream.Writer, this.script_src_buf_writer.writer(), this.route.file_path, this.route.client_framework_enabled);
|
||||
break :brk this.script_src_buf[0..this.script_src_buf_writer.pos];
|
||||
};
|
||||
var script_src_buffer = std.ArrayList(u8).init(bun.default_allocator);
|
||||
|
||||
return ZigString.init(src).toValueGC(ctx.ptr()).asObjectRef();
|
||||
var writer = script_src_buffer.writer();
|
||||
getScriptSrcString(@TypeOf(&writer), &writer, this.route.file_path, this.route.client_framework_enabled);
|
||||
|
||||
return ZigString.init(script_src_buffer.toOwnedSlice()).toExternalValue(ctx.ptr()).asObjectRef();
|
||||
}
|
||||
|
||||
pub fn getParams(
|
||||
|
||||
@@ -22,6 +22,7 @@ const Request = WebCore.Request;
|
||||
const Router = @import("./api/router.zig");
|
||||
const FetchEvent = WebCore.FetchEvent;
|
||||
const Headers = WebCore.Headers.RefCountedHeaders;
|
||||
const IdentityContext = @import("../../identity_context.zig").IdentityContext;
|
||||
|
||||
const Body = WebCore.Body;
|
||||
const TaggedPointerTypes = @import("../../tagged_pointer.zig");
|
||||
@@ -1869,13 +1870,78 @@ pub const MarkedArrayBuffer = struct {
|
||||
pub const toJS = toJSObjectRef;
|
||||
};
|
||||
|
||||
// expensive heap reference-counted string type
|
||||
// only use this for big strings
|
||||
// like source code
|
||||
// not little ones
|
||||
pub const RefString = struct {
|
||||
ptr: [*]const u8 = undefined,
|
||||
len: usize = 0,
|
||||
hash: Hash = 0,
|
||||
|
||||
count: u32 = 0,
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
ctx: ?*anyopaque = null,
|
||||
onBeforeDeinit: ?Callback = null,
|
||||
|
||||
pub const Hash = u32;
|
||||
pub const Map = std.HashMap(Hash, *JSC.RefString, IdentityContext(Hash), 80);
|
||||
|
||||
pub const Callback = fn (ctx: *anyopaque, str: *RefString) void;
|
||||
|
||||
pub fn computeHash(input: []const u8) u32 {
|
||||
return @truncate(u32, std.hash.Wyhash.hash(0, input));
|
||||
}
|
||||
|
||||
pub fn ref(this: *RefString) void {
|
||||
this.count += 1;
|
||||
}
|
||||
|
||||
pub fn slice(this: *RefString) []const u8 {
|
||||
this.ref();
|
||||
|
||||
return this.leak();
|
||||
}
|
||||
|
||||
pub fn leak(this: RefString) []const u8 {
|
||||
@setRuntimeSafety(false);
|
||||
return this.ptr[0..this.len];
|
||||
}
|
||||
|
||||
pub fn deref(this: *RefString) void {
|
||||
this.count -|= 1;
|
||||
|
||||
if (this.count == 0) {
|
||||
this.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
pub export fn RefString__free(this: *RefString, _: [*]const u8, _: usize) void {
|
||||
this.deref();
|
||||
}
|
||||
|
||||
pub fn deinit(this: *RefString) void {
|
||||
if (this.onBeforeDeinit) |onBeforeDeinit| {
|
||||
onBeforeDeinit(this.ctx.?, this);
|
||||
}
|
||||
|
||||
this.allocator.free(this.leak());
|
||||
this.allocator.destroy(this);
|
||||
}
|
||||
};
|
||||
|
||||
comptime {
|
||||
std.testing.refAllDecls(RefString);
|
||||
}
|
||||
|
||||
export fn MarkedArrayBuffer_deallocator(bytes_: *anyopaque, _: *anyopaque) void {
|
||||
const mimalloc = @import("../../allocators/mimalloc.zig");
|
||||
// zig's memory allocator interface won't work here
|
||||
// mimalloc knows the size of things
|
||||
// but we don't
|
||||
mimalloc.mi_free(bytes_);
|
||||
|
||||
}
|
||||
pub export fn BlobArrayBuffer_deallocator(_: *anyopaque, blob: *anyopaque) void {
|
||||
// zig's memory allocator interface won't work here
|
||||
// mimalloc knows the size of things
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <JavaScriptCore/JSMicrotask.h>
|
||||
// #include <JavaScriptCore/JSContextInternal.h>
|
||||
#include <JavaScriptCore/CatchScope.h>
|
||||
#include <JavaScriptCore/DeferredWorkTimer.h>
|
||||
#include <JavaScriptCore/JSInternalPromise.h>
|
||||
#include <JavaScriptCore/JSLock.h>
|
||||
#include <JavaScriptCore/JSMap.h>
|
||||
@@ -160,15 +161,16 @@ extern "C" bool Zig__GlobalObject__resetModuleRegistryMap(JSC__JSGlobalObject* g
|
||||
|
||||
if (JSC::JSMap* oldMap = JSC::jsDynamicCast<JSC::JSMap*>(
|
||||
globalObject->vm(), obj->getDirect(globalObject->vm(), identifier))) {
|
||||
oldMap->clear(globalObject);
|
||||
// vm.finalizeSynchronousJSExecution();
|
||||
|
||||
vm.finalizeSynchronousJSExecution();
|
||||
|
||||
obj->putDirect(globalObject->vm(), identifier,
|
||||
map->clone(globalObject, globalObject->vm(), globalObject->mapStructure()));
|
||||
|
||||
// vm.deleteAllLinkedCode(JSC::DeleteAllCodeEffort::DeleteAllCodeIfNotCollecting);
|
||||
// JSC::Heap::PreventCollectionScope(vm.heap);
|
||||
|
||||
oldMap->clear(globalObject);
|
||||
JSC::gcUnprotect(oldMap);
|
||||
// vm.heap.completeAllJITPlans();
|
||||
|
||||
// vm.forEachScriptExecutableSpace([&](auto &spaceAndSet) {
|
||||
@@ -181,48 +183,13 @@ extern "C" bool Zig__GlobalObject__resetModuleRegistryMap(JSC__JSGlobalObject* g
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// globalObject->vm().heap.deleteAllUnlinkedCodeBlocks(
|
||||
// JSC::DeleteAllCodeEffort::PreventCollectionAndDeleteAllCode);
|
||||
}
|
||||
// globalObject->vm().heap.deleteAllUnlinkedCodeBlocks(
|
||||
// JSC::DeleteAllCodeEffort::PreventCollectionAndDeleteAllCode);
|
||||
// vm.whenIdle([globalObject, oldMap, map]() {
|
||||
// auto recordIdentifier = JSC::Identifier::fromString(globalObject->vm(), "module");
|
||||
|
||||
// JSC::JSModuleRecord *record;
|
||||
// JSC::JSValue key;
|
||||
// JSC::JSValue value;
|
||||
// JSC::JSObject *mod;
|
||||
// JSC::JSObject *nextObject;
|
||||
// JSC::forEachInIterable(
|
||||
// globalObject, oldMap,
|
||||
// [&](JSC::VM &vm, JSC::JSGlobalObject *globalObject, JSC::JSValue nextValue) {
|
||||
// nextObject = JSC::jsDynamicCast<JSC::JSObject *>(vm, nextValue);
|
||||
// key = nextObject->getIndex(globalObject, static_cast<unsigned>(0));
|
||||
|
||||
// if (!map->has(globalObject, key)) {
|
||||
// value = nextObject->getIndex(globalObject, static_cast<unsigned>(1));
|
||||
// mod = JSC::jsDynamicCast<JSC::JSObject *>(vm, value);
|
||||
// if (mod) {
|
||||
// record = JSC::jsDynamicCast<JSC::JSModuleRecord *>(
|
||||
// vm, mod->getDirect(vm, recordIdentifier));
|
||||
// if (record) {
|
||||
// auto code = &record->sourceCode();
|
||||
// if (code) {
|
||||
|
||||
// Zig::SourceProvider *provider =
|
||||
// reinterpret_cast<Zig::SourceProvider *>(code->provider());
|
||||
// // code->~SourceCode();
|
||||
// if (provider) { provider->freeSourceCode(); }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// oldMap->clear(globalObject);
|
||||
// }
|
||||
// }
|
||||
// map
|
||||
}
|
||||
// map
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -317,7 +284,11 @@ JSC_DEFINE_CUSTOM_GETTER(property_lazyProcessGetter,
|
||||
|
||||
globalObject->m_process = Zig::Process::create(
|
||||
vm, Zig::Process::createStructure(vm, globalObject, globalObject->objectPrototype()));
|
||||
|
||||
// We must either:
|
||||
// - GC it and re-create it
|
||||
// - keep it alive forever
|
||||
// I think it is more correct to keep it alive forever
|
||||
JSC::gcProtect(globalObject->m_process);
|
||||
{
|
||||
auto jsClass = dot_env_class_ref;
|
||||
|
||||
@@ -329,6 +300,8 @@ JSC_DEFINE_CUSTOM_GETTER(property_lazyProcessGetter,
|
||||
globalObject->m_process->putDirect(vm, JSC::Identifier::fromString(vm, "env"),
|
||||
JSC::JSValue(object),
|
||||
JSC::PropertyAttribute::DontDelete | 0);
|
||||
|
||||
JSC::gcProtect(JSC::JSValue(object));
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(JSC::JSValue(globalObject->m_process));
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <wtf/Scope.h>
|
||||
#include <wtf/text/StringHash.h>
|
||||
|
||||
extern "C" void RefString__free(void*, void*, unsigned);
|
||||
|
||||
namespace Zig {
|
||||
|
||||
using Base = JSC::SourceProvider;
|
||||
@@ -25,22 +27,20 @@ using SourceProviderSourceType = JSC::SourceProviderSourceType;
|
||||
|
||||
static char* wasmSourceName = "[WebAssembly Source]";
|
||||
static size_t wasmSourceName_len = 20;
|
||||
|
||||
Ref<SourceProvider> SourceProvider::create(ResolvedSource resolvedSource)
|
||||
{
|
||||
void* allocator = resolvedSource.allocator;
|
||||
|
||||
JSC::SourceProviderSourceType sourceType = JSC::SourceProviderSourceType::Module;
|
||||
|
||||
WTF::StringImpl* stringImpl = nullptr;
|
||||
if (allocator) {
|
||||
Ref<WTF::ExternalStringImpl> stringImpl_ = WTF::ExternalStringImpl::create(
|
||||
resolvedSource.source_code.ptr, resolvedSource.source_code.len,
|
||||
nullptr,
|
||||
[allocator](void* str, void* ptr, unsigned int len) {
|
||||
ZigString__free((const unsigned char*)ptr, len, allocator);
|
||||
});
|
||||
allocator,
|
||||
RefString__free);
|
||||
return adoptRef(*new SourceProvider(
|
||||
resolvedSource, reinterpret_cast<WTF::StringImpl*>(stringImpl_.ptr()),
|
||||
resolvedSource, stringImpl_,
|
||||
JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(toString(resolvedSource.source_url))),
|
||||
toStringNotConst(resolvedSource.source_url), TextPosition(),
|
||||
sourceType));
|
||||
@@ -48,7 +48,7 @@ Ref<SourceProvider> SourceProvider::create(ResolvedSource resolvedSource)
|
||||
Ref<WTF::ExternalStringImpl> stringImpl_ = WTF::ExternalStringImpl::createStatic(
|
||||
resolvedSource.source_code.ptr, resolvedSource.source_code.len);
|
||||
return adoptRef(*new SourceProvider(
|
||||
resolvedSource, reinterpret_cast<WTF::StringImpl*>(stringImpl_.ptr()),
|
||||
resolvedSource, stringImpl_,
|
||||
JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(toString(resolvedSource.source_url))),
|
||||
toStringNotConst(resolvedSource.source_url), TextPosition(),
|
||||
sourceType));
|
||||
|
||||
@@ -63,11 +63,11 @@ public:
|
||||
void freeSourceCode();
|
||||
|
||||
private:
|
||||
SourceProvider(ResolvedSource resolvedSource, WTF::StringImpl* sourceImpl,
|
||||
SourceProvider(ResolvedSource resolvedSource, WTF::StringImpl& sourceImpl,
|
||||
const SourceOrigin& sourceOrigin, WTF::String&& sourceURL,
|
||||
const TextPosition& startPosition, JSC::SourceProviderSourceType sourceType)
|
||||
: Base(sourceOrigin, WTFMove(sourceURL), startPosition, sourceType)
|
||||
, m_source(*sourceImpl)
|
||||
, m_source(sourceImpl)
|
||||
{
|
||||
|
||||
m_resolvedSource = resolvedSource;
|
||||
|
||||
@@ -246,13 +246,11 @@ export fn ZigString__free(ptr: [*]const u8, len: usize, allocator_: ?*anyopaque)
|
||||
allocator.free(str);
|
||||
}
|
||||
|
||||
export fn ZigString__free_global(ptr: [*]const u8, len: usize) void {
|
||||
var str = ptr[0..len];
|
||||
export fn ZigString__free_global(ptr: [*]const u8, _: usize) void {
|
||||
if (comptime Environment.allow_assert) {
|
||||
std.debug.assert(Mimalloc.mi_check_owned(ptr));
|
||||
}
|
||||
|
||||
default_allocator.free(str);
|
||||
Mimalloc.mi_free(@intToPtr(*anyopaque, @ptrToInt(ptr)));
|
||||
}
|
||||
|
||||
export fn Zig__getAPIGlobals(count: *usize) [*]JSC.C.JSClassRef {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//-- AUTOGENERATED FILE -- 1647230769
|
||||
//-- AUTOGENERATED FILE -- 1647318604
|
||||
// clang-format off
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// clang-format: off
|
||||
//-- AUTOGENERATED FILE -- 1647230769
|
||||
//-- AUTOGENERATED FILE -- 1647318604
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -527,6 +527,7 @@ CPP_DECL JSC__VM* JSC__VM__create(unsigned char HeapType0);
|
||||
CPP_DECL void JSC__VM__deferGC(JSC__VM* arg0, void* arg1, void (* ArgFn2)(void* arg0));
|
||||
CPP_DECL void JSC__VM__deinit(JSC__VM* arg0, JSC__JSGlobalObject* arg1);
|
||||
CPP_DECL void JSC__VM__deleteAllCode(JSC__VM* arg0, JSC__JSGlobalObject* arg1);
|
||||
CPP_DECL void JSC__VM__doWork(JSC__VM* arg0);
|
||||
CPP_DECL void JSC__VM__drainMicrotasks(JSC__VM* arg0);
|
||||
CPP_DECL bool JSC__VM__executionForbidden(JSC__VM* arg0);
|
||||
CPP_DECL void JSC__VM__holdAPILock(JSC__VM* arg0, void* arg1, void (* ArgFn2)(void* arg0));
|
||||
|
||||
@@ -365,6 +365,7 @@ pub extern fn JSC__VM__create(HeapType0: u8) [*c]JSC__VM;
|
||||
pub extern fn JSC__VM__deferGC(arg0: [*c]JSC__VM, arg1: ?*anyopaque, ArgFn2: ?fn (?*anyopaque) callconv(.C) void) void;
|
||||
pub extern fn JSC__VM__deinit(arg0: [*c]JSC__VM, arg1: [*c]JSC__JSGlobalObject) void;
|
||||
pub extern fn JSC__VM__deleteAllCode(arg0: [*c]JSC__VM, arg1: [*c]JSC__JSGlobalObject) void;
|
||||
pub extern fn JSC__VM__doWork(arg0: [*c]JSC__VM) void;
|
||||
pub extern fn JSC__VM__drainMicrotasks(arg0: [*c]JSC__VM) void;
|
||||
pub extern fn JSC__VM__executionForbidden(arg0: [*c]JSC__VM) bool;
|
||||
pub extern fn JSC__VM__holdAPILock(arg0: [*c]JSC__VM, arg1: ?*anyopaque, ArgFn2: ?fn (?*anyopaque) callconv(.C) void) void;
|
||||
|
||||
@@ -688,7 +688,10 @@ pub const Bun = struct {
|
||||
arguments: []const js.JSValueRef,
|
||||
_: js.ExceptionRef,
|
||||
) js.JSValueRef {
|
||||
Global.mimalloc_cleanup(true);
|
||||
// it should only force cleanup on thread exit
|
||||
|
||||
Global.mimalloc_cleanup(false);
|
||||
|
||||
return ctx.ptr().vm().runGC(arguments.len > 0 and JSValue.fromRef(arguments[0]).toBoolean()).asRef();
|
||||
}
|
||||
|
||||
@@ -1809,6 +1812,8 @@ pub const VirtualMachine = struct {
|
||||
regular_event_loop: EventLoop = EventLoop{},
|
||||
event_loop: *EventLoop = undefined,
|
||||
|
||||
ref_strings: JSC.RefString.Map = undefined,
|
||||
|
||||
source_mappings: SavedSourceMap = undefined,
|
||||
|
||||
pub inline fn eventLoop(this: *VirtualMachine) *EventLoop {
|
||||
@@ -1889,7 +1894,9 @@ pub const VirtualMachine = struct {
|
||||
pub fn tick(this: *EventLoop) void {
|
||||
while (true) {
|
||||
this.tickConcurrent();
|
||||
|
||||
while (this.tickWithCount() > 0) {}
|
||||
|
||||
this.tickConcurrent();
|
||||
|
||||
if (this.tickWithCount() == 0) break;
|
||||
@@ -1897,10 +1904,9 @@ pub const VirtualMachine = struct {
|
||||
}
|
||||
|
||||
pub fn waitForPromise(this: *EventLoop, promise: *JSC.JSInternalPromise) void {
|
||||
var _vm = this.global.vm();
|
||||
switch (promise.status(_vm)) {
|
||||
switch (promise.status(this.global.vm())) {
|
||||
JSC.JSPromise.Status.Pending => {
|
||||
while (promise.status(_vm) == .Pending) {
|
||||
while (promise.status(this.global.vm()) == .Pending) {
|
||||
this.tick();
|
||||
}
|
||||
},
|
||||
@@ -1937,9 +1943,11 @@ pub const VirtualMachine = struct {
|
||||
}
|
||||
|
||||
pub fn tick(this: *VirtualMachine) void {
|
||||
this.eventLoop().tickConcurrent();
|
||||
this.eventLoop().tick();
|
||||
}
|
||||
|
||||
while (this.eventLoop().tickWithCount() > 0) {}
|
||||
pub fn waitForPromise(this: *VirtualMachine, promise: *JSC.JSInternalPromise) void {
|
||||
this.eventLoop().waitForPromise(promise);
|
||||
}
|
||||
|
||||
pub fn waitForTasks(this: *VirtualMachine) void {
|
||||
@@ -2027,6 +2035,7 @@ pub const VirtualMachine = struct {
|
||||
.macros = MacroMap.init(allocator),
|
||||
.macro_entry_points = @TypeOf(VirtualMachine.vm.macro_entry_points).init(allocator),
|
||||
.origin_timer = std.time.Timer.start() catch @panic("Please don't mess with timers."),
|
||||
.ref_strings = JSC.RefString.Map.init(allocator),
|
||||
};
|
||||
|
||||
VirtualMachine.vm.regular_event_loop.tasks = EventLoop.Queue.init(
|
||||
@@ -2076,6 +2085,47 @@ pub const VirtualMachine = struct {
|
||||
|
||||
threadlocal var source_code_printer: ?*js_printer.BufferPrinter = null;
|
||||
|
||||
pub fn clearRefString(_: *anyopaque, ref_string: *JSC.RefString) void {
|
||||
_ = VirtualMachine.vm.ref_strings.remove(ref_string.hash);
|
||||
}
|
||||
|
||||
pub fn refCountedResolvedSource(this: *VirtualMachine, code: []const u8, specifier: []const u8, source_url: []const u8, hash_: ?u32) ResolvedSource {
|
||||
var source = this.refCountedString(code, hash_, true);
|
||||
|
||||
return ResolvedSource{
|
||||
.source_code = ZigString.init(source.slice()),
|
||||
.specifier = ZigString.init(specifier),
|
||||
.source_url = ZigString.init(source_url),
|
||||
.hash = source.hash,
|
||||
.allocator = source,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn refCountedString(this: *VirtualMachine, input_: []const u8, hash_: ?u32, comptime dupe: bool) *JSC.RefString {
|
||||
const hash = hash_ orelse JSC.RefString.computeHash(input_);
|
||||
|
||||
var entry = this.ref_strings.getOrPut(hash) catch unreachable;
|
||||
if (!entry.found_existing) {
|
||||
const input = if (comptime dupe)
|
||||
(this.allocator.dupe(u8, input_) catch unreachable)
|
||||
else
|
||||
input_;
|
||||
|
||||
var ref = this.allocator.create(JSC.RefString) catch unreachable;
|
||||
ref.* = JSC.RefString{
|
||||
.allocator = this.allocator,
|
||||
.ptr = input.ptr,
|
||||
.len = input.len,
|
||||
.hash = hash,
|
||||
.ctx = this,
|
||||
.onBeforeDeinit = VirtualMachine.clearRefString,
|
||||
};
|
||||
entry.value_ptr.* = ref;
|
||||
}
|
||||
|
||||
return entry.value_ptr.*;
|
||||
}
|
||||
|
||||
pub fn preflush(this: *VirtualMachine) void {
|
||||
// We flush on the next tick so that if there were any errors you can still see them
|
||||
this.blobs.?.temporary.reset() catch {};
|
||||
@@ -2350,8 +2400,12 @@ pub const VirtualMachine = struct {
|
||||
return error.PrintingErrorWriteFailed;
|
||||
}
|
||||
|
||||
if (jsc_vm.has_loaded) {
|
||||
return jsc_vm.refCountedResolvedSource(printer.ctx.written, specifier, path.text, null);
|
||||
}
|
||||
|
||||
return ResolvedSource{
|
||||
.allocator = if (jsc_vm.has_loaded) &jsc_vm.allocator else null,
|
||||
.allocator = null,
|
||||
.source_code = ZigString.init(jsc_vm.allocator.dupe(u8, printer.ctx.written) catch unreachable),
|
||||
.specifier = ZigString.init(specifier),
|
||||
.source_url = ZigString.init(path.text),
|
||||
@@ -2590,11 +2644,15 @@ pub const VirtualMachine = struct {
|
||||
return slice;
|
||||
}
|
||||
|
||||
// This double prints
|
||||
pub fn promiseRejectionTracker(_: *JSGlobalObject, _: *JSPromise, _: JSPromiseRejectionOperation) callconv(.C) JSValue {
|
||||
// VirtualMachine.vm.defaultErrorHandler(promise.result(global.vm()), null);
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
// // This double prints
|
||||
// pub fn promiseRejectionTracker(global: *JSGlobalObject, promise: *JSPromise, _: JSPromiseRejectionOperation) callconv(.C) JSValue {
|
||||
// const result = promise.result(global.vm());
|
||||
// if (@enumToInt(VirtualMachine.vm.last_error_jsvalue) != @enumToInt(result)) {
|
||||
// VirtualMachine.vm.defaultErrorHandler(result, null);
|
||||
// }
|
||||
|
||||
// return JSValue.jsUndefined();
|
||||
// }
|
||||
|
||||
const main_file_name: string = "bun:main";
|
||||
pub threadlocal var errors_stack: [256]*anyopaque = undefined;
|
||||
@@ -2725,6 +2783,8 @@ pub const VirtualMachine = struct {
|
||||
}
|
||||
|
||||
pub fn defaultErrorHandler(this: *VirtualMachine, result: JSValue, exception_list: ?*ExceptionList) void {
|
||||
this.last_error_jsvalue = result;
|
||||
|
||||
if (result.isException(this.global.vm())) {
|
||||
var exception = @ptrCast(*Exception, result.asVoid());
|
||||
|
||||
@@ -2763,26 +2823,14 @@ pub const VirtualMachine = struct {
|
||||
this.has_loaded_node_modules = true;
|
||||
promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(bun_file_import_path)));
|
||||
|
||||
this.tick();
|
||||
|
||||
while (promise.status(this.global.vm()) == JSPromise.Status.Pending) {
|
||||
this.tick();
|
||||
}
|
||||
|
||||
if (promise.status(this.global.vm()) == JSPromise.Status.Rejected) {
|
||||
this.waitForPromise(promise);
|
||||
if (promise.status(this.global.vm()) == .Rejected)
|
||||
return promise;
|
||||
}
|
||||
|
||||
_ = promise.result(this.global.vm());
|
||||
}
|
||||
|
||||
promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(std.mem.span(main_file_name)));
|
||||
|
||||
this.tick();
|
||||
|
||||
while (promise.status(this.global.vm()) == JSPromise.Status.Pending) {
|
||||
this.tick();
|
||||
}
|
||||
this.waitForPromise(promise);
|
||||
|
||||
return promise;
|
||||
}
|
||||
@@ -2990,6 +3038,11 @@ pub const VirtualMachine = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reportUncaughtExceptio(_: *JSGlobalObject, exception: *JSC.Exception) JSValue {
|
||||
VirtualMachine.vm.defaultErrorHandler(exception.value(), null);
|
||||
return JSC.JSValue.jsUndefined();
|
||||
}
|
||||
|
||||
pub fn printStackTrace(comptime Writer: type, writer: Writer, trace: ZigStackTrace, comptime allow_ansi_colors: bool) !void {
|
||||
const stack = trace.frames();
|
||||
if (stack.len > 0) {
|
||||
@@ -3359,9 +3412,7 @@ pub const EventListenerMixin = struct {
|
||||
comptime onError: fn (ctx: *CtxType, err: anyerror, value: JSValue, request_ctx: *http.RequestContext) anyerror!void,
|
||||
) !void {
|
||||
if (comptime JSC.is_bindgen) unreachable;
|
||||
defer {
|
||||
if (request_context.has_called_done) request_context.arena.deinit();
|
||||
}
|
||||
|
||||
var listeners = vm.event_listeners.get(EventType.fetch) orelse (return onError(ctx, error.NoListeners, JSValue.jsUndefined(), request_context) catch {});
|
||||
if (listeners.items.len == 0) return onError(ctx, error.NoListeners, JSValue.jsUndefined(), request_context) catch {};
|
||||
const FetchEventRejectionHandler = struct {
|
||||
@@ -3370,7 +3421,7 @@ pub const EventListenerMixin = struct {
|
||||
@intToPtr(*CtxType, @ptrToInt(_ctx)),
|
||||
err,
|
||||
value,
|
||||
fetch_event.request_context,
|
||||
fetch_event.request_context.?,
|
||||
) catch {};
|
||||
}
|
||||
};
|
||||
@@ -3386,9 +3437,11 @@ pub const EventListenerMixin = struct {
|
||||
};
|
||||
|
||||
var fetch_args: [1]js.JSObjectRef = undefined;
|
||||
for (listeners.items) |listener_ref| {
|
||||
fetch_args[0] = FetchEvent.Class.make(vm.global.ref(), fetch_event);
|
||||
fetch_args[0] = FetchEvent.Class.make(vm.global.ref(), fetch_event);
|
||||
JSC.C.JSValueProtect(vm.global.ref(), fetch_args[0]);
|
||||
defer JSC.C.JSValueUnprotect(vm.global.ref(), fetch_args[0]);
|
||||
|
||||
for (listeners.items) |listener_ref| {
|
||||
vm.tick();
|
||||
var result = js.JSObjectCallAsFunctionReturnValue(vm.global.ref(), listener_ref, null, 1, &fetch_args);
|
||||
vm.tick();
|
||||
@@ -3399,7 +3452,7 @@ pub const EventListenerMixin = struct {
|
||||
if (fetch_event.rejected) return;
|
||||
|
||||
if (promise.status(vm.global.vm()) == .Rejected) {
|
||||
onError(ctx, error.JSError, promise.result(vm.global.vm()), fetch_event.request_context) catch {};
|
||||
onError(ctx, error.JSError, promise.result(vm.global.vm()), request_context) catch {};
|
||||
return;
|
||||
} else {
|
||||
_ = promise.result(vm.global.vm());
|
||||
@@ -3407,13 +3460,13 @@ pub const EventListenerMixin = struct {
|
||||
|
||||
vm.waitForTasks();
|
||||
|
||||
if (fetch_event.request_context.has_called_done) {
|
||||
if (request_context.has_called_done) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fetch_event.request_context.has_called_done) {
|
||||
onError(ctx, error.FetchHandlerRespondWithNeverCalled, JSValue.jsUndefined(), fetch_event.request_context) catch {};
|
||||
if (!request_context.has_called_done) {
|
||||
onError(ctx, error.FetchHandlerRespondWithNeverCalled, JSValue.jsUndefined(), request_context) catch {};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user