mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
merge
This commit is contained in:
5
.github/workflows/build-windows.yml
vendored
5
.github/workflows/build-windows.yml
vendored
@@ -36,6 +36,7 @@ env:
|
||||
BUN_GARBAGE_COLLECTOR_LEVEL: 1
|
||||
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: 1
|
||||
CI: true
|
||||
USE_LTO: 1
|
||||
|
||||
jobs:
|
||||
build-submodules:
|
||||
@@ -87,6 +88,7 @@ jobs:
|
||||
env:
|
||||
CPU_TARGET: ${{ inputs.cpu }}
|
||||
CCACHE_DIR: ccache
|
||||
USE_LTO: 1
|
||||
run: |
|
||||
.\scripts\env.ps1 ${{ contains(inputs.tag, '-baseline') && '-Baseline' || '' }}
|
||||
choco install -y nasm --version=2.16.01
|
||||
@@ -172,6 +174,7 @@ jobs:
|
||||
env:
|
||||
CPU_TARGET: ${{ inputs.cpu }}
|
||||
CCACHE_DIR: ccache
|
||||
USE_LTO: 1
|
||||
run: |
|
||||
# $CANARY_REVISION = if (Test-Path build/.canary_revision) { Get-Content build/.canary_revision } else { "0" }
|
||||
$CANARY_REVISION = 0
|
||||
@@ -181,6 +184,7 @@ jobs:
|
||||
cd build
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release `
|
||||
-DNO_CODEGEN=1 `
|
||||
-DUSE_LTO=1 `
|
||||
-DNO_CONFIGURE_DEPENDS=1 `
|
||||
"-DCANARY=${CANARY_REVISION}" `
|
||||
-DBUN_CPP_ONLY=1 ${{ contains(inputs.tag, '-baseline') && '-DUSE_BASELINE_BUILD=1' || '' }}
|
||||
@@ -276,6 +280,7 @@ jobs:
|
||||
-DNO_CONFIGURE_DEPENDS=1 `
|
||||
"-DCANARY=${CANARY_REVISION}" `
|
||||
-DBUN_LINK_ONLY=1 `
|
||||
-DUSE_LTO=1 `
|
||||
"-DBUN_DEPS_OUT_DIR=$(Resolve-Path ../bun-deps)" `
|
||||
"-DBUN_CPP_ARCHIVE=$(Resolve-Path ../bun-cpp/bun-cpp-objects.a)" `
|
||||
"-DBUN_ZIG_OBJ_DIR=$(Resolve-Path ../bun-zig)" `
|
||||
|
||||
@@ -15,8 +15,6 @@ set(CMAKE_C_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF)
|
||||
|
||||
# Should not start with v
|
||||
# Used in process.version, process.versions.node, napi, and elsewhere
|
||||
set(REPORTED_NODEJS_VERSION "22.3.0")
|
||||
@@ -324,6 +322,11 @@ option(USE_STATIC_LIBATOMIC "Statically link libatomic, requires the presence of
|
||||
|
||||
option(USE_LTO "Enable Link-Time Optimization" ${DEFAULT_LTO})
|
||||
|
||||
if(WIN32 AND USE_LTO)
|
||||
set(CMAKE_LINKER_TYPE LLD)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF)
|
||||
endif()
|
||||
|
||||
option(BUN_TIDY_ONLY "Only run clang-tidy" OFF)
|
||||
option(BUN_TIDY_ONLY_EXTRA " Only run clang-tidy, with extra checks for local development" OFF)
|
||||
|
||||
@@ -1080,6 +1083,8 @@ elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(LTO_LINK_FLAG "")
|
||||
|
||||
if(USE_LTO)
|
||||
target_compile_options(${bun} PUBLIC -Xclang -emit-llvm-bc)
|
||||
|
||||
# -emit-llvm seems to not be supported or under a different name on Windows.
|
||||
list(APPEND LTO_FLAG "-flto=full")
|
||||
list(APPEND LTO_LINK_FLAG "/LTCG")
|
||||
@@ -1120,10 +1125,6 @@ if(WIN32)
|
||||
# set_property(TARGET ${bun} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
|
||||
set_property(TARGET ${bun} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
|
||||
|
||||
if(USE_LTO)
|
||||
target_compile_options(${bun} PUBLIC "-Xclang -emit-llvm-bc")
|
||||
endif()
|
||||
|
||||
target_compile_options(${bun} PUBLIC "/EHsc" "/GR-")
|
||||
target_link_options(${bun} PUBLIC "/STACK:0x1200000,0x100000" "/DEF:${BUN_SRC}/symbols.def" "/errorlimit:0")
|
||||
else()
|
||||
|
||||
BIN
bench/bun.lockb
BIN
bench/bun.lockb
Binary file not shown.
@@ -3,6 +3,7 @@
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.16.10",
|
||||
"@babel/preset-react": "^7.16.7",
|
||||
"@babel/standalone": "^7.24.7",
|
||||
"@swc/core": "^1.2.133",
|
||||
"benchmark": "^2.1.4",
|
||||
"braces": "^3.0.2",
|
||||
|
||||
14
bench/snippets/transpiler-2.mjs
Normal file
14
bench/snippets/transpiler-2.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
import { bench, run } from "mitata";
|
||||
import { join } from "path";
|
||||
|
||||
const code = require("fs").readFileSync(
|
||||
process.argv[2] || join(import.meta.dir, "../node_modules/@babel/standalone/babel.min.js"),
|
||||
);
|
||||
|
||||
const transpiler = new Bun.Transpiler({ minify: true });
|
||||
|
||||
bench("transformSync", () => {
|
||||
transpiler.transformSync(code);
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -13,7 +13,7 @@ console.log(Bun.argv);
|
||||
Running this file with arguments results in the following:
|
||||
|
||||
```sh
|
||||
$ bun run cli.tsx --flag1 --flag2 value
|
||||
$ bun run cli.ts --flag1 --flag2 value
|
||||
[ '/path/to/bun', '/path/to/cli.ts', '--flag1', '--flag2', 'value' ]
|
||||
```
|
||||
|
||||
@@ -47,7 +47,7 @@ console.log(positionals);
|
||||
then it outputs
|
||||
|
||||
```
|
||||
$ bun run cli.tsx --flag1 --flag2 value
|
||||
$ bun run cli.ts --flag1 --flag2 value
|
||||
{
|
||||
flag1: true,
|
||||
flag2: "value",
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
# ...
|
||||
- uses: actions/checkout@v4
|
||||
+ - uses: oven-sh/setup-bun@v1
|
||||
+ - uses: oven-sh/setup-bun@v2
|
||||
|
||||
# run any `bun` or `bunx` command
|
||||
+ - run: bun install
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# ...
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
+ with:
|
||||
+ bun-version: 1.0.11 # or "latest", "canary", <sha>
|
||||
```
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#define HAS_MSGX
|
||||
#endif
|
||||
|
||||
|
||||
/* We need to emulate sendmmsg, recvmmsg on platform who don't have it */
|
||||
int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_sendbuf* sendbuf, int flags) {
|
||||
#if defined(_WIN32)// || defined(__APPLE__)
|
||||
@@ -397,7 +398,9 @@ int bsd_addr_get_port(struct bsd_addr_t *addr) {
|
||||
// called by dispatch_ready_poll
|
||||
LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) {
|
||||
LIBUS_SOCKET_DESCRIPTOR accepted_fd;
|
||||
addr->len = sizeof(addr->mem);
|
||||
|
||||
while (1) {
|
||||
addr->len = sizeof(addr->mem);
|
||||
|
||||
#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
|
||||
// Linux, FreeBSD
|
||||
@@ -405,12 +408,18 @@ LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd
|
||||
#else
|
||||
// Windows, OS X
|
||||
accepted_fd = accept(fd, (struct sockaddr *) addr, &addr->len);
|
||||
|
||||
#endif
|
||||
|
||||
/* We cannot rely on addr since it is not initialized if failed */
|
||||
if (accepted_fd == LIBUS_SOCKET_ERROR) {
|
||||
return LIBUS_SOCKET_ERROR;
|
||||
if (UNLIKELY(IS_EINTR(accepted_fd))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We cannot rely on addr since it is not initialized if failed */
|
||||
if (accepted_fd == LIBUS_SOCKET_ERROR) {
|
||||
return LIBUS_SOCKET_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
internal_finalize_bsd_addr(addr);
|
||||
@@ -423,14 +432,22 @@ LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd
|
||||
#endif
|
||||
}
|
||||
|
||||
int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags) {
|
||||
return recv(fd, buf, length, flags);
|
||||
ssize_t bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags) {
|
||||
while (1) {
|
||||
ssize_t ret = recv(fd, buf, length, flags);
|
||||
|
||||
if (UNLIKELY(IS_EINTR(ret))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/uio.h>
|
||||
|
||||
int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length) {
|
||||
ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length) {
|
||||
struct iovec chunks[2];
|
||||
|
||||
chunks[0].iov_base = (char *)header;
|
||||
@@ -438,13 +455,21 @@ int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length
|
||||
chunks[1].iov_base = (char *)payload;
|
||||
chunks[1].iov_len = payload_length;
|
||||
|
||||
return writev(fd, chunks, 2);
|
||||
while (1) {
|
||||
ssize_t written = writev(fd, chunks, 2);
|
||||
|
||||
if (UNLIKELY(IS_EINTR(written))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
}
|
||||
#else
|
||||
int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length) {
|
||||
int written = bsd_send(fd, header, header_length, 0);
|
||||
ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length) {
|
||||
ssize_t written = bsd_send(fd, header, header_length, 0);
|
||||
if (written == header_length) {
|
||||
int second_write = bsd_send(fd, payload, payload_length, 0);
|
||||
ssize_t second_write = bsd_send(fd, payload, payload_length, 0);
|
||||
if (second_write > 0) {
|
||||
written += second_write;
|
||||
}
|
||||
@@ -453,26 +478,28 @@ int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length
|
||||
}
|
||||
#endif
|
||||
|
||||
int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more) {
|
||||
|
||||
ssize_t bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more) {
|
||||
while (1) {
|
||||
// MSG_MORE (Linux), MSG_PARTIAL (Windows), TCP_NOPUSH (BSD)
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
#ifdef MSG_MORE
|
||||
#ifdef MSG_MORE
|
||||
// for Linux we do not want signals
|
||||
ssize_t rc = send(fd, buf, length, ((msg_more != 0) * MSG_MORE) | MSG_NOSIGNAL | MSG_DONTWAIT);
|
||||
#else
|
||||
// use TCP_NOPUSH
|
||||
ssize_t rc = send(fd, buf, length, MSG_NOSIGNAL | MSG_DONTWAIT);
|
||||
#endif
|
||||
|
||||
// for Linux we do not want signals
|
||||
return send(fd, buf, length, ((msg_more != 0) * MSG_MORE) | MSG_NOSIGNAL | MSG_DONTWAIT);
|
||||
if (UNLIKELY(IS_EINTR(rc))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// use TCP_NOPUSH
|
||||
|
||||
return send(fd, buf, length, MSG_NOSIGNAL | MSG_DONTWAIT);
|
||||
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
int bsd_would_block() {
|
||||
@@ -483,6 +510,23 @@ int bsd_would_block() {
|
||||
#endif
|
||||
}
|
||||
|
||||
static int us_internal_bind_and_listen(LIBUS_SOCKET_DESCRIPTOR listenFd, struct sockaddr *listenAddr, socklen_t listenAddrLength, int backlog) {
|
||||
int result;
|
||||
do
|
||||
result = bind(listenFd, listenAddr, listenAddrLength);
|
||||
while (IS_EINTR(result));
|
||||
|
||||
if (result == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
do
|
||||
result = listen(listenFd, backlog);
|
||||
while (IS_EINTR(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline __attribute__((always_inline)) LIBUS_SOCKET_DESCRIPTOR bsd_bind_listen_fd(
|
||||
LIBUS_SOCKET_DESCRIPTOR listenFd,
|
||||
struct addrinfo *listenAddr,
|
||||
@@ -512,7 +556,7 @@ inline __attribute__((always_inline)) LIBUS_SOCKET_DESCRIPTOR bsd_bind_listen_fd
|
||||
setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled));
|
||||
#endif
|
||||
|
||||
if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen) || listen(listenFd, 512)) {
|
||||
if (us_internal_bind_and_listen(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen, 512)) {
|
||||
return LIBUS_SOCKET_ERROR;
|
||||
}
|
||||
|
||||
@@ -690,7 +734,7 @@ static LIBUS_SOCKET_DESCRIPTOR internal_bsd_create_listen_socket_unix(const char
|
||||
unlink(path);
|
||||
#endif
|
||||
|
||||
if (bind(listenFd, (struct sockaddr *)server_address, addrlen) || listen(listenFd, 512)) {
|
||||
if (us_internal_bind_and_listen(listenFd, (struct sockaddr *) server_address, (socklen_t) addrlen, 512)) {
|
||||
#if defined(_WIN32)
|
||||
int shouldSimulateENOENT = WSAGetLastError() == WSAENETDOWN;
|
||||
#endif
|
||||
@@ -925,7 +969,7 @@ static int bsd_do_connect_raw(LIBUS_SOCKET_DESCRIPTOR fd, struct sockaddr *addr,
|
||||
do {
|
||||
errno = 0;
|
||||
r = connect(fd, (struct sockaddr *)addr, namelen);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
} while (IS_EINTR(r));
|
||||
|
||||
// connect() can return -1 with an errno of 0.
|
||||
// the errno is the correct one in that case.
|
||||
|
||||
@@ -109,6 +109,51 @@ struct us_loop_t *us_timer_loop(struct us_timer_t *t) {
|
||||
return internal_cb->loop;
|
||||
}
|
||||
|
||||
|
||||
#if defined(LIBUS_USE_EPOLL)
|
||||
|
||||
#include <sys/syscall.h>
|
||||
static int has_epoll_pwait2 = -1;
|
||||
|
||||
#ifndef SYS_epoll_pwait2
|
||||
// It's consistent on multiple architectures
|
||||
// https://github.com/torvalds/linux/blob/9d1ddab261f3e2af7c384dc02238784ce0cf9f98/include/uapi/asm-generic/unistd.h#L795
|
||||
// https://github.com/google/gvisor/blob/master/test/syscalls/linux/epoll.cc#L48C1-L50C7
|
||||
#define SYS_epoll_pwait2 441
|
||||
#endif
|
||||
|
||||
static ssize_t sys_epoll_pwait2(int epfd, struct epoll_event *events, int maxevents, const struct timespec *timeout, const sigset_t *sigmask, size_t sigsetsize) {
|
||||
return syscall(SYS_epoll_pwait2, epfd, events, maxevents, timeout, sigmask, sigsetsize);
|
||||
}
|
||||
|
||||
static int bun_epoll_pwait2(int epfd, struct epoll_event *events, int maxevents, const struct timespec *timeout) {
|
||||
int ret;
|
||||
if (has_epoll_pwait2 != 0) {
|
||||
do {
|
||||
ret = sys_epoll_pwait2(epfd, events, maxevents, timeout, NULL, 0);
|
||||
} while (IS_EINTR(ret));
|
||||
|
||||
if (LIKELY(ret != -1 || errno != ENOSYS)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
has_epoll_pwait2 = 0;
|
||||
}
|
||||
|
||||
int timeoutMs = -1;
|
||||
if (timeout) {
|
||||
timeoutMs = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = epoll_wait(epfd, events, maxevents, timeoutMs);
|
||||
} while (IS_EINTR(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Loop */
|
||||
struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) {
|
||||
struct us_loop_t *loop = (struct us_loop_t *) us_calloc(1, sizeof(struct us_loop_t) + ext_size);
|
||||
@@ -139,9 +184,11 @@ void us_loop_run(struct us_loop_t *loop) {
|
||||
|
||||
/* Fetch ready polls */
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
loop->num_ready_polls = epoll_wait(loop->fd, loop->ready_polls, 1024, -1);
|
||||
loop->num_ready_polls = bun_epoll_pwait2(loop->fd, loop->ready_polls, 1024, NULL);
|
||||
#else
|
||||
loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024, 0, NULL);
|
||||
do {
|
||||
loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024, 0, NULL);
|
||||
} while (IS_EINTR(loop->num_ready_polls));
|
||||
#endif
|
||||
|
||||
/* Iterate ready polls, dispatching them by type */
|
||||
@@ -183,12 +230,6 @@ void us_loop_run(struct us_loop_t *loop) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(LIBUS_USE_EPOLL)
|
||||
|
||||
// static int has_epoll_pwait2 = 0;
|
||||
// TODO:
|
||||
|
||||
#endif
|
||||
|
||||
void us_loop_run_bun_tick(struct us_loop_t *loop, const struct timespec* timeout) {
|
||||
if (loop->num_polls == 0)
|
||||
@@ -207,13 +248,12 @@ void us_loop_run_bun_tick(struct us_loop_t *loop, const struct timespec* timeout
|
||||
|
||||
/* Fetch ready polls */
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
int timeoutMs = -1;
|
||||
if (timeout) {
|
||||
timeoutMs = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
|
||||
}
|
||||
loop->num_ready_polls = epoll_wait(loop->fd, loop->ready_polls, 1024, timeoutMs);
|
||||
|
||||
loop->num_ready_polls = bun_epoll_pwait2(loop->fd, loop->ready_polls, 1024, timeout);
|
||||
#else
|
||||
loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024, 0, timeout);
|
||||
do {
|
||||
loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024, 0, timeout);
|
||||
} while (IS_EINTR(loop->num_ready_polls));
|
||||
#endif
|
||||
|
||||
/* Iterate ready polls, dispatching them by type */
|
||||
@@ -296,7 +336,10 @@ int kqueue_change(int kqfd, int fd, int old_events, int new_events, void *user_d
|
||||
EV_SET64(&change_list[change_length++], fd, EVFILT_WRITE, (new_events & LIBUS_SOCKET_WRITABLE) ? EV_ADD : EV_DELETE, 0, 0, (uint64_t)(void*)user_data, 0, 0);
|
||||
}
|
||||
|
||||
int ret = kevent64(kqfd, change_list, change_length, change_list, change_length, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
int ret;
|
||||
do {
|
||||
ret = kevent64(kqfd, change_list, change_length, change_list, change_length, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
} while (IS_EINTR(ret));
|
||||
|
||||
// ret should be 0 in most cases (not guaranteed when removing async)
|
||||
|
||||
@@ -332,7 +375,10 @@ void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events) {
|
||||
struct epoll_event event;
|
||||
event.events = events;
|
||||
event.data.ptr = p;
|
||||
epoll_ctl(loop->fd, EPOLL_CTL_ADD, p->state.fd, &event);
|
||||
int ret;
|
||||
do {
|
||||
ret = epoll_ctl(loop->fd, EPOLL_CTL_ADD, p->state.fd, &event);
|
||||
} while (IS_EINTR(ret));
|
||||
#else
|
||||
kqueue_change(loop->fd, p->state.fd, 0, events, p);
|
||||
#endif
|
||||
@@ -348,7 +394,10 @@ void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events) {
|
||||
struct epoll_event event;
|
||||
event.events = events;
|
||||
event.data.ptr = p;
|
||||
epoll_ctl(loop->fd, EPOLL_CTL_MOD, p->state.fd, &event);
|
||||
int rc;
|
||||
do {
|
||||
rc = epoll_ctl(loop->fd, EPOLL_CTL_MOD, p->state.fd, &event);
|
||||
} while (IS_EINTR(rc));
|
||||
#else
|
||||
kqueue_change(loop->fd, p->state.fd, old_events, events, p);
|
||||
#endif
|
||||
@@ -362,7 +411,10 @@ void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop) {
|
||||
int new_events = 0;
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
struct epoll_event event;
|
||||
epoll_ctl(loop->fd, EPOLL_CTL_DEL, p->state.fd, &event);
|
||||
int rc;
|
||||
do {
|
||||
rc = epoll_ctl(loop->fd, EPOLL_CTL_DEL, p->state.fd, &event);
|
||||
} while (IS_EINTR(rc));
|
||||
#else
|
||||
if (old_events) {
|
||||
kqueue_change(loop->fd, p->state.fd, old_events, new_events, NULL);
|
||||
@@ -373,12 +425,14 @@ void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop) {
|
||||
us_internal_loop_update_pending_ready_polls(loop, p, 0, old_events, new_events);
|
||||
}
|
||||
|
||||
unsigned int us_internal_accept_poll_event(struct us_poll_t *p) {
|
||||
size_t us_internal_accept_poll_event(struct us_poll_t *p) {
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
int fd = us_poll_fd(p);
|
||||
uint64_t buf;
|
||||
int read_length = read(fd, &buf, 8);
|
||||
(void)read_length;
|
||||
ssize_t read_length = 0;
|
||||
do {
|
||||
read_length = read(fd, &buf, 8);
|
||||
} while (IS_EINTR(read_length));
|
||||
return buf;
|
||||
#else
|
||||
/* Kqueue has no underlying FD for timers or user events */
|
||||
@@ -467,7 +521,11 @@ void us_timer_close(struct us_timer_t *timer, int fallthrough) {
|
||||
|
||||
struct kevent64_s event;
|
||||
EV_SET64(&event, (uint64_t) (void*) internal_cb, EVFILT_TIMER, EV_DELETE, 0, 0, (uint64_t)internal_cb, 0, 0);
|
||||
kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
int ret;
|
||||
do {
|
||||
ret = kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
} while (IS_EINTR(ret));
|
||||
|
||||
|
||||
/* (regular) sockets are the only polls which are not freed immediately */
|
||||
if(fallthrough){
|
||||
@@ -486,7 +544,11 @@ void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms
|
||||
struct kevent64_s event;
|
||||
uint64_t ptr = (uint64_t)(void*)internal_cb;
|
||||
EV_SET64(&event, ptr, EVFILT_TIMER, EV_ADD | (repeat_ms ? 0 : EV_ONESHOT), 0, ms, (uint64_t)internal_cb, 0, 0);
|
||||
kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
|
||||
int ret;
|
||||
do {
|
||||
ret = kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
} while (IS_EINTR(ret));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -581,7 +643,11 @@ void us_internal_async_close(struct us_internal_async *a) {
|
||||
struct kevent64_s event;
|
||||
uint64_t ptr = (uint64_t)(void*)internal_cb;
|
||||
EV_SET64(&event, ptr, EVFILT_MACHPORT, EV_DELETE, 0, 0, (uint64_t)(void*)internal_cb, 0,0);
|
||||
kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
|
||||
int ret;
|
||||
do {
|
||||
ret = kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
} while (IS_EINTR(ret));
|
||||
|
||||
mach_port_deallocate(mach_task_self(), internal_cb->port);
|
||||
us_free(internal_cb->machport_buf);
|
||||
@@ -609,7 +675,10 @@ void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_int
|
||||
event.ext[1] = MACHPORT_BUF_LEN;
|
||||
event.udata = (uint64_t)(void*)internal_cb;
|
||||
|
||||
int ret = kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
int ret;
|
||||
do {
|
||||
ret = kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
|
||||
} while (IS_EINTR(ret));
|
||||
|
||||
if (UNLIKELY(ret == -1)) {
|
||||
abort();
|
||||
|
||||
@@ -125,7 +125,7 @@ int us_poll_events(struct us_poll_t *p) {
|
||||
((p->poll_type & POLL_TYPE_POLLING_OUT) ? LIBUS_SOCKET_WRITABLE : 0);
|
||||
}
|
||||
|
||||
unsigned int us_internal_accept_poll_event(struct us_poll_t *p) { return 0; }
|
||||
size_t us_internal_accept_poll_event(struct us_poll_t *p) { return 0; }
|
||||
|
||||
int us_internal_poll_type(struct us_poll_t *p) { return p->poll_type & POLL_TYPE_KIND_MASK; }
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef INTERNAL_H
|
||||
#define INTERNAL_H
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
#ifndef __cplusplus
|
||||
#define alignas(x) __declspec(align(x))
|
||||
#endif
|
||||
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
|
||||
#else
|
||||
#include <stdalign.h>
|
||||
#endif
|
||||
@@ -52,6 +56,17 @@ void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop,
|
||||
#include "internal/eventing/libuv.h"
|
||||
#endif
|
||||
|
||||
#ifndef LIKELY
|
||||
#define LIKELY(cond) __builtin_expect((_Bool)(cond), 1)
|
||||
#define UNLIKELY(cond) __builtin_expect((_Bool)(cond), 0)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define IS_EINTR(rc) (rc == SOCKET_ERROR && WSAGetLastError() == WSAEINTR)
|
||||
#else
|
||||
#define IS_EINTR(rc) (rc == -1 && errno == EINTR)
|
||||
#endif
|
||||
|
||||
/* Poll type and what it polls for */
|
||||
enum {
|
||||
/* Three first bits */
|
||||
@@ -118,7 +133,7 @@ void us_internal_async_set(struct us_internal_async *a,
|
||||
void us_internal_async_wakeup(struct us_internal_async *a);
|
||||
|
||||
/* Eventing related */
|
||||
unsigned int us_internal_accept_poll_event(struct us_poll_t *p);
|
||||
size_t us_internal_accept_poll_event(struct us_poll_t *p);
|
||||
int us_internal_poll_type(struct us_poll_t *p);
|
||||
void us_internal_poll_set_type(struct us_poll_t *p, int poll_type);
|
||||
|
||||
|
||||
@@ -134,9 +134,9 @@ int bsd_addr_get_port(struct bsd_addr_t *addr);
|
||||
// called by dispatch_ready_poll
|
||||
LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr);
|
||||
|
||||
int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags);
|
||||
int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more);
|
||||
int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length);
|
||||
ssize_t bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags);
|
||||
ssize_t bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more);
|
||||
ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length);
|
||||
int bsd_would_block();
|
||||
|
||||
// return LIBUS_SOCKET_ERROR or the fd that represents listen socket
|
||||
|
||||
@@ -52,11 +52,16 @@ $CPUS = if ($env:CPUS) { $env:CPUS } else { (Get-CimInstance -Class Win32_Proces
|
||||
$CC = "clang-cl"
|
||||
$CXX = "clang-cl"
|
||||
|
||||
$CFLAGS = '/O2 /Zi'
|
||||
$CFLAGS = '/O2 /Zi '
|
||||
# $CFLAGS = '/O2 /Z7 /MT'
|
||||
$CXXFLAGS = '/O2 /Zi'
|
||||
$CXXFLAGS = '/O2 /Zi '
|
||||
# $CXXFLAGS = '/O2 /Z7 /MT'
|
||||
|
||||
if ($env:USE_LTO -eq "1") {
|
||||
$CXXFLAGS += " -fuse-ld=lld -flto -Xclang -emit-llvm-bc "
|
||||
$CFLAGS += " -fuse-ld=lld -flto -Xclang -emit-llvm-bc "
|
||||
}
|
||||
|
||||
$CPU_NAME = if ($Baseline) { "nehalem" } else { "haswell" };
|
||||
$env:CPU_TARGET = $CPU_NAME
|
||||
|
||||
@@ -71,6 +76,15 @@ $CMAKE_FLAGS = @(
|
||||
"-DCMAKE_C_FLAGS=$CFLAGS",
|
||||
"-DCMAKE_CXX_FLAGS=$CXXFLAGS"
|
||||
)
|
||||
|
||||
if ($env:USE_LTO -eq "1") {
|
||||
if (Get-Command lld-lib -ErrorAction SilentlyContinue) {
|
||||
$AR = Get-Command lld-lib -ErrorAction SilentlyContinue
|
||||
$env:AR = $AR
|
||||
$CMAKE_FLAGS += "-DCMAKE_AR=$AR"
|
||||
}
|
||||
}
|
||||
|
||||
$env:CC = "clang-cl"
|
||||
$env:CXX = "clang-cl"
|
||||
$env:CFLAGS = $CFLAGS
|
||||
|
||||
@@ -43,7 +43,7 @@ export CMAKE_FLAGS=(
|
||||
)
|
||||
|
||||
CCACHE=$(which ccache || which sccache || echo "")
|
||||
if [ -n "$CCACHE" ]; then
|
||||
if [ -f "$CCACHE" ]; then
|
||||
CMAKE_FLAGS+=(
|
||||
-DCMAKE_C_COMPILER_LAUNCHER="$CCACHE"
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER="$CCACHE"
|
||||
|
||||
@@ -461,10 +461,7 @@ static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_set2Body(JSC::J
|
||||
auto name = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value());
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
|
||||
|
||||
EnsureStillAliveScope argument2 = callFrame->argument(2);
|
||||
auto filename = argument2.value().isUndefined() ? String() : convert<IDLUSVString>(*lexicalGlobalObject, argument2.value());
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
RefPtr<Blob> blobValue = nullptr;
|
||||
if (argument1.value().inherits<JSBlob>()) {
|
||||
@@ -473,8 +470,11 @@ static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_set2Body(JSC::J
|
||||
|
||||
if (!blobValue) {
|
||||
throwTypeError(lexicalGlobalObject, throwScope, "Expected argument to be a Blob."_s);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
}
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
auto filename = argument2.value().isUndefined() ? Blob__getFileNameString(blobValue->impl()).toWTFString(BunString::ZeroCopy) : convert<IDLUSVString>(*lexicalGlobalObject, argument2.value());
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.set(WTFMove(name), WTFMove(blobValue), WTFMove(filename)); })));
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ pub const Mode = enum {
|
||||
/// This must match the behavior of node.js, and supports bun <--> node.js/etc communication.
|
||||
json,
|
||||
|
||||
const Map = std.StaticStringMap(Mode).initComptime(.{
|
||||
const Map = bun.ComptimeStringMap(Mode, .{
|
||||
.{ "advanced", .advanced },
|
||||
.{ "json", .json },
|
||||
});
|
||||
|
||||
@@ -1828,7 +1828,7 @@ pub const ${className(typeName)} = struct {
|
||||
/// Return the pointer to the wrapped object.
|
||||
/// If the object does not match the type, return null.
|
||||
pub fn fromJS(value: JSC.JSValue) ?*${typeName} {
|
||||
JSC.markBinding(@src());
|
||||
if (comptime Environment.enable_logs) zig("<r>${typeName}<d>.<r>fromJS", .{});
|
||||
return ${symbolName(typeName, "fromJS")}(value);
|
||||
}
|
||||
|
||||
@@ -1837,7 +1837,7 @@ pub const ${className(typeName)} = struct {
|
||||
/// If the object is a subclass of the type or has mutated the structure, return null.
|
||||
/// Note: this may return null for direct instances of the type if the user adds properties to the object.
|
||||
pub fn fromJSDirect(value: JSC.JSValue) ?*${typeName} {
|
||||
JSC.markBinding(@src());
|
||||
if (comptime Environment.enable_logs) zig("<r>${typeName}<d>.<r>fromJSDirect", .{});
|
||||
return ${symbolName(typeName, "fromJSDirect")}(value);
|
||||
}
|
||||
|
||||
@@ -1849,7 +1849,7 @@ pub const ${className(typeName)} = struct {
|
||||
/// Get the ${typeName} constructor value.
|
||||
/// This loads lazily from the global object.
|
||||
pub fn getConstructor(globalObject: *JSC.JSGlobalObject) JSC.JSValue {
|
||||
JSC.markBinding(@src());
|
||||
if (comptime Environment.enable_logs) zig("<r>${typeName}<d>.<r>getConstructor", .{});
|
||||
return ${symbolName(typeName, "getConstructor")}(globalObject);
|
||||
}
|
||||
`
|
||||
@@ -1861,7 +1861,7 @@ pub const ${className(typeName)} = struct {
|
||||
? `
|
||||
/// Create a new instance of ${typeName}
|
||||
pub fn toJS(this: *${typeName}, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
|
||||
JSC.markBinding(@src());
|
||||
if (comptime Environment.enable_logs) zig("<r>${typeName}<d>.<r>toJS", .{});
|
||||
if (comptime Environment.allow_assert) {
|
||||
const value__ = ${symbolName(typeName, "create")}(globalObject, this);
|
||||
@import("root").bun.assert(value__.as(${typeName}).? == this); // If this fails, likely a C ABI issue.
|
||||
|
||||
@@ -84,10 +84,9 @@ function setTimeoutPromise(after = 1, value, options = {}) {
|
||||
signal.addEventListener("abort", onCancel);
|
||||
}
|
||||
});
|
||||
if (typeof onCancel !== "undefined") {
|
||||
returnValue.finally(() => signal.removeEventListener("abort", onCancel));
|
||||
}
|
||||
return returnValue;
|
||||
return typeof onCancel !== "undefined"
|
||||
? returnValue.finally(() => signal.removeEventListener("abort", onCancel))
|
||||
: returnValue;
|
||||
}
|
||||
|
||||
function setImmediatePromise(value, options = {}) {
|
||||
@@ -124,10 +123,9 @@ function setImmediatePromise(value, options = {}) {
|
||||
signal.addEventListener("abort", onCancel);
|
||||
}
|
||||
});
|
||||
if (typeof onCancel !== "undefined") {
|
||||
returnValue.finally(() => signal.removeEventListener("abort", onCancel));
|
||||
}
|
||||
return returnValue;
|
||||
return typeof onCancel !== "undefined"
|
||||
? returnValue.finally(() => signal.removeEventListener("abort", onCancel))
|
||||
: returnValue;
|
||||
}
|
||||
|
||||
function setIntervalPromise(after = 1, value, options = {}) {
|
||||
|
||||
@@ -7,6 +7,7 @@ const unicode = std.unicode;
|
||||
const default_allocator = bun.default_allocator;
|
||||
const string = @import("string_types.zig").string;
|
||||
const CodePoint = @import("string_types.zig").CodePoint;
|
||||
const ComptimeStringMap = bun.ComptimeStringMap;
|
||||
|
||||
pub const T = enum(u8) {
|
||||
t_end_of_file,
|
||||
@@ -159,7 +160,7 @@ pub const T = enum(u8) {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Keywords = std.StaticStringMap(T).initComptime(.{
|
||||
pub const Keywords = ComptimeStringMap(T, .{
|
||||
.{ "break", .t_break },
|
||||
.{ "case", .t_case },
|
||||
.{ "catch", .t_catch },
|
||||
@@ -198,7 +199,7 @@ pub const Keywords = std.StaticStringMap(T).initComptime(.{
|
||||
.{ "with", .t_with },
|
||||
});
|
||||
|
||||
pub const StrictModeReservedWords = std.StaticStringMap(void).initComptime(.{
|
||||
pub const StrictModeReservedWords = ComptimeStringMap(void, .{
|
||||
.{ "implements", {} },
|
||||
.{ "interface", {} },
|
||||
.{ "let", {} },
|
||||
@@ -210,7 +211,7 @@ pub const StrictModeReservedWords = std.StaticStringMap(void).initComptime(.{
|
||||
.{ "yield", {} },
|
||||
});
|
||||
|
||||
pub const StrictModeReservedWordsRemap = std.StaticStringMap(string).initComptime(.{
|
||||
pub const StrictModeReservedWordsRemap = ComptimeStringMap(string, .{
|
||||
.{ "implements", "_implements" },
|
||||
.{ "interface", "_interface" },
|
||||
.{ "let", "_let" },
|
||||
@@ -235,7 +236,7 @@ pub const PropertyModifierKeyword = enum {
|
||||
p_set,
|
||||
p_static,
|
||||
|
||||
pub const List = std.StaticStringMap(PropertyModifierKeyword).initComptime(.{
|
||||
pub const List = ComptimeStringMap(PropertyModifierKeyword, .{
|
||||
.{ "abstract", .p_abstract },
|
||||
.{ "async", .p_async },
|
||||
.{ "declare", .p_declare },
|
||||
@@ -250,7 +251,7 @@ pub const PropertyModifierKeyword = enum {
|
||||
});
|
||||
};
|
||||
|
||||
pub const TypeScriptAccessibilityModifier = std.StaticStringMap(void).initComptime(.{
|
||||
pub const TypeScriptAccessibilityModifier = ComptimeStringMap(void, .{
|
||||
.{ "override", void },
|
||||
.{ "private", void },
|
||||
.{ "protected", void },
|
||||
@@ -519,7 +520,7 @@ pub const TypescriptStmtKeyword = enum {
|
||||
ts_stmt_global,
|
||||
ts_stmt_declare,
|
||||
|
||||
pub const List = std.StaticStringMap(TypescriptStmtKeyword).initComptime(.{
|
||||
pub const List = ComptimeStringMap(TypescriptStmtKeyword, .{
|
||||
.{
|
||||
"type",
|
||||
TypescriptStmtKeyword.ts_stmt_type,
|
||||
@@ -552,7 +553,7 @@ pub const TypescriptStmtKeyword = enum {
|
||||
};
|
||||
|
||||
// Error: meta is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.
|
||||
pub const ChildlessJSXTags = std.StaticStringMap(void).initComptime(.{
|
||||
pub const ChildlessJSXTags = ComptimeStringMap(void, .{
|
||||
.{ "area", void },
|
||||
.{ "base", void },
|
||||
.{ "br", void },
|
||||
@@ -572,7 +573,7 @@ pub const ChildlessJSXTags = std.StaticStringMap(void).initComptime(.{
|
||||
});
|
||||
|
||||
// In a microbenchmark, this outperforms
|
||||
pub const jsxEntity = std.StaticStringMap(CodePoint).initComptime(.{
|
||||
pub const jsxEntity = ComptimeStringMap(CodePoint, .{
|
||||
.{ "Aacute", @as(CodePoint, 0x00C1) },
|
||||
.{ "aacute", @as(CodePoint, 0x00E1) },
|
||||
.{ "Acirc", @as(CodePoint, 0x00C2) },
|
||||
|
||||
@@ -1013,7 +1013,7 @@ pub const TypeScript = struct {
|
||||
else => return null,
|
||||
}
|
||||
}
|
||||
pub const IMap = std.StaticStringMap(Kind).initComptime(.{
|
||||
pub const IMap = bun.ComptimeStringMap(Kind, .{
|
||||
.{ "unique", .unique },
|
||||
.{ "abstract", .abstract },
|
||||
.{ "asserts", .asserts },
|
||||
@@ -1859,7 +1859,7 @@ pub const SideEffects = enum(u1) {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn simpifyUnusedExpr(p: anytype, expr: Expr) ?Expr {
|
||||
pub fn simplifyUnusedExpr(p: anytype, expr: Expr) ?Expr {
|
||||
if (!p.options.features.dead_code_elimination) return expr;
|
||||
switch (expr.data) {
|
||||
.e_null,
|
||||
@@ -1894,12 +1894,12 @@ pub const SideEffects = enum(u1) {
|
||||
}
|
||||
},
|
||||
.e_if => |__if__| {
|
||||
__if__.yes = simpifyUnusedExpr(p, __if__.yes) orelse __if__.yes.toEmpty();
|
||||
__if__.no = simpifyUnusedExpr(p, __if__.no) orelse __if__.no.toEmpty();
|
||||
__if__.yes = simplifyUnusedExpr(p, __if__.yes) orelse __if__.yes.toEmpty();
|
||||
__if__.no = simplifyUnusedExpr(p, __if__.no) orelse __if__.no.toEmpty();
|
||||
|
||||
// "foo() ? 1 : 2" => "foo()"
|
||||
if (__if__.yes.isEmpty() and __if__.no.isEmpty()) {
|
||||
return simpifyUnusedExpr(p, __if__.test_);
|
||||
return simplifyUnusedExpr(p, __if__.test_);
|
||||
}
|
||||
|
||||
// "foo() ? 1 : bar()" => "foo() || bar()"
|
||||
@@ -1927,7 +1927,7 @@ pub const SideEffects = enum(u1) {
|
||||
// such as "toString" or "valueOf". They must also never throw any exceptions.
|
||||
switch (un.op) {
|
||||
.un_void, .un_not => {
|
||||
return simpifyUnusedExpr(p, un.value);
|
||||
return simplifyUnusedExpr(p, un.value);
|
||||
},
|
||||
.un_typeof => {
|
||||
// "typeof x" must not be transformed into if "x" since doing so could
|
||||
@@ -1937,7 +1937,7 @@ pub const SideEffects = enum(u1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return simpifyUnusedExpr(p, un.value);
|
||||
return simplifyUnusedExpr(p, un.value);
|
||||
},
|
||||
|
||||
else => {},
|
||||
@@ -1950,7 +1950,7 @@ pub const SideEffects = enum(u1) {
|
||||
// can be removed. The annotation causes us to ignore the target.
|
||||
if (call.can_be_unwrapped_if_unused) {
|
||||
if (call.args.len > 0) {
|
||||
return Expr.joinAllWithCommaCallback(call.args.slice(), @TypeOf(p), p, comptime simpifyUnusedExpr, p.allocator);
|
||||
return Expr.joinAllWithCommaCallback(call.args.slice(), @TypeOf(p), p, comptime simplifyUnusedExpr, p.allocator);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1959,13 +1959,10 @@ pub const SideEffects = enum(u1) {
|
||||
switch (bin.op) {
|
||||
// These operators must not have any type conversions that can execute code
|
||||
// such as "toString" or "valueOf". They must also never throw any exceptions.
|
||||
.bin_strict_eq, .bin_strict_ne, .bin_comma => {
|
||||
return Expr.joinWithComma(
|
||||
simpifyUnusedExpr(p, bin.left) orelse bin.left.toEmpty(),
|
||||
simpifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty(),
|
||||
p.allocator,
|
||||
);
|
||||
},
|
||||
.bin_strict_eq,
|
||||
.bin_strict_ne,
|
||||
.bin_comma,
|
||||
=> return simplifyUnusedBinaryCommaExpr(p, expr),
|
||||
|
||||
// We can simplify "==" and "!=" even though they can call "toString" and/or
|
||||
// "valueOf" if we can statically determine that the types of both sides are
|
||||
@@ -1975,18 +1972,18 @@ pub const SideEffects = enum(u1) {
|
||||
.bin_loose_ne,
|
||||
=> {
|
||||
if (isPrimitiveWithSideEffects(bin.left.data) and isPrimitiveWithSideEffects(bin.right.data)) {
|
||||
return Expr.joinWithComma(simpifyUnusedExpr(p, bin.left) orelse bin.left.toEmpty(), simpifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty(), p.allocator);
|
||||
return Expr.joinWithComma(simplifyUnusedExpr(p, bin.left) orelse bin.left.toEmpty(), simplifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty(), p.allocator);
|
||||
}
|
||||
},
|
||||
|
||||
.bin_logical_and, .bin_logical_or, .bin_nullish_coalescing => {
|
||||
bin.right = simpifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty();
|
||||
bin.right = simplifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty();
|
||||
// Preserve short-circuit behavior: the left expression is only unused if
|
||||
// the right expression can be completely removed. Otherwise, the left
|
||||
// expression is important for the branch.
|
||||
|
||||
if (bin.right.isEmpty())
|
||||
return simpifyUnusedExpr(p, bin.left);
|
||||
return simplifyUnusedExpr(p, bin.left);
|
||||
},
|
||||
|
||||
else => {},
|
||||
@@ -2006,7 +2003,7 @@ pub const SideEffects = enum(u1) {
|
||||
for (properties_slice) |prop_| {
|
||||
var prop = prop_;
|
||||
if (prop_.kind != .spread) {
|
||||
const value = simpifyUnusedExpr(p, prop.value.?);
|
||||
const value = simplifyUnusedExpr(p, prop.value.?);
|
||||
if (value != null) {
|
||||
prop.value = value;
|
||||
} else if (!prop.flags.contains(.is_computed)) {
|
||||
@@ -2046,7 +2043,7 @@ pub const SideEffects = enum(u1) {
|
||||
);
|
||||
}
|
||||
result = result.joinWithComma(
|
||||
simpifyUnusedExpr(p, prop.value.?) orelse prop.value.?.toEmpty(),
|
||||
simplifyUnusedExpr(p, prop.value.?) orelse prop.value.?.toEmpty(),
|
||||
p.allocator,
|
||||
);
|
||||
}
|
||||
@@ -2078,7 +2075,7 @@ pub const SideEffects = enum(u1) {
|
||||
items,
|
||||
@TypeOf(p),
|
||||
p,
|
||||
comptime simpifyUnusedExpr,
|
||||
comptime simplifyUnusedExpr,
|
||||
p.allocator,
|
||||
);
|
||||
},
|
||||
@@ -2092,7 +2089,7 @@ pub const SideEffects = enum(u1) {
|
||||
call.args.slice(),
|
||||
@TypeOf(p),
|
||||
p,
|
||||
comptime simpifyUnusedExpr,
|
||||
comptime simplifyUnusedExpr,
|
||||
p.allocator,
|
||||
);
|
||||
}
|
||||
@@ -2106,6 +2103,56 @@ pub const SideEffects = enum(u1) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
const BinaryExpressionSimplifyVisitor = struct {
|
||||
bin: *E.Binary,
|
||||
};
|
||||
|
||||
///
|
||||
fn simplifyUnusedBinaryCommaExpr(p: anytype, expr: Expr) ?Expr {
|
||||
if (Environment.allow_assert) {
|
||||
assert(expr.data == .e_binary);
|
||||
assert(switch (expr.data.e_binary.op) {
|
||||
.bin_strict_eq,
|
||||
.bin_strict_ne,
|
||||
.bin_comma,
|
||||
=> true,
|
||||
else => false,
|
||||
});
|
||||
}
|
||||
const stack: *std.ArrayList(BinaryExpressionSimplifyVisitor) = &p.binary_expression_simplify_stack;
|
||||
const stack_bottom = stack.items.len;
|
||||
defer stack.shrinkRetainingCapacity(stack_bottom);
|
||||
|
||||
stack.append(.{ .bin = expr.data.e_binary }) catch bun.outOfMemory();
|
||||
|
||||
// Build stack up of expressions
|
||||
var left: Expr = expr.data.e_binary.left;
|
||||
while (left.data.as(.e_binary)) |left_bin| {
|
||||
switch (left_bin.op) {
|
||||
.bin_strict_eq,
|
||||
.bin_strict_ne,
|
||||
.bin_comma,
|
||||
=> {
|
||||
stack.append(.{ .bin = left_bin }) catch bun.outOfMemory();
|
||||
left = left_bin.left;
|
||||
},
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
|
||||
// Ride the stack downwards
|
||||
var i = stack.items.len;
|
||||
var result = simplifyUnusedExpr(p, left) orelse Expr.empty;
|
||||
while (i > stack_bottom) {
|
||||
i -= 1;
|
||||
const top = stack.items[i];
|
||||
const visited_right = simplifyUnusedExpr(p, top.bin.right) orelse Expr.empty;
|
||||
result = result.joinWithComma(visited_right, p.allocator);
|
||||
}
|
||||
|
||||
return if (result.isMissing()) Expr.empty else result;
|
||||
}
|
||||
|
||||
fn findIdentifiers(binding: Binding, decls: *std.ArrayList(G.Decl)) void {
|
||||
switch (binding.data) {
|
||||
.b_identifier => {
|
||||
@@ -2560,7 +2607,7 @@ const AsyncPrefixExpression = enum(u2) {
|
||||
is_async,
|
||||
is_await,
|
||||
|
||||
const map = std.StaticStringMap(AsyncPrefixExpression).initComptime(.{
|
||||
const map = bun.ComptimeStringMap(AsyncPrefixExpression, .{
|
||||
.{ "yield", .is_yield },
|
||||
.{ "await", .is_await },
|
||||
.{ "async", .is_async },
|
||||
@@ -3340,10 +3387,25 @@ pub const Parser = struct {
|
||||
|
||||
defer p.lexer.deinit();
|
||||
|
||||
var binary_expression_stack_heap = std.heap.stackFallback(1024, bun.default_allocator);
|
||||
p.binary_expression_stack = std.ArrayList(ParserType.BinaryExpressionVisitor).init(binary_expression_stack_heap.get());
|
||||
var binary_expression_stack_heap = std.heap.stackFallback(42 * @sizeOf(ParserType.BinaryExpressionVisitor), bun.default_allocator);
|
||||
p.binary_expression_stack = std.ArrayList(ParserType.BinaryExpressionVisitor).initCapacity(
|
||||
binary_expression_stack_heap.get(),
|
||||
41, // one less in case of unlikely alignment between the stack buffer and reality
|
||||
) catch unreachable; // stack allocation cannot fail
|
||||
defer p.binary_expression_stack.clearAndFree();
|
||||
|
||||
var binary_expression_simplify_stack_heap = std.heap.stackFallback(48 * @sizeOf(SideEffects.BinaryExpressionSimplifyVisitor), bun.default_allocator);
|
||||
p.binary_expression_simplify_stack = std.ArrayList(SideEffects.BinaryExpressionSimplifyVisitor).initCapacity(
|
||||
binary_expression_simplify_stack_heap.get(),
|
||||
47,
|
||||
) catch unreachable; // stack allocation cannot fail
|
||||
defer p.binary_expression_simplify_stack.clearAndFree();
|
||||
|
||||
if (Environment.allow_assert) {
|
||||
bun.assert(binary_expression_stack_heap.fixed_buffer_allocator.ownsPtr(@ptrCast(p.binary_expression_stack.items)));
|
||||
bun.assert(binary_expression_simplify_stack_heap.fixed_buffer_allocator.ownsPtr(@ptrCast(p.binary_expression_simplify_stack.items)));
|
||||
}
|
||||
|
||||
// defer {
|
||||
// if (p.allocated_names_pool) |pool| {
|
||||
// pool.data = p.allocated_names;
|
||||
@@ -5218,7 +5280,9 @@ fn NewParser_(
|
||||
|
||||
const_values: js_ast.Ast.ConstValuesMap = .{},
|
||||
|
||||
binary_expression_stack: std.ArrayList(BinaryExpressionVisitor) = undefined,
|
||||
// These are backed by stack fallback allocators in _parse, and are uninitialized until then.
|
||||
binary_expression_stack: ListManaged(BinaryExpressionVisitor) = undefined,
|
||||
binary_expression_simplify_stack: ListManaged(SideEffects.BinaryExpressionSimplifyVisitor) = undefined,
|
||||
|
||||
/// We build up enough information about the TypeScript namespace hierarchy to
|
||||
/// be able to resolve scope lookups and property accesses for TypeScript enum
|
||||
@@ -7444,7 +7508,7 @@ fn NewParser_(
|
||||
// "(1, 2)" => "2"
|
||||
// "(sideEffects(), 2)" => "(sideEffects(), 2)"
|
||||
if (p.options.features.minify_syntax) {
|
||||
e_.left = SideEffects.simpifyUnusedExpr(p, e_.left) orelse return e_.right;
|
||||
e_.left = SideEffects.simplifyUnusedExpr(p, e_.left) orelse return e_.right;
|
||||
}
|
||||
},
|
||||
.bin_loose_eq => {
|
||||
@@ -17327,7 +17391,7 @@ fn NewParser_(
|
||||
p.is_control_flow_dead = old;
|
||||
|
||||
if (side_effects.side_effects == .could_have_side_effects) {
|
||||
return Expr.joinWithComma(SideEffects.simpifyUnusedExpr(p, e_.test_) orelse p.newExpr(E.Missing{}, e_.test_.loc), e_.yes, p.allocator);
|
||||
return Expr.joinWithComma(SideEffects.simplifyUnusedExpr(p, e_.test_) orelse p.newExpr(E.Missing{}, e_.test_.loc), e_.yes, p.allocator);
|
||||
}
|
||||
|
||||
// "(1 ? fn : 2)()" => "fn()"
|
||||
@@ -17348,7 +17412,7 @@ fn NewParser_(
|
||||
|
||||
// "(a, false) ? b : c" => "a, c"
|
||||
if (side_effects.side_effects == .could_have_side_effects) {
|
||||
return Expr.joinWithComma(SideEffects.simpifyUnusedExpr(p, e_.test_) orelse p.newExpr(E.Missing{}, e_.test_.loc), e_.no, p.allocator);
|
||||
return Expr.joinWithComma(SideEffects.simplifyUnusedExpr(p, e_.test_) orelse p.newExpr(E.Missing{}, e_.test_.loc), e_.no, p.allocator);
|
||||
}
|
||||
|
||||
// "(1 ? fn : 2)()" => "fn()"
|
||||
@@ -18671,7 +18735,7 @@ fn NewParser_(
|
||||
for (props) |prop| {
|
||||
const key = prop.key.?.data.e_string.string(p.allocator) catch unreachable;
|
||||
const visited_value = p.visitExpr(prop.value.?);
|
||||
const value = SideEffects.simpifyUnusedExpr(p, visited_value) orelse visited_value;
|
||||
const value = SideEffects.simplifyUnusedExpr(p, visited_value) orelse visited_value;
|
||||
|
||||
// We are doing `module.exports = { ... }`
|
||||
// lets rewrite it to a series of what will become export assignments
|
||||
@@ -19481,7 +19545,7 @@ fn NewParser_(
|
||||
}
|
||||
|
||||
// simplify unused
|
||||
data.value = SideEffects.simpifyUnusedExpr(p, data.value) orelse return;
|
||||
data.value = SideEffects.simplifyUnusedExpr(p, data.value) orelse return;
|
||||
|
||||
if (comptime FeatureFlags.unwrap_commonjs_to_esm) {
|
||||
if (is_top_level) {
|
||||
@@ -19693,7 +19757,7 @@ fn NewParser_(
|
||||
if (data.no == null or !SideEffects.shouldKeepStmtInDeadControlFlow(p, data.no.?, p.allocator)) {
|
||||
if (effects.side_effects == .could_have_side_effects) {
|
||||
// Keep the condition if it could have side effects (but is still known to be truthy)
|
||||
if (SideEffects.simpifyUnusedExpr(p, data.test_)) |test_| {
|
||||
if (SideEffects.simplifyUnusedExpr(p, data.test_)) |test_| {
|
||||
stmts.append(p.s(S.SExpr{ .value = test_ }, test_.loc)) catch unreachable;
|
||||
}
|
||||
}
|
||||
@@ -19707,7 +19771,7 @@ fn NewParser_(
|
||||
if (!SideEffects.shouldKeepStmtInDeadControlFlow(p, data.yes, p.allocator)) {
|
||||
if (effects.side_effects == .could_have_side_effects) {
|
||||
// Keep the condition if it could have side effects (but is still known to be truthy)
|
||||
if (SideEffects.simpifyUnusedExpr(p, data.test_)) |test_| {
|
||||
if (SideEffects.simplifyUnusedExpr(p, data.test_)) |test_| {
|
||||
stmts.append(p.s(S.SExpr{ .value = test_ }, test_.loc)) catch unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ pub const Editor = enum(u8) {
|
||||
const StringMap = std.EnumMap(Editor, string);
|
||||
const StringArrayMap = std.EnumMap(Editor, []const [:0]const u8);
|
||||
|
||||
const name_map = std.StaticStringMap(Editor).initComptime(.{
|
||||
const name_map = bun.ComptimeStringMap(Editor, .{
|
||||
.{ "sublime", .sublime },
|
||||
.{ "subl", .sublime },
|
||||
.{ "vscode", .vscode },
|
||||
|
||||
@@ -298,7 +298,7 @@ pub const ExternalModules = struct {
|
||||
"zlib",
|
||||
};
|
||||
|
||||
pub const NodeBuiltinsMap = std.StaticStringMap(void).initComptime(.{
|
||||
pub const NodeBuiltinsMap = bun.ComptimeStringMap(void, .{
|
||||
.{ "_http_agent", {} },
|
||||
.{ "_http_client", {} },
|
||||
.{ "_http_common", {} },
|
||||
@@ -370,7 +370,7 @@ pub const ModuleType = enum {
|
||||
cjs,
|
||||
esm,
|
||||
|
||||
pub const List = std.StaticStringMap(ModuleType).initComptime(.{
|
||||
pub const List = bun.ComptimeStringMap(ModuleType, .{
|
||||
.{ "commonjs", ModuleType.cjs },
|
||||
.{ "module", ModuleType.esm },
|
||||
});
|
||||
|
||||
@@ -67,7 +67,7 @@ pub const TSConfigJSON = struct {
|
||||
remove,
|
||||
invalid,
|
||||
|
||||
pub const List = std.StaticStringMap(ImportsNotUsedAsValue).initComptime(.{
|
||||
pub const List = bun.ComptimeStringMap(ImportsNotUsedAsValue, .{
|
||||
.{ "preserve", .preserve },
|
||||
.{ "error", .err },
|
||||
.{ "remove", .remove },
|
||||
|
||||
BIN
test/bun.lockb
BIN
test/bun.lockb
Binary file not shown.
@@ -1301,6 +1301,14 @@ describe("bundler", () => {
|
||||
"-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 /* g */",
|
||||
],
|
||||
});
|
||||
// Stack overflow possibility
|
||||
itBundled("edgecase/AwsCdkLib", {
|
||||
files: {
|
||||
"entry.js": `import * as aws from ${JSON.stringify(require.resolve("aws-cdk-lib"))}; aws;`,
|
||||
},
|
||||
target: "bun",
|
||||
run: true,
|
||||
});
|
||||
|
||||
// TODO(@paperdave): test every case of this. I had already tested it manually, but it may break later
|
||||
const requireTranspilationListESM = [
|
||||
|
||||
63
test/js/bun/http/form-data-set-append.test.js
Normal file
63
test/js/bun/http/form-data-set-append.test.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { expect, test } from "bun:test";
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/12325
|
||||
|
||||
test("formdata set with File works as expected", async () => {
|
||||
const expected = ["617580375", "text-notes1.txt"];
|
||||
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch: async req => {
|
||||
const data = await req.formData();
|
||||
const chat_id = data.get("chat_id");
|
||||
const document = data.get("document");
|
||||
expect(chat_id).toEqual(expected[0]);
|
||||
expect(document.name).toEqual(expected[1]);
|
||||
return new Response("");
|
||||
},
|
||||
});
|
||||
|
||||
async function sendDocument(body) {
|
||||
const response = await fetch(server.url, {
|
||||
method: "POST",
|
||||
body: body,
|
||||
});
|
||||
const text = await response.text();
|
||||
return text;
|
||||
}
|
||||
|
||||
const formDataSet = new FormData();
|
||||
formDataSet.set("chat_id", expected[0]);
|
||||
formDataSet.set("document", new File(["some text notes 1"], expected[1]));
|
||||
await sendDocument(formDataSet);
|
||||
});
|
||||
|
||||
test("formdata apppend with File works as expected", async () => {
|
||||
const expected = ["617580376", "text-notes2.txt"];
|
||||
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
fetch: async req => {
|
||||
const data = await req.formData();
|
||||
const chat_id = data.get("chat_id");
|
||||
const document = data.get("document");
|
||||
expect(chat_id).toEqual(expected[0]);
|
||||
expect(document.name).toEqual(expected[1]);
|
||||
return new Response("");
|
||||
},
|
||||
});
|
||||
|
||||
async function sendDocument(body) {
|
||||
const response = await fetch(server.url, {
|
||||
method: "POST",
|
||||
body: body,
|
||||
});
|
||||
const text = await response.text();
|
||||
return text;
|
||||
}
|
||||
|
||||
const formDataSet = new FormData();
|
||||
formDataSet.append("chat_id", expected[0]);
|
||||
formDataSet.append("document", new File(["some text notes 2"], expected[1]));
|
||||
await sendDocument(formDataSet);
|
||||
});
|
||||
52
test/js/node/timers.promises/timers.promises.test.ts
Normal file
52
test/js/node/timers.promises/timers.promises.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { describe, test, it, expect } from "bun:test";
|
||||
import { setTimeout, setImmediate } from "node:timers/promises";
|
||||
|
||||
describe("setTimeout", () => {
|
||||
it("abort() does not emit global error", async () => {
|
||||
let unhandledRejectionCaught = false;
|
||||
|
||||
const catchUnhandledRejection = () => {
|
||||
unhandledRejectionCaught = true;
|
||||
};
|
||||
process.on("unhandledRejection", catchUnhandledRejection);
|
||||
|
||||
const c = new AbortController();
|
||||
|
||||
global.setTimeout(() => c.abort());
|
||||
|
||||
await setTimeout(100, undefined, { signal: c.signal }).catch(() => "aborted");
|
||||
|
||||
// let unhandledRejection to be fired
|
||||
await setTimeout(100);
|
||||
|
||||
process.off("unhandledRejection", catchUnhandledRejection);
|
||||
|
||||
expect(c.signal.aborted).toBe(true);
|
||||
expect(unhandledRejectionCaught).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setImmediate", () => {
|
||||
it("abort() does not emit global error", async () => {
|
||||
let unhandledRejectionCaught = false;
|
||||
|
||||
const catchUnhandledRejection = () => {
|
||||
unhandledRejectionCaught = true;
|
||||
};
|
||||
process.on("unhandledRejection", catchUnhandledRejection);
|
||||
|
||||
const c = new AbortController();
|
||||
|
||||
global.setImmediate(() => c.abort());
|
||||
|
||||
await setImmediate(undefined, { signal: c.signal }).catch(() => "aborted");
|
||||
|
||||
// let unhandledRejection to be fired
|
||||
await setTimeout(100);
|
||||
|
||||
process.off("unhandledRejection", catchUnhandledRejection);
|
||||
|
||||
expect(c.signal.aborted).toBe(true);
|
||||
expect(unhandledRejectionCaught).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -14,6 +14,7 @@
|
||||
"@resvg/resvg-js": "2.4.1",
|
||||
"@swc/core": "1.3.38",
|
||||
"@types/ws": "8.5.10",
|
||||
"aws-cdk-lib": "2.148.0",
|
||||
"axios": "1.6.8",
|
||||
"body-parser": "1.20.2",
|
||||
"comlink": "4.4.1",
|
||||
|
||||
Reference in New Issue
Block a user