quickjs-oxide 0.4.1

A safe Rust JavaScript engine targeting semantic parity with QuickJS
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
//! Function, parameter, closure, and eval descriptors of the compiler's stack
//! code, which the register lowering reads.

/// Body storage selected for one named physical parameter after its
/// initializer-visible lexical cell has been initialized. Ordinary parameter
/// environments keep the raw argument slot; the explicit enum leaves room
/// for QuickJS's direct-eval `arguments` override without weakening the ABI.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ParameterBodyStorage {
    Argument(u16),
    #[allow(dead_code, reason = "No producer emits this form.")]
    Local(u16),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ParameterArgumentCell {
    pub argument: u16,
    pub parameter_local: u16,
    pub body: ParameterBodyStorage,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ParameterPatternCopy {
    pub parameter_local: u16,
    pub body_local: u16,
}

/// Authored top-level initializer attached to one formal. Defaults nested
/// inside a BindingPattern deliberately do not appear here: QuickJS lets only
/// the whole formal initializer cut `Function.length` and select the incoming
/// argument before destructuring.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ParameterDefaultSource {
    Argument(u16),
    RestPattern(u16),
}

/// Immutable description of QuickJS's parentless
/// argument scope. `Some` with empty cell arrays is semantically meaningful:
/// a standalone `=` can create a zero-cell environment whose expressions are
/// still barred from the function's variable scope.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ParameterEnvironmentLayout {
    pub initialization_end: u32,
    pub argument_cells: Box<[ParameterArgumentCell]>,
    pub pattern_copies: Box<[ParameterPatternCopy]>,
    pub default_sources: Box<[ParameterDefaultSource]>,
    /// QuickJS's sloppy direct-eval arg-scope `arguments` cell.
    pub synthetic_arguments_local: Option<u16>,
    /// The independent sloppy direct-eval `<arg_var>` object.
    pub arg_eval_variable_object_local: Option<u16>,
}

