Comment by lucacasonato

3 years ago

JS runtime != JS engine

Runtime: - Chrome - Deno - Node - Bun

Engine: - V8 - JSC - SpiderMonkey - LibJS

That's a weird usage of the word "runtime" then. To me, the definition is closer to Wikipedia's definition of "runtime system" [0] and includes any interpreters, JITs, and any other language elements present while a program is running.

  [0]: https://en.wikipedia.org/wiki/Runtime_system

No.

The runtime is the combination of the execution engine, the garbage collector, and the environment.

V8, JSC, SpiderMonkey, LibJS, etc all provide all of those.

They all have APIs that let you (the embedder) a few new objects and functions, as it would be a useless embedding API otherwise. But that's all you're doing: adding some glorified callbacks to an existing runtime. You are in no way "rolling your own JS runtime".

To break it down:

Execution Engine: the part of the runtime that evaluates JS, an interpreter, jit, or some combination. That would be "Ignition" and "TurboFan" in V8, "LLInt" and "FTL" in JSC, "WarpMonkey" in SpiderMonkey.

Garbage Collector: the part of the runtime that supports object allocation and reclamation. "Orinoco" in V8, but not sure if given a separate marketing name in other runtimes.

The environment: this is all the builtin objects and functionality, things like the global object, the regex engine (note it's not a regex runtime because all it does find the start and stop sections of matches, the embedding environment is responsible for everything else), all those core things like the Object, Array, Math, Number, etc types, and all of their implementations and runtime functions. The Execution engine does not need any of those to be implemented, as to the engine there is essentially no distinction between those builtin things and anything else written in JS.

When you embed V8, JSC, LibJS, etc you are getting a full runtime, that can do a huge amount. You _might_ choose to use there APIs to add some new objects or or functions, but what you are doing is negligible, and certainly not "a runtime".

Specifically, a runtime (IMO) is the set of variables and their associated functionality that exist in the global scope, or are expected to be importable in a predictable and standardized way.

Part 2 of this series, for instance, shows the instantiator of the V8 engine (via deno_core, a runtime-less wrapper as explained in Part 1) implementing a simplified fetch API with some trivial JS and the bulk of the logic in Rust: https://deno.com/blog/roll-your-own-javascript-runtime-pt2#i... - this would then be available to any code executing in their custom JS environment.

This is useful even outside of creating a full Node-style implementation; for instance, Cloudflare created a locked-down runtime (a reasonable subset of the browser runtime) for v8 isolates for their workers: https://developers.cloudflare.com/workers/runtime-apis/

It's super cool to know this stuff as it lets you consider (when appropriate!) using JS as a way to accept Turing-complete user-submitted logic rather than just accepting, say, JSON configs, while limiting the surface with which it can interact with your system.

  • > Specifically, a runtime (IMO) is the set of variables and their associated functionality that exist in the global scope, or are expected to be importable in a predictable and standardized way.

    All of which is provided by V8 in this example. The global object, math object, "Object" in general, Arrays, etc are all the runtime. All this tutorial series on embedding V8 is doing is instantiating an existing runtime environment and adding using the APIs to insert a few new APIs.

    All together you might say you've got a custom runtime as you have the baseline "JS runtime" + some new APIs and that's clearly a new runtime environment that is distinct from the JS environment on a web page, vs. the one in a worker, vs. in node, etc. But that is at best "extending a runtime", not "rolling your own".

    Using the embedding APIs provided by a JS runtime as documented and intended, is not "rolling your own". By that definition I could make an app, embed a WebView, and claim I rolled my own browser runtime, which I would hope is more clearly absurd. Or I could "roll my own Command-line" by reading a string from a user, prepending some commands, and then passing it to system().