Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions core/ast/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,10 @@ pub(crate) struct Inner {
index: Cell<u32>,
bindings: RefCell<Vec<Binding>>,
function: bool,
// Has the `this` been accessed/escaped outside the function environment boundary.
this_escaped: Cell<bool>,

has_using_declarations: Cell<bool>,

context: Rc<ScopeContext>,
}

Expand All @@ -121,6 +122,7 @@ impl Scope {
bindings: RefCell::default(),
function: true,
this_escaped: Cell::new(false),
has_using_declarations: Cell::new(false),
context: Rc::default(),
}),
}
Expand All @@ -136,6 +138,7 @@ impl Scope {
bindings: RefCell::default(),
function,
this_escaped: Cell::new(false),
has_using_declarations: Cell::new(false),
context: parent.inner.context.clone(),
outer: Some(parent),
}),
Expand All @@ -145,14 +148,28 @@ impl Scope {
/// Checks if the scope has only local bindings.
#[must_use]
pub fn all_bindings_local(&self) -> bool {
// if self.inner.function && self.inn
if self.inner.has_using_declarations.get() {
return false;
}

self.inner
.bindings
.borrow()
.iter()
.all(|binding| !binding.escapes())
}

/// Mark the scope as having using declarations, to prevent `PushScope` optimization
pub fn set_has_using_declarations(&self) {
self.inner.has_using_declarations.set(true);
}

/// Returns true if using declarations exist in this scope
#[must_use]
pub fn has_using_declarations(&self) -> bool {
self.inner.has_using_declarations.get()
}

/// Marks all bindings in this scope as escaping.
pub fn escape_all_bindings(&self) {
for binding in self.inner.bindings.borrow_mut().iter_mut() {
Expand Down
68 changes: 58 additions & 10 deletions core/ast/src/scope_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,14 @@ impl<'ast> VisitorMut<'ast> for BindingCollectorVisitor<'_> {
let scope = match &mut node.inner.init {
Some(ForLoopInitializer::Lexical(decl)) => {
let mut scope = Scope::new(self.scope.clone(), false);

if matches!(
decl.declaration,
LexicalDeclaration::Using(_) | LexicalDeclaration::AwaitUsing(_)
) {
scope.set_has_using_declarations();
}

let names = bound_names(&decl.declaration);
if decl.declaration.is_const() {
for name in &names {
Expand Down Expand Up @@ -1800,6 +1808,16 @@ fn global_declaration_instantiation(
env.create_immutable_binding(name, true);
}
}
Declaration::Lexical(
LexicalDeclaration::Using(declaration)
| LexicalDeclaration::AwaitUsing(declaration),
) => {
env.set_has_using_declarations();
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
env.create_immutable_binding(name.clone(), true);
}
}
_ => {}
}
}
Expand Down Expand Up @@ -1832,6 +1850,18 @@ where

// 3. For each element d of declarations, do
for d in &declarations {
// Mark scope as containing `using` declarations so the bytecompiler
// does NOT optimize away PushScope/PopEnvironment — we need those
// opcodes to trigger block-scoped resource disposal.
if matches!(
d,
LexicallyScopedDeclaration::LexicalDeclaration(
LexicalDeclaration::Using(_) | LexicalDeclaration::AwaitUsing(_)
)
) {
scope.set_has_using_declarations();
}

// i. If IsConstantDeclaration of d is true, then
if let LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Const(d)) = d {
// a. For each element dn of the BoundNames of d, do
Expand Down Expand Up @@ -1861,7 +1891,7 @@ where
}
}

if scope.num_bindings() > 0 {
if scope.num_bindings() > 0 || scope.has_using_declarations() {
Some(scope)
} else {
None
Expand Down Expand Up @@ -2180,6 +2210,16 @@ fn function_declaration_instantiation(
lex_env.create_immutable_binding(name, true);
}
}
Declaration::Lexical(
LexicalDeclaration::Using(declaration)
| LexicalDeclaration::AwaitUsing(declaration),
) => {
lex_env.set_has_using_declarations();
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
lex_env.create_immutable_binding(name.clone(), true);
}
}
_ => {}
}
}
Expand Down Expand Up @@ -2255,16 +2295,14 @@ fn module_instantiation(module: &Module, env: &Scope, interner: &Interner) {
drop(env.create_mutable_binding(name, false));
}
}
LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Using(u)) => {
for name in bound_names(u) {
let name = name.to_js_string(interner);
drop(env.create_mutable_binding(name, false));
}
}
LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::AwaitUsing(au)) => {
for name in bound_names(au) {
LexicallyScopedDeclaration::LexicalDeclaration(
LexicalDeclaration::Using(declaration)
| LexicalDeclaration::AwaitUsing(declaration),
) => {
env.set_has_using_declarations();
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
drop(env.create_mutable_binding(name, false));
env.create_immutable_binding(name.clone(), true);
}
}
LexicallyScopedDeclaration::AssignmentExpression(expr) => {
Expand Down Expand Up @@ -2498,6 +2536,16 @@ pub(crate) fn eval_declaration_instantiation_scope(
lex_env.create_immutable_binding(name, true);
}
}
Declaration::Lexical(
LexicalDeclaration::Using(declaration)
| LexicalDeclaration::AwaitUsing(declaration),
) => {
lex_env.set_has_using_declarations();
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
lex_env.create_immutable_binding(name, true);
}
}
_ => {}
}
}
Expand Down
14 changes: 6 additions & 8 deletions core/engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2259,9 +2259,7 @@ impl<'ctx> ByteCompiler<'ctx> {
self.bytecode.emit_store_undefined(value.variable());
}

// TODO(@abhinavs1920): Add resource to disposal stack
// For now, we just bind the variable like a let declaration
// Full implementation will add: AddDisposableResource opcode
self.bytecode.emit_add_disposable_resource(value.variable());

self.emit_binding(BindingOpcode::InitLexical, ident, &value);
self.register_allocator.dealloc(value);
Expand All @@ -2275,7 +2273,7 @@ impl<'ctx> ByteCompiler<'ctx> {
self.bytecode.emit_store_undefined(value.variable());
}

// TODO: Same as above
self.bytecode.emit_add_disposable_resource(value.variable());

self.compile_declaration_pattern(
pattern,
Expand All @@ -2300,9 +2298,8 @@ impl<'ctx> ByteCompiler<'ctx> {
self.bytecode.emit_store_undefined(value.variable());
}

// TODO: Add resource to async disposal stack
// For now, we just bind the variable like a let declaration
// Full implementation will add: AddAsyncDisposableResource opcode
self.bytecode
.emit_add_async_disposable_resource(value.variable());

self.emit_binding(BindingOpcode::InitLexical, ident, &value);
self.register_allocator.dealloc(value);
Expand All @@ -2316,7 +2313,8 @@ impl<'ctx> ByteCompiler<'ctx> {
self.bytecode.emit_store_undefined(value.variable());
}

// TODO: SAME
self.bytecode
.emit_add_async_disposable_resource(value.variable());
self.compile_declaration_pattern(
pattern,
BindingOpcode::InitLexical,
Expand Down
1 change: 1 addition & 0 deletions core/engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub mod value;
pub mod vm;

mod host_defined;
mod resource_management;
mod sys;

mod spanned_source_text;
Expand Down
Loading
Loading