Something went wrong. Try again.
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162// Downlevel mode (ABI mode 1): down-levels enums, instantiated namespaces, parameter properties, `import x =` and// `export =`, matching ts.transpileModule's runtime behavior. strip.zena calls the hooks below from its handlers.// Every inserted text is free of line breaks, so lines are preserved; columns shift only inside these constructs.//// Enums and namespaces become `var E;(function(E){...}).call(E||(E={}),E);`, with the source's own braces as the// function body. Inside, `this` is the enum or namespace object, so a member or export named like the container// cannot shadow it. Enum members are also bound as locals so later initializers can name them bare.import { Memory } from 'zena:memory';import { scan, seek, reseek, more, tb, ts, te, tt, tk, tnl, delta, rescanRegex, rescanTemplate, rescanGreater, isBinop, precOf, isAssign, T_EOF, T_NAME, T_NUM, T_STR, T_TEMPLATE, T_TEMPLATE_HEAD, T_TEMPLATE_TAIL, T_LBRACE, T_RBRACE, T_LPAREN, T_RPAREN, T_SEMI, T_LBRACK, T_RBRACK, T_DOT, T_QDOT, T_ELLIPSIS, T_COMMA, T_COLON, T_EQ, T_BANG, T_TILDE, T_INC, T_DEC, T_LT, T_GT, T_PLUS, T_MINUS, T_SLASH, T_SLASH_EQ, T_STARSTAR, T_SHL, T_BAR, T_CARET, T_AMP, tokenIn,} from './lex.zena';import { keywordAt, K_CONST, K_ENUM, K_EXPORT, K_FUNCTION, K_TYPE, K_REQUIRE, K_SUPER, K_IN, K_INSTANCEOF, K_TYPEOF, K_VOID, K_DELETE, K_AWAIT, K_NEW, K_LET, K_STATIC, K_YIELD, K_IMPLEMENTS, K_INTERFACE, K_PRIVATE, K_PROTECTED, K_PUBLIC, K_FIRST_CONTEXTUAL, K_CLASS, K_VAR, K_NAMESPACE, K_MODULE, K_GLOBAL, K_DECLARE, keywordIn, KS_STRICT_RESERVED, KS_DL_DECL, TS_OPEN, TS_POSTFIX_UPDATE,} from './keywords.zena';import { getSp, getNest, getBlankDepth, getVerbatim, getStmtNest, getAnchLen, fa, fb, fc, setA, setB, setC, setSt, pushFrame, pop, belowSt, belowA, belowB, setBelowA, adv, advQuiet, advBlank, emitTo, blankRange, copyThrough, anchorPush, anchorPushAt, anchorPop, anchorOut, anchorIn, anchorErr, anchorBlank, restoreFrom, markValue, enterBlank, endStmt, back, skipBalanced, skipTemplate, pushExpr, openBlock, importEqualsEnd, failAt, outReserve, outCommit, outInsert, dropTo, setNest, setErrLen, lookType, pruneNamespaceFallbacks, errorNamespaceFallback, getStmtUnits, spansLines, errorAtStmt, releaseDecorators,} from './strip.zena';
// strip.zena's values (module-level bindings cannot cross the import cycle).let S_TOP: i32 = 1;let S_BLOCK: i32 = 2;let S_EXPRSTMT: i32 = 4;let S_CLASSBODY: i32 = 26;let S_NAMESPACE: i32 = 30;let S_DL_ENUM: i32 = 40;let B_BLOCK: i32 = 1;let B_NAMESPACE: i32 = 2;let ANCH_IN: i32 = 1;let ANCH_SEMI: i32 = 2;// pendAdd's `live` carries this bit when the declarator had no initializer: `tsc` emits no member at// all for it (`export var x: number;` leaves `N.x` absent, not merely `undefined`), unlike every other// exported declarator, which it always assigns. Only a bare identifier can lack an initializer at all// (a destructuring pattern requires one), so this is only ever computed for that shape.let PEND_NOINIT: i32 = 256;
// Enum member classes: TS writes a reverse mapping unless the value is a constant string.let C_NUM: i32 = 1; // a known number: reverse mappinglet C_STR: i32 = 2; // syntactically a string: nonelet C_EVAL: i32 = 3; // constant to TS, type known only at run time: decided by typeoflet C_OPAQUE: i32 = 4; // not constant to TS: reverse mapping
// Name table kinds.let N_DECL: i32 = 1; // a class or function declared in scope `key`let N_ENUM: i32 = 2; // an enum declared in scope `key`let N_MEMBER: i32 = 3; // a member of the enum whose N_ENUM record is `key`let N_ENUMANY: i32 = 4; // an enum of this name in any scopelet N_NS: i32 = 5; // a namespace declared in scope `key`let N_NSX: i32 = 6; // a namespace exported from the namespace whose record is `key`let N_NSEXP: i32 = 7; // a name exported from the namespace whose record is `key`
// Ctor body block kinds while waiting for (3) or right after (4) the top-level `super(...)` statement.let B_CTORW: i32 = 3;let B_CTORS: i32 = 4;let DL_CLS: i32 = 2097152; // S_CLASSBODY.a: downlevel frame; bit 20 extends; bits 0..19 class anchor index + 1
export var dlExported: i32 = 0;var dlScope: i32 = 0;var dlScopeNext: i32 = 0;var dlHookSp: i32 = -1;var dlPendBase: i32 = 0;var dlNsRec: i32 = 0; // the N_NS/N_NSX record of the namespace body being parsed, 0 outside
// Module-kind tracking (see "Module-kind tracking" below, near importEquals/exportAssign): whether this// file has committed to a value-level ESM import/export declaration (mkEsm) or a CommonJS-only `import x// =`/`export =` (mkCjs) so far, and whether the file is already known to mix both (mixed). Reset by// init() per file/stream.var mkEsm: boolean = false;var mkCjs: boolean = false;var mixed: boolean = false;// Scopes (dlScope ids) a fast-pathed namespace's own `namespaceEnd` must decline instead of wrapping in// an IIFE, because a *nested namespace* (not a module/global augmentation directly in its own body —// `namespaceEnd`'s own `tx[base + 8]` count covers that shape instead, see its own comment) itself// declined and restored its own span to literal, un-erased source text: `namespaceEnd`'s own propagation// (when it declines) adds the restored *parent* scope here, checked back by that scope's own// `namespaceEnd` later. Never cleared, same as the rest of this file's small never-shrinking lookup lists// (`nm`, `declSpan`, ...) — real files decline rarely, so this stays short. Entirely confined to this// file (no erase.wasm cost): nothing outside namespaceEnd itself ever writes or reads it.var declinedScope: FixedArray<i32> = new FixedArray<i32>(8, 0);var declinedScopeN: i32 = 0;
function declineScope(scope: i32): void { var i = 0; while (i < declinedScopeN) { if (declinedScope[i] == scope) return; i = i + 1; } declinedScope = grow(declinedScope, declinedScopeN + 1); declinedScope[declinedScopeN] = scope; declinedScopeN = declinedScopeN + 1;}
function scopeDeclined(scope: i32): boolean { var i = 0; while (i < declinedScopeN) { if (declinedScope[i] == scope) return true; i = i + 1; } return false;}
// ---------------------------------------------------------------------------// Text stack: name entries [len, bytes...] and small ints, owned LIFO by the open constructs.
var tx: FixedArray<i32> = new FixedArray<i32>(256, 0);var txLen: i32 = 0;
// Returns `a` if it holds `need` entries, else a copy with at least twice the room. Every table's// capacity check goes through here, so a call site is one call instead of an inlined compare.function grow(a: FixedArray<i32>, need: i32): FixedArray<i32> { if (need <= a.length) return a; var cap = a.length * 2; while (cap < need) cap = cap * 2; let b = new FixedArray<i32>(cap, 0); var i = 0; while (i < a.length) { b[i] = a[i]; i = i + 1; } return b;}
function txPush(v: i32): void { tx = grow(tx, txLen + 1); tx[txLen] = v; txLen = txLen + 1;}
// Pushes input [a, b) as a name entry; returns its offset.function txName(a: i32, b: i32): i32 { let o = txLen; txPush(b - a); var i = a; while (i < b) { txPush(Memory.default.getU8(i + delta)); i = i + 1; } return o;}
// Pushes input [a, b) in double quotes.function txQuoted(a: i32, b: i32): i32 { let o = txLen; txPush(b - a + 2); txPush(34); var i = a; while (i < b) { txPush(Memory.default.getU8(i + delta)); i = i + 1; } txPush(34); return o;}
function txNext(o: i32): i32 { return o + 1 + tx[o];}
// BEGIN GENERATED// GENERATED by scripts/gen/downlevel-text.mjs from its template table. Do not edit by hand.let TEXT: i32 = 2304;
function litVar(): void { tpl(0, 0);}
function litConst(): void { tpl(4, 0);}
function litModExp(): void { tpl(10, 0);}
function litSemi(): void { tpl(25, 0);}
function litRb(): void { tpl(27, 0);}
function litDestr(): void { tpl(29, 0);}
function litDestrEnd(): void { tpl(35, 0);}
function tDestrEndAs(o: i32): void { tpl(43, o);}
function tItem(o: i32): void { tpl(48, o);}
function tName(o: i32): void { tpl(51, o);}
function tFn(o: i32): void { tpl(53, o);}
function litLb(): void { tpl(67, 0);}
function tDotFn(o: i32): void { tpl(69, o);}
function tCall(o: i32): void { tpl(88, o);}
function tCallX(o: i32): void { tpl(109, o);}
function tEiNum(o: i32): void { tpl(142, o);}
function tEiEval(o: i32): void { tpl(171, o);}
function tEiStr(o: i32): void { tpl(222, o);}
function tEnNum(o: i32): void { tpl(239, o);}
function tEnEval(o: i32): void { tpl(257, o);}
function tThisIdx(o: i32): void { tpl(303, o);}
function litAuto0(): void { tpl(311, 0);}
function tAutoI(o: i32): void { tpl(314, o);}
function tAutoN(o: i32): void { tpl(319, o);}
function litAutoVoid(): void { tpl(330, 0);}
function tAssign(o: i32): void { tpl(338, o);}
function tLive(o: i32): void { tpl(348, o);}
function tAmbientLive(o: i32): void { tpl(447, o);}
function wInfinity(a: i32, b: i32): boolean { return isWord(a, b, 597, 8);}
function wNaN(a: i32, b: i32): boolean { return isWord(a, b, 605, 3);}
function wEval(a: i32, b: i32): boolean { return isWord(a, b, 608, 4);}
function wArguments(a: i32, b: i32): boolean { return isWord(a, b, 612, 9);}
// END GENERATED
// Emits the template at offset `at` of the text, with `@` replaced by the name entry at `o`.function tpl(at: i32, o: i32): void { var i = TEXT + at; while (true) { let c = Memory.default.getU8(i); if (c == 126) return; if (c == 64) { nameOut(o); } else { Memory.default.setU8(outReserve(1), c); outCommit(1); } i = i + 1; }}
function nameOut(o: i32): void { let len = tx[o]; var d = outReserve(len); var j = 0; while (j < len) { Memory.default.setU8(d, tx[o + 1 + j]); d = d + 1; j = j + 1; } outCommit(len);}
// Input [a, b) equals the n bytes at offset `at` of the text.function isWord(a: i32, b: i32, at: i32, n: i32): boolean { if (b - a != n) return false; var i = 0; while (i < n) { if (Memory.default.getU8(TEXT + at + i) != Memory.default.getU8(a + i + delta)) return false; i = i + 1; } return true;}
// ---------------------------------------------------------------------------// Name table: records [hash, kind, key, len, head, link, stamp, bytes...] with an open-addressing index. A member or// export record (`key` is its owner's record) is linked from its owner's `head`, newest first. Records are never// removed only when the namespace body they were declared in closes (prune), and scope ids are unique per body,// so names of closed scopes never match again. The index marks a removed record's slot with -1.
var nm: FixedArray<i32> = new FixedArray<i32>(1024, 0);var nmLen: i32 = 1;var ht: FixedArray<i32> = new FixedArray<i32>(256, 0);var htCount: i32 = 0; // used index slots, removed ones includedvar nmCount: i32 = 0; // records
function hashOf(kind: i32, key: i32, a: i32, b: i32): i32 { var h = kind * 31 + key * 1000003; var i = a; while (i < b) { h = (h ^ Memory.default.getU8(i + delta)) * 16777619; i = i + 1; } return h & 0x7fffffff;}
function htInsert(r: i32): void { let mask = ht.length - 1; var i = nm[r] & mask; while (ht[i] != 0) i = (i + 1) & mask; ht[i] = r;}
function nmFind(kind: i32, key: i32, a: i32, b: i32): i32 { let h = hashOf(kind, key, a, b); let mask = ht.length - 1; var i = h & mask; while (true) { let r = ht[i]; if (r == 0) return -1; if (r > 0 && nm[r] == h && nm[r + 1] == kind && nm[r + 2] == key && nm[r + 3] == b - a) { var j = 0; while (j < b - a && nm[r + 7 + j] == Memory.default.getU8(a + j + delta)) j = j + 1; if (j == b - a) return r; } i = (i + 1) & mask; }}
function rehash(): void { var size = 256; while (size < (nmCount + 1) * 4) size = size * 2; ht = new FixedArray<i32>(size, 0); htCount = 0; var r = 1; while (r < nmLen) { htInsert(r); htCount = htCount + 1; r = r + 7 + nm[r + 3]; }}
function nmAdd(kind: i32, key: i32, a: i32, b: i32): i32 { if ((htCount + 1) * 2 > ht.length) rehash(); let need = nmLen + 7 + (b - a); nm = grow(nm, need); let r = nmLen; nm[r] = hashOf(kind, key, a, b); nm[r + 1] = kind; nm[r + 2] = key; nm[r + 3] = b - a; nm[r + 4] = 0; nm[r + 5] = 0; nm[r + 6] = 0; if (kind == N_MEMBER || kind == N_NSEXP) { nm[r + 5] = nm[key + 4]; nm[key + 4] = r; } var j = 0; while (j < b - a) { nm[r + 7 + j] = Memory.default.getU8(a + j + delta); j = j + 1; } nmLen = need; htInsert(r); htCount = htCount + 1; nmCount = nmCount + 1; return r;}
// Drops the records added since `mark` by a namespace body that just closed, except what a later declaration of// the same namespace, or a qualified enum reference, can still look up.function prune(mark: i32): void { if (nmLen == mark) return; let fwd = new FixedArray<i32>(nmLen - mark, 0); var r = mark; var w = mark; while (r < nmLen) { let k = nm[r + 1]; let size = 7 + nm[r + 3]; fwd[r - mark] = -1; if (k == N_NSX || k == N_NSEXP || k == N_ENUMANY) { fwd[r - mark] = w; w = w + size; } r = r + size; } let mask = ht.length - 1; r = mark; while (r < nmLen) { var i = nm[r] & mask; while (ht[i] != r) i = (i + 1) & mask; ht[i] = -1; r = r + 7 + nm[r + 3]; } r = mark; while (r < nmLen) { let size = 7 + nm[r + 3]; let d = fwd[r - mark]; if (d >= 0) { var j = 0; while (j < size) { nm[d + j] = nm[r + j]; j = j + 1; } if (nm[d + 4] >= mark) nm[d + 4] = fwd[nm[d + 4] - mark]; if (nm[d + 5] >= mark) nm[d + 5] = fwd[nm[d + 5] - mark]; } else { nmCount = nmCount - 1; } r = r + size; } if (dlNsRec != 0 && nm[dlNsRec + 4] >= mark) nm[dlNsRec + 4] = fwd[nm[dlNsRec + 4] - mark]; nmLen = w; r = mark; while (r < nmLen) { htInsert(r); htCount = htCount + 1; r = r + 7 + nm[r + 3]; }}
// ---------------------------------------------------------------------------// Context.
function kw(k: i32): boolean { return tt == T_NAME && tk == k;}
// The statement on top sits directly in the module or a namespace body, where declarations merge.function inScope(): boolean { if (getSp() < 2 || getBlankDepth() > 0) return false; let s = belowSt(1); return s == S_TOP || (s == S_BLOCK && belowA(1) == B_NAMESPACE);}
// `getVerbatim() == 0` excludes a `module N {}`-keyword statement's own declined body (`stmtNamespace`'s// "module N {} is always an error" branch, engine/strip.zena): it still opens a B_NAMESPACE block so its// own nested statements parse structurally, but everything inside is meant to pass through completely// unchanged (verbatim/declined), never down-leveled. Without this, `ambientVar` (below) still saw// `inNs()` true for a `declare module Name { export var x: T; }` whose name happens to use the `module`// keyword, and inserted a live-binding accessor into output text that was supposed to stay untouched.function inNs(): boolean { return getSp() >= 2 && getBlankDepth() == 0 && getVerbatim() == 0 && belowSt(1) == S_BLOCK && belowA(1) == B_NAMESPACE;}
export function init(): void { dlExported = 0; dlScope = 0; dlScopeNext = 0; dlHookSp = -1; dlNsRec = 0; txLen = 0; nmLen = 1; htCount = 0; nmCount = 0; mkEsm = false; mkCjs = false; mixed = false; var i = 0; while (i < ht.length) { ht[i] = 0; i = i + 1; }}
// A name already declared in this scope by a class, function, enum or namespace: a merging enum or namespace// reuses that binding, so it writes neither `var` nor `export`.function declared(a: i32, b: i32): boolean { return inScope() && nmFind(N_DECL, dlScope, a, b) >= 0;}
function declare(a: i32, b: i32): void { if (inScope() && nmFind(N_DECL, dlScope, a, b) < 0) nmAdd(N_DECL, dlScope, a, b);}
// At `export` used as a modifier: 1 when it must be blanked, 2 when the step must wait for input. Inside a// namespace body the declaration that follows is attached to the namespace instead.export function exportModifier(): i32 { if (inNs()) { dlExported = 1; return 1; } let m = tb; scan(); if (more == 0 && kw(K_CONST)) scan(); var r = 0; if (more == 0 && tt == T_NAME && tk == K_ENUM) { scan(); if (more == 0 && tt == T_NAME && declared(ts, te)) r = 1; } if (!back(m)) return 2; return r;}
// Names exported by the namespace statement in progress, attached after it ends (flushed from hBlock).function pendAdd(live: i32, a: i32, b: i32): void { if (dlHookSp != getSp() - 1) { dlHookSp = getSp() - 1; dlPendBase = txLen; } txPush(live); txName(a, b); nsExport(a, b);}
// Later declarations of the same namespace bind its earlier exports as locals.function nsExport(a: i32, b: i32): void { if (dlNsRec != 0 && nmFind(N_NSEXP, dlNsRec, a, b) < 0) nmAdd(N_NSEXP, dlNsRec, a, b);}
var stamp: i32 = 0;var used: FixedArray<i32> = new FixedArray<i32>(32, 0);var usedN: i32 = 0;var kinds: FixedArray<i32> = new FixedArray<i32>(32, 0);
// namespaceStart's own ancestor-merge destructure target, set during its lookahead and read during its// commit-phase dotted loop: the depth (0 = the outermost, non-dotted segment) of the *deepest existing*// ancestor along a dotted path that the full requested path doesn't itself reach (`namespace A.B.C {}`// reopening `A.B` while `A.B.C` itself is new) — `-1` when there is none to destructure (either nothing// along the path pre-exists, or the full path itself is the merge, which the ordinary `destructure()` at// the deepest level already covers).var ambDestrDepth: i32 = -1;
// Lookahead from a body's `{`: the records of `kind` under `key` that the body names, each once. Merged// declarations bind only those as locals, so a long run of reopened declarations stays linear.//// `excludeShadow`, used only for a merge block's own destructure (namespaceStart): a depth-0 `class`/`let`/// `const` declaration re-using one of these names doesn't just "use" the earlier merged member, it creates a// fresh lexical binding that the destructured local of the same name (`var {Point,}=this;`) would collide with// ("Identifier 'Point' has already been declared" — `var` is compatible with a later `var`/`function` of the// same name, which is why those two keywords aren't included, but never with `let`/`const`/`class`). That// occurrence, and the lexical declaration it introduces, must drop the name from `used` instead of adding it// (corpus/downlevel.jsonl, "namespace-merge-var-class-collision").//// `writeOnly`, used only for declNames' own "is this uninitialized export ever touched again" check: a plain// read of the name (or a mention inside some other declarator's own type annotation, e.g. a self-referencing// interface merge — `export var X: X;`) never counts, only an actual write (`name =`/a compound-assign op, or// `name++`/`--name`) does — a read gets the same value either way (the namespace member and the disconnected// local both read as `undefined`), but a write needs the member to exist for the assignment to land on it// ("namespace-uninitialized-export-overcounted", corpus/downlevel.jsonl).function scanUsed(kind: i32, key: i32, excludeShadow: boolean, writeOnly: boolean): void { stamp = stamp + 1; usedN = 0; var depth = 0; var declKw = false; var pendingR = -1; // writeOnly: the record the previous token named, still waiting to see a write marker var wasIncDec = false; while (true) { let wasDeclKw = declKw; declKw = false; let prevR = pendingR; pendingR = -1; let hadIncDec = wasIncDec; wasIncDec = false; scan(); if (more == 1 || tt == T_EOF) return; if (writeOnly && prevR >= 0 && (isAssign(tt) || tt == T_INC || tt == T_DEC) && nm[prevR + 6] != stamp) { nm[prevR + 6] = stamp; used = grow(used, usedN + 1); used[usedN] = prevR; usedN = usedN + 1; } if (tt == T_NAME) { let r = nmFind(kind, key, ts, te); if (excludeShadow && depth == 0 && wasDeclKw) { if (r >= 0) { if (nm[r + 6] != stamp) { nm[r + 6] = stamp; } else { var k = 0; while (k < usedN) { if (used[k] == r) { used[k] = used[usedN - 1]; usedN = usedN - 1; break; } k = k + 1; } } } } else if (writeOnly) { if (r >= 0 && hadIncDec && nm[r + 6] != stamp) { nm[r + 6] = stamp; used = grow(used, usedN + 1); used[usedN] = r; usedN = usedN + 1; } pendingR = r; } else if (r >= 0 && nm[r + 6] != stamp) { nm[r + 6] = stamp; used = grow(used, usedN + 1); used[usedN] = r; usedN = usedN + 1; } if (excludeShadow && depth == 0 && (tk == K_CLASS || tk == K_LET || tk == K_CONST)) declKw = true; } else if (tt == T_INC || tt == T_DEC) { wasIncDec = true; } else if (tt == T_LBRACE || tt == T_TEMPLATE_HEAD) { kinds = grow(kinds, depth + 1); kinds[depth] = 0; if (tt == T_TEMPLATE_HEAD) kinds[depth] = 1; depth = depth + 1; } else if (tt == T_RBRACE) { if (depth == 0) return; if (kinds[depth - 1] == 1) { rescanTemplate(); if (more == 1) return; if (tt == T_TEMPLATE_TAIL) depth = depth - 1; } else { depth = depth - 1; } } }}
// ---------------------------------------------------------------------------// A down-leveled namespace/enum IIFE's own parameter name. Normally it's just the construct's own name, but// `tsc`'s module/enum transform conservatively renames it (`name_1`, `name_2`, ...) whenever some declaration// inside the about-to-open body — at ANY depth, even a nested function's own unrelated local, matching `tsc`'s// own depth-insensitive `isUniqueLocalName` check, not real scope analysis — already binds that exact name// ("namespace-lexical-member-named-like-namespace", corpus/downlevel.jsonl). The outer `var <name>;` and the// merge call's own `<name>||(<name>={})` are never renamed, only the function's formal parameter, so this is// invisible outside the down-leveled span (which the oracle never line/shape-compares inside) — the exact// suffix `tsc` would have picked doesn't matter, only that the one picked here is actually collision-free.
var declSpan: FixedArray<i32> = new FixedArray<i32>(64, 0); // pairs [a, b, a, b, ...], input spansvar declSpanN: i32 = 0;// Set by declSpanScan's own walk (below) alongside its declaration-name collection: the number of depth-0// `module`/`global` augmentation statements directly in the body — ts-blank-space's own, non-recursive// "conservatively a value" rule for exactly these (engine/strip.zena's stmtNamespace, which calls plain// `markValue` for one of these exactly once each, same as for an ordinary value-producing statement) —// read by namespaceStart/namespaceEnd (below) to tell "every mark `values` counted came from one of// these" apart from "something else also contributed", without strip.zena's shared `markValue` needing to// distinguish the two itself (which would cost erase mode bytes for a signal it never reads — see// declineModuleKeyword, above, for the identical reasoning applied to the outer statement itself). See// corpus/downlevel.jsonl, "namespace-uninstantiated-ambient-only-wrapped".var declSpanModuleOrGlobalN: i32 = 0;
function declSpanAdd(a: i32, b: i32): void { declSpan = grow(declSpan, declSpanN + 2); declSpan[declSpanN] = a; declSpan[declSpanN + 1] = b; declSpanN = declSpanN + 2;}
// Compares an input span against a tx name entry ([len, bytes...] at offset `o`).function sameAsEntry(a: i32, b: i32, o: i32): boolean { let len = tx[o]; if (b - a != len) return false; var i = 0; while (i < len) { if (Memory.default.getU8(a + i + delta) != tx[o + 1 + i]) return false; i = i + 1; } return true;}
function declSpanHasEntry(o: i32): boolean { var i = 0; while (i < declSpanN) { if (sameAsEntry(declSpan[i], declSpan[i + 1], o)) return true; i = i + 2; } return false;}
// Scans the upcoming namespace/enum body (cursor at its own `{`) for every declaration name at any depth, into// declSpan: a `var`/`let`/`const` declarator's first (simple) name, or a `class`/`function`/`namespace`/`module`// declaration's own name. A mere reference, or a name inside a string/template, is never a declaration and// never collected — only an actually-bound identifier can collide with the IIFE's own parameter.function declSpanScan(): void { declSpanN = 0; declSpanModuleOrGlobalN = 0; var depth = 0; var declKw = false; var atStart = true; while (true) { let wasDeclKw = declKw; let wasAtStart = atStart; declKw = false; atStart = false; scan(); if (more == 1 || tt == T_EOF) return; if (tt == T_NAME) { if (wasDeclKw) { declSpanAdd(ts, te); } else if (keywordIn(tk, KS_DL_DECL)) { declKw = true; } if (depth == 0 && wasAtStart) { if (tk == K_MODULE || tk == K_GLOBAL) declSpanModuleOrGlobalN = declSpanModuleOrGlobalN + 1; else if (tk == K_EXPORT || tk == K_DECLARE) atStart = true; } } else if (tt == T_LBRACE || tt == T_TEMPLATE_HEAD) { kinds = grow(kinds, depth + 1); kinds[depth] = 0; if (tt == T_TEMPLATE_HEAD) kinds[depth] = 1; depth = depth + 1; } else if (tt == T_RBRACE) { if (depth == 0) return; if (kinds[depth - 1] == 1) { rescanTemplate(); if (more == 1) return; if (tt == T_TEMPLATE_TAIL) { depth = depth - 1; if (depth == 0) atStart = true; } } else { depth = depth - 1; if (depth == 0) atStart = true; } } else if (tt == T_SEMI && depth == 0) { atStart = true; } }}
// Pushes n (n >= 0) in decimal. Zena's `/` is a float division, so n / 10 is the exact reciprocal// multiply for any non-negative i32: n * ceil(2^35 / 10) >> 35.function txDecimal(n: i32): void { if (n >= 10) txDecimal((((n as i64) * (3435973837 as i64)) >> (35 as i64)) as i32); txPush(48 + n % 10);}
// Builds "<name [a,b)>_<n>" (n >= 1) at the current txLen; returns its offset. The caller rolls txLen back.function txSuffixed(a: i32, b: i32, n: i32): i32 { let o = txName(a, b); txPush(95); txDecimal(n); tx[o] = txLen - o - 1; return o;}
// The `{a,b,}` items scanUsed found, shared by `destructure` (closes over `this`) and `destructureAs`// (closes over a dotted chain's own outer segment instead — see `ambDestrDepth` above).function destructureItems(): void { var i = 0; while (i < usedN) { let r = used[i]; let o = txLen; txPush(nm[r + 3]); var j = 0; while (j < nm[r + 3]) { txPush(nm[r + 7 + j]); j = j + 1; } tItem(o); txLen = o; i = i + 1; }}
// `var {a,b,}=this;` for the names scanUsed found.function destructure(): void { litDestr(); destructureItems(); litDestrEnd();}
// `var {a,b,}=<nameOff>;` for the names scanUsed found, closing over a dotted chain's own ancestor// segment (its already-emitted IIFE parameter) instead of `this` — see `ambDestrDepth` above.function destructureAs(nameOff: i32): void { litDestr(); destructureItems(); tDestrEndAs(nameOff);}
// An overload signature of an exported function: it is blanked and exports nothing.export function dropPending(): void { if (dlHookSp == getSp() - 1) { txLen = dlPendBase; dlHookSp = -1; }}
export function hookSp(): i32 { return dlHookSp;}
// At a statement boundary of the block whose frame is on top and that registered a hook. Returns true when the// step must wait for input.export function blockHook(): boolean { let k = fa(); if (k == B_NAMESPACE) { litSemi(); var i = dlPendBase; while (i < txLen) { let live = tx[i]; if ((live & PEND_NOINIT) == 0) { if ((live & 128) != 0) tLive(i + 1); else tAssign(i + 1); } i = txNext(i + 1); } txLen = dlPendBase; dlHookSp = -1; return false; } if (k == B_CTORS) { litSemi(); ctorAssigns(belowB(3)); dlHookSp = fb(); setA(B_BLOCK); return false; } if (tt == T_RBRACE) { dlHookSp = fb(); setA(B_BLOCK); return false; } if (kw(K_SUPER)) { let m = tb; scan(); let call = more == 0 && tt == T_LPAREN; if (!back(m)) return true; if (call) setA(B_CTORS); } return false;}
// ---------------------------------------------------------------------------// Enums. Frame (the statement's): a = the N_ENUM record, b = text base, c = 1 member start / 2 after a member.// Text at base: [exported, prevClass, curClass, prevIdent, curIdent, enum name, prev member name, cur member name].
function reservedName(k: i32): boolean { if (k != 0 && k < K_FIRST_CONTEXTUAL) return true; return keywordIn(k, KS_STRICT_RESERVED);}
// A name usable as a local `var`: an ASCII identifier that is not reserved in strict code.function bindable(a: i32, b: i32): boolean { if (b <= a) return false; var i = a; while (i < b) { let c = Memory.default.getU8(i + delta); let alpha = (c >= 97 && c <= 122) || (c >= 65 && c <= 90) || c == 95 || c == 36; if (!alpha && !(i > a && c >= 48 && c <= 57)) return false; i = i + 1; } if (reservedName(keywordAt(a + delta, b - a))) return false; return !wEval(a, b) && !wArguments(a, b);}
export function enumStart(): void { let m = tb; let isConst = kw(K_CONST); if (isConst) scan(); if (more == 0) scan(); var ok = more == 0 && tt == T_NAME; let na = ts; let nb = te; if (ok) scan(); ok = ok && more == 0 && tt == T_LBRACE; usedN = 0; if (ok && inScope()) { let prior = nmFind(N_ENUM, dlScope, na, nb); if (prior >= 0) scanUsed(N_MEMBER, prior, false, false); } if (!back(m)) return; if (!ok) { failAt(47); return; } markValue(); let exported = dlExported; dlExported = 0; if (declared(na, nb)) { advBlank(); } else { declare(na, nb); emitTo(ts); litVar(); dropTo(ts + 3); blankRange(ts + 3, te, false); scan(); } if (isConst) advBlank(); var key = dlScope; if (!inScope()) { dlScopeNext = dlScopeNext + 1; key = -dlScopeNext; } var owner = nmFind(N_ENUM, key, ts, te); let merged = owner >= 0; if (!merged) { owner = nmAdd(N_ENUM, key, ts, te); if (nmFind(N_ENUMANY, 0, ts, te) < 0) nmAdd(N_ENUMANY, 0, ts, te); } if (exported != 0) nsExport(ts, te); let base = txLen; txPush(exported); txPush(0); txPush(0); txPush(0); txPush(0); txName(ts, te); adv(); tFn(base + 5); setNest(getNest() + 1); adv(); if (merged && usedN > 0) destructure(); setSt(S_DL_ENUM); setA(owner); setB(base); setC(1);}
var cOwner: i32 = 0;var cBail: i32 = 0;
// Classifier bits: 1 constant to TS's evaluator, 2 syntactically a string, 4 a known number.function clsName(a: i32, b: i32): i32 { if (nmFind(N_MEMBER, cOwner, a, b) >= 0) return 1; if (wInfinity(a, b) || wNaN(a, b)) return 5; return 0;}
// Postfix operators after an operand. `n` counts them: `E.X` and `E["X"]` stay constant for a known enum E.function clsPostfix(r: i32, ent: i32): i32 { var n = 0; while (true) { if (more == 1 || cBail != 0) return 0; if (tt == T_DOT || tt == T_QDOT) { if (tt == T_QDOT) n = n + 1; scan(); if (more == 0 && tt == T_NAME) scan(); n = n + 1; continue; } if (tt == T_LBRACK) { let m = tb; scan(); let str = more == 0 && tt == T_STR; if (str) scan(); if (str && more == 0 && tt == T_RBRACK) { scan(); n = n + 1; continue; } if (more == 1) return 0; reseek(m); if (skipBalanced() < 0) return 0; n = n + 2; continue; } if (tt == T_LPAREN) { if (skipBalanced() < 0) return 0; n = n + 2; continue; } if (tt == T_TEMPLATE) { scan(); n = n + 2; continue; } if (tt == T_TEMPLATE_HEAD) { if (skipTemplate() < 0) return 0; n = n + 2; continue; } if ((tokenIn(TS_POSTFIX_UPDATE)) && tnl == 0) { scan(); n = n + 2; continue; } break; } if (n == 0) return r; if (n == 1) return ent; return 0;}
function clsPrimary(): i32 { if (more == 1) return 0; if (tt == T_NUM) { scan(); return clsPostfix(5, 0); } if (tt == T_STR || tt == T_TEMPLATE) { scan(); return clsPostfix(3, 0); } if (tt == T_TEMPLATE_HEAD) { var ev = 1; while (true) { scan(); if (more == 1) return 0; let r = clsExpr(0); if ((r & 1) == 0) ev = 0; if (more == 1 || cBail != 0 || tt != T_RBRACE) { cBail = 1; return 2; } rescanTemplate(); if (more == 1) return 0; if (tt == T_TEMPLATE_TAIL) { scan(); return clsPostfix(2 | ev, 0); } } } if (tt == T_LPAREN) { scan(); let r = clsExpr(0); if (more == 1 || cBail != 0 || tt != T_RPAREN) { cBail = 1; return 0; } scan(); return clsPostfix(r, 0); } if (tt == T_NAME && (tk == 0 || tk >= K_FIRST_CONTEXTUAL)) { let a = ts; let b = te; scan(); var ent = 0; if (nmFind(N_ENUMANY, 0, a, b) >= 0) ent = 1; return clsPostfix(clsName(a, b), ent); } if (tt == T_SLASH || tt == T_SLASH_EQ) { rescanRegex(); if (more == 0) scan(); return clsPostfix(0, 0); } if (tt == T_LBRACK || tt == T_LBRACE) { if (skipBalanced() < 0) return 0; return clsPostfix(0, 0); } cBail = 1; return 0;}
function clsUnary(): i32 { if (more == 1) return 0; if (tt == T_PLUS || tt == T_MINUS || tt == T_TILDE) { scan(); let r = clsUnary(); return (r & 1) * 5; } if (tt == T_BANG || tt == T_INC || tt == T_DEC || kw(K_TYPEOF) || kw(K_VOID) || kw(K_DELETE) || kw(K_AWAIT) || kw(K_NEW)) { scan(); clsUnary(); return 0; } if (tt == T_LT) { cBail = 1; return 0; } return clsPrimary();}
function evalOp(op: i32): boolean { return (op >= T_SHL && op <= T_STARSTAR) || op == T_BAR || op == T_CARET || op == T_AMP;}
function clsExpr(minPrec: i32): i32 { var l = clsUnary(); while (more == 0 && cBail == 0) { if (tt == T_GT) { rescanGreater(); if (more == 1) return 0; } var p = 0; if (isBinop(tt)) p = precOf(tt); else if (kw(K_IN) || kw(K_INSTANCEOF)) p = 11; if (p == 0 || p < minPrec) break; let op = tt; scan(); var rp = p + 1; if (op == T_STARSTAR) rp = p; let r = clsExpr(rp); var v = 0; if ((l & 1) != 0 && (r & 1) != 0 && evalOp(op)) { v = 1; if (op != T_PLUS || (l & r & 4) != 0) v = 5; } if (op == T_PLUS && ((l | r) & 2) != 0) v = v | 2; l = v; } return l;}
// The current token starts an initializer; read-only, the caller restores the lexer.function classify(owner: i32): i32 { cOwner = owner; cBail = 0; var r = clsExpr(0); if (cBail != 0) r = r & 2; else if (tt != T_COMMA && tt != T_RBRACE) r = 0; if ((r & 2) != 0) return C_STR; if ((r & 5) == 5) return C_NUM; if ((r & 1) != 0) return C_EVAL; return C_OPAQUE;}
export function enumStep(): void { let base = fb(); let enumEnd = txNext(base + 5); if (fc() == 2) { enumTail(base, enumEnd); return; } if (tt == T_RBRACE) { setNest(getNest() - 1); adv(); if (tx[base] != 0) tCallX(base + 5); else tCall(base + 5); txLen = base; pop(); return; } // kind: 1 identifier kept in place, 2 identifier spelled as a string, 3 other literal name, // 4 reserved identifier, 5 computed `[literal]`. let m = tb; var kind = 0; var na = ts; var nb = te; if (tt == T_NAME) { if (reservedName(tk) || wEval(ts, te) || wArguments(ts, te)) kind = 4; else kind = 1; } else if (tt == T_STR) { if (bindable(ts + 1, te - 1)) kind = 2; else kind = 3; } else if (tt == T_NUM) { kind = 3; } var nameEnd = te; if (tt == T_LBRACK) { let open = tb; scan(); if (more == 0 && (tt == T_STR || tt == T_TEMPLATE)) { na = ts; nb = te; scan(); } if (more == 0 && tt == T_RBRACK) { kind = 5; nameEnd = te; scan(); } else if (more == 0) { // Not a literal, which TS rejects: the key expression is copied as it is. reseek(open); na = te; nameEnd = skipBalanced(); nb = nameEnd - 1; if (nameEnd >= 0) kind = 5; } } else if (more == 0) { scan(); } let hasInit = more == 0 && tt == T_EQ; var cls = C_NUM; if (hasInit) { scan(); if (more == 0) cls = classify(fa()); } if (!back(m)) return; if (kind == 0) { failAt(48); return; } let owner = fa(); let prevClass = tx[base + 1]; var cur = 0; if (kind == 1) { nmAdd(N_MEMBER, owner, ts, te); cur = txName(ts, te); adv(); } else if (kind == 2) { nmAdd(N_MEMBER, owner, ts + 1, te - 1); cur = txName(ts + 1, te - 1); emitTo(ts); tName(cur); blankRange(ts, te, false); scan(); } else { if (kind == 4) cur = txQuoted(na, nb); else cur = txName(na, nb); emitTo(ts); tThisIdx(cur); blankRange(ts, nameEnd, false); seek(nameEnd); scan(); } tx[base + 4] = 0; if (kind <= 2) tx[base + 4] = 1; setC(2); if (hasInit) { tx[base + 2] = cls; adv(); pushExpr(0, 2); return; } tx[base + 2] = C_NUM; if (prevClass == 0) { litAuto0(); } else if (prevClass == C_NUM || prevClass == C_EVAL) { if (tx[base + 3] != 0) tAutoI(enumEnd); else tAutoN(enumEnd); } else { litAutoVoid(); }}
function enumTail(base: i32, enumEnd: i32): void { if (tt != T_COMMA && tt != T_RBRACE) { failAt(49); return; } let cls = tx[base + 2]; let ident = tx[base + 4]; var cur = enumEnd; if (tx[base + 1] != 0) cur = txNext(enumEnd); if (ident != 0) { if (cls == C_STR) tEiStr(cur); else if (cls == C_EVAL) tEiEval(cur); else tEiNum(cur); } else { if (cls == C_STR) litSemi(); else if (cls == C_EVAL) tEnEval(cur); else tEnNum(cur); } if (cur != enumEnd) { let len = tx[cur] + 1; var i = 0; while (i < len) { tx[enumEnd + i] = tx[cur + i]; i = i + 1; } } txLen = txNext(enumEnd); tx[base + 1] = cls; tx[base + 3] = ident; setC(1); if (tt == T_COMMA) advBlank();}
// ---------------------------------------------------------------------------// Namespaces. The S_NAMESPACE frame has b = 3 and c = text base: [parent scope, exported, name count, parent// record, first name start, first name end, name table mark, own start (UTF-16 units), depth-0// module/global augmentation count, names...].// The header tokens were verified by stmtNamespace's lookahead; `lead` is 1 after `export`.
// Declines a `module N {}`/`export module N {}` statement that reached this function's own fast path// (stmtNamespace's own fast-path condition, engine/strip.zena, doesn't exclude the `module` keyword — see// namespaceStart's own call site below for why), matching erase mode's unconditional "module N {} is// always an error and left untouched" rule for the identical shape (ts-blank-space's own rule for any// `ModuleDeclaration` not itself flagged `Namespace`, confirmed in its `visitModule`) — one error at the// statement's own start, and the statement's full original text, untouched. Cursor is at the `module`// keyword itself (`m0` marks the statement's own true start, before any `export`). Returns false, leaving// the cursor exactly where it started, without committing anything, only when more input is needed — the// caller then falls through to its own (never reached in practice: declare is excluded by the fast path,// and `declineModuleKeyword` only exists for non-declare statements, see note above) namespace parsing,// which will retry this whole call from scratch once more input arrives, same as every other downlevel hook.function declineModuleKeyword(m0: i32, lead: i32): boolean { while (more == 0 && tt != T_LBRACE) scan(); if (more == 1) { reseek(m0); return false; } let end = skipBalanced(); if (end < 0) { reseek(m0); return false; } reseek(m0); releaseDecorators(); errorNamespaceFallback(getStmtUnits(), getStmtUnits() + 1); copyThrough(end); pop(); return true;}
export function namespaceStart(start: i32, lead: i32): void { let exported = lead == 1 && inNs(); // Find the earlier declaration this one merges with, if any, and the exports of it the body names. let m0 = tb; if (lead == 1) scan(); // stmtNamespace's own fast-path condition (engine/strip.zena) deliberately doesn't exclude the `module` // keyword (as opposed to `namespace`) — doing so there would cost erase mode a few bytes for a boolean // it never uses, since `mode` is a genuine runtime parameter to `init`, never provably constant there. // Checked here instead, at zero cost to erase mode (this whole file is out of its build): `module N {}` // is unconditionally declined by ts-blank-space (confirmed in its `visitModule`) regardless of content, // the same as `isModule && !isString`'s own branch just below this call's own site in stmtNamespace — // matched exactly by `declineModuleKeyword` instead of ever starting this function's own rewriting. if (kw(K_MODULE) && declineModuleKeyword(m0, lead)) return; scan(); var kind0 = N_NS; var key0 = dlScope; if (exported) { kind0 = N_NSX; key0 = dlNsRec; } var prior = nmFind(kind0, key0, ts, te); // `lastFound`/`lastFoundDepth` track the deepest existing ancestor along the dotted chain even once // `prior` itself falls off it (the chain's own full path doesn't pre-exist, but a shallower prefix of // it does — "namespace A.B.C {}" reopening "A.B" while "A.B.C" itself is new): depth 0 is the outermost, // non-dotted segment. When the full path *does* pre-exist, `lastFound`/`lastFoundDepth` just end up // equal to `prior`/the total depth, so this subsumes the single-level case without changing it. var lastFound = prior; var lastFoundDepth = 0; var totalDepth = 0; scan(); while (more == 0 && tt == T_DOT) { scan(); totalDepth = totalDepth + 1; if (prior >= 0) { prior = nmFind(N_NSX, prior, ts, te); if (prior >= 0) { lastFound = prior; lastFoundDepth = totalDepth; } } scan(); } usedN = 0; declSpanN = 0; var moduleOrGlobalN = 0; if (more == 0) { let mBody = tb; declSpanScan(); moduleOrGlobalN = declSpanModuleOrGlobalN; // Only reseek back to the body's own `{` when declSpanScan actually reached its matching close (more == 0 // still): if it ran out of input instead, leave `more` set so the single combined back(m0) below reports // "need more input" the same way it already does for scanUsed, and this whole call gets retried from m0 // once more input arrives — not from mBody, which was never itself mark-protected. if (more == 0) reseek(mBody); } if (more == 0 && lastFound >= 0) scanUsed(N_NSEXP, lastFound, true, false); ambDestrDepth = -1; if (lastFoundDepth < totalDepth && usedN > 0) ambDestrDepth = lastFoundDepth; let ownUnits = getStmtUnits(); if (!back(m0)) return; releaseDecorators(); anchorPushAt(start, ANCH_IN | ANCH_SEMI); setSt(S_NAMESPACE); setB(3); let base = txLen; setC(base); txPush(dlScope); txPush(0); txPush(0); txPush(dlNsRec); let m = tb; if (lead == 1) scan(); scan(); let na = ts; let nb = te; back(m); let merges = declared(na, nb); txPush(na); txPush(nb); txPush(0); txPush(ownUnits); txPush(moduleOrGlobalN); if (lead == 1) { // `advQuiet`, not `adv`, for the same reason as importEquals's own leading `export`: this is the // statement's leading token, not its last. Latent here (never yet exercised, since a namespace body // always ends in `}`, matching `semiNeeded`'s stale value either way) but fixed for the same root // cause as importeq-asi-leak-onto-declare (corpus/downlevel.jsonl). if (exported || merges) advBlank(); else advQuiet(); if (exported) tx[base + 1] = 1; } if (merges) { advBlank(); } else { emitTo(ts); litVar(); dropTo(ts + 3); blankRange(ts + 3, te, false); scan(); } var kind = N_NS; var key = dlScope; if (exported) { kind = N_NSX; key = dlNsRec; nsExport(ts, te); } var rec = nmFind(kind, key, ts, te); var merged = rec >= 0; if (!merged) rec = nmAdd(kind, key, ts, te); var off = txName(ts, te); tx[base + 2] = 1; let savedLen = txLen; var fnOff = off; if (declSpanHasEntry(off)) { var n = 1; while (true) { let cand = txSuffixed(ts, te, n); if (!declSpanHasEntry(cand)) { fnOff = cand; break; } txLen = cand; n = n + 1; } } adv(); tFn(fnOff); txLen = savedLen; var dottedDepth = 0; var prevOff = fnOff; while (tt == T_DOT) { advBlank(); off = txName(ts, te); dottedDepth = dottedDepth + 1; tx[base + 2] = tx[base + 2] + 1; let inner = nmFind(N_NSX, rec, ts, te); merged = inner >= 0; if (merged) rec = inner; else rec = nmAdd(N_NSX, rec, ts, te); emitTo(ts); // `litLb` closes the *previous* segment's own signature (completing its "(function(name)" into // "(function(name){") — the one point where a statement can be inserted into that ancestor's own // scope, before this dot's `var @;(function(@)` (tDotFn) opens the next one. `ambDestrDepth` (set by // this call's own lookahead, above) names which ancestor, by depth, needs its already-merged exports // destructured as locals here, so a deeper, newly-opened segment's body can still reference them bare // (`namespace A.B.C {}` reopening `A.B`'s `B` while `A.B.C` itself is new); `prevOff` is that // ancestor's own emitted name. litLb(); if (dottedDepth - 1 == ambDestrDepth) destructureAs(prevOff); tDotFn(off); blankRange(ts, te, false); scan(); prevOff = off; } dlScopeNext = dlScopeNext + 1; dlScope = dlScopeNext; dlNsRec = rec; tx[base + 6] = nmLen; openBlock(B_NAMESPACE); if (merged && usedN > 0) destructure();}
export function namespaceEnd(values: i32): void { let base = fc(); // Two independent ways this namespace's own `values > 0` (strip.zena's shared, unmodified counter) can // be entirely explained by something this engine can't safely wrap in an IIFE, rather than real content: // // - Every mark came from one of this body's own depth-0 `module`/`global` augmentations // (`tx[base + 8]`, counted independently by this same lookahead's `declSpanScan`, against the *exact // same* `values` strip.zena's shared, unconditional `markValue` call for one of these built — see // declSpanScan's own comment). `values <= tx[base + 8]` (not `==`: a real statement that happened to // mark more than once would only make `values` larger, correctly falling through to the wrap branch // below, never mistaken for "explained entirely by augmentations") catches exactly our 4 fixed files // (moduleAugmentationGlobal7/7_1/8/8_1) without strip.zena's own shared code needing to distinguish // this from real content at all — that would cost erase mode bytes for a signal it never reads. // - A nested *namespace* (not a module/global augmentation directly in this body, which the bullet // above already covers) itself declined below and restored its own span to literal, un-erased source // text (`declineScope`, propagated into the parent scope right after `dlScope` is restored to it) — // `scopeDeclined` reads that back now that `dlScope` (still this namespace's own scope; the restore // to the parent's, `tx[base]`, happens further down) names it. // // Either way wins over real content elsewhere in the body, not just "neither is true": wrapping in an // IIFE is only safe once everything nested is known to erase to *something* (even just blank spaces), // and a decline leaves literal, un-erased source text sitting in this body instead — embedding that // inside a wrapping IIFE would emit invalid JS. See corpus/downlevel.jsonl, // "namespace-uninstantiated-ambient-only-wrapped". let declined = scopeDeclined(dlScope) || (values > 0 && values <= tx[base + 8]); if (values > 0 && !declined) { let count = tx[base + 2]; var i = count - 1; while (i >= 0) { var off = base + 9; var j = 0; while (j < i) { off = txNext(off); j = j + 1; } if (i < count - 1) litRb(); if (i > 0 || tx[base + 1] != 0) tCallX(off); else tCall(off); i = i - 1; } // Drop only the namespace/module fallback-collapse errors raised while this body was open (a // nested statement that couldn't fast-path itself, e.g. `public namespace X {}`'s own illegal // modifier — see errorNamespaceFallback, engine/strip.zena) — not every error, so an ordinary // unhandled construct inside this namespace (its own erasure declined, same as outside any // namespace) still gets reported instead of silently producing invalid, unflagged output. pruneNamespaceFallbacks(anchorErr()); prune(tx[base + 6]); dlScope = tx[base]; // Only an instantiated namespace binds its name; the input is still retained by the anchor. declare(tx[base + 4], tx[base + 5]); anchorPop(); markValue(); } else { prune(tx[base + 6]); restoreFrom(anchorOut(), anchorIn()); setErrLen(anchorErr()); if (declined) { // Match erase mode's own decline for the identical shape (a plain, non-fast-pathed namespace whose // "resolved by its own body" check, `hNamespace`'s `kind == 0` branch, hits the same unconditional // mark): restore this namespace's own literal original text, report one error at its own start // (`errorNamespaceFallback`, tagged the same way so an enclosing namespace that itself fast-pathed can // prune it rather than double-report), and propagate the decline — both to `values` (`markValue`, so // an enclosing namespace that *didn't* fast-path itself, `hNamespace`'s shared, mode-independent // `kind != 3` paths, still sees "has a value") and to the restored parent scope (`declineScope`, so an // enclosing namespace that *did* fast-path itself also declines rather than wrapping this still // literal, un-erased text inside its own IIFE). anchorPop(); markValue(); errorNamespaceFallback(tx[base + 7], tx[base + 7] + 1); declineScope(tx[base]); } else { anchorBlank(); } } dlScope = tx[base]; dlNsRec = tx[base + 3]; txLen = base; pop();}
// ---------------------------------------------------------------------------// Declarations inside a namespace body, and declaration names for merging.
// The function head loop of stmtFunction: `n` tokens through the name.export function functionHead(n: i32): void { let exp = dlExported; dlExported = 0; let scoped = inScope(); var i = 0; while (i < n) { if (i == n - 1 && tt == T_NAME && !kw(K_FUNCTION)) { if (scoped) declare(ts, te); if (exp != 0) pendAdd(0, ts, te); } if (exp != 0 && kw(K_EXPORT)) advBlank(); else adv(); i = i + 1; }}
// At a class declaration's name.export function className(): void { let exp = dlExported; dlExported = 0; if (getBlankDepth() > 0) return; declare(ts, te); if (exp != 0) pendAdd(0, ts, te);}
// S_VARDECL flags for `var`/`let`/`const` k: 64 exported from a namespace, 128 also a live binding.export function varFlags(k: i32): i32 { if (dlExported == 0) return 0; dlExported = 0; if (k == K_CONST) return 64; return 192;}
var bindA: FixedArray<i32> = new FixedArray<i32>(32, 0);var bindN: i32 = 0;
function bindAdd(a: i32, b: i32): void { bindA = grow(bindA, bindN + 2); bindA[bindN] = a; bindA[bindN + 1] = b; bindN = bindN + 2;}
// Skips a default value through the `,` or closer that ends it.function skipDefault(): void { while (more == 0 && tt != T_COMMA && tt != T_RBRACE && tt != T_RBRACK) { if (tokenIn(TS_OPEN)) { if (skipBalanced() < 0) return; } else if (tt == T_TEMPLATE_HEAD) { if (skipTemplate() < 0) return; } else { scan(); } }}
function bindPattern(): void { if (more == 1) return; if (tt == T_NAME) { bindAdd(ts, te); scan(); return; } if (tt == T_LBRACE) { scan(); while (more == 0 && tt != T_RBRACE) { if (tt == T_ELLIPSIS) { scan(); bindPattern(); } else { let a = ts; let b = te; let isName = tt == T_NAME; if (tt == T_LBRACK) { if (skipBalanced() < 0) return; } else { scan(); } if (more == 0 && tt == T_COLON) { scan(); bindPattern(); } else if (isName) { bindAdd(a, b); } if (more == 0 && tt == T_EQ) { scan(); skipDefault(); } } if (more == 0 && tt == T_COMMA) scan(); } if (more == 0) scan(); return; } if (tt == T_LBRACK) { scan(); while (more == 0 && tt != T_RBRACK) { if (tt == T_COMMA) { scan(); continue; } if (tt == T_ELLIPSIS) scan(); bindPattern(); if (more == 0 && tt == T_EQ) { scan(); skipDefault(); } if (more == 0 && tt == T_COMMA) scan(); } if (more == 0) scan(); }}
// At a declarator of an exported namespace variable: queues its bound names. True when input ran out.export function declNames(flags: i32): boolean { let m = tb; let simple = tt == T_NAME; bindN = 0; bindPattern(); if (!back(m)) return true; var hasInit = true; if (simple) { scan(); if (more == 0 && tt == T_COLON) { let end = lookType(); if (more == 0 && end >= 0) reseek(end); } if (more == 1) { seek(m); return true; } hasInit = tt == T_EQ; if (!back(m)) return true; } var pf = flags & 128; if (!hasInit && simple && bindN == 2 && dlNsRec != 0) { // Only a member that's never referenced again stays unmaterialized: `tsc` rewrites every later // reference (read or write) to go through the namespace object directly, so a later assignment // (`x = 1;` elsewhere in this body) does still need the member to exist. scanUsed's own depth // tracking is relative, not tied to starting right at the body's `{`, so it still stops at this // same body's own closing `}` even starting mid-body, here. nsExport(bindA[0], bindA[1]); let r = nmFind(N_NSEXP, dlNsRec, bindA[0], bindA[1]); if (r >= 0) { let m2 = tb; scanUsed(N_NSEXP, dlNsRec, false, true); var referenced = false; var j = 0; while (j < usedN) { if (used[j] == r) { referenced = true; break; } j = j + 1; } if (!back(m2)) return true; if (!referenced) pf = pf | PEND_NOINIT; } } var i = 0; while (i < bindN) { pendAdd(pf, bindA[i], bindA[i + 1]); i = i + 2; } return false;}
// ---------------------------------------------------------------------------// `export declare var/let/const <name>[: T][, <name>[: T]]*;` directly inside an instantiated namespace body.// An ambient (declare) member has no runtime value of its own: nothing in the namespace body initializes it.// `tsc`'s checker still knows it's a member of the enclosing namespace, so every later bare reference to the// name gets qualified through the namespace object at emit time: a read gets `undefined` until something// writes it, and a write (from inside the body, from another merged declaration, or — the common case for an// ambient member, since the whole point of `declare` is "provided from outside" — from completely unrelated// code after the namespace closes, e.g. `(N as any).x = 5`) lands on the namespace object itself. Downlevel mode has// no checker and no general identifier-rewriting pass over the body, and can't see a write from outside the// body at all, so a plain disconnected local (this code's first attempt) silently reads stale/undefined// forever once something writes the real member from outside — a *silent wrong value*, worse than the// ReferenceError erasing the whole declaration used to throw.//// Instead, each exported name gets `declNames`/`varFlags`' own live-binding accessor machinery, but starting// NON-enumerable: `tAmbientLive` (like `tLive`, its getter/setter close over a real local so a bare reference// inside the body and a `this.<name>` access from outside always see the same value) and, the first time// anything actually writes it — through `this`, whether from inside the body or completely outside it — its// own setter flips the SAME property to enumerable with a second, get/set-preserving `defineProperty` call.// `tsc` only ever creates the member once something assigns it; this matches that without knowing in advance// whether a write is coming, so a namespace whose ambient member genuinely never gets written (every file in// the corpus so far) stays correctly absent from `Object.keys`/structural comparison — not reintroducing// "namespace-uninitialized-export-overcounted"'s own over-eager materialization. The one gap this doesn't// close: a write *inside* the namespace body as a bare, unqualified assignment (`x = 5;`, not `this.x = 5`)// updates the closure variable directly, bypassing the setter, so it doesn't flip the property enumerable —// not exercised by any known case (the 13 conformance files in the baseline never write the ambient member at// all, in or out of the body); `tsc` itself rewrites every such in-body reference to go through the namespace// object too, which this architecture has no general way to do for an arbitrary expression position.//// A non-exported `declare var` is left on the ordinary erasure path below: `tsc` never qualifies a reference// to one (confirmed directly — it isn't a namespace member at all, just a free identifier left bare in the// reference's own output too), so downlevel mode's existing unqualified erasure already matches it.//// Returns 0 when this doesn't apply (not directly inside a namespace body, or not exported) — the caller falls// back to its own unqualified erasure; 1 when handled; 2 when input ran out (nothing committed; the caller// retries the whole statement once more input arrives).export function ambientVar(n: i32): i32 { if (!inNs()) return 0; let m = tb; var exported = false; var i = 0; while (i < n) { if (kw(K_EXPORT)) exported = true; scan(); i = i + 1; } if (!exported) { reseek(m); return 0; } if (more == 1) { seek(m); return 2; } // `tt` is the var/let/const keyword; move past it to the first declarator name. scan(); while (true) { if (more == 1) { seek(m); return 2; } if (tt != T_NAME) { reseek(m); return 0; } scan(); if (more == 1) { seek(m); return 2; } if (tt == T_COLON) { let end = lookType(); if (more == 1) { seek(m); return 2; } if (end < 0) { reseek(m); return 0; } reseek(end); if (more == 1) { seek(m); return 2; } } if (tt == T_COMMA) { scan(); continue; } break; } if (!back(m)) return 2; // Committing: the same walk, now with real output. Every declarator name is kept verbatim as a real local // (so a bare reference inside the body resolves to it); everything else (modifiers, the keyword, each type // annotation) is blanked. Each name's lazy-enumerable accessor is queued (as a [nameOff] entry in tx, // starting at regBase) rather than emitted inline, since they can only go after the whole `var a,b;` // statement — interleaving them between comma-separated names would not be valid JS. markValue(); var j = 0; while (j < n) { advBlank(); j = j + 1; } emitTo(ts); litVar(); dropTo(ts + 3); blankRange(ts + 3, te, false); scan(); let regBase = txLen; while (true) { txName(ts, te); adv(); if (tt == T_COLON) { let m2 = tb; let end = lookType(); back(m2); blankRange(ts, end, false); seek(end); scan(); } if (tt == T_COMMA) { adv(); continue; } break; } if (tt == T_SEMI) adv(); var o = regBase; while (o < txLen) { tAmbientLive(o); o = txNext(o); } txLen = regBase; pop(); return 1;}
// ---------------------------------------------------------------------------// Module-kind tracking: a file mixes CommonJS-only syntax (`import x = require(...)`,// `export =`) with value-level ESM (`import`/`export` surviving erasure) can run in// neither module system (new ESM `import`/`export` alongside emitted `require`/// `module.exports`), so downlevel must report instead of silently emitting it. See// README.md's downlevel-mode section and docs/downlevel-oracle.md for the contract.//// Three flags, reset by init() per file/stream: mkEsm/mkCjs record "has this kind been// committed to" (converted, or marked ESM, without reporting), mixed records "do we// already know this file is mixed" — once true it stays true for the rest of the file.// strip.zena calls moduleEsmSeen() at each value-level ESM import/export declaration// (its own classification already matches tsc's elision rules: `import type`/`export// type`/an all-type-only clause never reaches that call site) gated by its own// blankDepth==0 && verbatim==0, so ambient content (`declare module`/`declare global`,// entered via enterBlank) never calls it, matching the "nothing ambient counts" rule.// moduleCjsSeen() is called the same way, but only for a CommonJS-only construct itself// (`import x = require(...)`, `export =`); the exact call sites are importEquals() below// (only its `require(...)` form — `import x = A.B`, a plain namespace alias, is neither// CommonJS nor gated here) and strip.zena's stmtExport (every `export =`).//// Streaming means the engine can't always know a file is mixed before it has already// converted (and emitted) an earlier `import x =`/`export =`: that output can't be// un-emitted. The chosen fix is to report the *later* conflicting construct instead of// buffering: the first construct that reveals the mix is reported in place of (not// instead of fixing) whichever already-streamed construct it conflicts with, and once// `mixed` is true every further CommonJS-only construct is also reported (the normal// rule applies again once there is no more streaming ambiguity) while further ESM// declarations stay silent (only the one that revealed the mix is ever reported — the// contract only asks for CommonJS-only constructs to be flagged). This keeps the engine// fully streaming (no buffering, no replay) while guaranteeing at least one error for// any mixed file regardless of which kind comes first.
// Called at a value-level ESM import/export declaration (one that survives type// erasure). Returns true exactly once per file, the first time a CommonJS-only construct// was already committed (mkCjs) when this ESM declaration is reached: the caller must// then report *this* declaration's own position instead of marking it as ESM, since the// earlier CommonJS construct is already converted and streamed. Returns false (and, the// first time, records the file as ESM-bearing) every other time, including every call// once `mixed` is already true — only the one ESM declaration that first reveals the mix// is ever reported.//// strip.zena's own call site (stmtDecl's "export as a plain modifier" branch) is reached// for both a true top-level `export class`/`function`/`enum`/`var`/`let`/`const` and the// exact same shape *inside* a namespace body (`namespace N { export function f() {} }`),// since both go through the same statement dispatch. Only the former is a real top-level// ESM module declaration; the latter is just a namespace member export (the namespace// merges it into its own object; it never changes whether the *file* is ESM or// CommonJS), so it must not count here. Checked with getSp()/belowSt() — already imported// from strip.zena for inScope()/inNs() above — rather than a parameter strip.zena itself// would have to compute and pass at every one of its own (simpler) call sites, to keep// those call sites themselves exactly as cheap as every other hooks-seam call (see// engine/hooks-erase.zena: this file is never linked into build/erase.wasm at all, so the// check costs nothing there either way, but computing it in strip.zena *would* have).export function moduleEsmSeen(): boolean { if (getSp() >= 2 && belowSt(1) != S_TOP) return false; if (mixed) return false; if (mkCjs) { mixed = true; return true; } mkEsm = true; return false;}
// Called before converting an `import x = require(...)`/`export =`. Returns true when// this file is already known mixed, or becomes known mixed right now (an ESM declaration// was already committed): the caller must report this construct at its own statement// start and leave it unconverted (mirroring erase mode's own fallback) instead of// converting it. Returns false (and records the file as CommonJS-bearing) otherwise,// letting the caller proceed with the conversion.export function moduleCjsSeen(): boolean { if (mixed) return true; if (mkEsm) { mixed = true; return true; } mkCjs = true; return false;}
// ---------------------------------------------------------------------------// `import x = require('y')`, `import x = A.B`, `export = e`.
export function importEquals(): void { let m = tb; let exp = kw(K_EXPORT); if (exp) scan(); if (more == 0) scan(); var isType = false; if (more == 0 && kw(K_TYPE)) { let m2 = tb; scan(); isType = more == 0 && tt == T_NAME; if (!isType && more == 0) reseek(m2); } let na = ts; let nb = te; if (more == 0) scan(); if (more == 0 && tt == T_EQ) scan(); let isRequire = more == 0 && kw(K_REQUIRE); var end = -1; if (more == 0) { reseek(m); if (more == 0) end = importEqualsEnd(); } if (!back(m)) return; if (end < 0) { failAt(50); return; } if (isType) { enterBlank(true); copyThrough(end); endStmt(); return; } // `import x = require(...)` is the only `import x =` form that is itself a CommonJS-only construct // (needsCommonJS's own rule, test/downlevel.mjs): a plain namespace alias (`import x = A.B`) is unaffected // by module-kind tracking either way. See "Module-kind tracking" above moduleEsmSeen/moduleCjsSeen. if (isRequire && moduleCjsSeen()) { errorAtStmt(); copyThrough(end); pop(); return; } let ns = inNs(); if (exp) { // `advQuiet`, not `adv`: this `export` is the statement's leading token, not its last, so it must // not touch `semiNeeded` — doing so (via `adv`) left a stale value for the *next* statement to read, // leaking a spurious `;` onto a following blanked `declare namespace`/`declare enum` whenever this // statement's own real last token (copied through `copyThrough` below, which never touches // `semiNeeded` either) was already a semicolon. if (ns) advBlank(); else advQuiet(); } emitTo(ts); if (isRequire) { litConst(); dropTo(ts + 5); blankRange(ts + 5, te, false); } else { litVar(); dropTo(ts + 3); blankRange(ts + 3, te, false); } scan(); if (exp && ns) pendAdd(0, na, nb); copyThrough(end); pop();}
export function exportAssign(): void { markValue(); emitTo(ts); litModExp(); dropTo(te); scan(); setSt(S_EXPRSTMT); setC(0); adv();}
// ---------------------------------------------------------------------------// Parameter properties. A class body anchors its `{` so the fields can be inserted there once the constructor's// parameters are known; the anchor is released at the constructor body or the class end.
export function classOpen(ext: i32): void { var a = DL_CLS | (ext << 20); if (getBlankDepth() == 0 && getVerbatim() == 0) { anchorPush(0); a = a | getAnchLen(); } pushFrame(S_CLASSBODY, a, txLen, getStmtNest());}
export function classEnd(): void { let a = fa(); let ai = a & 1048575; if (ai != 0 && ai == getAnchLen()) anchorPop(); txLen = fb();}
// The current token names a constructor parameter that had modifiers.export function paramProp(): void { if (tt == T_NAME) txName(ts, te);}
function ctorAssigns(base: i32): void { var o = base; while (o < txLen) { tAssign(o); o = txNext(o); }}
// Right after a constructor body's `{`: stack ... S_CLASSBODY, S_MEMBER, S_FUNC, S_BLOCK.export function ctorBody(): void { if (getSp() < 4 || belowSt(3) != S_CLASSBODY) return; let a = belowA(3); if ((a & DL_CLS) == 0) return; let base = belowB(3); let ai = a & 1048575; if (ai != 0) { if (ai == getAnchLen()) { var n = 0; var o = base; while (o < txLen) { n = n + tx[o] + 1; o = txNext(o); } if (n > 0) { var d = outInsert(anchorOut(), n); o = base; while (o < txLen) { var j = 0; while (j < tx[o]) { Memory.default.setU8(d, tx[o + 1 + j]); d = d + 1; j = j + 1; } Memory.default.setU8(d, 59); d = d + 1; o = txNext(o); } } anchorPop(); } setBelowA(3, a & -1048576); } if (getBlankDepth() != 0 || getVerbatim() != 0 || txLen == base) return; if (((a >> 20) & 1) == 0) { ctorAssigns(base); return; } setA(B_CTORW); setB(dlHookSp); dlHookSp = getSp();}
// strip.zena's own F_RESTRICT/F_ARROWBODY/F_STMTSTART, masked into ctxFlags below the same way the other// strip.zena values at the top of this file are (module-level bindings cannot cross the import cycle).let F_RESTRICT: i32 = 128;let F_ARROWBODY: i32 = 256;let F_STMTSTART: i32 = 512;
// genericArrow's own erase/report/abort decision for a legacy `<T>x` cast, called only when mode == 1 (strip.zena// checks that; this hook is never reached otherwise). `m` is the `<` token's own start, `end` is the cast's own// end (both already known to genericArrow); `ctxFlags` is `flags` masked to F_RESTRICT | F_ARROWBODY |// F_STMTSTART. Returns 0 (report, exactly as erase mode always does), 1 (safe to erase), or 2 (abort:// genericArrow must return having done nothing further - mirrors the lexer running out of input, or `back`// itself failing, while peeking the operand).export function castErase(m: i32, end: i32, ctxFlags: i32): i32 { if (ctxFlags == 0) return 1; // A pure lookahead past `end` to the operand's own start and first token, with no output effect either // way; back(m) always restores the lexer to `<` before genericArrow does anything else. reseek(end); if (more == 1) { back(m); return 2; } let opStart = ts; let opTt = tt; let opTk = tk; if (!back(m)) return 2; if ((ctxFlags & F_RESTRICT) != 0 && spansLines(m, opStart)) return 0; if ((ctxFlags & F_ARROWBODY) != 0 && opTt == T_LBRACE) return 0; if ((ctxFlags & F_STMTSTART) != 0 && (opTt == T_LBRACE || (opTt == T_NAME && (opTk == K_FUNCTION || opTk == K_CLASS)))) { return 0; } return 1;}