Skip to content

Latest commit

 

History

History
97 lines (81 loc) · 6.57 KB

File metadata and controls

97 lines (81 loc) · 6.57 KB

Reactive graph trace

Pass options.explain: true to transformSync or transform. Successful reactive emission adds result.explain.reactiveGraph. Within the configured request limits, tracing does not change generated JavaScript, source maps, diagnostics, metadata or compiler optimization policy. The larger explanation payload counts toward limits.maxOutputBytes; exceeding that ceiling returns the existing bounded FICT-REQUEST error.

import { transformSync } from '@fictjs/compiler'

const result = transformSync({
  filename: '/counter.tsx',
  code: `import { $state } from 'fict';
    export function Counter() {
      let count = $state(1)
      const doubled = count * 2
      return <span>{doubled}</span>
    }`,
  options: { dev: false, strictGuarantee: true, explain: true },
})

console.log(result.explain?.reactiveGraph)

The version 1 trace covers the main generated module. Standalone Preview handler artifacts, imported implementation bodies, dynamic invocation counts and runtime subscription graphs are outside its scope.

Field Meaning
bindings Source binding identities, declaration names/spans and authored reference spans. References include writes and retain their authored function owner.
functions Verified EmitIR function, context helper and slots. Captured, imported and aliased slots remain distinguishable from owned slots.
operations Relevant EmitIR operations with function-local indices, source anchors, slots, bindings and helper keys. These are plans before final code generation.
decisions Actual implicit memo inlining/elimination and retention reasons, or a preserved authored/runtime creation.
calls Resolved compiler-helper calls in the reparsed final JavaScript, with generated byte spans and lexical function owners.
owners Lexical functions/arrows in generated JavaScript. A generated function is not necessarily a runtime cleanup owner.
counters Separate source, EmitIR, decision and final-output counts.
fusion not-attempted:no-general-fusion-pass; this compiler does not claim a general fusion search.

Source bindings are resolved before JSX lowering duplicates references. For example, one {doubled} read remains one authored reference even when HTML and namespace fallback code contain several generated accessor reads. An inlined CreateDerived remains visible in the EmitIR plan; its helper call disappears from the final output census. This lets a reviewer distinguish planned work from work still present in generated code.

Operation purposes identify explicit effect order/cleanup, tracked statement reexecution, shared control-flow results, DOM bindings and keyed/branch lifetimes. When EmitIR provides an explicit cleanup owner, operations[].cleanup preserves its function, slot or region identity. functions[].slots[].owner separately identifies owned/captured storage. Neither field infers a cleanup root for every generated JavaScript closure.

decisions distinguishes disabled optimization, disabled name policy, Preview lifetimes, shared references, uncached derivations, removed total scalar memos, unused memos lacking a total-value proof, and cases lacking a movement/lifetime proof. A retained decision means this rewrite retained that binding. Final call counts separately describe output after all code-generation transforms.

Core SSA analysis is reported separately in result.stats.counters:

Counter Meaning
dceInlineCandidatesAnalyzed Same-block single-use candidates observed by DCE before compaction, summed across analyzed functions
dceTrivialPhisAnalyzed Phi candidates with one alias root observed at that stage
dceInlineCandidatesRewritten 0: the DCE compaction rewrite does not consume its inline plan
dceTrivialPhisRewritten 0: the DCE compaction rewrite does not consume its Phi plan

These analyzed counts are not final remaining opportunities or generated-code improvements. They are zero when optimization is disabled. Codegen's separately proved memo rewrites use the graph decisions above; they do not increment these SSA rewrite counters. HIR compaction alone does not certify a source JavaScript rewrite because emission also depends on the authored program and EmitIR.

General branch-aware lazy placement, recursive dead-derived elimination and global region fusion/splitting remain unimplemented. A call marked pure can still throw, and moving it into a branch can change error timing or evaluation count. Existing SSA facts are therefore reported without promising those rewrites.

Each call's operations array contains only exact source-span/helper matches. Synthetic calls can have no source anchor or matching operation. null and empty arrays are explicit absence of a proven association. sourceAnchorsVerified indicates that call/owner traversal identities agree before generation and after reparsing; it does not make synthetic code acquire an authored source location. All spans use UTF-8 byte offsets, as in native diagnostics.

The census resolves generated import bindings, including CommonJS lowering. Same-spelled user functions, shadowed bindings and arbitrary accessor aliases are not treated as compiler helpers. Authored runtime APIs remain visible through their EmitIR slots/operations but are not added to compiler-helper call counts.

These are static call sites, not runtime node counts, allocation counts or CPU estimates. Alternatives may be mutually exclusive; a list body may run many times; one binding helper can allocate several objects or reuse existing work. Performance decisions still require executed workloads and allocation/CPU profiles.