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
18 changes: 8 additions & 10 deletions core/ast/src/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2462,11 +2462,10 @@ impl<'ast> Visitor<'ast> for AnnexBFunctionDeclarationNamesVisitor<'_> {
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())?;

if let IterableLoopInitializer::Let(node) = node.initializer() {
let bound_names = bound_names(node);
self.0.retain(|name| !bound_names.contains(name));
}
if let IterableLoopInitializer::Const(node) = node.initializer() {
if let IterableLoopInitializer::Let(node)
| IterableLoopInitializer::Const(node)
| IterableLoopInitializer::Using(node) = node.initializer()
{
let bound_names = bound_names(node);
self.0.retain(|name| !bound_names.contains(name));
}
Expand All @@ -2480,11 +2479,10 @@ impl<'ast> Visitor<'ast> for AnnexBFunctionDeclarationNamesVisitor<'_> {
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())?;

if let IterableLoopInitializer::Let(node) = node.initializer() {
let bound_names = bound_names(node);
self.0.retain(|name| !bound_names.contains(name));
}
if let IterableLoopInitializer::Const(node) = node.initializer() {
if let IterableLoopInitializer::Let(node)
| IterableLoopInitializer::Const(node)
| IterableLoopInitializer::Using(node) = node.initializer()
{
let bound_names = bound_names(node);
self.0.retain(|name| !bound_names.contains(name));
}
Expand Down
34 changes: 27 additions & 7 deletions core/ast/src/scope_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,12 @@ impl<'ast> VisitorMut<'ast> for BindingCollectorVisitor<'_> {
Some(ForLoopInitializer::Lexical(decl)) => {
let mut scope = Scope::new(self.scope.clone(), false);
let names = bound_names(&decl.declaration);
if decl.declaration.is_const() {
if decl.declaration.is_const()
|| matches!(
decl.declaration,
LexicalDeclaration::Using(_) | LexicalDeclaration::AwaitUsing(_)
)
{
for name in &names {
let name = name.to_js_string(self.interner);
scope.create_immutable_binding(name, true);
Expand Down Expand Up @@ -1011,7 +1016,8 @@ impl<'ast> VisitorMut<'ast> for BindingCollectorVisitor<'_> {
fn visit_for_in_loop_mut(&mut self, node: &'ast mut ForInLoop) -> ControlFlow<Self::BreakTy> {
let initializer_bound_names = match node.initializer() {
IterableLoopInitializer::Let(declaration)
| IterableLoopInitializer::Const(declaration) => bound_names(declaration),
| IterableLoopInitializer::Const(declaration)
| IterableLoopInitializer::Using(declaration) => bound_names(declaration),
_ => Vec::new(),
};
if initializer_bound_names.is_empty() {
Expand Down Expand Up @@ -1078,7 +1084,8 @@ impl<'ast> VisitorMut<'ast> for BindingCollectorVisitor<'_> {
fn visit_for_of_loop_mut(&mut self, node: &'ast mut ForOfLoop) -> ControlFlow<Self::BreakTy> {
let initializer_bound_names = match node.initializer() {
IterableLoopInitializer::Let(declaration)
| IterableLoopInitializer::Const(declaration) => bound_names(declaration),
| IterableLoopInitializer::Const(declaration)
| IterableLoopInitializer::Using(declaration) => bound_names(declaration),
_ => Vec::new(),
};
if initializer_bound_names.is_empty() {
Expand Down Expand Up @@ -1111,7 +1118,8 @@ impl<'ast> VisitorMut<'ast> for BindingCollectorVisitor<'_> {
}
Some(scope)
}
IterableLoopInitializer::Const(declaration) => {
IterableLoopInitializer::Const(declaration)
| IterableLoopInitializer::Using(declaration) => {
let scope = Scope::new(self.scope.clone(), false);
match declaration {
Binding::Identifier(ident) => {
Expand Down Expand Up @@ -1794,7 +1802,11 @@ fn global_declaration_instantiation(
drop(env.create_mutable_binding(name, false));
}
}
Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {
Declaration::Lexical(
LexicalDeclaration::Const(declaration)
| LexicalDeclaration::Using(declaration)
| LexicalDeclaration::AwaitUsing(declaration),
) => {
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
env.create_immutable_binding(name, true);
Expand Down Expand Up @@ -2174,7 +2186,11 @@ fn function_declaration_instantiation(
drop(lex_env.create_mutable_binding(name, false));
}
}
Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {
Declaration::Lexical(
LexicalDeclaration::Const(declaration)
| LexicalDeclaration::Using(declaration)
| LexicalDeclaration::AwaitUsing(declaration),
) => {
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
lex_env.create_immutable_binding(name, true);
Expand Down Expand Up @@ -2492,7 +2508,11 @@ pub(crate) fn eval_declaration_instantiation_scope(
drop(lex_env.create_mutable_binding(name, false));
}
}
Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {
Declaration::Lexical(
LexicalDeclaration::Const(declaration)
| LexicalDeclaration::Using(declaration)
| LexicalDeclaration::AwaitUsing(declaration),
) => {
for name in bound_names(declaration) {
let name = name.to_js_string(interner);
lex_env.create_immutable_binding(name, true);
Expand Down
7 changes: 5 additions & 2 deletions core/ast/src/statement/iteration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub enum IterableLoopInitializer {
Let(Binding),
/// A new const declaration.
Const(Binding),
/// A new using declaration.
Using(Binding),
/// A pattern with already declared variables.
Pattern(Pattern),
}
Expand All @@ -61,6 +63,7 @@ impl ToInternedString for IterableLoopInitializer {
Self::Var(binding) => (binding.to_interned_string(interner), "var"),
Self::Let(binding) => (binding.to_interned_string(interner), "let"),
Self::Const(binding) => (binding.to_interned_string(interner), "const"),
Self::Using(binding) => (binding.to_interned_string(interner), "using"),
};

format!("{pre} {binding}")
Expand All @@ -76,7 +79,7 @@ impl VisitWith for IterableLoopInitializer {
Self::Identifier(id) => visitor.visit_identifier(id),
Self::Access(pa) => visitor.visit_property_access(pa),
Self::Var(b) => visitor.visit_variable(b),
Self::Let(b) | Self::Const(b) => visitor.visit_binding(b),
Self::Let(b) | Self::Const(b) | Self::Using(b) => visitor.visit_binding(b),
Self::Pattern(p) => visitor.visit_pattern(p),
}
}
Expand All @@ -89,7 +92,7 @@ impl VisitWith for IterableLoopInitializer {
Self::Identifier(id) => visitor.visit_identifier_mut(id),
Self::Access(pa) => visitor.visit_property_access_mut(pa),
Self::Var(b) => visitor.visit_variable_mut(b),
Self::Let(b) | Self::Const(b) => visitor.visit_binding_mut(b),
Self::Let(b) | Self::Const(b) | Self::Using(b) => visitor.visit_binding_mut(b),
Self::Pattern(p) => visitor.visit_pattern_mut(p),
}
}
Expand Down
10 changes: 10 additions & 0 deletions core/engine/src/builtins/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub(crate) mod aggregate;
pub(crate) mod eval;
pub(crate) mod range;
pub(crate) mod reference;
pub(crate) mod suppressed;
pub(crate) mod syntax;
pub(crate) mod r#type;
pub(crate) mod uri;
Expand All @@ -40,6 +41,7 @@ pub(crate) use self::aggregate::AggregateError;
pub(crate) use self::eval::EvalError;
pub(crate) use self::range::RangeError;
pub(crate) use self::reference::ReferenceError;
pub(crate) use self::suppressed::SuppressedError;
pub(crate) use self::syntax::SyntaxError;
pub(crate) use self::r#type::TypeError;
pub(crate) use self::uri::UriError;
Expand Down Expand Up @@ -116,6 +118,14 @@ pub enum ErrorKind {
///
/// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror
Uri,

/// The `SuppressedError` type.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-suppressederror
Suppressed,
}

/// A built-in `Error` object, per the [ECMAScript spec][spec].
Expand Down
119 changes: 119 additions & 0 deletions core/engine/src/builtins/error/suppressed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//! This module implements the global `SuppressedError` object.
//!
//! More information:
//! - [MDN documentation][mdn]
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma262/#sec-suppressederror-constructor
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SuppressedError

use crate::{
Context, JsArgs, JsResult, JsString, JsValue,
builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
js_string,
object::{JsObject, internal_methods::get_prototype_from_constructor},
property::Attribute,
realm::Realm,
string::StaticJsStrings,
};

use super::{Error, ErrorKind};

#[derive(Debug, Clone, Copy)]
pub(crate) struct SuppressedError;

impl IntrinsicObject for SuppressedError {
fn init(realm: &Realm) {
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(realm.intrinsics().constructors().error().constructor())
.inherits(Some(realm.intrinsics().constructors().error().prototype()))
.property(js_string!("name"), Self::NAME, attribute)
.property(js_string!("message"), js_string!(), attribute)
.build();
}

fn get(intrinsics: &Intrinsics) -> JsObject {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
}
}

impl BuiltInObject for SuppressedError {
const NAME: JsString = StaticJsStrings::SUPPRESSED_ERROR;
}

impl BuiltInConstructor for SuppressedError {
const CONSTRUCTOR_ARGUMENTS: usize = 3;
const PROTOTYPE_STORAGE_SLOTS: usize = 2;
const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;

const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
StandardConstructors::suppressed_error;

/// `SuppressedError ( error, suppressed [ , message ] )`
///
/// Creates a new `SuppressedError` object.
///
/// [spec]: https://tc39.es/ecma262/#sec-suppressederror
fn constructor(
new_target: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. If NewTarget is undefined, let newTarget be the active function object;
// else let newTarget be NewTarget.
let new_target = &if new_target.is_undefined() {
context
.active_function_object()
.unwrap_or_else(|| {
context
.intrinsics()
.constructors()
.suppressed_error()
.constructor()
})
.into()
} else {
new_target.clone()
};

// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%SuppressedError.prototype%",
// « [[ErrorData]] »).
let prototype = get_prototype_from_constructor(
new_target,
StandardConstructors::suppressed_error,
context,
)?;
let o = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
Error::with_caller_position(ErrorKind::Suppressed, context),
)
.upcast();

// 3. Perform ! CreateNonEnumerableDataPropertyOrThrow(O, "error", error).
let error = args.get_or_undefined(0);
o.create_non_enumerable_data_property_or_throw(js_string!("error"), error.clone(), context);

// 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(O, "suppressed", suppressed).
let suppressed = args.get_or_undefined(1);
o.create_non_enumerable_data_property_or_throw(
js_string!("suppressed"),
suppressed.clone(),
context,
);

// 5. If message is not undefined, then
let message = args.get_or_undefined(2);
if !message.is_undefined() {
// a. Let msg be ? ToString(message).
let msg = message.to_string(context)?;
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw(js_string!("message"), msg, context);
}

// 6. Return O.
Ok(o.into())
}
}
Loading
Loading