The runtime primitives a compiled expression calls.
Why these exist separately from the evaluator
Avenx used to evaluate template expressions by walking an AST in the browser.
The security boundary lived inside that walk: every property read went
through one function with the key already resolved, so x.constructor and
x['const'+'ructor'] arrived at the same check as the same string.
Expressions are now compiled to JavaScript at build time, so the walk is gone. The boundary is not: the compiler emits a call to readMember wherever the AST walk would have made one, and to callFunction wherever it would have invoked. The guarantees are therefore identical, and the cost per access drops from a recursive dispatch to one monomorphic call.
This module is what a production bundle keeps. The parser and the evaluator that used to sit above it are build-time only.
What is guaranteed, and what is not
Guaranteed: a compiled expression cannot reach the Function constructor,
cannot read or write __proto__ / constructor / prototype however the
key is spelled, cannot obtain a built-in prototype object, and cannot name a
global outside ALLOWED_GLOBALS. Nothing here uses eval or
new Function, and neither does the code the compiler emits, so a page of
compiled Avenx expressions needs no 'unsafe-eval'.
Not guaranteed: this is not an isolation boundary against hostile expression source. An expression can still call any function the scope legitimately exposes. The boundary protects the runtime from accidents and from reaching outside the declared scope; it does not make it safe to compile expressions written by an untrusted party.
- Source:
Members
(static, constant) ALLOWED_GLOBALS :Set:.<string:>
Globals an expression may name.
Deliberately identical to the set the AST evaluator allowed, so migrating an application changes nothing about which globals its templates can see.
Type:
- Source:
(static, constant) EXPRESSION_OPS :Object:.<string:, function()>
The primitives a compiled expression calls, keyed by the name the generator emits for each.
One map rather than a list repeated at every consumer. A generated module
gets these as named imports the bundler resolves; a host that evaluates a
bare class body instead (avenx-core/tooling, the Vite plugin) injects them
from here. lib/compiler/codegen/expression.js declares the same names on
the emitting side, and a test requires the two to agree.
Type:
- Source:
(static, constant) FORBIDDEN_KEYS :Set:.<string:>
Property names an expression may never read or write.
Checked against the resolved key, so a computed access spelled
x['const' + 'ructor'] is rejected on the same terms as x.constructor.
Type:
- Source:
(static, constant) RESTRICTED_GLOBALS :Set:.<string:>
Globals an expression may never name, whether or not the host defines them.
Listed explicitly rather than inferred from in globalThis, because the
diagnostic has to be the same in every environment: localStorage is
restricted in a browser, in happy-dom and in bare Node, and a developer
reading AVX_R15 should not get a different answer depending on where the
expression happened to run.
Type:
- Source:
(inner, constant) FUNCTION_CONSTRUCTORS :Set:.<function()>
The dynamic-code constructors.
Reaching any of these would be arbitrary code execution, so they are refused as values however they were obtained.
Type:
- Source:
(inner, constant) PROTECTED_PROTOTYPES :Set:.<object:>
Built-in prototypes shared by every object in the realm.
Handing one to an expression would let it mutate state shared with the host page, so they are refused as values rather than by enumerating every mutating method on them.
Type:
- Source:
Methods
(static) callFunction(fn, thisArg, args, description) → {any}
Calls a function on behalf of an expression.
Parameters:
| Name | Type | Description |
|---|---|---|
fn |
any | The callee. |
thisArg |
any | The receiver. |
args |
Array:.<any:> | The arguments. |
description |
string | How the callee was written, for the error. |
- Source:
Returns:
The result.
- Type
- any
(static) construct(ctor, args, description) → {any}
Constructs a value on behalf of an expression.
Parameters:
| Name | Type | Description |
|---|---|---|
ctor |
any | The constructor. |
args |
Array:.<any:> | The arguments. |
description |
string | How the constructor was written, for the error. |
- Source:
Returns:
The constructed value.
- Type
- any
(static) guardKey(key) → {any}
Validates a computed property key before it is used in an object literal.
Parameters:
| Name | Type | Description |
|---|---|---|
key |
any | The evaluated key. |
- Source:
Returns:
The key, when it is allowed.
- Type
- any
(static) guardValue(value) → {any}
Refuses a value that must never reach an expression.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
any | The value about to be returned. |
- Source:
Returns:
The value, when it is allowed.
- Type
- any
(static) hasIn(key, target) → {boolean}
in applied with the right-hand side coerced, matching the interpreter.
Parameters:
| Name | Type | Description |
|---|---|---|
key |
any | The key to test. |
target |
any | The object to test against. |
- Source:
Returns:
Whether the key is present.
- Type
- boolean
(static) readIdentifier(scope, name) → {any}
Resolves a free identifier against the scope, then the allowed globals.
Scope first, so a component's own Date state key wins over the global, and
so the reactive scope registers the dependency. A global is resolved through
the tracer's substitution point rather than read straight off globalThis,
which is what lets a recording log the non-deterministic values an expression
observed and a replay hand the same ones back.
Parameters:
| Name | Type | Description |
|---|---|---|
scope |
object | The evaluation scope. |
name |
string | The identifier. |
- Source:
Returns:
The bound value, or undefined when nothing binds it.
- Type
- any
(static) readMember(object, key, optionalopt) → {any}
Reads a property, with the key already resolved.
This is the gate a source-text check cannot provide. Because the key arrives as a value rather than as source, there is no spelling of it that avoids the check.
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
object |
any | The object to read from. |
|
key |
any | The resolved property key. |
|
optional |
boolean |
<optional> |
Whether the access used |
- Source:
Returns:
The property value.
- Type
- any
(static) refuse(message)
Raises a sandbox violation.
Parameters:
| Name | Type | Description |
|---|---|---|
message |
string | What was refused. |
- Source:
Throws:
-
Always.
- Type
- AvenxError
(static) typeofIdentifier(scope, name) → {string}
typeof applied to a free identifier.
typeof maybeUndefined must not throw for an unbound name, which is the
whole reason the operator gets used in a template.
Parameters:
| Name | Type | Description |
|---|---|---|
scope |
object | The evaluation scope. |
name |
string | The identifier. |
- Source:
Returns:
The type name.
- Type
- string
(static) writeIdentifier(scope, name, value) → {any}
Assigns to a free identifier.
Parameters:
| Name | Type | Description |
|---|---|---|
scope |
object | The evaluation scope. |
name |
string | The identifier. |
value |
any | The value to assign. |
- Source:
Returns:
The assigned value.
- Type
- any
(static) writeMember(object, key, value) → {any}
Writes a property, with the key already resolved.
Parameters:
| Name | Type | Description |
|---|---|---|
object |
any | The object to write to. |
key |
any | The resolved property key. |
value |
any | The value to assign. |
- Source:
Returns:
The assigned value.
- Type
- any
(inner) isKnownGlobal(name) → {boolean}
Whether a name exists on the host global object.
Parameters:
| Name | Type | Description |
|---|---|---|
name |
string | The identifier. |
- Source:
Returns:
True when the host defines it.
- Type
- boolean