/// Immutable execution metadata kept beside bytecode.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct FunctionMetadata {
    pub argument_count: u16,
    /// QuickJS's observable `length`, distinct from frame slot count. A
    /// no-default terminal rest BindingPattern normally increments this even
    /// though it owns no physical slot. QuickJS loses that increment when the
    /// compiled function has zero arguments and zero locals because its
    /// zero-initialized bytecode record skips the metadata copy. Direct
    /// owning-function HomeObject/`this`/`new.target` reads count as QuickJS
    /// hidden locals even though this bytecode model does not allocate them a
    /// slot.
    pub defined_argument_count: u16,
    /// Physical argument slot initialized by authenticated `Rest` bytecode.
    /// `None` means this bytecode has no identifier rest parameter.
    pub rest_parameter: Option<u16>,
    /// First trailing argument consumed by a terminal rest BindingPattern.
    /// This equals `argument_count` because QuickJS allocates no named frame
    /// slot for the rest Array before destructuring it.
    pub rest_pattern_start: Option<u16>,
    /// Number of leading locals owned by the independent parameter
    /// environment. Each named formal contributes one mutable cell and each
    /// BindingPattern contributes one per BoundName; a standalone `=` can also
    /// create a meaningful zero-cell environment. The immutable layout assigns
    /// every leading cell an argument or pattern-copy role.
    pub parameter_environment_local_count: u16,
    /// Number of physical argument slots whose authored formal is a
    /// BindingPattern rather than a BindingIdentifier. Keeping the count explicit distinguishes semantic anonymity from
    /// ordinary bytecode whose debug argument names were erased.
    pub pattern_argument_count: u16,
    /// Bytecode PC of the authenticated Nop separating BindingPattern
    /// evaluation from the authored body. Parameter environments without a
    /// BindingPattern carry the same boundary only in
    /// `ParameterEnvironmentLayout::initialization_end`.
    pub parameter_pattern_end: Option<u32>,
    pub local_count: u16,
    /// Synthetic local initialized to the active function object for a named
    /// function expression. This is the typed equivalent of QuickJS's
    /// `func_var_idx` entry prologue.
    pub function_name_local: Option<u16>,
    /// Authenticated one-shot lexical `this` cell for a derived class
    /// constructor. Arrows/direct eval may capture this slot, but only the
    /// derived initialization opcodes may transition it out of TDZ.
    pub derived_this_local: Option<u16>,
    /// Authenticated QuickJS `this_active_func` pseudo local. It is initialized
    /// from the executing function object before parameters run and captured
    /// by arrows/direct eval which contain `super()`.
    pub active_function_local: Option<u16>,
    /// Synthetic local which owns QuickJS's hidden `<var>` object for one
    /// sloppy ordinary-function activation containing syntactic direct eval.
    /// Dynamic eval-name opcodes may name only this authenticated slot.
    pub eval_variable_object_local: Option<u16>,
    pub closure_count: u16,
    pub max_stack: u16,
    pub strict: bool,
    /// QuickJS `strip_var_debug`: this function sampled StripDebug and has no
    /// syntactic direct eval, so ordinary local/closure names are hidden from
    /// TDZ diagnostics even when Oxide retains them as semantic metadata.
    pub strip_variable_debug: bool,
    /// Authenticates the parentless callable owned by one Module record. A
    /// nested function may never carry this bit.
    pub is_module: bool,
    /// Whether `super()` is syntactically permitted in this bytecode. QuickJS
    /// carries this independently from HomeObject storage so direct eval can
    /// inherit constructor authority without inferring it from captured data.
    pub super_call_allowed: bool,
    /// Whether `super.` or `super[]` is syntactically permitted in this
    /// bytecode. This is parser authority, distinct from `needs_home_object`,
    /// which records whether the closure holds a home object.
    pub super_allowed: bool,
    /// QuickJS parser capability inherited by class field/static-block arrows
    /// and direct eval. When set, an implicit `arguments` binding is a syntax
    /// error rather than a lookup which can fall through to an outer/global
    /// environment.
    pub arguments_forbidden: bool,
    /// Whether the closure holds a method HomeObject. QuickJS
    /// derives this from `home_object_var_idx`/`need_home_object`; ordinary
    /// functions leave the flag clear and therefore retain no object edge.
    pub needs_home_object: bool,
    /// Whether this bytecode is the synthetic root compiled for an
    /// ECMAScript eval invocation. Ordinary scripts/functions use `None`;
    /// nested functions inside eval code also use `None` because only the
    /// synthetic root consumes an external caller environment.
    pub eval_kind: EvalKind,
    /// Source-level callable kind used by `Function.prototype.toString` when
    /// debug source has been stripped. This mirrors QuickJS `func_kind`
    /// independently from constructor protocol.
    pub function_kind: FunctionKind,
    /// Whether closure instantiation defines an own `.prototype` property.
    pub has_prototype: bool,
    /// Base/derived constructor protocol carried by QuickJS bytecode.
    pub constructor_kind: ConstructorKind,
    /// Synthetic class-element program emitted by `js_parse_class`-equivalent
    /// lowering.  Keeping this role orthogonal to ordinary/generator/async
    /// function kind marks the synthetic programs without exposing a
    /// JavaScript-visible marker.
    pub class_initializer_kind: Option<ClassInitializerKind>,
    /// Whether an aggregate instance/static class initializer owns the
    /// hidden brand lifecycle for private methods on that class side.
    /// Static blocks and ordinary authored functions may never carry it.
    pub class_private_brand: bool,
}

/// QuickJS eval type carried by one synthetic eval bytecode root.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum EvalKind {
    #[default]
    None,
    Direct,
    Indirect,
}

/// QuickJS bytecode callable kind. Keeping it in immutable function metadata
/// makes source stripping a representation change rather than a semantic one.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum FunctionKind {
    #[default]
    Normal,
    Generator,
    Async,
    AsyncGenerator,
}

/// Bytecode constructor protocol. Derived constructors deliberately remain a
/// separate state because they do not pre-create `this` and apply different
/// return validation in the caller realm.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum ConstructorKind {
    #[default]
    None,
    Base,
    Derived,
}

/// Authenticated role of one synthetic class-element bytecode function.
///
/// QuickJS compiles public instance fields and the ordered static element
/// sequence into hidden functions, with every static block represented by a
/// nested hidden child.  Oxide preserves those three distinct authorities in
/// typed metadata even though all three execute as ordinary synchronous
/// bytecode call frames.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClassInitializerKind {
    InstanceFields,
    StaticElements,
    StaticBlock,
}

