You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Latent on main, currently masked by eager type loading: dead-ended references warm typeStore
before cold lookup paths are ever exercised. The parse-skipping work for __ctrlFlow(enable=false)
(#601) removes that warming and reliably triggers RangeError: Maximum call stack size exceeded
while building Buildings.Templates.AirHandlersFans.Data.VAVMultiZone's parameter table
(schedule.test.ts, real-Buildings-library data). Root-caused with a cycle detector; this must be
fixed before or together with #601.
Root cause — four interacting defects
Store._generatePaths() (parser.ts:138-150) blindly prefixes already-qualified names.
Resolving Buildings.Templates.AirHandlersFans.Data.PartialAirHandler from basePath = ...Data.VAVMultiZone generates candidates like Buildings.Templates.AirHandlersFans.Data.Buildings.Templates.AirHandlersFans.Data.PartialAirHandler.
getPathFromClassName()'s directory-trimming fallback (loader.ts:186-207) fuzzy-"succeeds" on
those bogus candidates (the example above trims back to .../Data/package.json), so they load
real files. File's within-path validation (parser.ts:1212-1218) doesn't catch it because within is a segment-prefix of the bogus name.
getFile() (parser.ts:1234-1239) has no construction dedupe: loader() memoizes lookups
(keyed by the requested className string — each bogus candidate is a fresh key), but new File(...) and all element constructors re-run on every call.
ShortClass and Component resolve their type beforeregisterPath (parser.ts:503 vs 512;
829 vs 835), so duplicate punch-out never protects them (unlike LongClass, which registers at
607 and punches out at 609). Every redundant File reconstruction re-runs a full candidate sweep
per component/short class → more bogus candidates → more getFile calls. This closes the cycle: getFile ↔ pre-registration typeStore.get is mutual recursion with no terminator — genuinely
unbounded, not just deep (a 16x larger V8 stack doesn't survive it).
Proposed fix (in order of leverage)
Root-aware _generatePaths: if the first segment of path names a top-level loaded package
(derivable from MODELICA_JSON_PATH roots / PACKAGE_LIST[0]), return [path] only.
Dedupe File construction by resolved file identity (resolved json path or getClassNameFromJson): each real file constructed at most once per run. Makes the system robust
to any future candidate-generation slop.
Negative-cache the _load step in Store.get (a Set of candidate strings that failed to
load; keep checking the store positively since it grows). Perf polish once 1–3 are in — it cannot
bound the blow-up alone because the bogus keys are distinct.
Also add a permanent cheap getFile re-entrancy guard that throws with the active stack — turns any
regression here into a one-line diagnostic.
Latent on
main, currently masked by eager type loading: dead-ended references warmtypeStorebefore cold lookup paths are ever exercised. The parse-skipping work for
__ctrlFlow(enable=false)(#601) removes that warming and reliably triggers
RangeError: Maximum call stack size exceededwhile building
Buildings.Templates.AirHandlersFans.Data.VAVMultiZone's parameter table(
schedule.test.ts, real-Buildings-library data). Root-caused with a cycle detector; this must befixed before or together with #601.
Root cause — four interacting defects
Store._generatePaths()(parser.ts:138-150) blindly prefixes already-qualified names.Resolving
Buildings.Templates.AirHandlersFans.Data.PartialAirHandlerfrombasePath = ...Data.VAVMultiZonegenerates candidates likeBuildings.Templates.AirHandlersFans.Data.Buildings.Templates.AirHandlersFans.Data.PartialAirHandler.getPathFromClassName()'s directory-trimming fallback (loader.ts:186-207) fuzzy-"succeeds" onthose bogus candidates (the example above trims back to
.../Data/package.json), so they loadreal files.
File's within-path validation (parser.ts:1212-1218) doesn't catch it becausewithinis a segment-prefix of the bogus name.getFile()(parser.ts:1234-1239) has no construction dedupe:loader()memoizes lookups(keyed by the requested className string — each bogus candidate is a fresh key), but
new File(...)and all element constructors re-run on every call.ShortClassandComponentresolve their type beforeregisterPath(parser.ts:503 vs 512;829 vs 835), so duplicate punch-out never protects them (unlike
LongClass, which registers at607 and punches out at 609). Every redundant
Filereconstruction re-runs a full candidate sweepper component/short class → more bogus candidates → more
getFilecalls. This closes the cycle:getFile ↔ pre-registration typeStore.getis mutual recursion with no terminator — genuinelyunbounded, not just deep (a 16x larger V8 stack doesn't survive it).
Proposed fix (in order of leverage)
_generatePaths: if the first segment ofpathnames a top-level loaded package(derivable from
MODELICA_JSON_PATHroots /PACKAGE_LIST[0]), return[path]only.Fileconstruction by resolved file identity (resolved json path orgetClassNameFromJson): each real file constructed at most once per run. Makes the system robustto any future candidate-generation slop.
registerPath(and annotation parsing) ahead of type resolution inShortClassandComponent— also required by Fix__ctrlFlow.enableannotation handling #601._loadstep inStore.get(a Set of candidate strings that failed toload; keep checking the store positively since it grows). Perf polish once 1–3 are in — it cannot
bound the blow-up alone because the bogus keys are distinct.
Also add a permanent cheap
getFilere-entrancy guard that throws with the active stack — turns anyregression here into a one-line diagnostic.