Skip to content
metaPublic

About

A JavaScript engine optimized for running React Native.

Resources

Code of conduct

Contributing

Security policy

Stars

11.4k stars

Watchers

139 watching

Forks

Latest commit

 

History

9,762 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hermes JS Engine

MIT license PRs Welcome Hermes logo - large H with wings

Hermes is a JavaScript engine built for fast start-up and low memory use. Its whole design centers on compiling JavaScript ahead of time: apps ship compact bytecode, or optionally native code, and the engine does as little work as possible when they launch. Hermes was created for React Native, where it is the default engine. It also runs standalone or can be embedded as a library in other programs.

Ahead-of-time first

Most engines parse and compile JavaScript on the user's device, every time the app starts. Hermes moves that work to build time. The compiler parses, analyzes and optimizes the entire program once, and emits bytecode that the engine can memory-map and start executing immediately. Start-up cost, memory and binary size all benefit, and the optimizer can see the whole program instead of one function at a time.

In practice this means:

  • Ship bytecode. Compile at build time and load the result. This is how React Native apps use Hermes, and it is the path that gets all the optimizations.
  • Source still runs. The engine includes the compiler, so .js files can be run directly during development. It is not the intended production path.
  • eval() and new Function() are supported for small things. They run the compiler at runtime. Code passed to eval sees the global scope only; giving it access to the enclosing function's locals would force the compiler to pessimize every function, so that is deliberately left out.

Ways to run code

Hermes can execute JavaScript in four ways, and they can be mixed freely within one runtime.

  • From source. The engine can also be given JavaScript source, which it compiles to bytecode before running. By default, large sources are compiled lazily, one function at a time when it is first called. Good for development and for code generated at runtime.
  • From bytecode. The production path. Code is compiled ahead of time, the bytecode file is memory-mapped, and the interpreter runs it. This is how React Native apps run.
  • JIT. An optional baseline JIT compiles frequently executed functions to machine code while the app runs. It is deliberately simple; the heavy optimization already happened at build time, so the JIT stays small and predictable.
  • Native code. The shermes tool compiles JavaScript ahead of time all the way to native code, with no interpreter involved.

An app can ship some code as native, some as bytecode, and generate some at runtime, and the JIT will pick up hot functions from the latter two.

Static typing (in development)

Hermes is gaining the ability to use type annotations to compile JavaScript to much faster code. This is under active development and not ready for use. Both Flow and TypeScript syntax are being supported, to different degrees. Flow is the more static-typing-friendly of the two, so TypeScript is currently converted to Flow by an AST pass contributed by Amazon. That is an implementation detail and may change; TypeScript support is a priority.

Neither the Flow checker nor the TypeScript compiler guarantees that the annotations are true at runtime. Both accept this program, for example:

const a: number[] = [];
const n: number = a[0]; // the type says number, the value is undefined

A typed compiler has to strengthen the language semantics until the types are actually true. In this example, typed Hermes range-checks array indexing and throws on out-of-bounds access, so a[0] can never produce a value that contradicts its type. Where a value of unknown type flows into a typed one, for example from any or from untyped code, the compiler inserts a checked cast, which throws at runtime if the value is not of the expected type. With those guarantees in place, the generated code can rely on the types.

TypeScript is less friendly to static compilation than Flow because its typing is structural, and it is unsound in more places. This program type-checks in TypeScript and fails at runtime; Flow rejects it because object properties are invariant:

class Animal {}
class Dog extends Animal { bark() {} }
class Cat extends Animal {}

const d: { pet: Dog } = { pet: new Dog() };
const a: { pet: Animal } = d; // TypeScript accepts, Flow rejects
a.pet = new Cat();
d.pet.bark();                  // d.pet is a Cat: "bark is not a function"

In TypeScript a value of type Dog need not be an instance of Dog at all; any object with the same shape qualifies. A compiler can therefore not derive an object layout from a class, which is exactly what makes typed code fast.

See doc/TypedLanguage.md for the current state.

Independently from this, TypeScript can already be run in untyped, unsound mode: the type annotations are stripped and the result is treated as plain JavaScript. See doc/typescript-stripping.md.

Native compilation

Hermes can also compile JavaScript ahead of time to native code, using the shermes tool. It works both on ordinary untyped JavaScript and on typed JavaScript, where the latter produces much better native code.

About the name "Static Hermes"

"Static Hermes" was the code name for an umbrella project, developed on the static_h branch, to add static typing and optional native compilation to Hermes. The branch then became the main development line and gained the JIT, modern ECMAScript features, and a large amount of unrelated work. It is not a separate engine; it is Hermes.

Hermes releases had always been numbered 0.x. When the static_h line was judged ready, its releases moved to a new scheme built from the branch date and bytecode version (for example 260318099.0.0), and React Native calls it Hermes V1 to distinguish it from the legacy line on the main branch. See doc/Branches.md.

Try it in a minute

You need a C++17 compiler, CMake, Ninja and Python. Clang is strongly recommended; gcc is supported but produces noticeably worse code. On Ubuntu:

apt install clang build-essential cmake git ninja-build python3 tzdata libreadline-dev

Clone and build the two command line tools, hermes (compile to bytecode, run bytecode or source) and shermes (compile to native):

git clone https://github.com/facebook/hermes.git
cmake -S hermes -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
cmake --build build --target hermes shermes

Run a script from source, then the way apps do it, from bytecode:

echo "print('Hello from Hermes');" > hello.js
build/bin/hermes hello.js
build/bin/hermes -emit-binary -out hello.hbc hello.js
build/bin/hermes hello.hbc

Compile the same script to a native executable:

build/bin/shermes -O -o hello hello.js
./hello

Everything else, including macOS, Windows, release builds and running the test suites, is in doc/BuildingAndRunning.md.

Integration

Integration documentation is thin today and will grow. These are the starting points:

  • Embedding through JSI. JSI is the stable C++ API for hosting the engine and exchanging values and functions with JavaScript. Start with hermes-jsi-demos, a set of small standalone CMake projects, from hello world to a working event loop. The API itself is API/jsi/jsi/jsi.h and the Hermes entry point is API/hermes/hermes.h.
  • Node-API. Native addons written against the Node-API ABI run on Hermes without modification. See API/napi/README.md.
  • React Native. Hermes is the default engine. See doc/ReactNative.md.
  • Running in a browser. Hermes itself can be compiled to Wasm with Emscripten, so it runs in the browser and in other Wasm hosts. JavaScript compiled with shermes can be built to Wasm the same way. See doc/Emscripten.md.

Learn more

Contributing

Contributions are welcome. The preferred way to add functionality is a JSI extension: extensions are written against the stable JSI API rather than engine internals, so they are safer to write and do not break when the engine changes. Read CONTRIBUTING.md for the development process and how to propose changes.

This project follows the Meta Code of Conduct. Hermes is MIT licensed.

About

A JavaScript engine optimized for running React Native.

Resources

Code of conduct

Contributing

Security policy

Stars

11.4k stars

Watchers

139 watching

Forks

Releases

Packages

Used by

Contributors

Languages