Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions packages/bun-uws/src/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,19 +418,16 @@ struct TemplatedApp {

/* Terminate on misleading idleTimeout values */
if (behavior.idleTimeout && behavior.idleTimeout < 8) {
std::cerr << "Error: idleTimeout must be either 0 or greater than 8!" << std::endl;
std::terminate();
}

/* Maximum idleTimeout is 16 minutes */
if (behavior.idleTimeout > 240 * 4) {
std::cerr << "Error: idleTimeout must not be greater than 960 seconds!" << std::endl;
std::terminate();
}

/* Maximum maxLifetime is 4 hours */
if (behavior.maxLifetime > 240) {
std::cerr << "Error: maxLifetime must not be greater than 240 minutes!" << std::endl;
std::terminate();
}

Expand Down
1 change: 0 additions & 1 deletion packages/bun-uws/src/AsyncSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
* to signal error with -1 (which is how the entire UNIX syscalling is built). */

#include <cstring>
#include <iostream>

#include "libusockets.h"
#include "bun-usockets/src/internal/internal.h"
Expand Down
3 changes: 0 additions & 3 deletions packages/bun-uws/src/HttpContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#include <string>
#include <map>
#include <string_view>
#include <iostream>
#include "MoveOnlyFunction.h"
#include "HttpParser.h"
#include <span>
Expand Down Expand Up @@ -465,8 +464,6 @@ struct HttpContext {

/* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted && !httpResponseData->socketData) {
/* Throw exception here? */
std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl;
std::terminate();
}

Expand Down
1 change: 0 additions & 1 deletion packages/bun-uws/src/HttpRouter.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
#include <memory>
#include <utility>
#include <span>
#include <iostream>

#include "MoveOnlyFunction.h"

Expand Down
1 change: 0 additions & 1 deletion packages/bun-uws/src/Loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

#include "LoopData.h"
#include <libusockets.h>
#include <iostream>
#include "AsyncSocket.h"

extern "C" int bun_is_exiting();
Expand Down
5 changes: 0 additions & 5 deletions packages/bun-uws/src/TopicTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#pragma once
#include <map>
#include <list>
#include <iostream>
#include <unordered_set>
#include <utility>
#include <memory>
Expand Down Expand Up @@ -106,9 +105,7 @@ struct TopicTree {
std::vector<T> outgoingMessages;

void checkIteratingSubscriber(Subscriber *s) {
/* Notify user that they are doing something wrong here */
if (iteratingSubscriber == s) {
std::cerr << "Error: WebSocket must not subscribe or unsubscribe to topics while iterating its topics!" << std::endl;
std::terminate();
}
}
Comment thread
robobun marked this conversation as resolved.
Expand Down Expand Up @@ -163,7 +160,6 @@ struct TopicTree {

/* Subscribe fails if we already are subscribed */
Topic *subscribe(Subscriber *s, std::string_view topic) {
/* Notify user that they are doing something wrong here */
checkIteratingSubscriber(s);

/* Lookup or create new topic */
Expand All @@ -187,7 +183,6 @@ struct TopicTree {

/* Returns ok, last, newCount */
std::tuple<bool, bool, int> unsubscribe(Subscriber *s, std::string_view topic) {
/* Notify user that they are doing something wrong here */
checkIteratingSubscriber(s);

/* Lookup topic */
Expand Down
7 changes: 0 additions & 7 deletions scripts/build/deps/webkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@
* for local mode. Override via `--webkit-version=<hash>` to test a branch.
* From https://github.com/oven-sh/WebKit releases.
*/
// oven-sh/WebKit main: macOS + Windows artifacts cross-compiled on Linux,
// -lto variants built with ThinLTO (per-module summaries for cross-language
// importing), every x64 at the nehalem floor (no separate -baseline variant),
// typed-array constructor ClassInfo kept address-unique under LTO, the
// Windows ICU data table filtered + per-item zstd compressed, and Windows
// unwind info (RtlAddGrowableFunctionTable) registered for the fixed JIT
// pool (LLInt pending offlineasm .seh_* emission).
export const WEBKIT_VERSION = "a40d462206e1caf8388062120acde61e37a4ae7d";
Comment thread
robobun marked this conversation as resolved.
Outdated

/**
Expand Down
6 changes: 6 additions & 0 deletions scripts/build/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1512,6 +1512,12 @@ export const stripFlags: Flag[] = [
export function bunIncludes(cfg: Config): string[] {
const { cwd, codegenDir, vendorDir } = cfg;
const includes: string[] = [
// Release builds shadow <iostream> with a #error shim. A single <iostream>
// include anywhere (our headers, bun-uws, or a WebKit header pulled into a
// Bun TU) drags libstdc++'s globals_io.o into the link and runs
// std::ios_base::Init + the full locale facet set before main. Debug builds
// keep the real header available for ad-hoc printf-debugging.
...(cfg.release ? [join(cwd, "src/banned-includes")] : []),
join(cwd, "packages"),
join(cwd, "packages/bun-usockets"),
join(cwd, "packages/bun-usockets/src"),
Expand Down
27 changes: 27 additions & 0 deletions src/banned-includes/iostream
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This directory is placed first on the -I search path for Bun's C++ compile
// (release profile), so `#include <iostream>` resolves here instead of the
// toolchain header.
//
// <iostream> is unlike <ostream>/<istream>/<sstream>/<fstream>: on libstdc++
// it emits a reference to std::ios_base_library_init (or, on configurations
// without the init-priority attribute, a static `std::ios_base::Init __ioinit`
// object) in every translation unit that includes it. One such reference
// anywhere in the link pulls libstdc++'s globals_io.o in, whose
// _GLOBAL__sub_I.00090_globals_io.cc static initializer constructs
// cin/cout/cerr/clog and their wchar_t siblings before main. That in turn
// references the full std::locale facet set (ctype / numpunct / moneypunct /
// timepunct / messages, for both char and wchar_t), so roughly fifty libstdc++
// functions run on every Bun process start.
//
// Bun never touches C++ iostreams at runtime. Use fputs/fprintf for error
// output, or WTF's dataLog()/PrintStream in JSC-adjacent code.
//
// Because Bun's own headers include wtf/SIMDUTF.h (and others) from the
// WebKit prebuilt, this shim also catches a WebKit header that starts
// including <iostream>: it will fail Bun's release build rather than silently
// regressing startup.
#ifndef BUN_ALLOW_IOSTREAM
#error "<iostream> is banned in Bun release builds: it drags std::ios_base::Init and the full std::locale facet set into pre-main startup. Use <cstdio> fputs/fprintf for stderr output. See src/banned-includes/iostream."
#else
#include_next <iostream>
#endif
51 changes: 51 additions & 0 deletions test/internal/source-lints/no-iostream-include.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Glob } from "bun";
import { expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import path from "node:path";

// <iostream> is unique among the C++ stream headers: on libstdc++ it emits a
// reference to std::ios_base_library_init in every TU that includes it, which
// forces libstdc++'s globals_io.o into the link. That object's
// _GLOBAL__sub_I.00090_globals_io.cc initializer constructs cin/cout/cerr/clog
// (and the wchar_t variants) before main, dragging the full std::locale facet
// set (ctype/numpunct/moneypunct/timepunct/messages for char and wchar_t) into
// every Bun process startup. Bun never touches C++ iostreams at runtime.
//
// <ostream>, <istream>, <sstream> and <fstream> are fine: they declare the
// stream types but do not emit the static Init object. If you need to print to
// stderr from C++, use fputs/fprintf.
//
// The upstream source of the original leak was the vendored simdutf header
// inside WebKit (Source/WTF/wtf/simdutf/simdutf_impl.h); that is handled by
// the WebKit pin. This test guards Bun's own compiled C++ so the initializer
// cannot creep back in through packages/ or src/.
test("C++ sources compiled into Bun do not include <iostream>", async () => {
const repoRoot = path.resolve(import.meta.dir, "..", "..", "..");

const roots = ["src", "packages/bun-uws", "packages/bun-usockets"];
// sizegen.cpp is a build-time code generator, not linked into the bun binary.
const allowlist = new Set(["src/jsc/headergen/sizegen.cpp"]);

const iostreamInclude = /^\s*#\s*include\s*<iostream>/m;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const violations: string[] = [];

for (const root of roots) {
let scanned = 0;
const glob = new Glob("**/*.{h,hpp,hxx,cpp,cc,cxx}");
for await (const rel of glob.scan({ cwd: path.join(repoRoot, root) })) {
scanned++;
const relFromRepo = path.join(root, rel).replaceAll("\\", "/");
if (allowlist.has(relFromRepo)) continue;
const source = readFileSync(path.join(repoRoot, root, rel), "utf8");
if (iostreamInclude.test(source)) {
violations.push(relFromRepo);
}
}
// Guard against repoRoot resolving wrong (test file moved) or a scanned
// root going away, which would make the ban below pass vacuously.
expect(scanned).toBeGreaterThan(0);
}

violations.sort();
expect(violations).toEqual([]);
Comment thread
robobun marked this conversation as resolved.
});
Loading