Module: lib/compiler/codegen/expression

Turns a parsed template expression into JavaScript source.

What moved, and why

Avenx used to ship every template expression, computed value and handler to the browser as source text, and evaluate it by walking an AST there. That put a parser, an interpreter and a scope walker in every production bundle, and made the cost of reading count a recursive dispatch rather than a property access.

The parse is a pure function of the source, and the compiler already has the source. So the parse happens here, once, at build time, and what reaches the browser is a closure the engine itself compiled:

count * 2        →   ($s) => axGet($s, "count") * 2
item.qty         →   ($s) => axRead(axGet($s, "item"), "qty", false)
items.filter(i => !i.done)
                 →   ($s) => axCall(axRead(axGet($s,"items"), "filter", false),
                                    axGet($s,"items"), [(i) => !axRead(i,"done",false)],
                                    "items.filter")

The security boundary is unchanged

Every gate the interpreter applied is still applied, in the same place and on the same terms — it is simply called rather than interpreted. A member read emits module:lib/core/expression/ops.readMember with the key already resolved, so x['const'+'ructor'] and x.constructor still arrive at one check as one string. A call emits callFunction. A free identifier emits readIdentifier, which resolves scope-first and routes an allowed global through the tracer's substitution point.

Two checks get stronger by moving: naming a restricted global and writing a forbidden static key are now build errors with a source location, rather than runtime refusals a developer only sees when the branch executes.

No eval, by construction

The emitted text is written into the component module the bundler links, so the engine compiles it exactly as it compiles the rest of the bundle. Nothing here or downstream calls eval or new Function, which is what makes "an Avenx page needs no 'unsafe-eval'" a property of the pipeline rather than a claim about it.

Arrow parameters stop being a runtime concept

The interpreter carried a Frame chain so a lambda parameter would not resolve against component state. A compiled arrow's parameters are real JavaScript parameters, so the engine's own scoping does that work: this module only has to know which names are lexically bound so it emits the bare name instead of a scope read.

Source:

Classes

ExpressionCodegenError
Bound

Members

(static, constant) RUNTIME_BINDINGS :Object:.<string:, string:>

The local names the emitted code uses for the runtime primitives.

Short, prefixed, and declared in one place so the module emitter and the generator cannot drift. $s is the scope; everything else is imported from the runtime.

Type:
  • Object:.<string:, string:>
Source:

(static, constant) RUNTIME_IMPORT_NAMES :Array:.<string:>

The named runtime imports a module containing compiled expressions needs.

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

(inner, constant) DIRECT_BINARY :Set:.<string:>

Binary operators that map straight onto JavaScript's own.

in is absent deliberately: the interpreter coerced the right-hand side with Object(right), and emitting a bare in would throw where the interpreter returned false.

Type:
  • Set:.<string:>
Source:

(inner, constant) DIRECT_UNARY :Set:.<string:>

Unary operators that map straight onto JavaScript's own.

Type:
  • Set:.<string:>
Source:

Methods

(static) compileExpressionToSource(source) → {string}

Compiles one expression to a scope-taking arrow function.

Parameters:
Name Type Description
source string

The expression source.

Source:
Throws:

When the expression cannot be compiled.

Type
ExpressionCodegenError
Returns:

JavaScript source for ($s) => value.

Type
string

(static) compileStatementsToSource(source) → {string}

Compiles a run of expression statements to a scope-taking arrow function.

Used for inline event handlers and any action body that is a sequence of expressions rather than real statement syntax. The value is discarded, which is what the interpreter did for a Program.

Parameters:
Name Type Description
source string

The statement source.

Source:
Throws:

When the source cannot be compiled.

Type
ExpressionCodegenError
Returns:

JavaScript source for ($s) => { … }.

Type
string

(static) runtimeImportStatement(base, specifieropt, opsSpecifieropt) → {string}

The import statement a module holding a compiled class body needs.

One function rather than a literal repeated at each framing site. There are three -- the CLI's module emitter, the Vite plugin's wrapper, and the tests that frame a bare class body themselves -- and when the generator started emitting calls to the expression primitives, every site that still wrote the import by hand produced a module that threw axGet is not defined at its first render. Deriving the line from the same list the generator emits against makes that class of drift impossible.

Parameters:
Name Type Attributes Description
base string

The base class name to import (AvenxComponent or AvenxPage).

specifier string <optional>

Where the base class comes from.

opsSpecifier string <optional>

Where the primitives come from, when that is somewhere else. A caller importing the base class straight from its own module -- a test does -- still has to reach the runtime index for these.

Source:
Returns:

The import declaration, or two.

Type
string

(static) tryCompileExpression(source) → {Object|Object}

Compiles an expression, returning null rather than throwing.

Parameters:
Name Type Description
source string

The expression source.

Source:
Returns:

The generated source, or the reason.

Type
Object | Object

(static) tryCompileStatements(source) → {Object|Object}

Compiles a statement run, returning null rather than throwing.

Parameters:
Name Type Description
source string

The statement source.

Source:
Returns:

The generated source, or the reason.

Type
Object | Object

(inner) describe(node) → {string}

Describes a node for a runtime error message, matching the interpreter.

Parameters:
Name Type Description
node object

The node.

Source:
Returns:

A short human-readable description.

Type
string

(inner) emit(node, bound, source) → {string}

Generates JavaScript for one AST node.

Parameters:
Name Type Description
node object

The AST node.

bound Bound | null

Names bound by enclosing arrow parameters.

source string

The original expression, for error messages.

Source:
Returns:

JavaScript source for the node's value.

Type
string

(inner) emitArguments(args, bound, source) → {string}

Emits an argument list, including spreads.

Parameters:
Name Type Description
args Array:.<object:>

Argument nodes.

bound Bound | null

Enclosing bindings.

source string

The original expression.

Source:
Returns:

An array literal of the arguments.

Type
string

(inner) emitAssignment(node, bound, source) → {string}

Emits an assignment, routing writes through the guarded primitives.

Parameters:
Name Type Description
node object

The Assignment node.

bound Bound | null

Enclosing bindings.

source string

The original expression.

Source:
Returns:

JavaScript source for the assignment.

Type
string

(inner) emitCall(node, bound, source) → {string}

Emits a call, preserving the receiver for a method call.

The receiver has to be evaluated once and used twice — as the object of the member read and as this — so a method call binds it to a temporary rather than emitting the object expression twice, which would run its side effects twice.

Parameters:
Name Type Description
node object

The Call node.

bound Bound | null

Enclosing bindings.

source string

The original expression.

Source:
Returns:

JavaScript source for the call.

Type
string

(inner) emitLiteral(value) → {string}

Emits a literal value.

Parameters:
Name Type Description
value any

The literal's value.

Source:
Returns:

The literal source.

Type
string

(inner) emitUpdate(node, bound, source) → {string}

Emits ++ / --, preserving prefix and postfix value semantics.

Parameters:
Name Type Description
node object

The Update node.

bound Bound | null

Enclosing bindings.

source string

The original expression.

Source:
Returns:

JavaScript source for the update.

Type
string

(inner) quote(value) → {string}

Quotes a string as a JavaScript literal.

Parameters:
Name Type Description
value any

The value to quote.

Source:
Returns:

The literal source.

Type
string