CYNIC
A strict-only ECMAScript engine

Cynic.

A JavaScript engine that has heard enough.

Aimed at edge runtimes, Workers, and servers — eventually. Today, a lexer, parser, and bytecode interpreter chew through a growing slice of ECMAScript and politely decline everything the language has been trying to forget for the last twenty years. No sloppy mode. No with. No HTML-comment syntax. No DOM apologetics.

Pre-alpha. Lexer + parser + bytecode interpreter ship; the runtime is filling in. Read docs/ROADMAP.md before opening issues. Better yet, don't.
§01 · REFUSALS

Things Cynic won't do for you.

Other engines treat the spec like an all-you-can-eat buffet. Cynic reads it like a contract. If a feature exists only to keep a 2003 webpage from crashing, it's gone. No flags, no opt-ins, no half-measures.

REFUSED
with (obj)

The with statement

Allegedly part of the language. We checked. We disagree.

SyntaxError: with? in 2026? please.
RETIRED
0o7 → 07

Legacy octal literals

The 0o prefix exists for a reason. The bare-leading-zero form does not.

SyntaxError: legacy octal: not in strict mode, not anywhere.
DENIED
function f() in if(){}

Function-in-block, sloppy-mode style

Annex B's sloppy-only function-in-block hoisting is gone. Declare it where it lives.

SyntaxError: Annex B not on the menu.
PASS
for (var i in o = {}) {}

Initializer in for-in head

Annex B accident. Doesn't parse here. Hoist your initializer like an adult.

SyntaxError: for-in head is not for assignment.
NOPE
escape() · unescape()

The pre-URI globals

Use encodeURI / encodeURIComponent. They've been correct since 1999.

ReferenceError: 'escape' is not shipped.
EXPIRED
"hi".bold()

The String.prototype HTML wrappers

All thirteen of them — anchor, bold, fontcolor, … — left at the door. This is a string, not an HTML formatter.

TypeError: 'bold' is not a string method here.
NOPE
d.getYear()

Date.prototype.{getYear, setYear}

The two-digit-year ones. Cynic keeps the normative aliases (substr, trimLeft, toGMTString) — these don't make the cut.

TypeError: 'getYear' is not on this Date.
NEVER
<!-- -->

HTML comments in source

Yes, that's real ECMAScript. Yes, we are pretending it isn't.

SyntaxError: this is a JS file. compose yourself.
BANNED
eval() · new Function("…")

Runtime code construction

Out for good. Aligns with SES / Hardened JavaScript, kills the optimization fence, and removes the supply-chain bait. Multi-file scripts use a host hook, not user-reachable.

EvalError: not even once.
NOPE
Intl.*

The internationalization API

Locale-aware formatting is a separate runtime project. Date, Number.prototype.toLocaleString, and friends use ISO / C locale and stay that way.

ReferenceError: Intl is not shipped here.
DENIED
SharedArrayBuffer · Atomics

Shared-memory primitives

Defeats SES-style isolation. Cynic's edge-runtime hosts are single-agent-per-isolate by design — there's nothing to share memory with.

ReferenceError: no shared agents, no Atomics.
NOT YET
Temporal.*

The Temporal date API

Stage 4, but a huge surface (Calendar / TimeZone / Instant / PlainDate / …). Its own implementation phase, not a parser opinion. Until then: Date.

ReferenceError: Temporal is not shipped here.
STRICT MODE isn't a mode. it's a worldview.
§02 · WHERE WE ARE

Pre-alpha. Not pretending otherwise.

Lexer, parser, and bytecode interpreter ship. The runtime is filling in. Generational GC landed; JIT tiers are still future work. Below is what works, what kind-of works, and what Cynic refuses to pretend works yet.

The receipts
Cynic says
What that means
test262 · parser
scoped to the corpus Cynic targets
100% attempted.
Every fixture in scope either parses or correctly rejects. Spec coverage is the next number to grow.
test262 · runtime
parse → compile → execute
Getting there.
Bigger than last week. Smaller than next week.
In-tree unit tests
tests-first, allegedly
A lot.
Quadruple digits. Nobody's bragging.
Promises & async/await
full chaining; pending-await via JSGenerator
Works.
Yes, even the part where it suspends.
Async generators
yield-await chaining via promise reactions
Works.
Surprisingly. We checked.
RegExp · full ECMA-262
backrefs, named groups, lookaround, u/v flags
Works.
Bridged from QuickJS-NG. Credit where due.
Real Symbol & BigInt primitives
NaN-boxed, pointer-tagged; well-known symbols, registry, arbitrary-precision BigInt
Works.
Primitives. Not polyfills wearing a costume.
TypedArrays + ArrayBuffer + DataView
full %TypedArray%.prototype surface
Works.
All the views. All the bytes. None of the drama.
Proxy traps
get / set / has / deleteProperty / defineProperty / ownKeys / getPrototypeOf / setPrototypeOf / isExtensible / preventExtensions / apply / construct
Works.
Every trap the spec wrote down. Apologies to your debugger.
Dynamic import()
host-loader-backed; non-Promise & settled-Promise cases through await
Works.
Returns a real Promise. The loader is up to the host. The body is up to the spec.
WeakRef · FinalizationRegistry
§26.1 / §26.2; real weak references — major sweep clears dead targets and queues registry callbacks
Works.
Genuine weakness. The collector finally honours its end of the contract.
ES2025 collection & Promise additions
Set union / intersection / difference / symmetricDifference / isSubsetOf / isSupersetOf / isDisjointFrom · Promise.try · Promise.withResolvers
Works.
Shipped. Each one cost the runtime score nothing and the spec a little dignity.
TC39 Stage 3 · Iterator.zip · Map/WeakMap upsert
behind --enable=<name> or --enable-experimental; off by default. Each scored as its own isolated test262 sweep.
Opt-in.
Stage 3 means stable enough to ship and unstable enough to break. Cynic takes the trade. Embedders can decline.
yield* delegation · for await of
sync + async generators; IteratorClose on every abrupt path the spec describes
Works.
We learned §15.5.5 by failing it, then we stopped failing it.
Real module graph
cyclic imports, indirect-binding TDZ, namespace [[Get]] ReferenceError on uninit, dynamic import()
Works.
Cyclic. TDZ on indirect imports. The Namespace [[Get]] ReferenceError the spec asked for.
Top-level await
thenable adoption + suspension across module boundaries
Works.
Async modules suspend. Sibling modules don't block. The spec said so.
Garbage collector
generational mark-sweep — short-lived objects collected without scanning the whole heap
Works.
Most objects die young. The collector finally agrees.
Proper Tail Calls (PTC)
ECMA-262 §15.10; return f(x) reuses the caller's frame instead of pushing a fresh one
Works.
Second JS engine ever to ship this. JavaScriptCore was first in 2016. V8 tried and walked it back. SpiderMonkey's tracking bug is open since 2015.
Inline property caches
monomorphic shape ICs on lda_property / sta_property / call_method + proto-load; /perf & /profile harness scaffolded
Works.
Reads land on the slot in one shape compare. No speculation involved.
JIT tiers
bytecode interpreter today; baseline / optimising tiers on the long road
Future Cynic's problem.
The plan is real. The plan is also not the code.

Full per-bucket scoreboard, history, and per-day deltas live in test262-results.md.