Samsung/escargot

Escargot is a lightweight JavaScript engine designed specifically for resource-constrained environments.

C++

363

1,913 commits

updated Sep 21, 2026

See the code

README

Escargot

License GitHub release (latestSemVer) Actions Status Coverity Scan Build Status codecov

Escargot is an embeddable JavaScript engine developed by Samsung. It is designed for products that need more than a minimal scripting runtime but still have to manage CPU, memory, and platform constraints carefully.

Rather than minimizing the engine binary at all costs, Escargot balances runtime memory efficiency, interpreter performance, and modern JavaScript functionality. Its C++ implementation and built-in features can produce a larger native binary than ultra-minimal engines, but the engine is designed to use memory efficiently as applications and object graphs grow. This makes it a strong fit for substantial JavaScript workloads on embedded Linux, mobile, IoT, and other resource-aware products.

Escargot compiles JavaScript to bytecode and executes it without a JIT. The interpreter-based architecture avoids executable-memory requirements and provides predictable deployment characteristics, while the standards implementation supplies the language and internationalization features expected by real products.

Key capabilities include:

  • Modern JavaScript and internationalization: broad ECMAScript support, including the ECMAScript 2026 specification, plus Intl (ECMA-402) and Temporal backed by ICU.
  • Efficient execution of growing applications: runtime data structures and interpreter optimizations target good performance without allowing memory overhead to scale unnecessarily with larger programs.
  • Product-oriented portability: Linux, Android, Tizen, macOS, iOS, Windows, and reference bare-metal/RTOS ports are supported without depending on JIT availability.
  • Flexible feature footprint: WebAssembly, threading, code cache, debugger, ICU, and small-device optimizations can be selected for the target instead of imposing one fixed runtime configuration.
  • System-provided ICU option: instead of vendoring its own ICU, Escargot can link against the ICU already installed on the target OS, trading some Intl/Temporal functionality for a smaller binary.
  • Multiple embedding surfaces: applications can use the public C++ API in src/api/EscargotPublic.h, or enable the Node-API v10 layer and C-style hosting APIs documented in docs/n-api.md.
  • Deployment-ready outputs: CMake can produce static or shared libraries, a command-line shell, and C++ tests. Memory is managed by the Boehm-Demers-Weiser conservative garbage collector in third_party/GCutil.

Escargot powers services in Samsung products and is available as an LGPL-2.1 open source project for other embedders and contributors.

Contents 📋

Building 🛠️

Supported Platforms and Architectures

Architecture names below use x86 for 32-bit Intel, x64 for x86-64, arm for 32-bit ARM, and aarch64/arm64 for 64-bit ARM.

PlatformArchitecturesStatus
Linuxx86, x64, arm, aarch64, riscv64Supported Linux platform. Ubuntu is used by CI and in the package-install example; it is not an OS restriction.
macOSx64, aarch64Supported on both Intel and Apple Silicon.
iOS / iPadOSaarch64Simulator and device builds are supported.
Windowsx86, x64, arm64Supported; WebAssembly is currently unavailable on arm64.
Androidx86, x64, arm, aarch64Supported across the Android ABIs.
Tizenx86, x64, arm, aarch64, riscv64
Bare-metal / RTOSarm (Cortex-M)FreeRTOS and NuttX reference ports are provided.

Build Options

Pass these options when configuring with CMake.

