diff options
-rw-r--r-- | .dockerignore | 14 | ||||
-rw-r--r-- | .vscode/launch.json | 9 | ||||
-rw-r--r-- | Dockerfile | 8 | ||||
-rw-r--r-- | Dockerfile.zig | 52 | ||||
-rw-r--r-- | Makefile | 98 | ||||
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | build.zig | 6 | ||||
-rw-r--r-- | examples/hello-next/pages/index.tsx | 7 | ||||
-rw-r--r-- | examples/hello-next/styles/Home.module.css | 4 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/bundler.zig | 2 | ||||
-rw-r--r-- | src/css_scanner.zig | 5 | ||||
-rw-r--r-- | src/fs.zig | 26 | ||||
-rw-r--r-- | src/global.zig | 9 | ||||
-rw-r--r-- | src/http.zig | 54 | ||||
-rw-r--r-- | src/javascript/jsc/JavascriptCore.zig | 4 | ||||
m--------- | src/javascript/jsc/WebKit | 0 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/ZigGlobalObject.cpp | 17 | ||||
-rw-r--r-- | src/main.zig | 1 | ||||
-rw-r--r-- | src/runtime.version | 2 | ||||
-rw-r--r-- | src/watcher.zig | 95 |
21 files changed, 320 insertions, 100 deletions
diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..9d969e9d0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +node_modules +**/node_modules +src/javascript/jsc/WebKit/LayoutTests +zig-out +zig-build +**/*.o +**/*.a + +examples + +**/.next +.git +src/javascript/jsc/WebKit/WebKitBuild +**/CMakeCache.txt
\ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 53f298ca2..8501f676e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -79,6 +79,15 @@ "cwd": "${workspaceFolder}/src/test/fixtures", "console": "internalConsole" }, + { + "type": "lldb", + "request": "launch", + "name": "Linux Launch", + "program": "${workspaceFolder}/packages/debug-bun-cli-linux-x64/bin/bun-debug", + "args": ["--origin=http://jarred-desktop.local:3000/"], + "cwd": "${workspaceFolder}/examples/hello-next", + "console": "internalConsole" + }, { "type": "lldb", diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..16ee6dc40 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM bun-zig:latest + +COPY . /home/ubuntu/bun +WORKDIR /home/ubuntu/bun + +RUN make vendor-without-check + + diff --git a/Dockerfile.zig b/Dockerfile.zig new file mode 100644 index 000000000..57598be49 --- /dev/null +++ b/Dockerfile.zig @@ -0,0 +1,52 @@ +FROM ubuntu:latest + +RUN apt-get update && apt-get install --no-install-recommends -y wget gnupg2 curl lsb-release wget software-properties-common +RUN curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - + +RUN wget https://apt.llvm.org/llvm.sh --no-check-certificate +RUN chmod +x llvm.sh +RUN ./llvm.sh 12 + +RUN apt-get update && apt-get install --no-install-recommends -y \ + ca-certificates \ + curl \ + gnupg2 \ + software-properties-common \ + cmake \ + build-essential \ + git \ + libssl-dev \ + ruby \ + liblld-12-dev \ + libclang-12-dev \ + nodejs \ + gcc \ + g++ \ + npm \ + clang-12 \ + clang-format-12 \ + libc++-12-dev \ + libc++abi-12-dev \ + lld-12 \ + libicu-dev + +RUN update-alternatives --install /usr/bin/ld ld /usr/bin/lld-12 90 && \ + update-alternatives --install /usr/bin/cc cc /usr/bin/clang-12 90 && \ + update-alternatives --install /usr/bin/cpp cpp /usr/bin/clang++-12 90 && \ + update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-12 90 + + +ENV CC=clang-12 +ENV CXX=clang++-12 + +# Compile zig +RUN mkdir -p /home/ubuntu/zig; cd /home/ubuntu; git clone https://github.com/jarred-sumner/zig.git; cd /home/ubuntu/zig && git checkout jarred/zig-sloppy-with-small-structs && cmake . -DCMAKE_BUILD_TYPE=Release && make -j$(nproc) + +ENV PATH="/home/ubuntu/zig:$PATH" + +RUN npm install -g esbuild + + + + + @@ -24,7 +24,10 @@ CXX := clang++ bun: vendor build-obj bun-link-lld-release -vendor: require init-submodules api node-fallbacks runtime_js fallback_decoder bun_error mimalloc picohttp jsc + +vendor-without-check: api node-fallbacks runtime_js fallback_decoder bun_error mimalloc picohttp + +vendor: require init-submodules vendor-without-check require: @echo "Checking if the required utilities are available..." @@ -67,6 +70,30 @@ runtime_js: bun_error: @cd packages/bun-error; npm install; npm run --silent build + +DEFAULT_USE_BMALLOC := 1 +# ifeq ($(OS_NAME),linux) +# DEFAULT_USE_BMALLOC = 0 +# endif + +USE_BMALLOC ?= DEFAULT_USE_BMALLOC + +DEFAULT_JSC_LIB := + +ifeq ($(OS_NAME),linux) +DEFAULT_JSC_LIB = ${HOME}/webkit-build/lib +endif + +ifeq ($(OS_NAME),darwin) +DEFAULT_JSC_LIB = src/deps +endif + +JSC_LIB ?= $(DEFAULT_JSC_LIB) + +JSC_INCLUDE_DIR ?= ${HOME}/webkit-build/include + +JSC_FILES := $(JSC_LIB)/libJavaScriptCore.a $(JSC_LIB)/libWTF.a + JSC_BUILD_STEPS := ifeq ($(OS_NAME),linux) JSC_BUILD_STEPS += jsc-build-linux jsc-copy-headers @@ -75,6 +102,10 @@ ifeq ($(OS_NAME),darwin) JSC_BUILD_STEPS += jsc-build-mac jsc-copy-headers endif +# ifeq ($(USE_BMALLOC),1) +JSC_FILES += $(JSC_LIB)/libbmalloc.a +# endif + jsc: jsc-build jsc-bindings jsc-build: $(JSC_BUILD_STEPS) @@ -177,10 +208,12 @@ jsc-build-mac-copy: cp src/javascript/jsc/WebKit/WebKitBuild/Release/lib/libWTF.a src/deps/libWTF.a cp src/javascript/jsc/WebKit/WebKitBuild/Release/lib/libbmalloc.a src/deps/libbmalloc.a -JSC_FILES := src/deps/libJavaScriptCore.a \ - src/deps/libWTF.a \ - src/deps/libbmalloc.a +clean-bindings: + rm -rf $(OBJ_DIR)/*.o +clean: clean-bindings + rm src/deps/*.a src/deps/*.o + cd src/deps/mimalloc && make clean; ifeq ($(OS_NAME),darwin) HOMEBREW_PREFIX := $(shell brew --prefix)/ @@ -190,18 +223,31 @@ SRC_DIR := src/javascript/jsc/bindings OBJ_DIR := src/javascript/jsc/bindings-obj SRC_FILES := $(wildcard $(SRC_DIR)/*.cpp) OBJ_FILES := $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(SRC_FILES)) -INCLUDE_DIRS := -Isrc/javascript/jsc/WebKit/WebKitBuild/Release/JavaScriptCore/PrivateHeaders \ +MAC_INCLUDE_DIRS := -Isrc/javascript/jsc/WebKit/WebKitBuild/Release/JavaScriptCore/PrivateHeaders \ -Isrc/javascript/jsc/WebKit/WebKitBuild/Release/WTF/Headers \ -Isrc/javascript/jsc/WebKit/WebKitBuild/Release/ICU/Headers \ -Isrc/javascript/jsc/WebKit/WebKitBuild/Release/ \ -Isrc/javascript/jsc/bindings/ \ -Isrc/javascript/jsc/WebKit/Source/bmalloc +LINUX_INCLUDE_DIRS := -I$(JSC_INCLUDE_DIR) \ + -Isrc/javascript/jsc/bindings/ + +INCLUDE_DIRS := + +ifeq ($(OS_NAME),linux) + INCLUDE_DIRS += $(LINUX_INCLUDE_DIRS) +endif + +ifeq ($(OS_NAME),darwin) + INCLUDE_DIRS += $(MAC_INCLUDE_DIRS) +endif + CLANG_FLAGS := $(INCLUDE_DIRS) \ -std=gnu++17 \ - -stdlib=libc++ \ -DSTATICALLY_LINKED_WITH_JavaScriptCore=1 \ -DSTATICALLY_LINKED_WITH_WTF=1 \ + -DSTATICALLY_LINKED_WITH_BMALLOC=1 \ -DBUILDING_WITH_CMAKE=1 \ -DNDEBUG=1 \ -DNOMINMAX \ @@ -209,8 +255,15 @@ CLANG_FLAGS := $(INCLUDE_DIRS) \ -g \ -DENABLE_INSPECTOR_ALTERNATE_DISPATCHERS=0 \ -DBUILDING_JSCONLY__ \ - -DASSERT_ENABLED=0\ - -DDU_DISABLE_RENAMING=1 + -DASSERT_ENABLED=0 \ + -fPIE + +# This flag is only added to webkit builds on Apple platforms +# It has something to do with ICU +ifeq ($(OS_NAME), darwin) +CLANG_FLAGS += -DDU_DISABLE_RENAMING=1 +endif + jsc-bindings-mac: $(OBJ_FILES) @@ -239,7 +292,20 @@ BUN_LLD_FLAGS := $(OBJ_FILES) \ src/deps/picohttpparser.o \ src/deps/mimalloc/libmimalloc.a \ $(CLANG_FLAGS) \ - -fpie \ + + +ifeq ($(OS_NAME), linux) +BUN_LLD_FLAGS += -lstdc++fs \ + -pthread \ + -ldl \ + -lc \ + -fuse-ld=lld \ + -Wl,-z,now \ + -Wl,--as-needed \ + -Wl,-z,stack-size=12800000 \ + -Wl,-z,notext +endif + mimalloc: cd src/deps/mimalloc; cmake .; make; @@ -248,17 +314,19 @@ bun-link-lld-debug: $(CXX) $(BUN_LLD_FLAGS) \ $(DEBUG_BIN)/bun-debug.o \ -W \ - -ftls-model=local-exec \ - -flto \ - -o $(DEBUG_BIN)/bun-debug + -o $(DEBUG_BIN)/bun-debug \ + -march=native \ + -flto + bun-link-lld-release: $(CXX) $(BUN_LLD_FLAGS) \ $(BIN_DIR)/bun.o \ -o $(BIN_DIR)/bun \ -W \ - -ftls-model=local-exec \ -flto \ + -ftls-model=initial-exec \ + -march=native \ -O3 rm $(BIN_DIR)/bun.o @@ -267,7 +335,7 @@ bun-link-lld-release-aarch64: build/macos-aarch64/bun.o \ -o build/macos-aarch64/bun \ -Wl,-dead_strip \ - -ftls-model=local-exec \ + -ftls-model=initial-exec \ -flto \ -O3 @@ -283,4 +351,4 @@ sizegen: /tmp/sizegen > src/javascript/jsc/bindings/sizes.zig picohttp: - $(CC) -O3 -g -c src/deps/picohttpparser.c -Isrc/deps -o src/deps/picohttpparser.o; cd ../../ + $(CC) -O3 -g -fPIE -c src/deps/picohttpparser.c -Isrc/deps -o src/deps/picohttpparser.o; cd ../../ @@ -600,7 +600,4 @@ Additionally, you'll need `cmake`, `npm` and `esbuild` installed globally. ## Linux -```bash -git submodule update --init --recursive --progress --depth=1 -make vendor -``` +Compile Zig:
\ No newline at end of file @@ -237,9 +237,13 @@ pub fn build(b: *std.build.Builder) !void { true, ); + step.addObjectFile("src/deps/libJavaScriptCore.a"); step.addObjectFile("src/deps/libWTF.a"); - step.addObjectFile("src/deps/libbmalloc.a"); + + if (target.getOs().tag != .linux) { + step.addObjectFile("src/deps/libbmalloc.a"); + } step.addObjectFile("src/deps/mimalloc/libmimalloc.a"); step.addLibPath("src/deps/mimalloc"); diff --git a/examples/hello-next/pages/index.tsx b/examples/hello-next/pages/index.tsx index f733efb51..9101ed8c7 100644 --- a/examples/hello-next/pages/index.tsx +++ b/examples/hello-next/pages/index.tsx @@ -5,12 +5,11 @@ import styles from "../styles/Home.module.css"; export async function getStaticProps(ctx) { return { - props: { - }, + props: {}, }; } -export default function Home({ }) { +export default function Home({}) { return ( <div className={styles.container}> <Head> @@ -21,7 +20,7 @@ export default function Home({ }) { <main className={styles.main}> <h1 className={styles.title}> - Welcome to <a href="https://nextjs.org">Next.js!</a> + Welcome!!to <a href="https://nextjs.org">Next.js!</a> </h1> <p className={styles.description}> diff --git a/examples/hello-next/styles/Home.module.css b/examples/hello-next/styles/Home.module.css index 167d5f75b..4a04160d0 100644 --- a/examples/hello-next/styles/Home.module.css +++ b/examples/hello-next/styles/Home.module.css @@ -1,6 +1,6 @@ @import url("./2.css"); .container { - min-height: 100vh; + min-height: 99vh; padding: 0 0.5rem; display: flex; flex-direction: column; @@ -8,6 +8,8 @@ align-items: center; height: 100vh; } + + .main { padding: 5rem 0; flex: 1; diff --git a/package.json b/package.json index 2052081df..23453625a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "dependencies": { "moment": "^2.29.1", - "peechy": "^0.4.18", + "peechy": "^0.4.19", "puppeteer": "^10.2.0", "react": "^17.0.2", "react-dom": "^17.0.2" diff --git a/src/bundler.zig b/src/bundler.zig index cc82ccf41..262a22888 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -2920,7 +2920,7 @@ pub const Transformer = struct { var arena: std.heap.ArenaAllocator = undefined; const use_arenas = opts.entry_points.len > 8; - var ulimit: usize = Fs.FileSystem.RealFS.adjustUlimit(); + var ulimit: usize = Fs.FileSystem.RealFS.adjustUlimit() catch unreachable; var care_about_closing_files = !(FeatureFlags.store_file_descriptors and opts.entry_points.len * 2 < ulimit); var transformer = Transformer{ diff --git a/src/css_scanner.zig b/src/css_scanner.zig index 0adcd9917..41e43c22e 100644 --- a/src/css_scanner.zig +++ b/src/css_scanner.zig @@ -1263,7 +1263,12 @@ pub fn NewBundler( if (watcher_index == null) { var file = try std.fs.openFileAbsolute(absolute_path, .{ .read = true }); + try this.watcher.appendFile(file.handle, absolute_path, hash, .css, 0, null, true); + if (this.watcher.watchloop_handle == null) { + try this.watcher.start(); + } + } try this.import_queue.writeItem(hash); diff --git a/src/fs.zig b/src/fs.zig index 517dcdbd5..2313dc17b 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -541,16 +541,22 @@ pub const FileSystem = struct { } // Always try to max out how many files we can keep open - pub fn adjustUlimit() usize { - var limit = std.os.getrlimit(.NOFILE) catch return 32; - if (limit.cur < limit.max) { - var new_limit = std.mem.zeroes(std.os.rlimit); - new_limit.cur = limit.max; - new_limit.max = limit.max; - std.os.setrlimit(.NOFILE, new_limit) catch return limit.cur; - return new_limit.cur; + pub fn adjustUlimit() !usize { + const LIMITS = [_]std.os.rlimit_resource{std.os.rlimit_resource.STACK, std.os.rlimit_resource.NOFILE}; + inline for (LIMITS) |limit_type, i| { + const limit = try std.os.getrlimit(limit_type); + + if (limit.cur < limit.max) { + var new_limit = std.mem.zeroes(std.os.rlimit); + new_limit.cur = limit.max; + new_limit.max = limit.max; + + try std.os.setrlimit(limit_type, new_limit); + + } + + if (i == LIMITS.len - 1) return limit.max; } - return limit.cur; } var _entries_option_map: *EntriesOption.Map = undefined; @@ -559,7 +565,7 @@ pub const FileSystem = struct { allocator: *std.mem.Allocator, cwd: string, ) RealFS { - const file_limit = adjustUlimit(); + const file_limit = adjustUlimit() catch unreachable; if (!_entries_option_map_loaded) { _entries_option_map = EntriesOption.Map.init(allocator); diff --git a/src/global.zig b/src/global.zig index 236751a6b..64d5f1a82 100644 --- a/src/global.zig +++ b/src/global.zig @@ -1,10 +1,15 @@ const std = @import("std"); pub usingnamespace @import("strings.zig"); +pub const Environment = @import("env.zig"); + -pub const default_allocator: *std.mem.Allocator = if (isTest) std.heap.c_allocator else @import("./memory_allocator.zig").c_allocator; +pub const default_allocator: *std.mem.Allocator = if (isTest or Environment.isLinux) + std.heap.c_allocator + else + @import("./memory_allocator.zig").c_allocator; pub const C = @import("c.zig"); -pub const Environment = @import("env.zig"); + pub usingnamespace Environment; pub const FeatureFlags = @import("feature_flags.zig"); diff --git a/src/http.zig b/src/http.zig index 0ddc02540..72a452327 100644 --- a/src/http.zig +++ b/src/http.zig @@ -50,6 +50,7 @@ threadlocal var req_headers_buf: [100]picohttp.Header = undefined; threadlocal var res_headers_buf: [100]picohttp.Header = undefined; const sync = @import("./sync.zig"); const JavaScript = @import("./javascript/jsc/javascript.zig"); +const JavaScriptCore = @import("./javascript/jsc/JavascriptCore.zig"); usingnamespace @import("./javascript/jsc/bindings/bindings.zig"); usingnamespace @import("./javascript/jsc/bindings/exports.zig"); const Router = @import("./router.zig"); @@ -1102,11 +1103,11 @@ pub const RequestContext = struct { pub var channel: Channel = undefined; var has_loaded_channel = false; pub var javascript_disabled = false; - + var js_thread: std.Thread = undefined; pub fn spawnThread(handler: *HandlerThread) !void { - var thread = try std.Thread.spawn(.{}, spawn, .{handler}); - thread.setName("WebSocket") catch {}; - thread.detach(); + js_thread = try std.Thread.spawn(.{.stack_size = 64 * 1024 * 1024}, spawn, .{handler}); + js_thread.setName("JavaScript SSR") catch {}; + js_thread.detach(); } pub fn spawn(handler: *HandlerThread) void { @@ -1118,20 +1119,19 @@ pub const RequestContext = struct { javascript_disabled = true; } var start_timer = std.time.Timer.start() catch unreachable; + var stdout = std.io.getStdOut(); - // var stdout = std.io.bufferedWriter(stdout_file.writer()); var stderr = std.io.getStdErr(); - // var stderr = std.io.bufferedWriter(stderr_file.writer()); var output_source = Output.Source.init(stdout, stderr); - // defer stdout.flush() catch {}; - // defer stderr.flush() catch {}; + defer Output.flush(); Output.Source.set(&output_source); + js_ast.Stmt.Data.Store.create(std.heap.c_allocator); js_ast.Expr.Data.Store.create(std.heap.c_allocator); - defer Output.flush(); + var vm = JavaScript.VirtualMachine.init( std.heap.c_allocator, handler.args, @@ -1947,6 +1947,8 @@ pub const RequestContext = struct { // CSS handles this specially if (loader != .css and client_entry_point_ == null) { if (written.input_fd) |written_fd| { + + try ctx.watcher.addFile( written_fd, result.file.input.text, @@ -1958,8 +1960,9 @@ pub const RequestContext = struct { ); if (ctx.watcher.watchloop_handle == null) { - try ctx.watcher.start(); + ctx.watcher.start() catch {}; } + } } else { if (written.written > 0) { @@ -2008,6 +2011,7 @@ pub const RequestContext = struct { if (file.autowatch) { // we must never autowatch a file that will be closed std.debug.assert(!file.close_handle_on_complete); + if (ctx.watcher.addFile( file.fd, result.file.input.text, @@ -2438,16 +2442,6 @@ pub const Server = struct { transform_options: Api.TransformOptions, javascript_enabled: bool = false, - pub fn adjustUlimit() !void { - var limit = try std.os.getrlimit(.NOFILE); - if (limit.cur < limit.max) { - var new_limit = std.mem.zeroes(std.os.rlimit); - new_limit.cur = limit.max; - new_limit.max = limit.max; - try std.os.setrlimit(.NOFILE, new_limit); - } - } - pub fn onTCPConnection(server: *Server, conn: tcp.Connection, comptime features: ConnectionFeatures) void { conn.client.setNoDelay(true) catch {}; conn.client.setQuickACK(true) catch {}; @@ -2502,6 +2496,9 @@ pub const Server = struct { defer ctx.watcher.flushEvictions(); defer Output.flush(); + var rfs: *Fs.FileSystem.RealFS = &ctx.bundler.fs.fs; + + // It's important that this function does not do any memory allocations // If this blocks, it can cause cascading bad things to happen for (events) |event| { @@ -2526,7 +2523,6 @@ pub const Server = struct { switch (kind) { .file => { if (event.op.delete or event.op.rename) { - var rfs: *Fs.FileSystem.RealFS = &ctx.bundler.fs.fs; ctx.watcher.removeAtIndex( event.index, 0, @@ -2559,12 +2555,12 @@ pub const Server = struct { } }, .directory => { - var rfs: *Fs.FileSystem.RealFS = &ctx.bundler.fs.fs; + rfs.bustEntriesCache(file_path); ctx.bundler.resolver.dir_cache.remove(file_path); - if (event.op.delete or event.op.rename) - ctx.watcher.removeAtIndex(event.index, hashes[event.index], parent_hashes, .directory); + // if (event.op.delete or event.op.rename) + // ctx.watcher.removeAtIndex(event.index, hashes[event.index], parent_hashes, .directory); if (comptime is_emoji_enabled) { Output.prettyln("<r>📁 <d>Dir change: {s}<r>", .{ctx.bundler.fs.relativeTo(file_path)}); @@ -2577,7 +2573,7 @@ pub const Server = struct { } fn run(server: *Server, comptime features: ConnectionFeatures) !void { - adjustUlimit() catch {}; + _ = Fs.FileSystem.RealFS.adjustUlimit() catch {}; RequestContext.WebsocketHandler.open_websockets = @TypeOf( RequestContext.WebsocketHandler.open_websockets, ).init(server.allocator); @@ -2723,6 +2719,7 @@ pub const Server = struct { }; }; + threadlocal var req_ctx_: RequestContext = undefined; pub fn handleConnection(server: *Server, conn: *tcp.Connection, comptime features: ConnectionFeatures) void { // https://stackoverflow.com/questions/686217/maximum-on-http-header-values @@ -2745,9 +2742,9 @@ pub const Server = struct { var request_arena = server.allocator.create(std.heap.ArenaAllocator) catch unreachable; request_arena.* = std.heap.ArenaAllocator.init(server.allocator); - var req_ctx: RequestContext = undefined; + - req_ctx = RequestContext.init( + req_ctx_ = RequestContext.init( req, request_arena, conn, @@ -2759,6 +2756,7 @@ pub const Server = struct { conn.client.deinit(); return; }; + var req_ctx = &req_ctx_; req_ctx.timer.reset(); if (req_ctx.url.needs_redirect) { @@ -2880,7 +2878,7 @@ pub const Server = struct { if (comptime features.filesystem_router) { if (!finished) { - req_ctx.bundler.router.?.match(server, RequestContext, &req_ctx) catch |err| { + req_ctx.bundler.router.?.match(server, RequestContext, req_ctx) catch |err| { switch (err) { error.ModuleNotFound => { req_ctx.sendNotFound() catch {}; diff --git a/src/javascript/jsc/JavascriptCore.zig b/src/javascript/jsc/JavascriptCore.zig index f443799c1..dd271d2c0 100644 --- a/src/javascript/jsc/JavascriptCore.zig +++ b/src/javascript/jsc/JavascriptCore.zig @@ -544,3 +544,7 @@ pub const JSString = struct { } } }; + + +// not official api functions +pub extern "c" fn JSCInitialize() void; diff --git a/src/javascript/jsc/WebKit b/src/javascript/jsc/WebKit -Subproject 231267671049bbd7cd60cd6e66fbc76b9e5ee5e +Subproject 487a7b31de9fa54dab1611799db26907c14dc5a diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp index dd98742d2..e470e27b5 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp +++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp @@ -58,6 +58,8 @@ #include <wtf/text/StringView.h> #include <wtf/text/WTFString.h> +#include <wtf/Gigacage.h> + #include <cstdlib> #include <exception> #include <iostream> @@ -82,22 +84,21 @@ namespace JSCastingHelpers = JSC::JSCastingHelpers; bool has_loaded_jsc = false; -extern "C" JSC__JSGlobalObject *Zig__GlobalObject__create(JSClassRef *globalObjectClass, int count, - void *console_client) { - - if (!has_loaded_jsc) { +extern "C" void JSCInitialize() { + if (has_loaded_jsc) return; JSC::Options::useSourceProviderCache() = true; JSC::Options::useUnlinkedCodeBlockJettisoning() = false; // JSC::Options::useTopLevelAwait() = true; JSC::Options::exposeInternalModuleLoader() = true; - std::set_terminate([]() { Zig__GlobalObject__onCrash(); }); + // std::set_terminate([]() { Zig__GlobalObject__onCrash(); }); WTF::initializeMainThread(); JSC::initialize(); + // Gigacage::disablePrimitiveGigacage(); has_loaded_jsc = true; - } - - // JSC::Options::useCodeCache() = false; +} +extern "C" JSC__JSGlobalObject *Zig__GlobalObject__create(JSClassRef *globalObjectClass, int count, + void *console_client) { auto heapSize = JSC::LargeHeap; JSC::VM &vm = JSC::VM::create(heapSize).leakRef(); diff --git a/src/main.zig b/src/main.zig index 7b86f7360..f73f9c292 100644 --- a/src/main.zig +++ b/src/main.zig @@ -22,6 +22,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) nore } pub var start_time: i128 = 0; pub fn main() anyerror!void { + @import("javascript/jsc/JavascriptCore.zig").JSCInitialize(); start_time = std.time.nanoTimestamp(); // The memory allocator makes a massive difference. diff --git a/src/runtime.version b/src/runtime.version index 57659652b..2dcc8c529 100644 --- a/src/runtime.version +++ b/src/runtime.version @@ -1 +1 @@ -35057197d4ad54bc
\ No newline at end of file +4d09f9efba49d5ac
\ No newline at end of file diff --git a/src/watcher.zig b/src/watcher.zig index 63fdb9007..f088d31d2 100644 --- a/src/watcher.zig +++ b/src/watcher.zig @@ -60,17 +60,29 @@ pub const INotify = struct { var eventlist: EventListBuffer = undefined; var eventlist_ptrs: [128]*const INotifyEvent = undefined; - const add_mask = IN_EXCL_UNLINK | IN_MOVE_SELF | IN_CREATE | IN_DELETE | IN_DELETE_SELF; + var watch_count: std.atomic.Atomic(u32) = std.atomic.Atomic(u32).init(0); - pub fn watchPath(pathname: [*:0]const u8) !EventListIndex { + const watch_file_mask = IN_EXCL_UNLINK | IN_MOVE_SELF | IN_DELETE_SELF | IN_CLOSE_WRITE; + const watch_dir_mask = IN_EXCL_UNLINK | IN_DELETE | IN_DELETE_SELF | IN_CREATE | IN_MOVE_SELF | IN_ONLYDIR; + + pub fn watchPath(pathname: [:0]const u8) !EventListIndex { std.debug.assert(loaded_inotify); + const old_count = watch_count.fetchAdd(1, .Release); + defer if (old_count == 0) std.Thread.Futex.wake(&watch_count, 10); + return std.os.inotify_add_watchZ(inotify_fd, pathname, watch_file_mask); + } - return std.os.inotify_add_watchZ(inotify_fd, pathname, add_mask); + pub fn watchDir(pathname: [:0]const u8) !EventListIndex { + std.debug.assert(loaded_inotify); + const old_count = watch_count.fetchAdd(1, .Release); + defer if (old_count == 0) std.Thread.Futex.wake(&watch_count, 10); + return std.os.inotify_add_watchZ(inotify_fd, pathname, watch_dir_mask); } + pub fn unwatch(wd: EventListIndex) void { std.debug.assert(loaded_inotify); - + _ = watch_count.fetchSub(1, .Release); std.os.inotify_rm_watch(inotify_fd, wd); } @@ -84,6 +96,10 @@ pub const INotify = struct { pub fn read() ![]*const INotifyEvent { std.debug.assert(loaded_inotify); + restart: while (true) { + + + std.Thread.Futex.wait(&watch_count,0, null) catch unreachable; const rc = std.os.system.read( inotify_fd, @ptrCast([*]u8, @alignCast(@alignOf([*]u8), &eventlist)), @@ -110,12 +126,14 @@ pub const INotify = struct { return eventlist_ptrs[0..count]; }, + .AGAIN => continue :restart, .INVAL => return error.ShortRead, .BADF => return error.INotifyFailedToStart, else => unreachable, } +} unreachable; } @@ -183,6 +201,21 @@ pub const WatchEvent = struct { op: Op, const KEvent = std.os.Kevent; + + pub const Sorter = void; + + pub fn sortByIndex(context: Sorter, event: WatchEvent, rhs: WatchEvent) bool { + return event.index < rhs.index; + } + + pub fn merge(this: *WatchEvent, other: WatchEvent) void { + this.op = Op{ + .delete = this.op.delete or other.op.delete, + .metadata = this.op.metadata or other.op.metadata, + .rename = this.op.rename or other.op.rename, + .write = this.op.write or other.op.write, + }; + } pub fn fromKEvent(this: *WatchEvent, kevent: KEvent) void { this.* = @@ -200,13 +233,10 @@ pub const WatchEvent = struct { pub fn fromINotify(this: *WatchEvent, event: INotify.INotifyEvent, index: WatchItemIndex) void { this.* = WatchEvent{ .op = Op{ - .delete = (event.mask & INotify.IN_DELETE_SELF) > 0, - // only applies to directories - .metadata = (event.mask & INotify.IN_CREATE) > 0 or - (event.mask & INotify.IN_DELETE) > 0 or - (event.mask & INotify.IN_MOVE) > 0, + .delete = (event.mask & INotify.IN_DELETE_SELF) > 0 or (event.mask & INotify.IN_DELETE) > 0, + .metadata = false, .rename = (event.mask & INotify.IN_MOVE_SELF) > 0, - .write = (event.mask & INotify.IN_MODIFY) > 0, + .write = (event.mask & INotify.IN_MODIFY) > 0 or (event.mask & INotify.IN_MOVE) > 0, }, .index = index, }; @@ -257,6 +287,8 @@ pub fn NewWatcher(comptime ContextType: type) type { pub fn init(ctx: ContextType, fs: *Fs.FileSystem, allocator: *std.mem.Allocator) !*Watcher { var watcher = try allocator.create(Watcher); + try PlatformWatcher.init(); + watcher.* = Watcher{ .fs = fs, .fd = 0, @@ -272,7 +304,6 @@ pub fn NewWatcher(comptime ContextType: type) type { } pub fn start(this: *Watcher) !void { - try PlatformWatcher.init(); std.debug.assert(this.watchloop_handle == null); var thread = try std.Thread.spawn(.{}, Watcher.watchLoop, .{this}); thread.setName("File Watcher") catch {}; @@ -389,7 +420,7 @@ pub fn NewWatcher(comptime ContextType: type) type { this.ctx.onFileUpdate(watchevents, this.watchlist); } } else if (Environment.isLinux) { - while (true) { + restart: while (true) { defer Output.flush(); var events = try INotify.read(); @@ -417,7 +448,21 @@ pub fn NewWatcher(comptime ContextType: type) type { watch_event_id += 1; } - this.ctx.onFileUpdate(watchevents[0..watch_event_id], this.watchlist); + var all_events = watchevents[0..watch_event_id]; + std.sort.sort(WatchEvent, all_events, void{}, WatchEvent.sortByIndex); + + var last_event_index: usize = 0; + var last_event_id: INotify.EventListIndex = std.math.maxInt(INotify.EventListIndex); + for (all_events) |event, i| { + if (event.index == last_event_id) { + all_events[last_event_index].merge(event); + continue; + } + last_event_index = i; + last_event_id = event.index; + } + if (all_events.len == 0) continue :restart; + this.ctx.onFileUpdate(all_events[0..last_event_index+1], this.watchlist); remaining_events -= slice.len; } } @@ -503,11 +548,13 @@ pub fn NewWatcher(comptime ContextType: type) type { null, ); } else if (Environment.isLinux) { - var sentineled = file_path_; - var file_path_to_use_ptr: [*c]u8 = @intToPtr([*c]u8, @ptrToInt(file_path_.ptr)); - var file_path_to_use: [:0]u8 = file_path_to_use_ptr[0..sentineled.len :0]; - - index = try INotify.watchPath(file_path_to_use); + // var file_path_to_use_ = std.mem.trimRight(u8, file_path_, "/"); + // var buf: [std.fs.MAX_PATH_BYTES+1]u8 = undefined; + // std.mem.copy(u8, &buf, file_path_to_use_); + // buf[file_path_to_use_.len] = 0; + var buf = file_path_.ptr; + var slice: [:0]const u8 = buf[0..file_path_.len:0]; + index = try INotify.watchPath(slice); } this.watchlist.appendAssumeCapacity(.{ @@ -587,12 +634,12 @@ pub fn NewWatcher(comptime ContextType: type) type { null, ); } else if (Environment.isLinux) { - // This works around a Zig compiler bug when casting a slice from a string to a sentineled string. - var sentineled = file_path_; - var file_path_to_use_ptr: [*c]u8 = @intToPtr([*c]u8, @ptrToInt(file_path_.ptr)); - var file_path_to_use: [:0]u8 = file_path_to_use_ptr[0..sentineled.len :0]; - - index = try INotify.watchPath(file_path_to_use); + var file_path_to_use_ = std.mem.trimRight(u8, file_path_, "/"); + var buf: [std.fs.MAX_PATH_BYTES+1]u8 = undefined; + std.mem.copy(u8, &buf, file_path_to_use_); + buf[file_path_to_use_.len] = 0; + var slice: [:0]u8 = buf[0..file_path_to_use_.len:0]; + index = try INotify.watchDir(slice); } this.watchlist.appendAssumeCapacity(.{ |