Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Move redeclaration errors to parser #2027

Closed
wants to merge 2 commits into from
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
14 changes: 6 additions & 8 deletions boa_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
)]

use boa_engine::{syntax::ast::node::StatementList, Context};
use boa_interner::Interner;
use clap::{ArgEnum, Parser};
use colored::{Color, Colorize};
use rustyline::{config::Config, error::ReadlineError, EditMode, Editor};
Expand Down Expand Up @@ -135,29 +134,28 @@ enum DumpFormat {
///
/// Returns a error of type String with a message,
/// if the token stream has a parsing error.
fn parse_tokens<S>(src: S, interner: &mut Interner) -> Result<StatementList, String>
fn parse_tokens<S>(src: S, context: &mut Context) -> Result<StatementList, String>
where
S: AsRef<[u8]>,
{
use boa_engine::syntax::parser::Parser;

let src_bytes = src.as_ref();
Parser::new(src_bytes, false)
.parse_all(interner)
.parse_all(context)
.map_err(|e| format!("ParsingError: {e}"))
}

/// Dumps the AST to stdout with format controlled by the given arguments.
///
/// Returns a error of type String with a error message,
/// if the source has a syntax or parsing error.
fn dump<S>(src: S, args: &Opt) -> Result<(), String>
fn dump<S>(src: S, args: &Opt, context: &mut Context) -> Result<(), String>
where
S: AsRef<[u8]>,
{
if let Some(ref arg) = args.dump_ast {
let mut interner = Interner::default();
let ast = parse_tokens(src, &mut interner)?;
let ast = parse_tokens(src, context)?;

match arg {
Some(format) => match format {
Expand Down Expand Up @@ -194,7 +192,7 @@ pub fn main() -> Result<(), std::io::Error> {
let buffer = read(file)?;

if args.has_dump_flag() {
if let Err(e) = dump(&buffer, &args) {
if let Err(e) = dump(&buffer, &args, &mut context) {
eprintln!("{e}");
}
} else {
Expand Down Expand Up @@ -233,7 +231,7 @@ pub fn main() -> Result<(), std::io::Error> {
editor.add_history_entry(&line);

if args.has_dump_flag() {
if let Err(e) = dump(&line, &args) {
if let Err(e) = dump(&line, &args, &mut context) {
eprintln!("{e}");
}
} else {
Expand Down
86 changes: 35 additions & 51 deletions boa_engine/src/bytecompiler.rs

Large diffs are not rendered by default.

10 changes: 2 additions & 8 deletions boa_engine/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl Context {
where
S: AsRef<[u8]>,
{
Parser::new(src.as_ref(), self.strict).parse_all(&mut self.interner)
Parser::new(src.as_ref(), self.strict).parse_all(self)
}

/// <https://tc39.es/ecma262/#sec-call>
Expand All @@ -204,12 +204,6 @@ impl Context {
self.realm.global_object()
}

/// Return a reference to the global object string bindings.
#[inline]
pub(crate) fn global_bindings(&self) -> &GlobalPropertyMap {
self.realm.global_bindings()
}

/// Return a mutable reference to the global object string bindings.
#[inline]
pub(crate) fn global_bindings_mut(&mut self) -> &mut GlobalPropertyMap {
Expand Down Expand Up @@ -648,7 +642,7 @@ impl Context {
let main_timer = Profiler::global().start_event("Evaluation", "Main");

let parsing_result = Parser::new(src.as_ref(), false)
.parse_all(&mut self.interner)
.parse_all(self)
.map_err(|e| e.to_string());

let statement_list = match parsing_result {
Expand Down
59 changes: 15 additions & 44 deletions boa_engine/src/environments/compile.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{
environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsResult,
JsString, JsValue,
environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue,
};
use boa_interner::Sym;
use rustc_hash::FxHashMap;
Expand Down Expand Up @@ -154,22 +153,13 @@ impl Context {
///
/// Panics if the global environment is not function scoped.
#[inline]
pub(crate) fn create_mutable_binding(
&mut self,
name: Sym,
function_scope: bool,
allow_name_reuse: bool,
) -> JsResult<()> {
pub(crate) fn create_mutable_binding(&mut self, name: Sym, function_scope: bool) {
let name_str = JsString::from(self.interner().resolve_expect(name));

for (i, env) in self.realm.compile_env.stack.iter_mut().enumerate().rev() {
if !function_scope || env.function_scope {
if env.bindings.contains_key(&name) {
if allow_name_reuse {
return Ok(());
}
return self
.throw_syntax_error(format!("Redeclaration of variable {}", name_str));
return;
}

if i == 0 {
Expand All @@ -178,10 +168,6 @@ impl Context {
.global_property_map
.string_property_map()
.get(&name_str);
let non_configurable_binding_exists = match desc {
Some(desc) => !matches!(desc.configurable(), Some(true)),
None => false,
};
if function_scope && desc.is_none() {
self.global_bindings_mut().insert(
name_str,
Expand All @@ -192,15 +178,9 @@ impl Context {
.configurable(true)
.build(),
);
return Ok(());
return;
} else if function_scope {
return Ok(());
} else if !function_scope
&& !allow_name_reuse
&& non_configurable_binding_exists
{
return self
.throw_syntax_error(format!("Redeclaration of variable {}", name_str));
return;
}
}

Expand All @@ -212,7 +192,7 @@ impl Context {
mutable: true,
},
);
return Ok(());
return;
}
continue;
}
Expand Down Expand Up @@ -249,31 +229,22 @@ impl Context {
///
/// Panics if the global environment does not exist.
#[inline]
pub(crate) fn create_immutable_binding(&mut self, name: Sym) -> JsResult<()> {
let name_str = JsString::from(self.interner().resolve_expect(name));
let exists_global = self.realm.compile_env.stack.len() == 1
&& self.global_bindings().contains_key(&name_str);

pub(crate) fn create_immutable_binding(&mut self, name: Sym) {
let env = self
.realm
.compile_env
.stack
.last_mut()
.expect("global environment must always exist");

if env.bindings.contains_key(&name) || exists_global {
self.throw_syntax_error(format!("Redeclaration of variable {}", name_str))
} else {
let binding_index = env.bindings.len();
env.bindings.insert(
name,
CompileTimeBinding {
index: binding_index,
mutable: false,
},
);
Ok(())
}
let binding_index = env.bindings.len();
env.bindings.insert(
name,
CompileTimeBinding {
index: binding_index,
mutable: false,
},
);
}

/// Initialize an immutable binding at bytecode compile time and return it's binding locator.
Expand Down
5 changes: 0 additions & 5 deletions boa_engine/src/realm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ impl Realm {
&self.global_object
}

#[inline]
pub(crate) fn global_bindings(&self) -> &GlobalPropertyMap {
self.global_property_map.string_property_map()
}

#[inline]
pub(crate) fn global_bindings_mut(&mut self) -> &mut GlobalPropertyMap {
self.global_property_map.string_property_map_mut()
Expand Down
10 changes: 3 additions & 7 deletions boa_engine/src/syntax/ast/node/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use super::{Node, StatementList};
use boa_gc::{Finalize, Trace};
use boa_interner::{Interner, Sym, ToInternedString};

use rustc_hash::FxHashSet;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -40,12 +39,9 @@ impl Block {
self.statements.items()
}

pub(crate) fn lexically_declared_names(&self, interner: &Interner) -> FxHashSet<Sym> {
self.statements.lexically_declared_names(interner)
}

pub(crate) fn var_declared_named(&self) -> FxHashSet<Sym> {
self.statements.var_declared_names()
/// Get the lexically declared names of the block.
pub(crate) fn lexically_declared_names(&self) -> Vec<(Sym, bool)> {
self.statements.lexically_declared_names()
}

/// Implements the display formatting with indentation.
Expand Down
23 changes: 22 additions & 1 deletion boa_engine/src/syntax/ast/node/iteration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::syntax::ast::node::{
declaration::Declaration, identifier::Identifier, DeclarationPattern,
};
use boa_gc::{Finalize, Trace};
use boa_interner::{Interner, ToInternedString};
use boa_interner::{Interner, Sym, ToInternedString};

#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
Expand All @@ -26,6 +26,27 @@ pub enum IterableLoopInitializer {
DeclarationPattern(DeclarationPattern),
}

impl IterableLoopInitializer {
/// Return the bound names of a for loop initializer.
///
/// The returned list may contain duplicates.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames
pub(crate) fn bound_names(&self) -> Vec<Sym> {
raskad marked this conversation as resolved.
Show resolved Hide resolved
match self {
IterableLoopInitializer::Let(decl) | IterableLoopInitializer::Const(decl) => match decl
{
Declaration::Identifier { ident, .. } => vec![ident.sym()],
Declaration::Pattern(pattern) => pattern.idents(),
},
_ => Vec::new(),
}
}
}

impl ToInternedString for IterableLoopInitializer {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
Expand Down
Loading