Commit Graph
2971 Commits
Author SHA1 Message Date
Chris Tate c22302cfae Test runtime classes' dispatch branches before their subclasses' 2026-10-21 23:22:03 -05:00
Chris Tate 3e8f1bc608 Renumber the fiberless plain-call corpus program to 7103 2026-10-21 22:34:17 -05:00
Chris Tate 2c5771e98c Run never-suspending async calls in place from stackless async bodies 2026-10-21 22:19:38 -05:00
Chris Tate 2a48d46da3 Key weak collections by net sockets and servers, and carry the socket in undici client diagnostics (#1742) 2026-10-21 22:14:27 -05:00
Chris Tate d0d132080c Skip candidate buffering for classes whose trace visits nothing (#1743) 2026-10-21 22:11:25 -05:00
Chris Tate 4100cd3c04 Never buffer instances of classes whose trace visits nothing
A class carries the collector header when any class in its hierarchy can
be on a cycle, so instances of classes whose own fields hold nothing
cycle-capable were buffered as candidates and walked by every level. Such
an object has no traced children and is never on a cycle.

Their descriptors now name scr_cyc_no_children, and they are allocated
with buf set to SCR_CYC_ACYCLIC. That value has the buffered bit set, so
a release that leaves the object alive makes no call; scr_cyc_is_buffered
is false for it, and an emitted death or publication mark takes the object
out of a candidate buffer only when buf is below -1. A pass that walks
such an object as a child restores the value when it survives.
2026-10-21 22:08:30 -05:00
Chris Tate b0802dccc2 Give shared helpers the location of their earliest use (#1740) 2026-10-21 22:08:16 -05:00
Chris Tate e80be9c2e5 Keep properties untyped code adds to typed object literals (#1733) 2026-10-21 22:06:32 -05:00
Malte UblandClaude Opus 5.5 fc551c4a8b Key weak collections by net sockets and servers, and carry the socket in undici client diagnostics
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:06:28 -07:00
Chris Tate d5688d7dc5 Give shared helpers the location of their earliest use 2026-10-21 22:02:39 -05:00
Chris Tate 10820d488c Support SNICallback in tls.createServer (#1700) 2026-10-21 22:00:41 -05:00
Chris Tate ca446b2c30 Read the tls.createServer SNICallback in source order
The runtime call reads the cert and key options before the SNICallback,
so a cert or key initializer after the callback that reassigned the
callback's binding changed the callback the server used, and the server
could serve the wrong certificate. When SNICallback comes before a cert
or key entry, the callback is now saved just before the next of them is
evaluated. With the callback read in order, a callback expression with
side effects lowers instead of being refused.
2026-10-21 21:56:08 -05:00
Chris Tate 71e118be7b Count loops bounded by a string's length in integers
A counted for loop needs a stable limit to run its counter in integer
storage. Array lengths qualified when the loop provably keeps them, but
a string's length did not, so `for (let i = 0; i < s.length; i++)`
kept a double counter and evaluated the length, through the UTF-16
mapping of a non-ASCII string, on every iteration. A string's length
cannot change, so it is now stable whenever the string's binding is
not rebound in the loop or boxed.
2026-10-21 21:42:23 -05:00
Chris Tate db4eb50013 Convert unions of primitives with unary + (#1737) 2026-10-21 21:34:19 -05:00
Chris Tate 04401b6c92 Keep int32 fields of classes whose prototypes are named for inspection (#1736) 2026-10-21 21:31:22 -05:00
Chris Tate baa8fbdafe Hold any non-nullish value in an empty object type inferred from a literal (#1716) 2026-10-21 21:30:54 -05:00
Chris Tate c0014f683d Convert unions of primitives with unary + 2026-10-21 21:29:59 -05:00
Chris Tate 487f536a29 Keep int32 fields of classes whose prototypes are named for inspection
dyn.nameClassPrototype sets the class name on a class's runtime prototype
object and stores no property. The int32 field analysis did not list it as
read-only, so every program that gave a class a runtime prototype counted a
dynamic store that can name any property, and the number fields of every
class reaching dynamic code were stored as doubles.
2026-10-21 21:28:46 -05:00
Chris Tate 4a5ddadd3d Memoize an empty literal's type separately as a union arm
The type mapper maps the type inferred from an empty object literal to the
empty record as an arm of an unreduced union and to a checked-dynamic
value elsewhere, but both answers shared one memo entry. Once the type had
been mapped on its own, a union such as `typeof empty | DataView` reused
the dynamic answer for its arm and refused (SC2009). The memo key now
includes whether the mapping is for such an arm.
2026-10-21 21:28:27 -05:00
Chris Tate 3b5e233618 Lower to the same IR whatever order reachable bodies lower in 2026-10-21 20:28:16 -05:00
Chris Tate ec370196e0 Pass undefined as the zlib callback result on errors (#1689) 2026-10-21 20:24:46 -05:00
Chris Tate 37cdd92755 Keep properties untyped code adds to typed object literals 2026-10-21 20:23:53 -05:00
Chris Tate 6f0d047e8e Pass a reassigned zlib callback result on to typed functions
On the error path, a callback's result reads from a checked-dynamic
slot. Passing it on into a Buffer parameter still handed over the
adapter's original value, so after `if (!out) out = Buffer.from(...)`
a typed callee received the absent Buffer instead of the fallback and
crashed. The forward now tests the slot: while it holds undefined the
callee gets the absent Buffer, and otherwise the Buffer the slot holds.
2026-10-21 20:21:31 -05:00
Chris Tate 84036ecdc3 Lower a super() call that leads an if condition's comma chain (#1732) 2026-10-21 20:16:42 -05:00
Chris Tate e7c689ca1d Print class instances with their class name when instanceof sees unknown values (#1731) 2026-10-21 20:13:25 -05:00
Chris Tate 9b165aa044 Lower a super() call that leads an if condition's comma chain 2026-10-21 20:12:26 -05:00
Chris Tate 143c80eb1c Print class instances with their class name when instanceof sees unknown values 2026-10-21 20:10:43 -05:00
Chris Tate 1c03c0c17f Skip the pending test after callbacks that cannot throw
An array helper specialized for its callback calls the callback's
function directly once the closure's entry matches it, and falls back to
the entry otherwise. Both paths were followed by the pending-exception
test. When the direct target is outside the may-throw set, only the
fallback can leave an exception pending, so the test now runs behind a
guard that is true on that path alone; a known closure called directly
skips it. Worker programs keep the test on every path, where it also
observes termination.
2026-10-21 20:07:00 -05:00
Chris Tate be48947dbd Read a class branch's own-property bag from its receiver (#1727) 2026-10-21 20:03:14 -05:00
Chris Tate ed5f54c936 Keep constructor types of fetched provenance sources unmapped
Constructor types declared in the program hold class values as
checked-dynamic values since #1725. A fetched provenance source counts as
a program file, so the unused `@__PURE__` const typed `{ new (): any }` in
the provenance fixture now had a mapping, no longer elided, and its
initializer refused (SC1090). Fetched sources keep their constructor types
unmapped, as before #1725.
2026-10-21 19:51:21 -05:00
Chris Tate f4ab3572c1 Accept file URL paths in synchronous fs functions (#1656) 2026-10-21 19:36:18 -05:00
Chris Tate 4ab3850579 Keep named properties written to arrays through checked views (#1715) 2026-10-21 19:34:43 -05:00
Chris Tate 029906d861 Merge main 2026-10-21 19:34:10 -05:00
Chris Tate 62ddc2fd35 Read a class branch's own-property bag from its receiver 2026-10-21 19:31:56 -05:00
Chris Tate f8f315cc03 Warn about likely-hot functions that use untyped values (#1724) 2026-10-21 19:29:04 -05:00
Chris Tate c29ce641b2 Count only the repeated parts of loops as likely-hot work
A `for` loop's initializer and a `for...of` loop's iterable run once, but
SC6101 counted any dynamic operation inside a loop statement's span, so
untyped setup there produced the warning and failed --fail-on=dynamic.
Loop work is now found by walking the IR: a loop's condition, update and
body repeat, its initializer and iterable do not. Locations could not
tell them apart, because lowering gives the index loop of a `for...of`
the whole statement's span.
2026-10-21 19:26:00 -05:00
Chris Tate 3447e9a1c1 Unwrap a UMD wrapper only when its body is exactly TypeScript's emit
The wrapper is dropped when its factory body moves to the top level, so
any other statement in the wrapper, in its CommonJS branch or in its AMD
branch would be lost. The check now matches the whole wrapper body and
keeps the original text for any other shape.
2026-10-21 19:20:54 -05:00
Chris Tate 220199dfec Evaluate an fs argument inside satisfies once after a URL path
The arguments after a URL path that are evaluated ahead of the
conversion were keyed by the expression without its parentheses. When
the fs lowering read through a satisfies wrapper, it lowered the inner
expression again, so fs.chmodSync(url, mode() satisfies number) called
mode() twice. The saved arguments are now keyed and looked up without
their type-only wrappers (satisfies, as, type assertions, !), so each
runs once.
2026-10-21 19:17:07 -05:00
Chris Tate be23f71266 Compile TypeScript's UMD module wrapper as the module body 2026-10-21 19:02:52 -05:00
Chris Tateandblurname 5a3fbcc1ef Hold class values in structural constructor types declared in the program
A constructor type whose instances are not one program class's, such as
`new () => unknown`, a type that also names static members (`{ readonly
schema: unknown; new (): unknown }`), an abstract constructor type over a
record, or construct overloads, did not map and refused with SC2001. Such
a type declared in the program now lowers as a checked-dynamic value, as
an `unknown` slot already holds a class value: the value keeps its class
identity, `new` through it constructs that class, static reads see that
class's statics, and the constructed value checks into the signature's
instance type. Library and package constructor types keep their fences.

Refs #1372

Co-authored-by: blurname <50226652+blurname@users.noreply.github.com>
2026-10-21 18:57:17 -05:00
Chris Tate fa7bc7c7a0 Warn about likely-hot functions that use untyped values
A build now names the functions of the program's own files that compile
to the dynamic tier and likely run often, so a slow program with a little
untyped code says where the time goes:

  warning SC6101: 3 functions that likely run often use untyped values, which runs slower than typed code:
    advance (nbody.js:22): untyped JavaScript file, in a loop
    ...

- The functions come from the tier assignment lowering already makes
  (Lowerer.dynamicFunctions). Without a profile, a function counts as
  likely hot when a dynamic operation runs inside one of its loops, when
  a loop calls it, or when it calls itself (lowering/dynamic-tier.ts).
- A function is named only when its source says why its values are
  untyped: an `any` (for a for...of variable, the value it iterates), an
  untyped JSON.parse result, or an untyped JavaScript file. Generated
  %dyn.* helpers, module top levels and functions that are dynamic for an
  operation without a static lowering are left out.
- At most five functions are named, most likely hot first, with the
  total; dynamic-tier code in npm packages is counted per package on one
  line. All of the warning's text is in diagnostics/dynamic-tier.ts.
- SC6101 opens the SC61xx band, performance notes, with a new
  "performance" category in the diagnostics envelope; SC60xx stay the
  divergences.
- scriptc build prints it after the divergence warnings, and the
  executable cache stores it with the entry, so a cached build (including
  the CLI's routed fast path) repeats it. scriptc coverage reports it, and
  a comment-only edit no longer replays a verdict that carries it, since
  its text holds line numbers.
- --no-dynamic-tier-warning leaves it out of build and coverage;
  coverage --fail-on=dynamic fails on it.
- Docs: coverage.mdx (Dynamic tier, Gating builds and CI, the envelope
  categories) and cli.mdx.
2026-10-21 18:53:43 -05:00
Chris Tate 982ef6dbef Allow zlib callbacks to assign to their result
The error-path copy of a zlib callback reads its result from a hidden
local that was immutable, so a body that assigned a fallback Buffer to
the result (if (!out) out = Buffer.from("fallback")) failed to compile
with an internal compiler error. The local is now mutable, and an
assigned value is read back as a checked-dynamic value, as in Node.
2026-10-21 18:11:33 -05:00
Chris Tate 4fee347ca0 Address review feedback on array named properties 2026-10-21 17:57:00 -05:00
Chris Tate dfdc610c59 Replace every match of a block in one-character replace chains
Chains of global one-character replacements (the HTML-escape idiom) run
as one scr_str_replace_chars call. It searched for the next match eight
bytes at a time and started the search over after each replacement.

It now reads 16-byte blocks with SSE2 or NEON (8-byte words elsewhere on
little-endian hosts) and replaces every match of a block before reading
the next one. The table of characters and replacements is kept for the
next call with the same immortal operands, which are the literals the
compiler passes, instead of being rebuilt from the replacement text on
every call.
2026-10-21 17:36:58 -05:00
Chris Tate 8061060aff Lower a Buffer view of a dynamic typed-array value 2026-10-21 17:20:50 -05:00
Chris Tate 75436e9c86 Walk a file once for the references of its function declarations (#1717) 2026-10-21 17:20:00 -05:00
Chris Tate a7a42640dc Stop the type-parameter walk at recursive aliases in signatures
mentionsTypeParameter walked union constituents and type arguments
without remembering the types on its path. A parameter typed with an
alias that reaches itself through its own type arguments, such as
`type Tree = string | Tree[]` in `function f<T>(x: T, ...trees:
Tree[])`, recursed until the compiler ran out of stack. The walk now
skips a type it has already entered; a type parameter reached through
any other path is still found.
2026-10-21 17:17:15 -05:00
Chris Tate 70aed0f133 Walk a file once for the references of its function declarations 2026-10-21 17:17:02 -05:00
Chris Tate d15e822d26 Hold any non-nullish value in an empty object type inferred from a literal
The type mapper lowered the type tsc infers from an empty object literal
(`const o = {}`) as the empty record, while a declared `{}` already lowered
as a checked-dynamic value. tsc's `{}` admits every non-nullish value in
both cases: union subtype reduction folds primitives into a literal's `{}`
(`["12", {}]` is `{}[]`, `flag ? "x" : {}` is `{}`), and `let o = {}`
accepts `o = "x"`. Such a program built and then threw a TypeError at run
time ("expected object at $[0], got string" in a JavaScript file, "a
'string' value is not representable in the target union" in TypeScript),
where Node prints the values.

An empty object type with no members, index signatures or signatures now
lowers as a checked-dynamic value, the same rule as `object` and `Object`.
As an arm of a union that keeps another non-nullish arm, which subtype
reduction would have removed, it holds only the literal's objects and
stays the empty record.

An assertion's scalar check (`as string`, `as number`, `as boolean`) on a
dynamic value whose result goes straight back into a dynamic slot keeps
the original value, since JavaScript erases assertions: passing `{} as
unknown as string` to process.loadEnvFile reports Node's
ERR_INVALID_ARG_TYPE instead of a TypeError from the check.

Object.assign's two-argument path for a dynamic target now applies the
same refusal as the general path when a class instance is copied into a
fresh object literal whose result the checker types as the class, so
`Object.assign({}, instance)` keeps its compile-time refusal now that the
literal lowers as a dynamic value.
2026-10-21 17:16:12 -05:00
Chris Tate 8428bed383 Call an observed prototype method through a bag that does not hold it (#1712) 2026-10-21 17:04:57 -05:00