Skip to content

Commit 0d99bab

Browse files
committed
run_export
1 parent 8af6a40 commit 0d99bab

15 files changed

Lines changed: 359 additions & 325 deletions

src/arc.gleam

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,6 @@ fn handle_repl_line(
378378
entry.ReplEnv(
379379
global_object:,
380380
lexical_globals: dict.new(),
381-
const_lexical_globals: set.new(),
382381
symbol_descriptions: dict.new(),
383382
symbol_registry: dict.new(),
384383
realms: dict.new(),
@@ -628,7 +627,6 @@ fn new_repl_state() {
628627
env: entry.ReplEnv(
629628
global_object:,
630629
lexical_globals: dict.new(),
631-
const_lexical_globals: set.new(),
632630
symbol_descriptions: dict.new(),
633631
symbol_registry: dict.new(),
634632
realms: dict.new(),

src/arc/beam.gleam

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,6 @@ fn make_spawner(
304304
let builtins = state.builtins
305305
let global_object = state.global_object
306306
let lexical_globals = state.lexical_globals
307-
let const_lexical_globals = state.const_lexical_globals
308307
let symbol_descriptions = state.symbol_descriptions
309308
let symbol_registry = state.symbol_registry
310309
fn() {
@@ -326,7 +325,6 @@ fn make_spawner(
326325
builtins,
327326
global_object,
328327
lexical_globals,
329-
const_lexical_globals,
330328
symbol_descriptions,
331329
symbol_registry,
332330
)

src/arc/compiler/emit.gleam

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import arc/vm/opcode.{
2323
IrNewObject, IrNewRegExp, IrObjectRestCopy, IrObjectSpread, IrPop, IrPopTry,
2424
IrPrivateIn, IrPushConst, IrPushTry, IrPutElem, IrPutField, IrPutPrivateField,
2525
IrPutSuperProp, IrPutSuperPropComputed, IrRet, IrReturn, IrScopeGetVar,
26-
IrScopePutVar, IrScopeReboxVar,
26+
IrScopeInitVar, IrScopePutVar, IrScopeReboxVar,
2727
IrScopeTypeofVar, IrSetLine, IrSetThis, IrSetupDerivedClass, IrSwap, IrThrow,
2828
IrTypeOf, IrUnaryOp, IrYield, IrYieldStar,
2929
}
@@ -493,12 +493,13 @@ fn declare_lex(e: Emitter, name: String, is_const: Bool) -> Emitter {
493493
}
494494

495495
/// Store the value on top of stack into a let/const binding declared via
496-
/// declare_lex. Routes to IrInitGlobalLex (bypasses TDZ/const checks) or
497-
/// IrScopePutVar (resolves to PutLocal).
496+
/// declare_lex. Routes to IrInitGlobalLex (global lexical record) or
497+
/// IrScopeInitVar (local slot). Both are *initialization*, so neither triggers
498+
/// the const-reassignment check — only IrScopePutVar (true assignment) does.
498499
fn init_lex(e: Emitter, name: String) -> Emitter {
499500
case at_global_lex(e) {
500501
True -> emit_ir(e, IrInitGlobalLex(name))
501-
False -> emit_ir(e, IrScopePutVar(name))
502+
False -> emit_ir(e, IrScopeInitVar(name))
502503
}
503504
}
504505

src/arc/compiler/resolve.gleam

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ import arc/vm/opcode.{
2424
IrJumpIfTrue, IrLabel, IrMakeClosure, IrNewObject, IrNewRegExp,
2525
IrObjectRestCopy, IrObjectSpread, IrPop, IrPopTry, IrPrivateIn, IrPushConst,
2626
IrPushTry, IrPutBoxed, IrPutElem, IrPutEvalVar, IrPutField, IrPutGlobal,
27-
IrPutLocal, IrPutPrivateField, IrRet, IrReturn, IrScopeGetVar, IrScopePutVar,
27+
IrPutLocal, IrPutPrivateField, IrRet, IrReturn, IrScopeGetVar, IrScopeInitVar,
28+
IrScopePutVar,
2829
IrCheckSuperThis, IrGetSuperProp, IrGetSuperProp2, IrGetSuperPropComputed,
2930
IrGetSuperPropComputed2, IrPutSuperProp, IrPutSuperPropComputed,
3031
IrScopeReboxVar, IrScopeTypeofVar, IrSetLine, IrSetThis, IrSetupDerivedClass,
31-
IrSwap, IrThrow, IrTypeOf, IrTypeofEvalVar, IrTypeofGlobal, IrUnaryOp, IrYield,
32-
IrYieldStar,
32+
IrSwap, IrThrow, IrThrowError, IrTypeOf, IrTypeofEvalVar, IrTypeofGlobal,
33+
IrUnaryOp, IrYield, IrYieldStar,
3334
}
3435
import arc/vm/value.{
3536
type EnvCapture, type FuncTemplate, type JsValue, FuncTemplate,
@@ -141,6 +142,7 @@ fn resolve_ops(
141142
// Scope-aware ops should NOT appear here (consumed by Phase 2)
142143
[IrScopeGetVar(_), ..]
143144
| [IrScopePutVar(_), ..]
145+
| [IrScopeInitVar(_), ..]
144146
| [IrScopeTypeofVar(_), ..]
145147
| [IrScopeReboxVar(_), ..]
146148
| [IrGetThis, ..]
@@ -252,6 +254,8 @@ fn resolve_ops(
252254

253255
// Exception handling
254256
[IrThrow, ..rest] -> resolve_ops(rest, labels, [opcode.Throw, ..acc])
257+
[IrThrowError(kind), ..rest] ->
258+
resolve_ops(rest, labels, [opcode.ThrowError(kind), ..acc])
255259
[IrPopTry, ..rest] -> resolve_ops(rest, labels, [opcode.PopTry, ..acc])
256260
[IrRet, ..rest] -> resolve_ops(rest, labels, [opcode.Ret, ..acc])
257261

src/arc/compiler/scope.gleam

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import arc/compiler/emit.{
1414
LeaveScope, LetBinding, ParamBinding, VarBinding,
1515
}
1616
import arc/vm/opcode.{
17-
type IrOp, IrBoxLocal, IrDeclareEvalVar, IrDeclareGlobalVar, IrGetBoxed,
18-
IrGetEvalVar, IrGetGlobal, IrGetLocal, IrGetThis, IrPushConst, IrPutBoxed,
19-
IrPutEvalVar, IrPutGlobal, IrPutLocal, IrScopeGetVar, IrScopePutVar,
20-
IrScopeReboxVar, IrScopeTypeofVar, IrSetThis, IrTypeOf, IrTypeofEvalVar,
21-
IrTypeofGlobal,
17+
type IrOp, ConstAssignment, IrBoxLocal, IrDeclareEvalVar, IrDeclareGlobalVar,
18+
IrGetBoxed, IrGetEvalVar, IrGetGlobal, IrGetLocal, IrGetThis, IrPushConst,
19+
IrPutBoxed, IrPutEvalVar, IrPutGlobal, IrPutLocal, IrScopeGetVar,
20+
IrScopeInitVar, IrScopePutVar, IrScopeReboxVar, IrScopeTypeofVar, IrSetThis,
21+
IrThrowError, IrTypeOf, IrTypeofEvalVar, IrTypeofGlobal,
2222
}
2323
import arc/vm/value.{type JsValue, JsUndefined, JsUninitialized}
2424
import gleam/bool
@@ -323,7 +323,27 @@ fn resolve_one(r: Resolver, op: EmitterOp) -> Resolver {
323323
}
324324
}
325325

326+
// Assignment (`x = …`, `x += …`, `x++`, destructuring-assign). A store to a
327+
// const local binding is a runtime TypeError; the local store ops carry no
328+
// const flag, so — like QuickJS's resolve_scope_var — we resolve it here and
329+
// emit a throw. Initialization goes through IrScopeInitVar, never here.
326330
Ir(IrScopePutVar(name)) -> {
331+
case lookup(r.scopes, name) {
332+
Some(Binding(kind: ConstBinding, ..)) ->
333+
emit(r, IrThrowError(ConstAssignment))
334+
Some(Binding(index:, is_boxed: True, ..)) -> emit(r, IrPutBoxed(index))
335+
Some(Binding(index:, is_boxed: False, ..)) -> emit(r, IrPutLocal(index))
336+
None ->
337+
case r.fallthrough {
338+
ToGlobal -> emit(r, IrPutGlobal(name))
339+
ToEvalEnv -> emit(r, IrPutEvalVar(name))
340+
}
341+
}
342+
}
343+
344+
// Initialization of a let/const binding — same lowering as IrScopePutVar
345+
// but never const-checked (binding a const for the first time is allowed).
346+
Ir(IrScopeInitVar(name)) -> {
327347
case lookup(r.scopes, name) {
328348
Some(Binding(index:, is_boxed: True, ..)) -> emit(r, IrPutBoxed(index))
329349
Some(Binding(index:, is_boxed: False, ..)) -> emit(r, IrPutLocal(index))

src/arc/vm/exec/entry.gleam

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import arc/vm/value.{type FuncTemplate, type JsValue, type Ref, JsObject}
1616
import gleam/dict
1717
import gleam/option.{type Option, None, Some}
1818
import gleam/result
19-
import gleam/set
2019

2120
// ============================================================================
2221
// Public types
@@ -33,8 +32,7 @@ pub type ModuleResult {
3332
pub type ReplEnv {
3433
ReplEnv(
3534
global_object: Ref,
36-
lexical_globals: dict.Dict(String, JsValue),
37-
const_lexical_globals: set.Set(String),
35+
lexical_globals: dict.Dict(String, value.LexicalGlobal),
3836
symbol_descriptions: dict.Dict(value.SymbolId, String),
3937
symbol_registry: dict.Dict(String, value.SymbolId),
4038
/// Realm builtins registry, keyed by RealmSlot ref.
@@ -99,7 +97,6 @@ pub fn run_module(
9997
builtins,
10098
global_object,
10199
dict.new(),
102-
set.new(),
103100
dict.new(),
104101
dict.new(),
105102
)
@@ -131,7 +128,6 @@ pub fn run_and_drain_repl(
131128
builtins,
132129
env.global_object,
133130
env.lexical_globals,
134-
env.const_lexical_globals,
135131
env.symbol_descriptions,
136132
env.symbol_registry,
137133
),
@@ -145,7 +141,6 @@ pub fn run_and_drain_repl(
145141
ReplEnv(
146142
global_object: drained.global_object,
147143
lexical_globals: drained.lexical_globals,
148-
const_lexical_globals: drained.const_lexical_globals,
149144
symbol_descriptions: drained.symbol_descriptions,
150145
symbol_registry: drained.symbol_registry,
151146
realms: drained.realms,
@@ -154,13 +149,15 @@ pub fn run_and_drain_repl(
154149
}
155150

156151
/// Call a function value with `this` and `args`, then run the `finish` driver
157-
/// to drain. The counterpart to `run`/`run_with` for a value you already hold
158-
/// — e.g. a `receive` export read off a module namespace — so an embedder can
159-
/// invoke it the way the engine would, without re-evaluating a script. This is
160-
/// the host-call-then-drain pattern (cf. Node's MakeCallback, QuickJS
161-
/// `JS_Call` + the `JS_ExecutePendingJob` loop): draining happens at this
162-
/// outermost call only, so don't call it from inside a host function — use the
163-
/// re-entrant `state.call` there.
152+
/// to drain. The counterpart to `run`/`run_with` for a value you already hold —
153+
/// e.g. a `receive` export read off a module namespace — letting an embedder
154+
/// invoke it without re-evaluating a script. The host-call-then-drain pattern
155+
/// (cf. Node's MakeCallback, QuickJS `JS_Call` + `JS_ExecutePendingJob`).
156+
///
157+
/// Built on the lossless `interpreter.call_root`, so it shares its shape with
158+
/// `run`/`run_with`: a thrown value is a `ThrowCompletion`, an engine `VmError`
159+
/// surfaces as `Error` (not a panic — the embedder is outside the VM and can
160+
/// handle it). Draining happens once, at this outermost call.
164161
pub fn run_export(
165162
callee: JsValue,
166163
this_val: JsValue,
@@ -170,16 +167,10 @@ pub fn run_export(
170167
global_object: Ref,
171168
finish: fn(State) -> State,
172169
) -> Result(Completion, VmError) {
173-
let executed =
174-
interpreter.call_to_completion(
175-
callee,
176-
this_val,
177-
args,
178-
heap,
179-
builtins,
180-
global_object,
181-
)
182-
use #(settled, drained) <- result.map(settle(executed, finish))
170+
use #(settled, drained) <- result.map(settle(
171+
interpreter.call_root(callee, this_val, args, heap, builtins, global_object),
172+
finish,
173+
))
183174
completion_of(settled, drained.heap)
184175
}
185176

0 commit comments

Comments
 (0)