Module: lib/compiler/rewind/effects

Classifies the effects a body performs, for the two features that need to reason about them: compiler contracts and Avenx Rewind.

ContractValidator has always asked "does this expression have side effects?" as a yes/no question. Rewind asks a sharper one: which effects are there, where are they, and can a rewind put them back? A state.count++ is a side effect and is perfectly reversible; a localStorage.setItem() is a side effect and is not. Both questions are answered from the same pattern table so the two features can never disagree about what an effect is.

What this is not

It is not a JavaScript parser. It is a string- and comment-aware scan, in the same spirit as atlas/resolve.js, and it is deliberately conservative: a pattern it cannot classify is reported, never assumed harmless. The one place that judgement is inverted is the tail effect — a request whose result the action returns or awaits is the very thing whose failure drives the rewind, so flagging it would warn about the intended design.

Source:

Members

(static, constant) EffectKind :string

How an effect relates to a rewind.

Type:
  • string
Source:

(static, constant) IMPURE_PATTERNS :Array:.<RegExp:>

Known side-effecting / impure patterns in expressions.

This is the pure contract's list and its meaning is unchanged: any of these makes an expression impure, assignment included.

Type:
  • Array:.<RegExp:>
Source:

(static, constant) NON_DETERMINISTIC_PATTERNS :Array:.<RegExp:>

Known non-deterministic identifiers and expressions.

Moved here from ContractValidator so that the deterministic contract and any future consumer read the same list.

Type:
  • Array:.<RegExp:>
Source:

(inner, constant) CONTINUATION_PATTERN :RegExp

Promise continuations, where a state write lands outside the transaction.

Type:
  • RegExp
Source:

(inner, constant) IRREVERSIBLE_PATTERNS :Array:.<{kind:: string:, pattern:: RegExp:, label:: string:}>

The effect patterns Rewind reports, in scan order.

test is applied to the masked source; label is what a diagnostic prints. Ordered longest-prefix-first so sessionStorage is not reported as Storage twice.

Type:
  • Array:.<{kind:: string:, pattern:: RegExp:, label:: string:}>
Source:

(inner, constant) REQUEST_PATTERN :RegExp

Request-like calls, which are only irreversible when their result escapes the action unobserved.

Type:
  • RegExp
Source:

Methods

(static) findDeferredWrites(source, optionsopt) → {Array:.<{kind:: string:, text:: string:, line:: number:}>}

Finds promise continuations, where a state write escapes the transaction.

A write made inside .then(...) runs after the action has already returned, so the journal — which follows the dynamic extent of the call — never sees it. That is a completeness problem, not an irreversibility one, which is why it is reported separately, under AVX_W42.

Parameters:
Name Type Attributes Description
source string

The action body source.

options object <optional>

Scan options.

Properties
Name Type Attributes Description
baseLine number <optional>

Line the body starts on in its file.

Source:
Returns:

The continuations.

Type
Array:.<{kind:: string:, text:: string:, line:: number:}>

(static) findIrreversibleEffects(source, optionsopt) → {Array:.<{kind:: string:, label:: string:, text:: string:, line:: number:}>}

Finds every effect in a body that a rewind cannot undo.

Returns descriptors rather than booleans because the diagnostic's whole value is naming what will be left behind, and where.

Parameters:
Name Type Attributes Description
source string

The action body source.

options object <optional>

Scan options.

Properties
Name Type Attributes Description
baseLine number <optional>

Line the body starts on in its file, so reported lines are file lines rather than body lines. 1-based, defaults to 1.

Source:
Returns:

The effects, in source order.

Type
Array:.<{kind:: string:, label:: string:, text:: string:, line:: number:}>

(static) maskLiterals(source) → {string}

Masks string literals, template literals and comments.

Without this an emit inside a string, or a document. written in a comment explaining why it is not used, would be reported as an effect.

Parameters:
Name Type Description
source string

The body source.

Source:
Returns:

The source with literals and comments blanked, same length.

Type
string

(inner) blank(text) → {string}

Replaces a run of source with spaces, preserving newlines so that every offset in the masked text still points at the same line as in the original.

Parameters:
Name Type Description
text string

The run to blank out.

Source:
Returns:

The blanked run.

Type
string

(inner) isTailPosition(masked, offset) → {boolean}

Whether the statement containing an offset is returned or awaited.

The transaction outcome is the value the action hands back, so a request in that position is not a loose effect — it is the mechanism. Scans back to the nearest statement boundary rather than parsing, which is enough to tell return api.save() from api.save();.

Parameters:
Name Type Description
masked string

The masked body source.

offset number

Where the call starts.

Source:
Returns:

True when the call's result is returned or awaited.

Type
boolean

(inner) lineAt(source, offset) → {number}

Turns an offset into a 1-based line number within the body.

Parameters:
Name Type Description
source string

The body source.

offset number

A character offset.

Source:
Returns:

The 1-based line.

Type
number