/// Where one child function obtains a closure slot when `FClosure` runs.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClosureSource {
    ParentLocal(u16),
    ParentArgument(u16),
    ParentClosure(u16),
    /// Define a declared global binding when the eval/script closure is instantiated.
    GlobalDeclaration,
    /// Resolve a global binding when the eval/script closure is instantiated.
    Global,
    /// Reuse a parent's global binding without re-resolving the name.
    ParentGlobal(u16),
    /// Attach the synthetic direct-eval root to the exact live caller binding
    /// at this flattened environment index. This source is valid only on a
    /// direct-eval root, which the register lowering declines (a direct `eval`
    /// runs as indirect eval).
    EvalEnvironment(u16),
    /// Root VarRef owned by an ECMAScript Module Environment Record.
    ModuleDeclaration,
    /// Root live binding supplied by a requested module during linking.
    ModuleImport,
    /// Root import slot which also receives a pinned QuickJS declaration-time
    /// raw write. This provenance never propagates through child closures.
    ModuleImportCollision,
    /// Hidden, immutable cell containing one module record's cached
    /// null-prototype `import.meta` object.
    ModuleImportMeta,
}

/// A closure variable's name: an exact string constant of the function, or none.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ClosureVariableName {
    None,
    Constant(u32),
}

/// Semantics carried along a closure relay in addition to its storage source.
/// QuickJS uses `JS_VAR_FUNCTION_NAME` to distinguish the immutable private
/// name of a function expression from an ordinary mutable local.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum ClosureVariableKind {
    #[default]
    Normal,
    /// Sealed descriptor-only view of an imported ECMAScript module binding.
    ///
    /// The underlying VarRef remains the exporting module's ordinary
    /// [`Normal`](Self::Normal) cell. This kind authenticates the one legal
    /// metadata mismatch: an immutable lexical importing view over that live
    /// cell. It may appear on module-root, closure-relay, and direct-eval
    /// descriptors, but never on a VariableDefinition or VarRefData cell.
    ModuleImportView,
    FunctionName,
    /// QuickJS `JS_VAR_GLOBAL_FUNCTION_DECL`: a Program function declaration
    /// whose global-property preflight and creation rules differ from `var`.
    GlobalFunction,
    /// QuickJS's hidden `<var>` binding. A sloppy ordinary function with a
    /// syntactic direct-eval site stores one null-prototype variable object in
    /// this binding; eval-created names are properties of that object rather
    /// than fabricated ordinary locals.
    EvalVariableObject,
    /// QuickJS's hidden `<arg_var>` binding. A sloppy authored function with
    /// both a Parameter Environment and a syntactic direct-eval site stores a
    /// second, independent variable object here. Parameter-phase lookup uses
    /// this object as its declaration target; body lookup consults it only
    /// after the ordinary `<var>` object.
    ArgEvalVariableObject,
    /// QuickJS's hidden `<with>` binding. The binding carries the object
    /// environment introduced by one sloppy `with` statement and is relayed
    /// through closures and direct-eval environment descriptors without ever
    /// becoming a source-visible lexical or variable binding.
    WithObject,
    /// A class-private data-field identity. Its cell contains a fresh private
    /// atom for each class evaluation and may only be consumed through typed
    /// private-element bytecode; ordinary local/VarRef reads must reject it.
    PrivateField,
    /// A class-private method closure. Its cell contains the single callable
    /// shared by every branded receiver for one evaluated class side. Like a
    /// private-name cell, it is an immutable lexical capability which may
    /// only be consumed through authenticated private-element bytecode.
    PrivateMethod,
    /// Primary callable cell for a private getter.
    PrivateGetter,
    /// Private setter capability. Both the uninitialized source-visible
    /// primary cell of a setter-only declaration and the initialized synthetic
    /// `<set>` callable cell carry this exact QuickJS kind.
    PrivateSetter,
    /// Primary getter cell for a paired private getter/setter declaration.
    PrivateGetterSetter,
}

impl ClosureVariableKind {}

/// Runtime-independent closure metadata stored beside child bytecode.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ClosureVariable {
    pub source: ClosureSource,
    pub name: ClosureVariableName,
    pub is_lexical: bool,
    pub is_const: bool,
    pub kind: ClosureVariableKind,
}

