From 89419e202f904f22322997409030381e6fedecc1 Mon Sep 17 00:00:00 2001 From: Annika Date: Thu, 7 Jan 2021 10:55:14 -0800 Subject: [PATCH] VM: Implement variable declaration (var, const, and let) (#1030) * VM: Implement LetDeclList and the `let` keyword * Support var and const * Split defining and initializing into separate instructions * DefLet doesn't need the value * InitLexical initializes value if set * Code review * Fix rustfmt * Add documentation * InitLexical fix Co-authored-by: jasonwilliams --- boa/src/vm/compilation.rs | 40 +++++++++++++++++++++++++++++++++++++ boa/src/vm/instructions.rs | 13 ++++++++++++ boa/src/vm/mod.rs | 41 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/boa/src/vm/compilation.rs b/boa/src/vm/compilation.rs index b4f51b453b..4b1cb4fe1f 100644 --- a/boa/src/vm/compilation.rs +++ b/boa/src/vm/compilation.rs @@ -58,6 +58,46 @@ impl CodeGen for Node { } Node::BinOp(ref op) => op.compile(compiler), Node::UnaryOp(ref op) => op.compile(compiler), + Node::VarDeclList(ref list) => { + for var_decl in list.as_ref() { + let name = var_decl.name(); + let index = compiler.pool.len(); + compiler.add_instruction(Instruction::DefVar(index)); + compiler.pool.push(name.into()); + + if let Some(v) = var_decl.init() { + v.compile(compiler); + compiler.add_instruction(Instruction::InitLexical(index)) + }; + } + } + Node::LetDeclList(ref list) => { + for let_decl in list.as_ref() { + let name = let_decl.name(); + let index = compiler.pool.len(); + compiler.add_instruction(Instruction::DefLet(index)); + compiler.pool.push(name.into()); + + // If name has a value we can init here too + if let Some(v) = let_decl.init() { + v.compile(compiler); + compiler.add_instruction(Instruction::InitLexical(index)) + }; + } + } + Node::ConstDeclList(ref list) => { + for const_decl in list.as_ref() { + let name = const_decl.name(); + let index = compiler.pool.len(); + compiler.add_instruction(Instruction::DefConst(index)); + compiler.pool.push(name.into()); + + if let Some(v) = const_decl.init() { + v.compile(compiler); + compiler.add_instruction(Instruction::InitLexical(index)) + }; + } + } _ => unimplemented!(), } } diff --git a/boa/src/vm/instructions.rs b/boa/src/vm/instructions.rs index 86fc972dff..39ea22abbd 100644 --- a/boa/src/vm/instructions.rs +++ b/boa/src/vm/instructions.rs @@ -57,6 +57,15 @@ pub enum Instruction { Neg, BitNot, Not, + + /// The usize is the index of the variable name in the pool + DefVar(usize), + /// The usize is the index of the variable name in the pool + DefLet(usize), + /// The usize is the index of the variable name in the pool + DefConst(usize), + /// The usize is the index of the value to initiate the variable with in the pool + InitLexical(usize), } impl std::fmt::Display for Instruction { @@ -100,6 +109,10 @@ impl std::fmt::Display for Instruction { Self::Neg => write!(f, "Neg"), Self::BitNot => write!(f, "BitNot"), Self::Not => write!(f, "Not"), + Self::DefVar(name) => write!(f, "DefVar({})", name), + Self::DefLet(name) => write!(f, "DefLet({})", name), + Self::DefConst(name) => write!(f, "DefConst({})", name), + Self::InitLexical(value) => write!(f, "InitLexical({})", value), } } } diff --git a/boa/src/vm/mod.rs b/boa/src/vm/mod.rs index ac73339acf..ee831cecdc 100644 --- a/boa/src/vm/mod.rs +++ b/boa/src/vm/mod.rs @@ -1,9 +1,8 @@ -use crate::{Context, Result, Value}; +use crate::{environment::lexical_environment::VariableScope, BoaProfiler, Context, Result, Value}; pub(crate) mod compilation; pub(crate) mod instructions; -use crate::BoaProfiler; pub use compilation::Compiler; pub use instructions::Instruction; @@ -268,6 +267,44 @@ impl<'a> VM<'a> { }; self.push(value.into()); } + Instruction::DefVar(name_index) => { + let name: String = self.pool[name_index].to_string(self.ctx)?.to_string(); + + self.ctx + .realm_mut() + .environment + .create_mutable_binding(name.to_string(), false, VariableScope::Function) + .map_err(|e| e.to_error(self.ctx))?; + } + Instruction::DefLet(name_index) => { + let name = self.pool[name_index].to_string(self.ctx)?; + + self.ctx + .realm_mut() + .environment + .create_mutable_binding(name.to_string(), false, VariableScope::Block) + .map_err(|e| e.to_error(self.ctx))?; + } + Instruction::DefConst(name_index) => { + let name = self.pool[name_index].to_string(self.ctx)?; + + self.ctx + .realm_mut() + .environment + .create_immutable_binding(name.to_string(), false, VariableScope::Block) + .map_err(|e| e.to_error(self.ctx))?; + } + Instruction::InitLexical(name_index) => { + let name = self.pool[name_index].to_string(self.ctx)?; + let value = self.pop(); + self.ctx + .realm_mut() + .environment + .initialize_binding(&name, value.clone()) + .map_err(|e| e.to_error(self.ctx))?; + + self.push(value); + } } idx += 1;