Skip to content

[TS] Model property presence and mutations on symbolic objects (#425) - #458

Open
CaelmBleidd wants to merge 18 commits into
mainfrom
caelmbleidd/ts-425-in-operator
Open

CaelmBleidd wants to merge 18 commits into
mainfrom
caelmbleidd/ts-425-in-operator

Conversation

@CaelmBleidd

@CaelmBleidd CaelmBleidd commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Closes #425.

Object literals and symbolic input objects now track property presence separately from the stored value. After obj.fresh = undefined, "fresh" in obj is true; after delete obj.fresh, it is false and reads return undefined. Writes can add undeclared fields and change their runtime kind without requiring the receiver's nominal type to declare that field.

TsOptions.inputPropertyPresence controls initial own-property assumptions for symbolic inputs:

Policy Initial presence
DECLARED_FIELDS (default) Required declared fields exist; optional and undeclared fields have symbolic presence. Inherited declarations and field redeclarations are respected.
SYMBOLIC Every queried field may be present or absent.
ASSUME_PRESENT Every queried field initially exists.
ASSUME_ABSENT Every queried field is initially absent.

Writes and deletions override every policy. A present optional property may still contain undefined.

Supported behavior:

  • Concrete dot, bracket, and in keys, including numeric, empty, and Unicode names.
  • Boolean, number, string, null, undefined, object, and array payloads; literal and symbolic writes; copying unresolved values and changing runtime kinds.
  • Deletion/restoration, conditional mutations, aliases, nested objects and arrays, consistent reads, typeof, strict equality, string length/concatenation, and before/after snapshots for the covered scenarios.

Conditional heap references use StepScope.fork: the cached model orders the branches, and every other feasible receiver is scheduled at the same statement. Array storage lookup consumes the resulting leaf reference. Shared helpers resolve an object literal's own declaration, check unsupported prototype behavior, and join unresolved payloads with their kind selectors. Field reads separate presence, storage selection, initial annotation constraints, and value materialization; initial string constraints apply only while the initial value is active. An explicit internal runtime-object type replaces the empty structural-type filter. Mutation regions are named for both writes and deletions.

The one-line core fix forwards ignoreNullRefs when splitting conditional references. Without it, copying an absent field loses its undefined branch; both the core round-trip regression and a Node-replayed unknown-value copy reproduce the defect.

Symbolic keys, prototype lookup/mutation (including class prototype methods), named input-array properties, and in on arrays remain explicit unsupported outcomes. Arrays stored in object properties support element reads and writes.

Validation on ff1890aa4:

  • Full usvm-ts: 1035 passed, 142 skipped, 0 failures.
  • Full usvm-ts-calls: 13 passed, 0 failures.
  • All 18 affected property tests passed, including Node.js replay. Supported scenarios share one exhaustive analysis between discoverProperties and replay; unsupported outcomes retain separate checks.
  • Both dynamic-field/nominal-receiver regressions passed all ten repetitions; the runtime-object constraint has a dedicated primitive/reference contract test.
  • TypeScript Detekt main/test: 0 findings. git diff --check passed.
  • Unchanged core fix was separately verified on JDK 11: 221 passed, 1 skipped, 0 failures.

Remote CI is reported separately on the PR.

@CaelmBleidd
CaelmBleidd force-pushed the caelmbleidd/ts-425-in-operator branch from ad924e9 to 15babc4 Compare October 2, 2026 22:49
@CaelmBleidd CaelmBleidd changed the title [TS] Evaluate object-literal property presence for in (#425) [TS] Track object-literal property presence for in (#425) Oct 2, 2026
@CaelmBleidd
CaelmBleidd changed the base branch from main to caelmbleidd/ts-426-delete-property October 2, 2026 22:50
@CaelmBleidd
CaelmBleidd force-pushed the caelmbleidd/ts-425-in-operator branch 2 times, most recently from 7d41c41 to f28d30c Compare October 3, 2026 00:02
@CaelmBleidd
CaelmBleidd force-pushed the caelmbleidd/ts-426-delete-property branch from 8add94d to 3a06f16 Compare October 3, 2026 05:28
@CaelmBleidd
CaelmBleidd force-pushed the caelmbleidd/ts-425-in-operator branch 2 times, most recently from a69dc98 to a7cf800 Compare October 3, 2026 05:54
@CaelmBleidd
CaelmBleidd marked this pull request as ready for review October 3, 2026 06:43
@CaelmBleidd
CaelmBleidd force-pushed the caelmbleidd/ts-426-delete-property branch from b33728a to 2f50d8e Compare October 7, 2026 22:16
@CaelmBleidd
CaelmBleidd force-pushed the caelmbleidd/ts-425-in-operator branch from 39e51e9 to ffe2cb8 Compare October 7, 2026 22:16
@CaelmBleidd
CaelmBleidd force-pushed the caelmbleidd/ts-426-delete-property branch from 2f50d8e to 36e6d55 Compare October 9, 2026 12:54
Base automatically changed from caelmbleidd/ts-426-delete-property to main October 9, 2026 13:24
@CaelmBleidd
CaelmBleidd force-pushed the caelmbleidd/ts-425-in-operator branch from ffe2cb8 to 4e4f174 Compare October 9, 2026 13:27
@CaelmBleidd CaelmBleidd changed the title [TS] Track object-literal property presence for in (#425) [TS] Model property presence and mutations on symbolic objects (#425) Oct 9, 2026
Use the standard forker for conditional heap references and share literal and prototype helpers. Separate field read stages, reuse unresolved-value joins, and name the write/delete region after property mutations.

Replace the empty structural object filter with an explicit runtime object type and retain the default Object candidate when enumerating virtual receivers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TS] Evaluate property presence for the object in operator

1 participant