/// Storage occupied by a binding visible to one syntactic direct-eval site.
///
/// Unlike [`ClosureSource`], these sources always refer to the function whose
/// bytecode contains the eval instruction.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EvalBindingSource {
    Local(u16),
    Argument(u16),
    Closure(u16),
}

/// Syntactic scope represented in a direct-eval environment descriptor.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EvalScopeKind {
    FunctionRoot,
    /// QuickJS's parentless argument scope for a function whose formal
    /// parameters contain expressions. This terminates one function segment
    /// without making the function's body/root bindings visible.
    Parameter,
    FunctionBody,
    ProgramBody,
    Block,
    If,
    For,
    Switch,
    Catch,
    With,
}

/// Variable-environment destination used for declarations introduced by eval.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EvalVariableEnvironment {
    Global,
    /// Strict direct eval always creates declarations in its own eval frame.
    /// The scope ordinal authenticates the current caller-function segment
    /// which grants that strict-local destination; it may be a FunctionRoot or
    /// a parentless Parameter scope.
    StrictLocal(u16),
    /// Sloppy direct eval writes declarations through one exact hidden
    /// variable-object binding. `scope` identifies the descriptor scope which
    /// must contain `source`, which is never an Argument source: it is the
    /// body `<var>` or the parameter `<arg_var>`.
    VariableObject {
        scope: u16,
        source: EvalBindingSource,
    },
}

/// Caller-side provenance retained while compiling one synthetic eval root.
///
/// `ExternalBinding` names the exact flattened caller binding which owns the
/// variable environment.  It is deliberately not a closure-vector guess:
/// the runtime derives it from the authenticated call-site descriptor before
/// the compiler can relay that binding into nested eval bytecode.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EvalCallerVariableTarget {
    Global,
    ExternalBinding(u16),
    StrictLocal,
}

/// Shape of the caller scope chain imported by a synthetic eval root.
///
/// Bindings themselves remain in the root's ordered `EvalRootBinding` prefix;
/// their `scope` ordinals index this exact kind vector.  Keeping empty scopes
/// here is required to reproduce catch/block/function topology when that eval
/// source later contains another direct eval.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EvalCallerProfile {
    pub scope_kinds: Box<[EvalScopeKind]>,
    pub variable_target: EvalCallerVariableTarget,
}

/// One named binding visible from a syntactic direct-eval call site.
///
/// Names are [`JsString`](crate::engine::value::JsString)s.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EvalBinding<Name> {
    pub name: Name,
    pub source: EvalBindingSource,
    pub is_lexical: bool,
    pub is_const: bool,
    pub kind: ClosureVariableKind,
    /// Declaration provenance needed when a direct eval imports this binding:
    /// unlike ordinary lexical bindings, QuickJS permits a sloppy eval `var`
    /// declaration to reuse the live catch-parameter cell.
    pub is_catch_parameter: bool,
}

/// One lexical scope visible from a syntactic direct-eval call site.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EvalScope<Name> {
    pub kind: EvalScopeKind,
    pub bindings: Box<[EvalBinding<Name>]>,
}

/// Immutable description of the bindings and declaration destination visible
/// to one syntactic direct-eval call site.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EvalEnvironment<Name> {
    pub scopes: Box<[EvalScope<Name>]>,
    pub variable_environment: EvalVariableEnvironment,
    pub caller_strict: bool,
    /// Exact caller-bytecode syntax capability copied into a direct eval root.
    pub super_call_allowed: bool,
    /// Exact caller-bytecode syntax capability copied into a direct eval root.
    pub super_allowed: bool,
}

/// One exact caller binding imported by a synthetic direct-eval root.
///
/// The index of an entry in this list is also the operand carried by
/// [`ClosureSource::EvalEnvironment`]. `scope` preserves provenance back to
/// the immutable R1w environment descriptor even though identifier lookup in
/// the eval compiler only needs innermost-name precedence.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EvalRootBinding<Name> {
    pub name: Name,
    pub scope: u16,
    pub is_lexical: bool,
    pub is_const: bool,
    pub kind: ClosureVariableKind,
    /// Catch parameters are represented as lexical locals by the Oxide IR,
    /// but pinned QuickJS permits sloppy eval `var` to reuse their live cell.
    /// Retain that declaration-only distinction without weakening ordinary
    /// lexical reads, TDZ checks, or closure metadata.
    pub is_catch_parameter: bool,
}