Skip to content

SunsetFi/suntime-js

Repository files navigation

@suntime-js/core

(static-js was taken (So was js-engine))

A javascript interpreter built on the TC39 ECMAScript 2025 standard, seeking to implement modern language features.

A spiritual successor to static-eval.

Try it out in the sandbox!

Sandboxing and Security

Unlike static-eval, this project has ambitions of providing a secure sandbox from which untrusted code can be safely ran.

The fundimental difference with static-eval is that static-js operates entirely against its own implementation of the intrinsic javascript types, complete with its own prototype chain. This seeks to ensure that the code being ran is never able to manipulate the system into accessing the native properties of the underlying native objects, which would allow it to eventually reach a function constructor and therefor gain arbitrary code execution.

Instead, while the code in the sandbox will have access to eval() and the function constructor, those functions will instead run their code inside the sandbox, preserving the integrity of the host system.

More information on StaticJs security

What is supported

  • Strict directive
  • Primitives
  • Arrays
  • Math
  • Error (and variants), try / catch
  • Promises
  • Functions / Arrow functions
  • Async Functions
  • Top-level await
  • Symbols (including engine behavior)
    • Symbol.iterator
    • Symbol.hasInstance
    • Symbol.species
    • Symbol.isConcatSpreadable
    • Symbol.toPrimitive
  • Control flow / loops
  • Unary and binary operators
  • Destructuring
  • Spread operators (including Symbol.iterator usage)
  • eval / Function constructor.
  • ECMAScript Modules
    • Async modules
    • Exports
    • Importing from host-defined modules
    • Importing from sandboxed modules

Notable things not (yet) supported

  • Generator functions
  • All well-known symbols not listed above
  • Map, Set
  • WeakMap, WeakRef, FinalizationRegistry
  • Class syntax
  • Date
  • Regex
  • Iterator.return calls

Test262 coverage

This project is slowly working its way through the Test262 suite of JavaScript tests in order to ensure compliance with the spec.

Currently, around 4800 of the language tests are passing, or about 20%. Further work is ongoing in this area.

Quick Usage (native JS interop)

StaticJs provides quick functions for evaluating simple code: evaluateExpressionSync and evaluateScriptSync. These functions take strings as their first argument, and return a coerced native value.

Note that these functions will drain all microtasks enqueued during their evaluation before returning. This means that any promise resolutions in the scripts will also be ran to completion.

import { evaluateExpressionSync } from "@suntime-js/core";

const result = evaluateExpressionSync("2 + 2");

Warning: Using StaticJs this way is vulnurable to deadlocks with infinite loops, and can introduce security complications where VM code can be unexpectedly invoked through interacting with the resulting values (eg: property getters and setters).

For more information, including solutions for breaking loops, see Quick Start.

TODO:

  • Fix 'all' Test262 tests.
    • Currently only running tests in the language folder. Need to add built-ins
  • Figure out public API for invoking evaluators.
    • Probably hide evaluators and provide non-evaluator non-sync APIs from intrinsic types.
    • Figure out what it looks like for a consumer to want to call functions or invoke other evaluators as part of an api surface exposed within the sandbox (IE global scope functions, external modules).
      • Not good to have it start a new macrotask, as the task runner API should see calls to these apis happening while a task is active as continuations of the same task, not new nested tasks.
  • Fix host throwing error inside a sandbox promise continuation causing sandbox unhandled rejections.
  • Fix circular imports on modules. There's a skipped test for this.
  • Fix task runner not bound to continuations of promises
    • This is really thorny. On the surface, its suprising that a task runner passed to evaluateModule will only work for the initial evaluation and not for any runs after await, but it would also be suprising if the await is triggered by code that has its own runTask and that runTask isn't used in favor of the root runTask. Either way, this probably means we need to store the runTask on the context.
  • Report code coverage in repo coveralls.io? vitest-coverage-report-action?
  • Get api-extractor working. The only holdup right now is the combine and re-export of the interface and factory function of StaticJsRealm
    • Combine these into one file to make this work?
  • Rename toJs to toNative
  • Add scope, variable, and stack info to StaticJsTaskIterator for debugging.
  • Investigate debugger for monaco Example implementation? Docs

About

An ECMAScript 2025 (Modern JavaScript) interpreter written in Typescript

Topics

Resources

Stars

Watchers

Forks

Languages