-
Notifications
You must be signed in to change notification settings - Fork 20
Scope analysis
(work in progress)
This page describes what's done in scope crate.
Correct scope/binding information used by emitter.
- which binding is in which scope/environment
- what kind of binding it is
- which binding is closed over by inner functions
https://github.com/mozilla-spidermonkey/jsparagus/blob/master/crates/scope/src/pass.rs
Currently, we're using separate pass to do scope analysis, than AST building and emitting, so we have 3 passes:
- parse script, check early errors, and build AST
- traverse the AST and perform scope analysis
- traverse the AST and emit bytecode
eventually 2 will be merged into 1, by using grammar extension, so the whole API used in scope analysis is implemented based on callback for certain step in parsing, and currently scope analysis pass is using visitor to call them.
https://github.com/mozilla-spidermonkey/jsparagus/blob/master/crates/scope/src/context.rs
Each AST node that will hold bindings (global, block, function, etc) has corresponding *Context
struct,
that collects information necessary for scope analysis.
Fields in the *Context
mostly corresponds to local variable in spec
for example, GlobalDeclarationInstantiation
has functions_to_initialize
, declared_function_names
, declared_var_names
etc, that corresponds to functionsToInitialize
, declaredFunctionNames
, declaredVarNames
etc in the spec.
https://github.com/mozilla-spidermonkey/jsparagus/blob/b1c798cdf250b3d1a41217c78d2e540843f7afa5/crates/scope/src/context.rs#L283-L307 https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
While visiting sub-nodes inside the node, information is accumulated into those fields,
and after that, it performs corresponding spec steps and convert them into ScopeData
, that is used by emitter.
*Context
is stored in ContextStack
, stored in ScopeContext
, and it's the interface between visitor (eventually grammar extension) and each context.
https://github.com/mozilla-spidermonkey/jsparagus/blob/master/crates/scope/src/data.rs
Each generated data is stored in ScopeDataMap
, associated with certain AST node, using AssociatedData
.
(we use AssociatedData
to store data associated to AST node because AST node itself doesn't allow storing extra data)
Bindings are stored in *ScopeData
, that corresponds to *Scope::Data
in SpiderMonkey.
it holds the following information:
- the list of bindings
- what kind of binding it is
- if the binding is closed over by inner functions
https://searchfox.org/mozilla-central/source/js/src/vm/Scope.h
https://github.com/mozilla-spidermonkey/jsparagus/blob/master/crates/scope/src/free_name_tracker.rs
If a binding is closed over by inner function, it should live in heap-allocated EnvironmentObject
.
If not, it can live in call stack (FrameSlot
).
FreeNameTracker
is used to calculate whether a binding is closed over or not.
FreeNameTracker
is allocated for each context, and it collects the following information:
- the list of names defined in this scope
- the list of names used by this and inner scope
- the list of names closed over by inner scope
Each of them are accumulated while visiting nodes inside the context, and propagated to outer scope when leaving the context.
Assuming all early errors are handled by *EarlyErrorsContext
in early_errors.rs before performing scope analysis,
scope analysis doesn't check any error.
Scope data is constructed based on SpiderMonkey's scope data, that is slightly different than Spec's environment:
- In spec, bindings are stored in Environment Record, that is a simple list of bindings, and they're referred by execution context's LexicalEnvironment and VariableEnvironment components, so, single environment record has single kind of bindings
- In SpiderMonkey, bindings are stored in Scope, that is the list of bindings, and each binding has its kind, so single scope has multiple kinds of bindings
So, creating a new environment in term of spec doesn't necessarily create a new Scope in term of SpiderMonkey.
https://github.com/mozilla-spidermonkey/jsparagus/blob/master/crates/emitter/src/emitter_scope.rs
While emitting bytecode, scope data is pushed onto EmitterScopeStack
,
and when emitting bytecode to access bindings, helper structs (reference_op_emitter.rs etc) queries the data.
https://github.com/mozilla-spidermonkey/jsparagus/blob/master/crates/emitter/src/scope_notes.rs
https://github.com/mozilla-spidermonkey/jsparagus/blob/master/crates/emitter/src/gcthings.rs
Scope is allocated as GC thing in SpiderMonkey,
and data collected while scope analysis is passed there, via EmitResult