Skip to content
A compiled implementation of Javascript, targetting C
Branch: master
Clone or download
Latest commit 55e7fe5 Apr 19, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github explode all the things Feb 18, 2019
notes finishing implementation of dynamic runtime Feb 18, 2019
public npmi Apr 19, 2019
runtime cleanup warnings Apr 19, 2019
scripts rename Apr 19, 2019
specs Add specs - offline goodness Mar 2, 2019
src update expressions Apr 19, 2019
test update expressions Apr 19, 2019
.dockerignore explode all the things Feb 18, 2019
.editorconfig switch to editorconfig Jul 28, 2018
.gitignore build arte Mar 16, 2019
Makefile default target Apr 19, 2019 Use vendored libuv Mar 10, 2019
compile-and-run Fix up compiler tests Feb 18, 2019
compile-js make runtime a dynamic library - speed up compile times Feb 18, 2019
compile-js-lib finishing implementation of dynamic runtime Feb 18, 2019
compile-js-to-c More useful Feb 18, 2019
exploding-deref.bc lots of interesting things - my gc-sketch is failing cos I realised I… Jul 25, 2018
index.html fw Apr 19, 2019 Use vendored libuv Mar 10, 2019
package-lock.json deps up Feb 18, 2019
package.json fix function record GC Feb 18, 2019
server.Dockerfile work on demo site Mar 11, 2019
tsconfig.json Working on environment representation Jul 11, 2018

JS to C

A compiled implementation of ES5, targetting C.

Read my notes for a rough dev notes, or read more polished write ups on my blog.

Next thing to do - see


The following JS

function fact(n) {
    return n < 3 ? n : n * fact(n - 1);

outputs as a C program. For instance the compiled fact function looks like:

// ...
JsValue *fact_1(Env *env) {
  JsValue *return_2;
  JsValue *left_5 = (envGet(env, interned_7) /* n */);
  JsValue *right_6 = (jsValueCreateNumber(3));
  JsValue *conditionalPredicate_4 = (LTOperator(left_5, right_6));
  JsValue *conditionalValue_3;
  if (isTruthy(conditionalPredicate_4)) {
    return_2 = (envGet(env, interned_7) /* n */);
  } else {
    JsValue *left_8 = (envGet(env, interned_7) /* n */);
    JsValue *callee_10 = (envGet(env, interned_11) /* fact */);
    JsValue *left_12 = (envGet(env, interned_7) /* n */);
    JsValue *right_13 = (jsValueCreateNumber(1));
    JsValue *call10Arg_0 = (subtractOperator(left_12, right_13));
    JsValue *args_10[] = {call10Arg_0};
    JsValue *right_9 = (functionRunWithArguments(callee_10, env, args_10, 1));
    return_2 = (multiplyOperator(left_8, right_9));
  return return_2;
// ...


    the C implementation of the language runtime
    the TypeScript compiler



Run npm test to run/update tests. ./scripts/test-by-name $name to run a single test.

Failed tests provide a command to compile and run the C program with the debugger.


The runtime has unit tests - use the make test in ./runtime to run them, and ./runtime/scripts/compile-test-loop test-file-basename.


Runtime C libraries for js-to-c.



  • clang
  • Make
  • CMake (for libuv)

I'm trying to limit my focus to learning about compilation, so I've made no attempt to make this cross platform. It shouldn't be too tricky, libuv is the big dependency and it's cross platform.

make install


Builds and runs all tests:

make test


See lib - currently just

  • debug.h
    • debug macros

Dynamic libraries

The runtime is built into a dynamic library to avoid recompiling. There's also a prelude, which allows langauge features to be implemented in JS that's pre-compiled to C.


Files prefixed _ are private to the runtime and should not be relied on.

## Potential issues

grep for HMM to see potential problems I'm leaving alone for now as they seem tricky to solve properly/I'm unsure they'll be a problem in practice. Good place to look if weird bugs occur.

You can’t perform that action at this time.