FlagDescriptionValueDefault
-DESCARGOT_BUILD_SHARED_LIBSBuild shared libraryON/OFFOFF
-DESCARGOT_BUILD_GC_SHARED_LIBSBuild GCutil as a shared libraryON/OFFOFF
-DESCARGOT_ENABLE_SHELLBuild the Escargot shell (-DENABLE_SHELL remains a legacy alias)ON/OFFON, except OFF when ESCARGOT_NAPI is ON
-DESCARGOT_BUILD_CCTESTBuild the C++ testsON/OFFOFF
-DESCARGOT_LIBICU_SUPPORTInclude libicu libraryON/OFFON, except OFF on bare-metal
-DESCARGOT_WASMEnable WebAssembly supportON/OFFOFF
-DESCARGOT_CODE_CACHEEnable code cacheON/OFFOFF
-DESCARGOT_TCOEnable tail call optimizationON/OFFOFF
-DESCARGOT_THREADINGEnable threading features (e.g. Atomics, SharedArrayBuffer)ON/OFFON, except OFF on bare-metal
-DESCARGOT_TLS_ACCESS_BY_ADDRESSEnable thread local storage access optimization (offset)ON/OFFOFF everywhere (safety-first; opt in manually on stable glibc-style targets)
-DESCARGOT_TLS_ACCESS_BY_PTHREAD_KEYEnable thread local storage access optimization (pthread_key)ON/OFFON when THREADING is ON and host is Android, otherwise OFF
-DESCARGOT_TEMPORALEnable Temporal support (requires ICU)ON/OFFON when LIBICU is ON, otherwise OFF
-DESCARGOT_SHADOWREALMEnable ShadowRealm supportON/OFFOFF
-DESCARGOT_SMALL_CONFIGEnable aggressive memory optimizations for tiny devicesON/OFFOFF
-DESCARGOT_EXPORT_ALLExport all symbols instead of the default curated public APION/OFFOFF
-DESCARGOT_TESTEnable additional features used only for testingON/OFFOFF
-DESCARGOT_DEBUGGEREnable Debug serverON/OFFOFF
-DESCARGOT_NAPIEnable Node-API (N-API) support and C-style hosting APIsON/OFFOFF
Advanced / developer-only options (profiling, sanitizers, internal knobs)
FlagDescriptionValueDefault
-DESCARGOT_ASANBuild with AddressSanitizerON/OFFOFF
-DESCARGOT_COVERAGEBuild with gcov/Codecov instrumentationON/OFFOFF
-DESCARGOT_DEPLOYBuild for deployment (set up RPATH for a bundled ICU)ON/OFFOFF
-DESCARGOT_LIBICU_SUPPORT_WITH_DLOPENLoad libicu at runtime via dlopen() instead of linking directlyON/OFFON, except OFF on macOS (dlopen-loaded ICU doesn't work correctly there), disallowed entirely on iOS, and OFF when ESCARGOT_LIBICU_SUPPORT_VENDORED is ON
-DESCARGOT_LIBICU_SUPPORT_VENDOREDBuild/ship Escargot's own ICU instead of relying on a system-provided one (see "Vendored ICU" below)ON/OFFON on windows, macOS and iOS (the only ICU option there), OFF elsewhere (available on linux too)
-DESCARGOT_USE_EXTENDED_APIEnable the extended C++ API (FunctionTemplateRef, etc.)ON/OFFON when NAPI is ON, otherwise OFF
-DESCARGOT_USE_CUSTOM_LOGGINGUse a custom logging backend instead of the host's native log (e.g. dlog on Tizen)ON/OFFOFF
-DESCARGOT_YARR_START_CHAR_FILTEREnable the Yarr interpreter first-character prefilterON/OFFON
-DESCARGOT_TCO_DEBUGEnable extra tail-call-optimization debug checks (debug builds only, requires ESCARGOT_TCO)ON/OFFOFF
-DESCARGOT_PROFILE_BDWGCEnable bdwgc (Boehm GC) profilingON/OFFOFF
-DESCARGOT_MEM_STATSEnable memory usage statisticsON/OFFOFF
-DESCARGOT_VALGRINDBuild with Valgrind annotationsON/OFFOFF
-DESCARGOT_GOOGLE_PERFBuild with gperftools (Google Performance Tools) profilingON/OFFOFF
-DESCARGOT_BUILD_64BIT_FORCE_LARGEOn 64-bit targets, force full 64-bit pointers instead of 32-bit-in-64-bit compressionON/OFFON

Linux

Escargot supports Linux distributions generally. The commands below use Debian/Ubuntu package names because Ubuntu is used by CI; install the equivalent development packages on other distributions.

Debian/Ubuntu prerequisites:

sudo apt-get install build-essential cmake git libicu-dev pkg-config python3

Prerequisites for x86-64-to-x86 compilation:

sudo apt-get install gcc-multilib g++-multilib
sudo apt-get install libicu-dev:i386

Build Escargot:

git submodule update --init third_party
cmake -S . -B out -DCMAKE_BUILD_TYPE=Release -DESCARGOT_ENABLE_SHELL=ON
cmake --build out

# Run a JavaScript file with the shell produced by the default build.
./out/escargot path/to/script.js

The default configuration produces the static library out/libescargot.a and the out/escargot shell. Set ESCARGOT_BUILD_SHARED_LIBS=ON for a shared library, or ESCARGOT_ENABLE_SHELL=OFF when embedding the library without the command-line shell.

macOS

Install the Xcode Command Line Tools (or full Xcode), then install the remaining build prerequisites:

xcode-select --install
brew install cmake python

Build Escargot:

git submodule update --init third_party
cmake -S . -B out -DCMAKE_BUILD_TYPE=Release -DESCARGOT_ENABLE_SHELL=ON
cmake --build out

ICU is vendored by default on macOS (see "Vendored ICU" below) -- it's built from the third_party/icu submodule above and linked statically, so no Homebrew icu4c/pkg-config setup is needed for the default path. To opt back into a Homebrew/system-provided ICU instead:

brew install icu4c

# add icu path to pkg_config_path (x64)
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:$PKG_CONFIG_PATH"
# add icu path to pkg_config_path (arm64)
export PKG_CONFIG_PATH="/opt/homebrew/opt/icu4c/lib/pkgconfig:$PKG_CONFIG_PATH"

cmake -S . -B out -DCMAKE_BUILD_TYPE=Release \
    -DESCARGOT_LIBICU_SUPPORT_VENDORED=OFF \
    -DESCARGOT_ENABLE_SHELL=ON
cmake --build out

Android

Build prerequisites on Ubuntu:

sudo apt install openjdk-17-jdk # require java 17

Build Escargot using gradle:

git submodule update --init third_party
export ANDROID_SDK_ROOT=.... # set your android SDK root first
cd build/android/
./gradlew bundleReleaseAar # build escargot AAR
./gradlew bundleHostJar # bundle jar for host
./gradlew javadocJar # create java doc
./gradlew sourcesJar # create sources jar

./gradlew assembleDebug # build debug test shell
./gradlew :escargot:connectedDebugAndroidTest # run escargot-jni tests on android device
./gradlew :escargot:testDebugUnitTest # run escargot-jni tests on host

Tizen

Escargot is packaged and deployed in Tizen products. The RPM spec supports the Tizen package architectures x86, x64, arm, aarch64, and riscv64, and selects product profiles such as TV, mobile, wearable, and headless through the Tizen build environment.

For a reference GBS build, use the repository's Tizen profile:

git submodule update --init third_party/GCutil
gbs -c .github/workflows/gbs.conf build -A armv7l -P profile.tizen --define "enable_shell 1"

The public CI continuously validates the armv7 package build. Tizen product build and qualification flows use the same RPM packaging integration.

Bare-metal / RTOS

Escargot runs on bare-metal and RTOS targets with no OS underneath (no pthreads, no mmap, no filesystem). Specifying a bare-metal/RTOS target via CMAKE_SYSTEM_NAME (such as Generic, NuttX, FreeRTOS) automatically configures the engine side of this (-DOS_BAREMETAL=1 and friends, ICU/threading defaulted off):

cmake -DCMAKE_SYSTEM_NAME=Generic -DCMAKE_SYSTEM_PROCESSOR=arm ... /path/to/escargot

A full port additionally needs its own small CMake project for BDWGC (third_party/GCutil) and a PlatformRef implementation providing the RTOS's task stack bounds and tick source. See docs/porting/RTOS_PORTING_GUIDE.md for the full checklist and code contract, and samples/rtos/freertos/ for a complete, working in-tree sample (FreeRTOS / Cortex-M55, QEMU mps3-an547) — cross-compiled and boot-tested under QEMU by the RTOS-FreeRTOS CI job (.github/workflows/rtos-freertos.yml) whenever engine or sample sources change.

A second reference port, NuttX / Cortex-M55 (same QEMU target), is also in-tree: samples/rtos/nuttx/ has the escargot NSH app (interpreters-escargot/, meant to be dropped into your own NuttX+apps checkout's apps/interpreters/) and the out-of-tree CMake project that builds the engine for it (escargot-lib-cmake/). Unlike FreeRTOS-Kernel, NuttX itself isn't vendored as a submodule here (a NuttX app fundamentally needs a full NuttX+apps source tree, not a standalone library dependency) — CI-verified instead by the RTOS-NuttX job (.github/workflows/rtos-nuttx.yml), which checks out NuttX + its apps monorepo at pinned commits (cached across runs) and boot-tests the same way. Both ports' shared contract and checklist are in docs/porting/RTOS_PORTING_GUIDE.md.

Windows

Build from a Developer Command Prompt for Visual Studio with the Visual Studio CMake generator and MSBuild. The examples below use Visual Studio 2022 (-G "Visual Studio 17 2022"); substitute the generator name for another installed Visual Studio version (e.g. -G "Visual Studio 16 2019") if that's what you have.

Install the following:

  • Visual Studio's Desktop development with C++ workload, including MSVC v143 C++ build tools, CMake tools for Windows, and a Windows 10 or 11 SDK.
  • The MSVC target tools for the architecture you intend to build.
  • Python 3, available as python on PATH. Escargot uses it to generate Unicode tables during the build.
  • Git, for the source checkout, submodules, and vcpkg bootstrap.

Choose the matching Visual Studio platform and vcpkg triplet:

TargetCMake -A valuevcpkg tripletAdditional MSVC tools
x86Win32x86-windowsx86/x64 build tools
x64x64x64-windowsx86/x64 build tools
ARM64ARM64arm64-windowsARM64 build tools

ICU is supplied through vcpkg. The following x64 example can be adapted with the table above:

git clone --depth 1 --branch 2026.07.29 https://github.com/microsoft/vcpkg.git
call vcpkg\bootstrap-vcpkg.bat
vcpkg\vcpkg.exe install icu --triplet=x64-windows

git submodule update --init third_party

cmake -S . -B out -G "Visual Studio 17 2022" -A x64 ^
  -DICU_ROOT=vcpkg\installed\x64-windows ^
  -DESCARGOT_ENABLE_SHELL=ON
cmake --build out --config Release

For ARM64, substitute -A ARM64 and arm64-windows. Also pass -DESCARGOT_WASM=OFF: the vendored WABT dependency does not yet support MSVC/ARM64.

vcpkg\vcpkg.exe install icu --triplet=arm64-windows
cmake -S . -B out-arm64 -G "Visual Studio 17 2022" -A ARM64 ^
  -DICU_ROOT=vcpkg\installed\arm64-windows ^
  -DESCARGOT_ENABLE_SHELL=ON -DESCARGOT_WASM=OFF
cmake --build out-arm64 --config Release

The executable is written to out\Release\escargot.exe (or out-arm64\Release\escargot.exe). Copy the ICU DLLs it depends on from the matching vcpkg\installed\<triplet>\bin\ directory beside the executable when distributing it.

Use Windows' built-in ICU DLL (no vcpkg)

To avoid bundling ICU from vcpkg, explicitly select the Windows ICU path:

cmake -S . -B out -G "Visual Studio 17 2022" -A x64 ^
  -DESCARGOT_ENABLE_SHELL=ON ^
  -DESCARGOT_LIBICU_SUPPORT_VENDORED=OFF
cmake --build out --config Release

This links through the Windows SDK's icu.lib and uses the ICU DLL supplied by Windows at runtime, so there is no ICU_ROOT, vcpkg installation, or ICU DLL to copy beside escargot.exe. It requires a target with the built-in ICU available (Windows 10 version 1703 or later). Escargot also falls back to this runtime-ICU path when no vcpkg ICU is found, but passing -DESCARGOT_LIBICU_SUPPORT_VENDORED=OFF records the choice explicitly and avoids the configure-time fallback warning.

The Windows ICU version follows the operating system. Use the vcpkg path above when a pinned ICU version and identical Intl/Unicode behavior across machines are more important than avoiding the ICU deployment files.

iOS

ESCARGOT_HOST=ios cross-compiles Escargot from a macOS host to arm64 iOS. Which of Apple's two iOS SDKs is targeted is selected with -DCMAKE_OSX_SYSROOT:

-DCMAKE_OSX_SYSROOT=TargetStatus
iphonesimulatoriOS Simulator (arm64, i.e. an Apple Silicon build machine)Built and run in CI (build-test-on-ios-simulator-arm64, which runs Octane)
iphoneosReal iPhone/iPad hardware (arm64)Built and Mach-O-verified in CI (build-on-ios-device-arm64), never executed on physical hardware by this project -- see the caveats below

There is no separate "ipados" host: Apple ships one SDK/platform identifier ("iOS") and one arm64 sysroot/triple for both iPhone and iPad at the CMake/toolchain level. arm64 is the only supported architecture (armv7 devices predate every supported deployment target, and an x86_64 simulator would mean an Intel build machine).

Prerequisite: a full Xcode install (not just the Command Line Tools -- xcrun --sdk iphonesimulator --show-sdk-path, or --sdk iphoneos for a device build, must succeed).

ICU on iOS has exactly two supported configurations: vendored (the default; see "Vendored ICU" below) or off entirely (-DESCARGOT_LIBICU_SUPPORT=OFF). There is no system/pkg-config ICU dev package available on iOS, and dlopen-loading an arbitrary library is unavailable there too, so both of those other ICU paths are rejected with a FATAL_ERROR at configure time.

iOS Simulator

git submodule update --init third_party/GCutil third_party/icu # update submodules (+ vendored ICU source)

cmake -S . -B out \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=iphonesimulator \
    -DCMAKE_OSX_ARCHITECTURES=arm64 \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
    -DESCARGOT_ENABLE_SHELL=ON -DCMAKE_BUILD_TYPE=Release
cmake --build out

The resulting out/escargot is an arm64 Mach-O binary linked against the iphonesimulator SDK, and running it takes two steps -- neither of them optional, both confirmed against real-world reports of the same two failures (not guessed):

  1. Ad-hoc code-sign it. macOS on Apple Silicon enforces code signing on every arm64 executable, including plain command-line tools -- the linker only emits a minimal "linker-signed" signature by default, which recent macOS versions reject outright (Killed: 9) even though nothing else about the binary is wrong:
    codesign --sign - --force out/escargot   # ad-hoc signature, no identity/provisioning needed
    
  2. Run it inside a booted simulator device via simctl spawn, not by invoking it bare from Terminal. A simulator-platform Mach-O binary still needs DYLD_ROOT_PATH pointed at a booted simulator runtime's root (not the Xcode SDK path used at build time) or it fails at dyld startup with dyld: attempt to run simulator program outside simulator (DYLD_ROOT_PATH not set) -- xcrun simctl spawn sets this up for you (and everything else the simulator runtime environment needs), so it's the robust way to do this rather than hand-deriving that runtime-root path yourself:
    xcrun simctl list devices available   # pick any pre-provisioned iOS (not watchOS/tvOS) device's UDID
    xcrun simctl boot <device-udid>
    xcrun simctl spawn <device-udid> "$(pwd)/out/escargot" run.js   # simctl spawn needs an absolute path
    

A GitHub Actions macos-latest runner already has Xcode-provisioned simulator devices available (no extra download), so both steps above are CI-safe as-is. Manually exporting DYLD_ROOT_PATH=<a booted device's CoreSimulator runtime root> and invoking out/escargot directly (bypassing simctl spawn entirely) is also technically possible -- it's the lower-level mechanism simctl spawn itself relies on internally -- but that runtime-root path lives under /Library/Developer/CoreSimulator/..., resolved per-runtime/per-Xcode-version rather than being a fixed, easy-to-derive path (unlike the build-time SDK path from xcrun --sdk iphonesimulator --show-sdk-path), so simctl spawn is the supported, non-fragile way to do this and what the CI job below actually uses.

See the build-test-on-ios-simulator-arm64 CI job (.github/workflows/es-actions.yml) for a full working example, including running the Octane benchmark this way.

iOS device (iphoneos)

Same build, different sysroot -- the two SDKs share one set of headers and API-availability annotations and differ only in sysroot and target triple (arm64-apple-ios<ver> vs arm64-apple-ios<ver>-simulator), which the vendored ICU cross build picks up too:

git submodule update --init third_party/GCutil third_party/icu

# static libescargot.a (+ the escargot shell binary)
cmake -S . -B out-device \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=iphoneos \
    -DCMAKE_OSX_ARCHITECTURES=arm64 \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
    -DESCARGOT_ENABLE_SHELL=ON -DCMAKE_BUILD_TYPE=Release
cmake --build out-device

# or a shared libescargot.dylib to embed in an app bundle's Frameworks/
cmake -S . -B out-device-shared \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=iphoneos \
    -DCMAKE_OSX_ARCHITECTURES=arm64 \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
    -DESCARGOT_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build out-device-shared

⚠️ Read this before shipping a device build. Escargot's own CI builds this configuration and verifies the resulting Mach-O really is tagged for the iOS device platform (vtool -show-build -> platform IOS), that it is arm64, that ICU ended up statically linked, and that the dylib's install name is @rpath-relative -- but nothing in this project has ever executed Escargot on physical iOS hardware. The runtime coverage for the iOS host comes from the Simulator job. Treat the device build as "expected to work, build-verified, unproven at runtime", and please file an issue if it isn't.

What the build gives you, and what stays your responsibility:

  • The escargot shell binary is a compile/link check, not a deliverable. A stock (non-jailbroken) device cannot launch a bare CLI executable at all. On device you link libescargot.a (or embed libescargot.dylib) into your own app target and drive the engine through src/api/EscargotPublic.h or the Node-API layer (docs/n-api.md) from your app's code.
  • Code signing, provisioning and bundle packaging are yours. This build emits plain unsigned Mach-O artifacts; Xcode (or codesign with a real identity + a provisioning profile that covers the device) is what makes them runnable. A shared libescargot.dylib has to be embedded under the app bundle's Frameworks/ and signed along with the app.
  • No JIT, so no entitlement problems. Escargot is a pure interpreter -- it never maps writable-executable memory, so it needs neither the dynamic-codesigning entitlement (which Apple grants no third-party app) nor any JIT-related workaround. This is the reason an ordinary App Store app can embed it in the first place.
  • All file paths must be inside the app sandbox. Anything the embedder hands the engine has to be a container-relative path -- most notably VMInstanceRef::create(locale, timezone, baseCacheDir)'s baseCacheDir when built with -DESCARGOT_CODE_CACHE=ON, which must point at a writable directory in your container (e.g. Caches), not a hardcoded /tmp.
  • Deployment target. -DCMAKE_OSX_DEPLOYMENT_TARGET is forwarded to the vendored ICU cross build as well, so both halves agree on the minimum iOS version; raise it as your app needs.

See the build-on-ios-device-arm64 CI job (.github/workflows/es-actions.yml) for the exact commands and verification steps.

Vendored ICU

By default, Escargot loads ICU from wherever the target OS/dev environment already provides it (system package on linux/Android, Homebrew on macOS, OS-built-in on Windows) -- except on iOS, which has no system ICU at all. -DESCARGOT_LIBICU_SUPPORT_VENDORED=ON (the windows, macOS and iOS default -- and, on iOS, the only supported ICU option, see the "iOS" section above; opt-in on linux) makes Escargot bring/build its own ICU instead -- useful for targets with no usable system ICU, or to pin an exact ICU version/build independent of the host. The actual mechanism differs per host, since ICU's own build system does too:

  • linux and macOS: both build the third_party/icu submodule (pinned to tag release-78.1, matching this repo's CI pin) from source -- via runConfigureICU Linux/gcc/MacOSX respectively -- with its data trimmed via build/icu-filters/escargot.json to just what third_party/runtime_icu_binder/RuntimeICUBinder.h's call surface uses, and links the result statically -- no separate ICU data file, no runtime ICU dependency at all. (The macOS static archives are just as mutually referential as the linux ones, but don't need linux's -Wl,--start-group/--end-group treatment -- Apple's ld64 doesn't understand that GNU ld syntax, and doesn't need an equivalent either since it resolves undefined symbols across all archives on the command line regardless of order.)
  • windows: ICU's own Windows build only ships common/i18n as DLLs (no static .lib variant), so this locates an ICU installed via vcpkg (-DICU_ROOT=<vcpkg>/installed/<triplet>) and links against its import libs; the matching DLLs (selected via dumpbin /dependents, not a blanket copy) need to ship next to escargot.exe/escargot.dll -- see the Windows build instructions above and the build-on-windows-x86-x64/build-windows CI jobs.
  • ios: ICU has no native "iOS" autoconf target, so this does the standard two-pass cross build from ICU's User Guide's cross-compilation section (--with-cross-build): first build ICU's own tools (genrb, genbrk, ...) natively for the macOS build machine (runConfigureICU MacOSX), then cross-compile the real target ICU (configure --with-cross-build=<pass-1 build dir>) with CC/CFLAGS/ LDFLAGS pointed at the selected iOS SDK's sysroot (iphonesimulator or iphoneos, following this build's own -DCMAKE_OSX_SYSROOT) and a matching explicit -target arm64-apple-ios<ver>[-simulator] triple, reusing pass 1's tools to generate its (filtered, per build/icu-filters/escargot.json) data. The result is linked statically -- no separate ICU data file, no runtime ICU dependency at all.

See build/VendoredICU.cmake for the implementation and .github/workflows/es-actions.yml's build-test-on-vendored-icu-linux/ build-on-macos/build-on-macos-arm64/build-test-on-ios-simulator-arm64/ build-on-ios-device-arm64 jobs for full end-to-end examples (build, verify static linking via ldd/otool -L, run tests, and for the iOS Simulator run the Octane benchmark).

Debugger

Make sure Escargot is built with the -DESCARGOT_DEBUGGER=1 flag (off by default) enabled; then start Escargot with the --start-debug-server option.

Connect using a debugger client

  • Escargot python debugger
    • run ./tools/debugger/debugger.py; It will automatically connect to a debug server on the default port 6501
    • run ./tools/debugger/debugger.py --help for a list of options
  • Visual Studio Code extension
  • Chrome Devtools ⚠️ Early in development ⚠️
    • Initial setup:
      • Navigate to chrome://inspect
      • Make sure Discover network targets is enabled; click configure
      • Add localhost:6501 as a target; click Done
    • Usage:
      • The started debug server will be listed in the Remote Target list (If it is not, the page may need to be reloaded using the browser reload button)
      • Click inspect
      • A new window with the Chrome Devtools debugger UI will open

Testing ✅

Escargot supports various benchmark sets, which can be run using the tools/run-tests.py script.

Prerequisites:

# Python 3 only -- the v8/spidermonkey/test262 runners are pure python3, no python2 needed.
sudo apt-get install python3
sudo apt-get install python3-chardet  # or: pip install chardet -- required by the test262 runner
Benchmarkflag
SunSpider 1.0.2sunspider
Octane 2.0octane
test262test262
Web Tooling Benchmarkweb-tooling-benchmark
SpiderMonkey (vendor-made)spidermonkey
ChakraCore (vendor-made)chakracore
V8 (vendor-made)v8

After a default build, run individual benchmark suites (or combine suites in one command) as follows:

tools/run-tests.py --engine=./out/escargot web-tooling-benchmark
tools/run-tests.py --engine=./out/escargot spidermonkey test262 v8

Contributing 💡

Escargot welcomes contributions of code, documentation, bug reports, and suggestions. By contributing, you agree to license your contribution under the LGPL-2.1.

❗ Vulnerability Reporting

⚠️ If you identify any vulnerabilities, please report them through the Issues page. Reports sent via other channels may not be considered or may be processed with delays. Please note that our project assumes the execution of valid JavaScript source code only. Handling of invalid source code is not within the main scope of this project and might not be addressed.

Research Papers 📝

License 📜

Escargot is open-source software primarily licensed under LGPL-2.1, with some components covered by other licenses. Complete license and copyright information can be found in the source code.

javascript-engine

Contributors

(top 30 of 31)

ksh8281

1,347 commits

zherczeg

89 commits

june0cho

86 commits

bbrto21

57 commits

Samsung/escargot

Escargot is a lightweight JavaScript engine designed specifically for resource-constrained environments.

C++

363

1,913 commits

updated Sep 21, 2026

See the code

README

Escargot

License GitHub release (latestSemVer) Actions Status Coverity Scan Build Status codecov

Escargot is an embeddable JavaScript engine developed by Samsung. It is designed for products that need more than a minimal scripting runtime but still have to manage CPU, memory, and platform constraints carefully.

Rather than minimizing the engine binary at all costs, Escargot balances runtime memory efficiency, interpreter performance, and modern JavaScript functionality. Its C++ implementation and built-in features can produce a larger native binary than ultra-minimal engines, but the engine is designed to use memory efficiently as applications and object graphs grow. This makes it a strong fit for substantial JavaScript workloads on embedded Linux, mobile, IoT, and other resource-aware products.

Escargot compiles JavaScript to bytecode and executes it without a JIT. The interpreter-based architecture avoids executable-memory requirements and provides predictable deployment characteristics, while the standards implementation supplies the language and internationalization features expected by real products.

Key capabilities include:

  • Modern JavaScript and internationalization: broad ECMAScript support, including the ECMAScript 2026 specification, plus Intl (ECMA-402) and Temporal backed by ICU.
  • Efficient execution of growing applications: runtime data structures and interpreter optimizations target good performance without allowing memory overhead to scale unnecessarily with larger programs.
  • Product-oriented portability: Linux, Android, Tizen, macOS, iOS, Windows, and reference bare-metal/RTOS ports are supported without depending on JIT availability.
  • Flexible feature footprint: WebAssembly, threading, code cache, debugger, ICU, and small-device optimizations can be selected for the target instead of imposing one fixed runtime configuration.
  • System-provided ICU option: instead of vendoring its own ICU, Escargot can link against the ICU already installed on the target OS, trading some Intl/Temporal functionality for a smaller binary.
  • Multiple embedding surfaces: applications can use the public C++ API in src/api/EscargotPublic.h, or enable the Node-API v10 layer and C-style hosting APIs documented in docs/n-api.md.
  • Deployment-ready outputs: CMake can produce static or shared libraries, a command-line shell, and C++ tests. Memory is managed by the Boehm-Demers-Weiser conservative garbage collector in third_party/GCutil.

Escargot powers services in Samsung products and is available as an LGPL-2.1 open source project for other embedders and contributors.

Contents 📋

Building 🛠️

Supported Platforms and Architectures

Architecture names below use x86 for 32-bit Intel, x64 for x86-64, arm for 32-bit ARM, and aarch64/arm64 for 64-bit ARM.

PlatformArchitecturesStatus
Linuxx86, x64, arm, aarch64, riscv64Supported Linux platform. Ubuntu is used by CI and in the package-install example; it is not an OS restriction.
macOSx64, aarch64Supported on both Intel and Apple Silicon.
iOS / iPadOSaarch64Simulator and device builds are supported.
Windowsx86, x64, arm64Supported; WebAssembly is currently unavailable on arm64.
Androidx86, x64, arm, aarch64Supported across the Android ABIs.
Tizenx86, x64, arm, aarch64, riscv64
Bare-metal / RTOSarm (Cortex-M)FreeRTOS and NuttX reference ports are provided.

Build Options

Pass these options when configuring with CMake.

FlagDescriptionValueDefault
-DESCARGOT_BUILD_SHARED_LIBSBuild shared libraryON/OFFOFF
-DESCARGOT_BUILD_GC_SHARED_LIBSBuild GCutil as a shared libraryON/OFFOFF
-DESCARGOT_ENABLE_SHELLBuild the Escargot shell (-DENABLE_SHELL remains a legacy alias)ON/OFFON, except OFF when ESCARGOT_NAPI is ON
-DESCARGOT_BUILD_CCTESTBuild the C++ testsON/OFFOFF
-DESCARGOT_LIBICU_SUPPORTInclude libicu libraryON/OFFON, except OFF on bare-metal
-DESCARGOT_WASMEnable WebAssembly supportON/OFFOFF
-DESCARGOT_CODE_CACHEEnable code cacheON/OFFOFF
-DESCARGOT_TCOEnable tail call optimizationON/OFFOFF
-DESCARGOT_THREADINGEnable threading features (e.g. Atomics, SharedArrayBuffer)ON/OFFON, except OFF on bare-metal
-DESCARGOT_TLS_ACCESS_BY_ADDRESSEnable thread local storage access optimization (offset)ON/OFFOFF everywhere (safety-first; opt in manually on stable glibc-style targets)
-DESCARGOT_TLS_ACCESS_BY_PTHREAD_KEYEnable thread local storage access optimization (pthread_key)ON/OFFON when THREADING is ON and host is Android, otherwise OFF
-DESCARGOT_TEMPORALEnable Temporal support (requires ICU)ON/OFFON when LIBICU is ON, otherwise OFF
-DESCARGOT_SHADOWREALMEnable ShadowRealm supportON/OFFOFF
-DESCARGOT_SMALL_CONFIGEnable aggressive memory optimizations for tiny devicesON/OFFOFF
-DESCARGOT_EXPORT_ALLExport all symbols instead of the default curated public APION/OFFOFF
-DESCARGOT_TESTEnable additional features used only for testingON/OFFOFF
-DESCARGOT_DEBUGGEREnable Debug serverON/OFFOFF
-DESCARGOT_NAPIEnable Node-API (N-API) support and C-style hosting APIsON/OFFOFF
Advanced / developer-only options (profiling, sanitizers, internal knobs)
FlagDescriptionValueDefault
-DESCARGOT_ASANBuild with AddressSanitizerON/OFFOFF
-DESCARGOT_COVERAGEBuild with gcov/Codecov instrumentationON/OFFOFF
-DESCARGOT_DEPLOYBuild for deployment (set up RPATH for a bundled ICU)ON/OFFOFF
-DESCARGOT_LIBICU_SUPPORT_WITH_DLOPENLoad libicu at runtime via dlopen() instead of linking directlyON/OFFON, except OFF on macOS (dlopen-loaded ICU doesn't work correctly there), disallowed entirely on iOS, and OFF when ESCARGOT_LIBICU_SUPPORT_VENDORED is ON
-DESCARGOT_LIBICU_SUPPORT_VENDOREDBuild/ship Escargot's own ICU instead of relying on a system-provided one (see "Vendored ICU" below)ON/OFFON on windows, macOS and iOS (the only ICU option there), OFF elsewhere (available on linux too)
-DESCARGOT_USE_EXTENDED_APIEnable the extended C++ API (FunctionTemplateRef, etc.)ON/OFFON when NAPI is ON, otherwise OFF
-DESCARGOT_USE_CUSTOM_LOGGINGUse a custom logging backend instead of the host's native log (e.g. dlog on Tizen)ON/OFFOFF
-DESCARGOT_YARR_START_CHAR_FILTEREnable the Yarr interpreter first-character prefilterON/OFFON
-DESCARGOT_TCO_DEBUGEnable extra tail-call-optimization debug checks (debug builds only, requires ESCARGOT_TCO)ON/OFFOFF
-DESCARGOT_PROFILE_BDWGCEnable bdwgc (Boehm GC) profilingON/OFFOFF
-DESCARGOT_MEM_STATSEnable memory usage statisticsON/OFFOFF
-DESCARGOT_VALGRINDBuild with Valgrind annotationsON/OFFOFF
-DESCARGOT_GOOGLE_PERFBuild with gperftools (Google Performance Tools) profilingON/OFFOFF
-DESCARGOT_BUILD_64BIT_FORCE_LARGEOn 64-bit targets, force full 64-bit pointers instead of 32-bit-in-64-bit compressionON/OFFON

Linux

Escargot supports Linux distributions generally. The commands below use Debian/Ubuntu package names because Ubuntu is used by CI; install the equivalent development packages on other distributions.

Debian/Ubuntu prerequisites:

sudo apt-get install build-essential cmake git libicu-dev pkg-config python3

Prerequisites for x86-64-to-x86 compilation:

sudo apt-get install gcc-multilib g++-multilib
sudo apt-get install libicu-dev:i386

Build Escargot:

git submodule update --init third_party
cmake -S . -B out -DCMAKE_BUILD_TYPE=Release -DESCARGOT_ENABLE_SHELL=ON
cmake --build out

# Run a JavaScript file with the shell produced by the default build.
./out/escargot path/to/script.js

The default configuration produces the static library out/libescargot.a and the out/escargot shell. Set ESCARGOT_BUILD_SHARED_LIBS=ON for a shared library, or ESCARGOT_ENABLE_SHELL=OFF when embedding the library without the command-line shell.

macOS

Install the Xcode Command Line Tools (or full Xcode), then install the remaining build prerequisites:

xcode-select --install
brew install cmake python

Build Escargot:

git submodule update --init third_party
cmake -S . -B out -DCMAKE_BUILD_TYPE=Release -DESCARGOT_ENABLE_SHELL=ON
cmake --build out

ICU is vendored by default on macOS (see "Vendored ICU" below) -- it's built from the third_party/icu submodule above and linked statically, so no Homebrew icu4c/pkg-config setup is needed for the default path. To opt back into a Homebrew/system-provided ICU instead:

brew install icu4c

# add icu path to pkg_config_path (x64)
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:$PKG_CONFIG_PATH"
# add icu path to pkg_config_path (arm64)
export PKG_CONFIG_PATH="/opt/homebrew/opt/icu4c/lib/pkgconfig:$PKG_CONFIG_PATH"

cmake -S . -B out -DCMAKE_BUILD_TYPE=Release \
    -DESCARGOT_LIBICU_SUPPORT_VENDORED=OFF \
    -DESCARGOT_ENABLE_SHELL=ON
cmake --build out

Android

Build prerequisites on Ubuntu:

sudo apt install openjdk-17-jdk # require java 17

Build Escargot using gradle:

git submodule update --init third_party
export ANDROID_SDK_ROOT=.... # set your android SDK root first
cd build/android/
./gradlew bundleReleaseAar # build escargot AAR
./gradlew bundleHostJar # bundle jar for host
./gradlew javadocJar # create java doc
./gradlew sourcesJar # create sources jar

./gradlew assembleDebug # build debug test shell
./gradlew :escargot:connectedDebugAndroidTest # run escargot-jni tests on android device
./gradlew :escargot:testDebugUnitTest # run escargot-jni tests on host

Tizen

Escargot is packaged and deployed in Tizen products. The RPM spec supports the Tizen package architectures x86, x64, arm, aarch64, and riscv64, and selects product profiles such as TV, mobile, wearable, and headless through the Tizen build environment.

For a reference GBS build, use the repository's Tizen profile:

git submodule update --init third_party/GCutil
gbs -c .github/workflows/gbs.conf build -A armv7l -P profile.tizen --define "enable_shell 1"

The public CI continuously validates the armv7 package build. Tizen product build and qualification flows use the same RPM packaging integration.

Bare-metal / RTOS

Escargot runs on bare-metal and RTOS targets with no OS underneath (no pthreads, no mmap, no filesystem). Specifying a bare-metal/RTOS target via CMAKE_SYSTEM_NAME (such as Generic, NuttX, FreeRTOS) automatically configures the engine side of this (-DOS_BAREMETAL=1 and friends, ICU/threading defaulted off):

cmake -DCMAKE_SYSTEM_NAME=Generic -DCMAKE_SYSTEM_PROCESSOR=arm ... /path/to/escargot

A full port additionally needs its own small CMake project for BDWGC (third_party/GCutil) and a PlatformRef implementation providing the RTOS's task stack bounds and tick source. See docs/porting/RTOS_PORTING_GUIDE.md for the full checklist and code contract, and samples/rtos/freertos/ for a complete, working in-tree sample (FreeRTOS / Cortex-M55, QEMU mps3-an547) — cross-compiled and boot-tested under QEMU by the RTOS-FreeRTOS CI job (.github/workflows/rtos-freertos.yml) whenever engine or sample sources change.

A second reference port, NuttX / Cortex-M55 (same QEMU target), is also in-tree: samples/rtos/nuttx/ has the escargot NSH app (interpreters-escargot/, meant to be dropped into your own NuttX+apps checkout's apps/interpreters/) and the out-of-tree CMake project that builds the engine for it (escargot-lib-cmake/). Unlike FreeRTOS-Kernel, NuttX itself isn't vendored as a submodule here (a NuttX app fundamentally needs a full NuttX+apps source tree, not a standalone library dependency) — CI-verified instead by the RTOS-NuttX job (.github/workflows/rtos-nuttx.yml), which checks out NuttX + its apps monorepo at pinned commits (cached across runs) and boot-tests the same way. Both ports' shared contract and checklist are in docs/porting/RTOS_PORTING_GUIDE.md.

Windows

Build from a Developer Command Prompt for Visual Studio with the Visual Studio CMake generator and MSBuild. The examples below use Visual Studio 2022 (-G "Visual Studio 17 2022"); substitute the generator name for another installed Visual Studio version (e.g. -G "Visual Studio 16 2019") if that's what you have.

Install the following:

  • Visual Studio's Desktop development with C++ workload, including MSVC v143 C++ build tools, CMake tools for Windows, and a Windows 10 or 11 SDK.
  • The MSVC target tools for the architecture you intend to build.
  • Python 3, available as python on PATH. Escargot uses it to generate Unicode tables during the build.
  • Git, for the source checkout, submodules, and vcpkg bootstrap.

Choose the matching Visual Studio platform and vcpkg triplet:

TargetCMake -A valuevcpkg tripletAdditional MSVC tools
x86Win32x86-windowsx86/x64 build tools
x64x64x64-windowsx86/x64 build tools
ARM64ARM64arm64-windowsARM64 build tools

ICU is supplied through vcpkg. The following x64 example can be adapted with the table above:

git clone --depth 1 --branch 2026.07.29 https://github.com/microsoft/vcpkg.git
call vcpkg\bootstrap-vcpkg.bat
vcpkg\vcpkg.exe install icu --triplet=x64-windows

git submodule update --init third_party

cmake -S . -B out -G "Visual Studio 17 2022" -A x64 ^
  -DICU_ROOT=vcpkg\installed\x64-windows ^
  -DESCARGOT_ENABLE_SHELL=ON
cmake --build out --config Release

For ARM64, substitute -A ARM64 and arm64-windows. Also pass -DESCARGOT_WASM=OFF: the vendored WABT dependency does not yet support MSVC/ARM64.

vcpkg\vcpkg.exe install icu --triplet=arm64-windows
cmake -S . -B out-arm64 -G "Visual Studio 17 2022" -A ARM64 ^
  -DICU_ROOT=vcpkg\installed\arm64-windows ^
  -DESCARGOT_ENABLE_SHELL=ON -DESCARGOT_WASM=OFF
cmake --build out-arm64 --config Release

The executable is written to out\Release\escargot.exe (or out-arm64\Release\escargot.exe). Copy the ICU DLLs it depends on from the matching vcpkg\installed\<triplet>\bin\ directory beside the executable when distributing it.

Use Windows' built-in ICU DLL (no vcpkg)

To avoid bundling ICU from vcpkg, explicitly select the Windows ICU path:

cmake -S . -B out -G "Visual Studio 17 2022" -A x64 ^
  -DESCARGOT_ENABLE_SHELL=ON ^
  -DESCARGOT_LIBICU_SUPPORT_VENDORED=OFF
cmake --build out --config Release

This links through the Windows SDK's icu.lib and uses the ICU DLL supplied by Windows at runtime, so there is no ICU_ROOT, vcpkg installation, or ICU DLL to copy beside escargot.exe. It requires a target with the built-in ICU available (Windows 10 version 1703 or later). Escargot also falls back to this runtime-ICU path when no vcpkg ICU is found, but passing -DESCARGOT_LIBICU_SUPPORT_VENDORED=OFF records the choice explicitly and avoids the configure-time fallback warning.

The Windows ICU version follows the operating system. Use the vcpkg path above when a pinned ICU version and identical Intl/Unicode behavior across machines are more important than avoiding the ICU deployment files.

iOS

ESCARGOT_HOST=ios cross-compiles Escargot from a macOS host to arm64 iOS. Which of Apple's two iOS SDKs is targeted is selected with -DCMAKE_OSX_SYSROOT:

-DCMAKE_OSX_SYSROOT=TargetStatus
iphonesimulatoriOS Simulator (arm64, i.e. an Apple Silicon build machine)Built and run in CI (build-test-on-ios-simulator-arm64, which runs Octane)
iphoneosReal iPhone/iPad hardware (arm64)Built and Mach-O-verified in CI (build-on-ios-device-arm64), never executed on physical hardware by this project -- see the caveats below

There is no separate "ipados" host: Apple ships one SDK/platform identifier ("iOS") and one arm64 sysroot/triple for both iPhone and iPad at the CMake/toolchain level. arm64 is the only supported architecture (armv7 devices predate every supported deployment target, and an x86_64 simulator would mean an Intel build machine).

Prerequisite: a full Xcode install (not just the Command Line Tools -- xcrun --sdk iphonesimulator --show-sdk-path, or --sdk iphoneos for a device build, must succeed).

ICU on iOS has exactly two supported configurations: vendored (the default; see "Vendored ICU" below) or off entirely (-DESCARGOT_LIBICU_SUPPORT=OFF). There is no system/pkg-config ICU dev package available on iOS, and dlopen-loading an arbitrary library is unavailable there too, so both of those other ICU paths are rejected with a FATAL_ERROR at configure time.

iOS Simulator

git submodule update --init third_party/GCutil third_party/icu # update submodules (+ vendored ICU source)

cmake -S . -B out \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=iphonesimulator \
    -DCMAKE_OSX_ARCHITECTURES=arm64 \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
    -DESCARGOT_ENABLE_SHELL=ON -DCMAKE_BUILD_TYPE=Release
cmake --build out

The resulting out/escargot is an arm64 Mach-O binary linked against the iphonesimulator SDK, and running it takes two steps -- neither of them optional, both confirmed against real-world reports of the same two failures (not guessed):

  1. Ad-hoc code-sign it. macOS on Apple Silicon enforces code signing on every arm64 executable, including plain command-line tools -- the linker only emits a minimal "linker-signed" signature by default, which recent macOS versions reject outright (Killed: 9) even though nothing else about the binary is wrong:
    codesign --sign - --force out/escargot   # ad-hoc signature, no identity/provisioning needed
    
  2. Run it inside a booted simulator device via simctl spawn, not by invoking it bare from Terminal. A simulator-platform Mach-O binary still needs DYLD_ROOT_PATH pointed at a booted simulator runtime's root (not the Xcode SDK path used at build time) or it fails at dyld startup with dyld: attempt to run simulator program outside simulator (DYLD_ROOT_PATH not set) -- xcrun simctl spawn sets this up for you (and everything else the simulator runtime environment needs), so it's the robust way to do this rather than hand-deriving that runtime-root path yourself:
    xcrun simctl list devices available   # pick any pre-provisioned iOS (not watchOS/tvOS) device's UDID
    xcrun simctl boot <device-udid>
    xcrun simctl spawn <device-udid> "$(pwd)/out/escargot" run.js   # simctl spawn needs an absolute path
    

A GitHub Actions macos-latest runner already has Xcode-provisioned simulator devices available (no extra download), so both steps above are CI-safe as-is. Manually exporting DYLD_ROOT_PATH=<a booted device's CoreSimulator runtime root> and invoking out/escargot directly (bypassing simctl spawn entirely) is also technically possible -- it's the lower-level mechanism simctl spawn itself relies on internally -- but that runtime-root path lives under /Library/Developer/CoreSimulator/..., resolved per-runtime/per-Xcode-version rather than being a fixed, easy-to-derive path (unlike the build-time SDK path from xcrun --sdk iphonesimulator --show-sdk-path), so simctl spawn is the supported, non-fragile way to do this and what the CI job below actually uses.

See the build-test-on-ios-simulator-arm64 CI job (.github/workflows/es-actions.yml) for a full working example, including running the Octane benchmark this way.

iOS device (iphoneos)

Same build, different sysroot -- the two SDKs share one set of headers and API-availability annotations and differ only in sysroot and target triple (arm64-apple-ios<ver> vs arm64-apple-ios<ver>-simulator), which the vendored ICU cross build picks up too:

git submodule update --init third_party/GCutil third_party/icu

# static libescargot.a (+ the escargot shell binary)
cmake -S . -B out-device \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=iphoneos \
    -DCMAKE_OSX_ARCHITECTURES=arm64 \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
    -DESCARGOT_ENABLE_SHELL=ON -DCMAKE_BUILD_TYPE=Release
cmake --build out-device

# or a shared libescargot.dylib to embed in an app bundle's Frameworks/
cmake -S . -B out-device-shared \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=iphoneos \
    -DCMAKE_OSX_ARCHITECTURES=arm64 \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
    -DESCARGOT_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build out-device-shared

⚠️ Read this before shipping a device build. Escargot's own CI builds this configuration and verifies the resulting Mach-O really is tagged for the iOS device platform (vtool -show-build -> platform IOS), that it is arm64, that ICU ended up statically linked, and that the dylib's install name is @rpath-relative -- but nothing in this project has ever executed Escargot on physical iOS hardware. The runtime coverage for the iOS host comes from the Simulator job. Treat the device build as "expected to work, build-verified, unproven at runtime", and please file an issue if it isn't.

What the build gives you, and what stays your responsibility:

  • The escargot shell binary is a compile/link check, not a deliverable. A stock (non-jailbroken) device cannot launch a bare CLI executable at all. On device you link libescargot.a (or embed libescargot.dylib) into your own app target and drive the engine through src/api/EscargotPublic.h or the Node-API layer (docs/n-api.md) from your app's code.
  • Code signing, provisioning and bundle packaging are yours. This build emits plain unsigned Mach-O artifacts; Xcode (or codesign with a real identity + a provisioning profile that covers the device) is what makes them runnable. A shared libescargot.dylib has to be embedded under the app bundle's Frameworks/ and signed along with the app.
  • No JIT, so no entitlement problems. Escargot is a pure interpreter -- it never maps writable-executable memory, so it needs neither the dynamic-codesigning entitlement (which Apple grants no third-party app) nor any JIT-related workaround. This is the reason an ordinary App Store app can embed it in the first place.
  • All file paths must be inside the app sandbox. Anything the embedder hands the engine has to be a container-relative path -- most notably VMInstanceRef::create(locale, timezone, baseCacheDir)'s baseCacheDir when built with -DESCARGOT_CODE_CACHE=ON, which must point at a writable directory in your container (e.g. Caches), not a hardcoded /tmp.
  • Deployment target. -DCMAKE_OSX_DEPLOYMENT_TARGET is forwarded to the vendored ICU cross build as well, so both halves agree on the minimum iOS version; raise it as your app needs.

See the build-on-ios-device-arm64 CI job (.github/workflows/es-actions.yml) for the exact commands and verification steps.

Vendored ICU

By default, Escargot loads ICU from wherever the target OS/dev environment already provides it (system package on linux/Android, Homebrew on macOS, OS-built-in on Windows) -- except on iOS, which has no system ICU at all. -DESCARGOT_LIBICU_SUPPORT_VENDORED=ON (the windows, macOS and iOS default -- and, on iOS, the only supported ICU option, see the "iOS" section above; opt-in on linux) makes Escargot bring/build its own ICU instead -- useful for targets with no usable system ICU, or to pin an exact ICU version/build independent of the host. The actual mechanism differs per host, since ICU's own build system does too:

  • linux and macOS: both build the third_party/icu submodule (pinned to tag release-78.1, matching this repo's CI pin) from source -- via runConfigureICU Linux/gcc/MacOSX respectively -- with its data trimmed via build/icu-filters/escargot.json to just what third_party/runtime_icu_binder/RuntimeICUBinder.h's call surface uses, and links the result statically -- no separate ICU data file, no runtime ICU dependency at all. (The macOS static archives are just as mutually referential as the linux ones, but don't need linux's -Wl,--start-group/--end-group treatment -- Apple's ld64 doesn't understand that GNU ld syntax, and doesn't need an equivalent either since it resolves undefined symbols across all archives on the command line regardless of order.)
  • windows: ICU's own Windows build only ships common/i18n as DLLs (no static .lib variant), so this locates an ICU installed via vcpkg (-DICU_ROOT=<vcpkg>/installed/<triplet>) and links against its import libs; the matching DLLs (selected via dumpbin /dependents, not a blanket copy) need to ship next to escargot.exe/escargot.dll -- see the Windows build instructions above and the build-on-windows-x86-x64/build-windows CI jobs.
  • ios: ICU has no native "iOS" autoconf target, so this does the standard two-pass cross build from ICU's User Guide's cross-compilation section (--with-cross-build): first build ICU's own tools (genrb, genbrk, ...) natively for the macOS build machine (runConfigureICU MacOSX), then cross-compile the real target ICU (configure --with-cross-build=<pass-1 build dir>) with CC/CFLAGS/ LDFLAGS pointed at the selected iOS SDK's sysroot (iphonesimulator or iphoneos, following this build's own -DCMAKE_OSX_SYSROOT) and a matching explicit -target arm64-apple-ios<ver>[-simulator] triple, reusing pass 1's tools to generate its (filtered, per build/icu-filters/escargot.json) data. The result is linked statically -- no separate ICU data file, no runtime ICU dependency at all.

See build/VendoredICU.cmake for the implementation and .github/workflows/es-actions.yml's build-test-on-vendored-icu-linux/ build-on-macos/build-on-macos-arm64/build-test-on-ios-simulator-arm64/ build-on-ios-device-arm64 jobs for full end-to-end examples (build, verify static linking via ldd/otool -L, run tests, and for the iOS Simulator run the Octane benchmark).

Debugger

Make sure Escargot is built with the -DESCARGOT_DEBUGGER=1 flag (off by default) enabled; then start Escargot with the --start-debug-server option.

Connect using a debugger client

  • Escargot python debugger
    • run ./tools/debugger/debugger.py; It will automatically connect to a debug server on the default port 6501
    • run ./tools/debugger/debugger.py --help for a list of options
  • Visual Studio Code extension
  • Chrome Devtools ⚠️ Early in development ⚠️
    • Initial setup:
      • Navigate to chrome://inspect
      • Make sure Discover network targets is enabled; click configure
      • Add localhost:6501 as a target; click Done
    • Usage:
      • The started debug server will be listed in the Remote Target list (If it is not, the page may need to be reloaded using the browser reload button)
      • Click inspect
      • A new window with the Chrome Devtools debugger UI will open

Testing ✅

Escargot supports various benchmark sets, which can be run using the tools/run-tests.py script.

Prerequisites:

# Python 3 only -- the v8/spidermonkey/test262 runners are pure python3, no python2 needed.
sudo apt-get install python3
sudo apt-get install python3-chardet  # or: pip install chardet -- required by the test262 runner
Benchmarkflag
SunSpider 1.0.2sunspider
Octane 2.0octane
test262test262
Web Tooling Benchmarkweb-tooling-benchmark
SpiderMonkey (vendor-made)spidermonkey
ChakraCore (vendor-made)chakracore
V8 (vendor-made)v8

After a default build, run individual benchmark suites (or combine suites in one command) as follows:

tools/run-tests.py --engine=./out/escargot web-tooling-benchmark
tools/run-tests.py --engine=./out/escargot spidermonkey test262 v8

Contributing 💡

Escargot welcomes contributions of code, documentation, bug reports, and suggestions. By contributing, you agree to license your contribution under the LGPL-2.1.

❗ Vulnerability Reporting

⚠️ If you identify any vulnerabilities, please report them through the Issues page. Reports sent via other channels may not be considered or may be processed with delays. Please note that our project assumes the execution of valid JavaScript source code only. Handling of invalid source code is not within the main scope of this project and might not be addressed.

Research Papers 📝

License 📜

Escargot is open-source software primarily licensed under LGPL-2.1, with some components covered by other licenses. Complete license and copyright information can be found in the source code.

javascript-engine

Contributors

(top 30 of 31)

ksh8281

1,347 commits

zherczeg

89 commits

june0cho

86 commits

bbrto21

57 commits

Languages

C++

90.2%

Python

4.5%

JavaScript

2.2%

Java

1.5%

CMake

1.3%