Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
14 changes: 7 additions & 7 deletions .github/tsan_suppressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
#
# These suppress data races internal to third-party libraries that we cannot fix.
# TSan on macOS (JavaScriptCore via Xcode) passes clean — these suppressions are
# only needed for the Ubuntu JSC build (libjavascriptcoregtk).
# only needed for the Ubuntu Bun JSC build.

# JavaScriptCore internal races in libjavascriptcoregtk.
# Races manifest in TSan interceptors (free/malloc/memcpy/close) called from
# JSC's JIT worker threads. function-name suppressions are needed because the
# top frame is a libc interceptor attributed to UnitTests, not libjavascriptcoregtk.
called_from_lib:libjavascriptcoregtk
# Bun's Linux JavaScriptCore is statically linked into the final executable, so
# a called_from_lib suppression cannot identify it. Match the interceptor frames
# previously observed on JSC's JIT worker threads instead.
# Races manifest in TSan interceptors (free/memcpy/close) called from
# JSC's JIT worker threads.
race:free
race:close
race:memcpy

# JSC signal handler that doesn't save/restore errno
signal:libjavascriptcoregtk
signal:WTF::jscSignalHandler
54 changes: 48 additions & 6 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ on:
cc:
required: false
type: string
default: gcc
default: clang-21
cxx:
required: false
type: string
default: g++
default: clang++-21
js-engine:
required: false
type: string
Expand All @@ -26,8 +26,10 @@ on:

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 15
# Bun's JSC requires GLIBCXX_3.4.30. Building on 22.04 preserves that practical
# runtime floor instead of accidentally importing newer glibc symbols.
runs-on: ubuntu-22.04
timeout-minutes: 30
env:
CC: ${{ inputs.cc }}
CXX: ${{ inputs.cxx }}
Expand All @@ -37,7 +39,22 @@ jobs:
- name: Install packages
run: |
sudo apt-get update
sudo apt-get install -y libjavascriptcoregtk-4.1-dev libcurl4-openssl-dev ninja-build clang
sudo apt-get install -y --no-install-recommends ca-certificates gnupg libcurl4-openssl-dev libstdc++-12-dev ninja-build wget

- name: Install LLVM 21 for Bun JavaScriptCore LTO
if: inputs.cc == 'clang-21' || inputs.cxx == 'clang++-21'
run: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/apt.llvm.org.gpg
echo "deb [signed-by=/usr/share/keyrings/apt.llvm.org.gpg] https://apt.llvm.org/jammy/ llvm-toolchain-jammy-21 main" | sudo tee /etc/apt/sources.list.d/llvm21.list
sudo apt-get update
sudo apt-get install -y --no-install-recommends clang-21 lld-21

- name: Install Clang sanitizer runtimes
if: (inputs.enable-sanitizers || inputs.enable-thread-sanitizer) && (inputs.cc == 'clang-21' || inputs.cxx == 'clang++-21')
run: sudo apt-get install -y --no-install-recommends libclang-rt-21-dev

- name: Reclaim package indexes
run: sudo rm -rf /var/lib/apt/lists/*

- name: Configure CMake
run: |
Expand All @@ -52,6 +69,32 @@ jobs:
- name: Build
run: ninja -C Build/ubuntu

- name: Audit statically linked Bun JavaScriptCore runtime
if: inputs.js-engine == '' || inputs.js-engine == 'JavaScriptCore'
run: |
jsc_archive=Build/ubuntu/Core/Node-API/libjsruntimehost-jsc.a
executable=Build/ubuntu/Tests/UnitTests/UnitTests
test -f "$jsc_archive"
test -x "$executable"
readelf --file-header "$executable" 2>/dev/null | grep 'DYN (Position-Independent Executable file)'
readelf --dynamic "$executable" 2>/dev/null | grep -E 'FLAGS_1.*PIE'
readelf --syms "$executable" 2>/dev/null | grep JSCBunInitialize

ldd_output="$(ldd -r "$executable" 2>&1)"
printf '%s\n' "$ldd_output"
if grep -q 'undefined symbol' <<<"$ldd_output"; then
exit 1
fi
if grep -q 'javascriptcore' <<<"${ldd_output,,}"; then
exit 1
fi

max_glibc="$(readelf --version-info "$executable" 2>/dev/null | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -n 1)"
max_glibcxx="$(readelf --version-info "$executable" 2>/dev/null | grep -o 'GLIBCXX_[0-9.]*' | sort -Vu | tail -n 1)"
echo "Maximum required symbol versions: $max_glibc, $max_glibcxx"
dpkg --compare-versions "${max_glibc#GLIBC_}" le 2.35
dpkg --compare-versions "${max_glibcxx#GLIBCXX_}" le 3.4.30

- name: Run Tests
working-directory: Build/ubuntu/Tests/UnitTests
run: ./UnitTests
Expand All @@ -64,4 +107,3 @@ jobs:
# on the mutator without cross-thread signals. macOS JSC uses Mach
# thread_suspend() and is unaffected.
JSC_useConcurrentGC: ${{ inputs.enable-thread-sanitizer && '0' || '' }}

16 changes: 4 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,30 +128,22 @@ jobs:
simulator: 'iPhone 17'

# Linux
Ubuntu_gcc:
Ubuntu_JSC_LTO:
uses: ./.github/workflows/build-linux.yml

Ubuntu_clang:
uses: ./.github/workflows/build-linux.yml
with:
cc: clang
cxx: clang++

Ubuntu_QuickJS:
Ubuntu_QuickJS_gcc:
uses: ./.github/workflows/build-linux.yml
with:
cc: gcc
cxx: g++
js-engine: QuickJS

Ubuntu_Sanitizers_clang:
uses: ./.github/workflows/build-linux.yml
with:
cc: clang
cxx: clang++
enable-sanitizers: true

Ubuntu_ThreadSanitizer_clang:
uses: ./.github/workflows/build-linux.yml
with:
cc: clang
cxx: clang++
enable-thread-sanitizer: true
19 changes: 18 additions & 1 deletion Core/AppRuntime/Source/AppRuntime_JavaScriptCore.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
#include "AppRuntime.h"
#include <napi/env.h>

#if defined(JSR_USE_BUN_JSC)
extern "C" void JSCBunInitialize();
#endif

namespace Babylon
{
void AppRuntime::RunEnvironmentTier(const char*)
{
#if defined(JSR_USE_BUN_JSC)
JSCBunInitialize();
#endif
auto globalContext = JSGlobalContextCreateInGroup(nullptr, nullptr);

#if __APPLE__
Expand All @@ -18,7 +25,17 @@ namespace Babylon

Run(env);

JSGlobalContextRelease(globalContext);
#if defined(JSR_USE_BUN_JSC)
{
// Scope this holder to the host's context reference. Keeping it alive across Detach
// delays VM destruction until after napi_env has been deleted, but JSC's last-chance
// finalizers are allowed to call Node-API with that environment.
Napi::ContextLock contextLock{env};
#endif
JSGlobalContextRelease(globalContext);
#if defined(JSR_USE_BUN_JSC)
}
#endif

// Detach must come after JSGlobalContextRelease since it triggers finalizers which require env.
Napi::Detach(env);
Expand Down
Loading