Browse Source

Implement Private Runtime Environments (#2929)

pull/2946/head
raskad 2 years ago committed by GitHub
parent
commit
5e9193aced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      boa_ast/src/function/class.rs
  2. 181
      boa_ast/src/operations.rs
  3. 42
      boa_engine/src/builtins/function/mod.rs
  4. 19
      boa_engine/src/bytecompiler/class.rs
  5. 15
      boa_engine/src/bytecompiler/declarations.rs
  6. 2
      boa_engine/src/environments/mod.rs
  7. 65
      boa_engine/src/environments/runtime/mod.rs
  8. 34
      boa_engine/src/environments/runtime/private.rs
  9. 9
      boa_engine/src/object/jsobject.rs
  10. 19
      boa_engine/src/object/mod.rs
  11. 5
      boa_engine/src/object/operations.rs
  12. 6
      boa_engine/src/vm/code_block.rs
  13. 9
      boa_engine/src/vm/flowgraph/mod.rs
  14. 8
      boa_engine/src/vm/opcode/binary_ops/mod.rs
  15. 7
      boa_engine/src/vm/opcode/get/private.rs
  16. 14
      boa_engine/src/vm/opcode/mod.rs
  17. 3
      boa_engine/src/vm/opcode/push/class/field.rs
  18. 12
      boa_engine/src/vm/opcode/push/class/private.rs
  19. 59
      boa_engine/src/vm/opcode/push/environment.rs
  20. 27
      boa_engine/src/vm/opcode/set/private.rs
  21. 36
      boa_parser/src/parser/cursor/mod.rs
  22. 6
      boa_parser/src/parser/expression/left_hand_side/call.rs
  23. 6
      boa_parser/src/parser/expression/left_hand_side/member.rs
  24. 14
      boa_parser/src/parser/expression/left_hand_side/optional/mod.rs
  25. 8
      boa_parser/src/parser/expression/mod.rs
  26. 16
      boa_parser/src/parser/mod.rs
  27. 22
      boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs
  28. 14
      boa_parser/src/parser/statement/mod.rs

8
boa_ast/src/function/class.rs

@ -538,9 +538,6 @@ impl VisitWith for ClassElement {
pub struct PrivateName { pub struct PrivateName {
/// The `[[Description]]` internal slot of the private name. /// The `[[Description]]` internal slot of the private name.
description: Sym, description: Sym,
/// The indices of the private name are included to ensure that private names are unique.
pub(crate) indices: (usize, usize),
} }
impl PrivateName { impl PrivateName {
@ -548,10 +545,7 @@ impl PrivateName {
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn new(description: Sym) -> Self { pub const fn new(description: Sym) -> Self {
Self { Self { description }
description,
indices: (0, 0),
}
} }
/// Get the description of the private name. /// Get the description of the private name.

181
boa_ast/src/operations.rs

@ -6,14 +6,18 @@ use core::ops::ControlFlow;
use std::convert::Infallible; use std::convert::Infallible;
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::FxHashSet;
use crate::{ use crate::{
declaration::{Binding, ExportDeclaration, ImportDeclaration, VarDeclaration, Variable}, declaration::{Binding, ExportDeclaration, ImportDeclaration, VarDeclaration, Variable},
expression::{access::SuperPropertyAccess, Await, Identifier, SuperCall, Yield}, expression::{
access::{PrivatePropertyAccess, SuperPropertyAccess},
operator::BinaryInPrivate,
Await, Identifier, SuperCall, Yield,
},
function::{ function::{
ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement,
Function, Generator, PrivateName, Function, Generator,
}, },
property::{MethodDefinition, PropertyDefinition}, property::{MethodDefinition, PropertyDefinition},
statement::{ statement::{
@ -21,7 +25,7 @@ use crate::{
LabelledItem, LabelledItem,
}, },
try_break, try_break,
visitor::{NodeRef, VisitWith, Visitor, VisitorMut}, visitor::{NodeRef, VisitWith, Visitor},
Declaration, Expression, ModuleItem, Statement, StatementList, StatementListItem, Declaration, Expression, ModuleItem, Statement, StatementList, StatementListItem,
}; };
@ -835,108 +839,105 @@ pub fn top_level_var_declared_names(stmts: &StatementList) -> FxHashSet<Identifi
names names
} }
/// Resolves the private names of a class and all of the contained classes and private identifiers. /// Returns `true` if all private identifiers in a node are valid.
pub fn class_private_name_resolver(node: &mut Class, top_level_class_index: usize) -> bool { ///
/// Visitor used by the function to search for an identifier with the name `arguments`. /// This is equivalent to the [`AllPrivateIdentifiersValid`][spec] syntax operation in the spec.
#[derive(Debug, Clone)] ///
struct ClassPrivateNameResolver { /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-allprivateidentifiersvalid
private_environments_stack: Vec<FxHashMap<Sym, PrivateName>>, #[must_use]
top_level_class_index: usize, #[inline]
} pub fn all_private_identifiers_valid<'a, N>(node: &'a N, private_names: Vec<Sym>) -> bool
where
&'a N: Into<NodeRef<'a>>,
{
AllPrivateIdentifiersValidVisitor(private_names)
.visit(node.into())
.is_continue()
}
impl<'ast> VisitorMut<'ast> for ClassPrivateNameResolver { struct AllPrivateIdentifiersValidVisitor(Vec<Sym>);
type BreakTy = ();
#[inline] impl<'ast> Visitor<'ast> for AllPrivateIdentifiersValidVisitor {
fn visit_class_mut(&mut self, node: &'ast mut Class) -> ControlFlow<Self::BreakTy> { type BreakTy = ();
let mut names = FxHashMap::default();
fn visit_class(&mut self, node: &'ast Class) -> ControlFlow<Self::BreakTy> {
for element in node.elements.iter_mut() { if let Some(node) = node.super_ref() {
match element { try_break!(self.visit(node));
ClassElement::PrivateMethodDefinition(name, _) }
| ClassElement::PrivateStaticMethodDefinition(name, _)
| ClassElement::PrivateFieldDefinition(name, _) let mut names = self.0.clone();
| ClassElement::PrivateStaticFieldDefinition(name, _) => { for element in node.elements() {
name.indices = ( match element {
self.top_level_class_index, ClassElement::PrivateMethodDefinition(name, _)
self.private_environments_stack.len(), | ClassElement::PrivateStaticMethodDefinition(name, _)
); | ClassElement::PrivateFieldDefinition(name, _)
names.insert(name.description(), *name); | ClassElement::PrivateStaticFieldDefinition(name, _) => {
} names.push(name.description());
_ => {}
} }
_ => {}
} }
}
self.private_environments_stack.push(names); let mut visitor = Self(names);
for element in node.elements.iter_mut() { if let Some(node) = node.constructor() {
match element { try_break!(visitor.visit(node));
ClassElement::MethodDefinition(name, method) }
| ClassElement::StaticMethodDefinition(name, method) => {
try_break!(self.visit_property_name_mut(name)); for element in node.elements() {
try_break!(self.visit_method_definition_mut(method)); match element {
} ClassElement::MethodDefinition(name, method)
ClassElement::PrivateMethodDefinition(_, method) | ClassElement::StaticMethodDefinition(name, method) => {
| ClassElement::PrivateStaticMethodDefinition(_, method) => { try_break!(visitor.visit(name));
try_break!(self.visit_method_definition_mut(method)); try_break!(visitor.visit(method));
} }
ClassElement::FieldDefinition(name, expression) ClassElement::FieldDefinition(name, expression)
| ClassElement::StaticFieldDefinition(name, expression) => { | ClassElement::StaticFieldDefinition(name, expression) => {
try_break!(self.visit_property_name_mut(name)); try_break!(visitor.visit(name));
if let Some(expression) = expression { if let Some(expression) = expression {
try_break!(self.visit_expression_mut(expression)); try_break!(visitor.visit(expression));
}
}
ClassElement::PrivateFieldDefinition(_, expression)
| ClassElement::PrivateStaticFieldDefinition(_, expression) => {
if let Some(expression) = expression {
try_break!(self.visit_expression_mut(expression));
}
} }
ClassElement::StaticBlock(statement_list) => { }
try_break!(self.visit_statement_list_mut(statement_list)); ClassElement::PrivateMethodDefinition(_, method)
| ClassElement::PrivateStaticMethodDefinition(_, method) => {
try_break!(visitor.visit(method));
}
ClassElement::PrivateFieldDefinition(_, expression)
| ClassElement::PrivateStaticFieldDefinition(_, expression) => {
if let Some(expression) = expression {
try_break!(visitor.visit(expression));
} }
} }
ClassElement::StaticBlock(statement_list) => {
try_break!(visitor.visit(statement_list));
}
} }
if let Some(function) = &mut node.constructor {
try_break!(self.visit_function_mut(function));
}
self.private_environments_stack.pop();
ControlFlow::Continue(())
} }
#[inline] ControlFlow::Continue(())
fn visit_private_name_mut( }
&mut self,
node: &'ast mut PrivateName,
) -> ControlFlow<Self::BreakTy> {
let mut found = false;
for environment in self.private_environments_stack.iter().rev() {
if let Some(n) = environment.get(&node.description()) {
found = true;
node.indices = n.indices;
break;
}
}
if found { fn visit_private_property_access(
ControlFlow::Continue(()) &mut self,
} else { node: &'ast PrivatePropertyAccess,
ControlFlow::Break(()) ) -> ControlFlow<Self::BreakTy> {
} if self.0.contains(&node.field().description()) {
self.visit(node.target())
} else {
ControlFlow::Break(())
} }
} }
let mut visitor = ClassPrivateNameResolver { fn visit_binary_in_private(
private_environments_stack: Vec::new(), &mut self,
top_level_class_index, node: &'ast BinaryInPrivate,
}; ) -> ControlFlow<Self::BreakTy> {
if self.0.contains(&node.lhs().description()) {
visitor.visit_class_mut(node).is_continue() self.visit(node.rhs())
} else {
ControlFlow::Break(())
}
}
} }
/// Errors that can occur when checking labels. /// Errors that can occur when checking labels.

42
boa_engine/src/builtins/function/mod.rs

@ -12,15 +12,15 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
use crate::{ use crate::{
builtins::BuiltInObject, builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
bytecompiler::FunctionCompiler, bytecompiler::FunctionCompiler,
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
environments::EnvironmentStack, environments::{EnvironmentStack, PrivateEnvironment},
error::JsNativeError, error::JsNativeError,
js_string, js_string,
native_function::NativeFunction, native_function::NativeFunction,
object::{internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, Object, ObjectData},
object::{JsFunction, PrivateElement}, object::{JsFunction, PrivateElement, PrivateName},
property::{Attribute, PropertyDescriptor, PropertyKey}, property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm, realm::Realm,
string::utf16, string::utf16,
@ -30,21 +30,22 @@ use crate::{
Context, JsArgs, JsResult, JsString, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_ast::{ use boa_ast::{
function::{FormalParameterList, PrivateName}, function::FormalParameterList,
operations::{bound_names, contains, lexically_declared_names, ContainsSymbol}, operations::{
all_private_identifiers_valid, bound_names, contains, lexically_declared_names,
ContainsSymbol,
},
StatementList, StatementList,
}; };
use boa_gc::{self, custom_trace, Finalize, Gc, Trace}; use boa_gc::{self, custom_trace, Finalize, Gc, Trace};
use boa_interner::Sym; use boa_interner::Sym;
use boa_parser::{Parser, Source}; use boa_parser::{Parser, Source};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use thin_vec::ThinVec;
use std::{fmt, io::Read}; use std::{fmt, io::Read};
use thin_vec::ThinVec;
use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};
pub(crate) mod arguments; pub(crate) mod arguments;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -308,6 +309,13 @@ impl Function {
Self { kind, realm } Self { kind, realm }
} }
/// Push a private environment to the function.
pub(crate) fn push_private_environment(&mut self, environment: Gc<PrivateEnvironment>) {
if let FunctionKind::Ordinary { environments, .. } = &mut self.kind {
environments.push_private(environment);
}
}
/// Returns true if the function object is a derived constructor. /// Returns true if the function object is a derived constructor.
pub(crate) const fn is_derived_constructor(&self) -> bool { pub(crate) const fn is_derived_constructor(&self) -> bool {
if let FunctionKind::Ordinary { if let FunctionKind::Ordinary {
@ -368,9 +376,9 @@ impl Function {
} }
/// Pushes a private value to the `[[Fields]]` internal slot if present. /// Pushes a private value to the `[[Fields]]` internal slot if present.
pub(crate) fn push_field_private(&mut self, key: PrivateName, value: JsFunction) { pub(crate) fn push_field_private(&mut self, name: PrivateName, value: JsFunction) {
if let FunctionKind::Ordinary { fields, .. } = &mut self.kind { if let FunctionKind::Ordinary { fields, .. } = &mut self.kind {
fields.push(ClassFieldDefinition::Private(key, value)); fields.push(ClassFieldDefinition::Private(name, value));
} }
} }
@ -705,6 +713,18 @@ impl BuiltInFunctionObject {
} }
} }
if !all_private_identifiers_valid(&parameters, Vec::new()) {
return Err(JsNativeError::syntax()
.with_message("invalid private identifier usage")
.into());
}
if !all_private_identifiers_valid(&body, Vec::new()) {
return Err(JsNativeError::syntax()
.with_message("invalid private identifier usage")
.into());
}
let code = FunctionCompiler::new() let code = FunctionCompiler::new()
.name(Sym::ANONYMOUS) .name(Sym::ANONYMOUS)
.generator(generator) .generator(generator)

19
boa_engine/src/bytecompiler/class.rs

@ -100,6 +100,23 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::SetClassPrototype); self.emit_opcode(Opcode::SetClassPrototype);
self.emit_opcode(Opcode::Swap); self.emit_opcode(Opcode::Swap);
let count_label = self.emit_opcode_with_operand(Opcode::PushPrivateEnvironment);
let mut count = 0;
for element in class.elements() {
match element {
ClassElement::PrivateMethodDefinition(name, _)
| ClassElement::PrivateStaticMethodDefinition(name, _)
| ClassElement::PrivateFieldDefinition(name, _)
| ClassElement::PrivateStaticFieldDefinition(name, _) => {
count += 1;
let index = self.get_or_insert_private_name(*name);
self.emit_u32(index);
}
_ => {}
}
}
self.patch_jump_with_target(count_label, count);
// TODO: set function name for getter and setters // TODO: set function name for getter and setters
for element in class.elements() { for element in class.elements() {
match element { match element {
@ -536,6 +553,8 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::PopEnvironment); self.emit_opcode(Opcode::PopEnvironment);
} }
self.emit_opcode(Opcode::PopPrivateEnvironment);
if !expression { if !expression {
self.emit_binding( self.emit_binding(
BindingOpcode::InitVar, BindingOpcode::InitVar,

15
boa_engine/src/bytecompiler/declarations.rs

@ -7,8 +7,9 @@ use boa_ast::{
declaration::{Binding, LexicalDeclaration, VariableList}, declaration::{Binding, LexicalDeclaration, VariableList},
function::FormalParameterList, function::FormalParameterList,
operations::{ operations::{
bound_names, lexically_scoped_declarations, top_level_lexically_declared_names, all_private_identifiers_valid, bound_names, lexically_scoped_declarations,
top_level_var_declared_names, top_level_var_scoped_declarations, VarScopedDeclaration, top_level_lexically_declared_names, top_level_var_declared_names,
top_level_var_scoped_declarations, VarScopedDeclaration,
}, },
visitor::NodeRef, visitor::NodeRef,
Declaration, StatementList, StatementListItem, Declaration, StatementList, StatementListItem,
@ -428,9 +429,17 @@ impl ByteCompiler<'_, '_> {
// 5. Let pointer be privateEnv. // 5. Let pointer be privateEnv.
// 6. Repeat, while pointer is not null, // 6. Repeat, while pointer is not null,
// a. For each Private Name binding of pointer.[[Names]], do // a. For each Private Name binding of pointer.[[Names]], do
// i. If privateIdentifiers does not contain binding.[[Description]], append binding.[[Description]] to privateIdentifiers. // i. If privateIdentifiers does not contain binding.[[Description]],
// append binding.[[Description]] to privateIdentifiers.
// b. Set pointer to pointer.[[OuterPrivateEnvironment]]. // b. Set pointer to pointer.[[OuterPrivateEnvironment]].
let private_identifiers = self.context.vm.environments.private_name_descriptions();
// 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception. // 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception.
if !all_private_identifiers_valid(body, private_identifiers) {
return Err(JsNativeError::syntax()
.with_message("invalid private identifier")
.into());
}
// 8. Let functionsToInitialize be a new empty List. // 8. Let functionsToInitialize be a new empty List.
let mut functions_to_initialize = Vec::new(); let mut functions_to_initialize = Vec::new();

2
boa_engine/src/environments/mod.rs

@ -31,7 +31,7 @@ pub(crate) use {
compile::CompileTimeEnvironment, compile::CompileTimeEnvironment,
runtime::{ runtime::{
BindingLocator, DeclarativeEnvironment, Environment, EnvironmentStack, FunctionSlots, BindingLocator, DeclarativeEnvironment, Environment, EnvironmentStack, FunctionSlots,
ThisBindingStatus, PrivateEnvironment, ThisBindingStatus,
}, },
}; };

65
boa_engine/src/environments/runtime/mod.rs

@ -1,17 +1,24 @@
use crate::{ use crate::{
environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, Context, environments::CompileTimeEnvironment,
JsResult, JsString, JsSymbol, JsValue, error::JsNativeError,
object::{JsObject, PrivateName},
Context, JsResult, JsString, JsSymbol, JsValue,
}; };
use boa_ast::expression::Identifier; use boa_ast::expression::Identifier;
use boa_gc::{empty_trace, Finalize, Gc, GcRefCell, Trace}; use boa_gc::{empty_trace, Finalize, Gc, GcRefCell, Trace};
use boa_interner::Sym;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
mod declarative; mod declarative;
mod private;
use self::declarative::ModuleEnvironment; use self::declarative::ModuleEnvironment;
pub(crate) use self::declarative::{ pub(crate) use self::{
DeclarativeEnvironment, DeclarativeEnvironmentKind, FunctionEnvironment, FunctionSlots, declarative::{
LexicalEnvironment, ThisBindingStatus, DeclarativeEnvironment, DeclarativeEnvironmentKind, FunctionEnvironment, FunctionSlots,
LexicalEnvironment, ThisBindingStatus,
},
private::PrivateEnvironment,
}; };
/// The environment stack holds all environments at runtime. /// The environment stack holds all environments at runtime.
@ -21,6 +28,8 @@ pub(crate) use self::declarative::{
#[derive(Clone, Debug, Trace, Finalize)] #[derive(Clone, Debug, Trace, Finalize)]
pub(crate) struct EnvironmentStack { pub(crate) struct EnvironmentStack {
stack: Vec<Environment>, stack: Vec<Environment>,
private_stack: Vec<Gc<PrivateEnvironment>>,
} }
/// A runtime environment. /// A runtime environment.
@ -56,6 +65,7 @@ impl EnvironmentStack {
)); ));
Self { Self {
stack: vec![Environment::Declarative(global)], stack: vec![Environment::Declarative(global)],
private_stack: Vec::new(),
} }
} }
@ -457,6 +467,51 @@ impl EnvironmentStack {
env.set(binding_index, value); env.set(binding_index, value);
} }
} }
/// Push a private environment to the private environment stack.
pub(crate) fn push_private(&mut self, environment: Gc<PrivateEnvironment>) {
self.private_stack.push(environment);
}
/// Pop a private environment from the private environment stack.
pub(crate) fn pop_private(&mut self) {
self.private_stack.pop();
}
/// `ResolvePrivateIdentifier ( privEnv, identifier )`
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-resolve-private-identifier
pub(crate) fn resolve_private_identifier(&self, identifier: Sym) -> Option<PrivateName> {
// 1. Let names be privEnv.[[Names]].
// 2. For each Private Name pn of names, do
// a. If pn.[[Description]] is identifier, then
// i. Return pn.
// 3. Let outerPrivEnv be privEnv.[[OuterPrivateEnvironment]].
// 4. Assert: outerPrivEnv is not null.
// 5. Return ResolvePrivateIdentifier(outerPrivEnv, identifier).
for environment in self.private_stack.iter().rev() {
if environment.descriptions().contains(&identifier) {
return Some(PrivateName::new(identifier, environment.id()));
}
}
None
}
/// Return all private name descriptions in all private environments.
pub(crate) fn private_name_descriptions(&self) -> Vec<Sym> {
let mut names = Vec::new();
for environment in self.private_stack.iter().rev() {
for name in environment.descriptions() {
if !names.contains(name) {
names.push(*name);
}
}
}
names
}
} }
/// A binding locator contains all information about a binding that is needed to resolve it at runtime. /// A binding locator contains all information about a binding that is needed to resolve it at runtime.

34
boa_engine/src/environments/runtime/private.rs

@ -0,0 +1,34 @@
use boa_gc::{empty_trace, Finalize, Trace};
use boa_interner::Sym;
/// Private runtime environment.
#[derive(Clone, Debug, Finalize)]
pub(crate) struct PrivateEnvironment {
/// The unique identifier of the private names.
id: usize,
/// The `[[Description]]` internal slot of the private names.
descriptions: Vec<Sym>,
}
// Safety: PrivateEnvironment does not contain any objects that need to be traced.
unsafe impl Trace for PrivateEnvironment {
empty_trace!();
}
impl PrivateEnvironment {
/// Creates a new `PrivateEnvironment`.
pub(crate) fn new(id: usize, descriptions: Vec<Sym>) -> Self {
Self { id, descriptions }
}
/// Gets the id of this private environment.
pub(crate) const fn id(&self) -> usize {
self.id
}
/// Gets the descriptions of this private environment.
pub(crate) fn descriptions(&self) -> &[Sym] {
&self.descriptions
}
}

9
boa_engine/src/object/jsobject.rs

@ -5,7 +5,7 @@
use super::{ use super::{
internal_methods::{InternalObjectMethods, ARRAY_EXOTIC_INTERNAL_METHODS}, internal_methods::{InternalObjectMethods, ARRAY_EXOTIC_INTERNAL_METHODS},
shape::{shared_shape::SharedShape, Shape}, shape::{shared_shape::SharedShape, Shape},
JsPrototype, NativeObject, Object, PropertyMap, JsPrototype, NativeObject, Object, PrivateName, PropertyMap,
}; };
use crate::{ use crate::{
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
@ -17,6 +17,7 @@ use crate::{
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
use boa_gc::{self, Finalize, Gc, GcRefCell, Trace}; use boa_gc::{self, Finalize, Gc, GcRefCell, Trace};
use boa_interner::Sym;
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::HashMap, collections::HashMap,
@ -944,6 +945,12 @@ Cannot both specify accessors and a value or writable attribute",
pub(crate) const fn inner(&self) -> &Gc<VTableObject> { pub(crate) const fn inner(&self) -> &Gc<VTableObject> {
&self.inner &self.inner
} }
/// Create a new private name with this object as the unique identifier.
pub(crate) fn private_name(&self, description: Sym) -> PrivateName {
let ptr: *const _ = self.as_ref();
PrivateName::new(description, ptr as usize)
}
} }
impl AsRef<GcRefCell<Object>> for JsObject { impl AsRef<GcRefCell<Object>> for JsObject {

19
boa_engine/src/object/mod.rs

@ -2,7 +2,7 @@
//! //!
//! For the builtin object wrappers, please see [`object::builtins`][builtins] for implementors. //! For the builtin object wrappers, please see [`object::builtins`][builtins] for implementors.
use boa_ast::function::PrivateName; use boa_interner::Sym;
pub use jsobject::{RecursionLimiter, Ref, RefMut}; pub use jsobject::{RecursionLimiter, Ref, RefMut};
pub use operations::IntegrityLevel; pub use operations::IntegrityLevel;
pub use property_map::*; pub use property_map::*;
@ -164,6 +164,23 @@ unsafe impl Trace for Object {
}); });
} }
/// A Private Name.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PrivateName {
/// The `[[Description]]` internal slot of the private name.
description: Sym,
/// The unique identifier of the private name.
id: usize,
}
impl PrivateName {
/// Create a new private name.
pub(crate) const fn new(description: Sym, id: usize) -> Self {
Self { description, id }
}
}
/// The representation of private object elements. /// The representation of private object elements.
#[derive(Clone, Debug, Trace, Finalize)] #[derive(Clone, Debug, Trace, Finalize)]
pub enum PrivateElement { pub enum PrivateElement {

5
boa_engine/src/object/operations.rs

@ -2,16 +2,13 @@ use crate::{
builtins::{function::ClassFieldDefinition, Array}, builtins::{function::ClassFieldDefinition, Array},
context::intrinsics::{StandardConstructor, StandardConstructors}, context::intrinsics::{StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
object::{JsObject, PrivateElement, PROTOTYPE}, object::{JsFunction, JsObject, PrivateElement, PrivateName, CONSTRUCTOR, PROTOTYPE},
property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind}, property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind},
realm::Realm, realm::Realm,
string::utf16, string::utf16,
value::Type, value::Type,
Context, JsResult, JsSymbol, JsValue, Context, JsResult, JsSymbol, JsValue,
}; };
use boa_ast::function::PrivateName;
use super::{JsFunction, CONSTRUCTOR};
/// Object integrity level. /// Object integrity level.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]

6
boa_engine/src/vm/code_block.rs

@ -391,6 +391,11 @@ impl CodeBlock {
interner.resolve_expect(self.private_names[operand as usize].description()), interner.resolve_expect(self.private_names[operand as usize].description()),
) )
} }
Opcode::PushPrivateEnvironment => {
let count = self.read::<u32>(*pc);
*pc += size_of::<u32>() * (count as usize + 1);
String::new()
}
Opcode::Pop Opcode::Pop
| Opcode::PopIfThrown | Opcode::PopIfThrown
| Opcode::Dup | Opcode::Dup
@ -505,6 +510,7 @@ impl CodeBlock {
| Opcode::PushObjectEnvironment | Opcode::PushObjectEnvironment
| Opcode::IsObject | Opcode::IsObject
| Opcode::SetNameByLocator | Opcode::SetNameByLocator
| Opcode::PopPrivateEnvironment
| Opcode::Nop => String::new(), | Opcode::Nop => String::new(),
} }
} }

9
boa_engine/src/vm/flowgraph/mod.rs

@ -478,6 +478,12 @@ impl CodeBlock {
); );
} }
} }
Opcode::PushPrivateEnvironment => {
let count = self.read::<u32>(pc);
pc += size_of::<u32>() * (count as usize + 1);
graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
}
Opcode::Pop Opcode::Pop
| Opcode::PopIfThrown | Opcode::PopIfThrown
| Opcode::Dup | Opcode::Dup
@ -588,7 +594,8 @@ impl CodeBlock {
| Opcode::IsObject | Opcode::IsObject
| Opcode::SetNameByLocator | Opcode::SetNameByLocator
| Opcode::Nop | Opcode::Nop
| Opcode::PushObjectEnvironment => { | Opcode::PushObjectEnvironment
| Opcode::PopPrivateEnvironment => {
graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
} }

8
boa_engine/src/vm/opcode/binary_ops/mod.rs

@ -122,12 +122,18 @@ impl Operation for InPrivate {
)) ))
.into()); .into());
}; };
let name = context
.vm
.environments
.resolve_private_identifier(name.description())
.expect("private name must be in environment");
if rhs.private_element_find(&name, true, true).is_some() { if rhs.private_element_find(&name, true, true).is_some() {
context.vm.push(true); context.vm.push(true);
} else { } else {
context.vm.push(false); context.vm.push(false);
} }
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }
} }

7
boa_engine/src/vm/opcode/get/private.rs

@ -19,6 +19,13 @@ impl Operation for GetPrivateField {
let name = context.vm.frame().code_block.private_names[index as usize]; let name = context.vm.frame().code_block.private_names[index as usize];
let value = context.vm.pop(); let value = context.vm.pop();
let base_obj = value.to_object(context)?; let base_obj = value.to_object(context)?;
let name = context
.vm
.environments
.resolve_private_identifier(name.description())
.expect("private name must be in environment");
let result = base_obj.private_get(&name, context)?; let result = base_obj.private_get(&name, context)?;
context.vm.push(result); context.vm.push(result);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)

14
boa_engine/src/vm/opcode/mod.rs

@ -1619,6 +1619,20 @@ generate_impl! {
/// Stack: count * (cooked_value, raw_value) **=>** template /// Stack: count * (cooked_value, raw_value) **=>** template
TemplateCreate, TemplateCreate,
/// Push a private environment.
///
/// Operands: count: `u32`, count * private_name_index: `u32`
///
/// Stack: class **=>** class
PushPrivateEnvironment,
/// Pop a private environment.
///
/// Operands:
///
/// Stack: **=>**
PopPrivateEnvironment,
/// No-operation instruction, does nothing. /// No-operation instruction, does nothing.
/// ///
/// Operands: /// Operands:

3
boa_engine/src/vm/opcode/push/class/field.rs

@ -74,12 +74,13 @@ impl Operation for PushClassFieldPrivate {
.expect("class must be function object"); .expect("class must be function object");
field_function.set_home_object(class_object.clone()); field_function.set_home_object(class_object.clone());
field_function.set_class_object(class_object.clone()); field_function.set_class_object(class_object.clone());
class_object class_object
.borrow_mut() .borrow_mut()
.as_function_mut() .as_function_mut()
.expect("class must be function object") .expect("class must be function object")
.push_field_private( .push_field_private(
name, class_object.private_name(name.description()),
JsFunction::from_object_unchecked(field_function_object.clone()), JsFunction::from_object_unchecked(field_function_object.clone()),
); );
Ok(CompletionType::Normal) Ok(CompletionType::Normal)

12
boa_engine/src/vm/opcode/push/class/private.rs

@ -36,11 +36,15 @@ impl Operation for PushClassPrivateMethod {
let class = context.vm.pop(); let class = context.vm.pop();
let class_object = class.as_object().expect("class must be function object"); let class_object = class.as_object().expect("class must be function object");
class_object class_object
.borrow_mut() .borrow_mut()
.as_function_mut() .as_function_mut()
.expect("class must be function object") .expect("class must be function object")
.push_private_method(name, PrivateElement::Method(method_object.clone())); .push_private_method(
class_object.private_name(name.description()),
PrivateElement::Method(method_object.clone()),
);
let mut method_object_mut = method_object.borrow_mut(); let mut method_object_mut = method_object.borrow_mut();
let function = method_object_mut let function = method_object_mut
@ -69,12 +73,13 @@ impl Operation for PushClassPrivateGetter {
let getter_object = getter.as_callable().expect("getter must be callable"); let getter_object = getter.as_callable().expect("getter must be callable");
let class = context.vm.pop(); let class = context.vm.pop();
let class_object = class.as_object().expect("class must be function object"); let class_object = class.as_object().expect("class must be function object");
class_object class_object
.borrow_mut() .borrow_mut()
.as_function_mut() .as_function_mut()
.expect("class must be function object") .expect("class must be function object")
.push_private_method( .push_private_method(
name, class_object.private_name(name.description()),
PrivateElement::Accessor { PrivateElement::Accessor {
getter: Some(getter_object.clone()), getter: Some(getter_object.clone()),
setter: None, setter: None,
@ -107,12 +112,13 @@ impl Operation for PushClassPrivateSetter {
let setter_object = setter.as_callable().expect("getter must be callable"); let setter_object = setter.as_callable().expect("getter must be callable");
let class = context.vm.pop(); let class = context.vm.pop();
let class_object = class.as_object().expect("class must be function object"); let class_object = class.as_object().expect("class must be function object");
class_object class_object
.borrow_mut() .borrow_mut()
.as_function_mut() .as_function_mut()
.expect("class must be function object") .expect("class must be function object")
.push_private_method( .push_private_method(
name, class_object.private_name(name.description()),
PrivateElement::Accessor { PrivateElement::Accessor {
getter: None, getter: None,
setter: Some(setter_object.clone()), setter: Some(setter_object.clone()),

59
boa_engine/src/vm/opcode/push/environment.rs

@ -1,7 +1,9 @@
use crate::{ use crate::{
environments::PrivateEnvironment,
vm::{opcode::Operation, CompletionType}, vm::{opcode::Operation, CompletionType},
Context, JsResult, Context, JsResult,
}; };
use boa_gc::Gc;
/// `PushDeclarativeEnvironment` implements the Opcode Operation for `Opcode::PushDeclarativeEnvironment` /// `PushDeclarativeEnvironment` implements the Opcode Operation for `Opcode::PushDeclarativeEnvironment`
/// ///
@ -74,3 +76,60 @@ impl Operation for PushObjectEnvironment {
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }
} }
/// `PushPrivateEnvironment` implements the Opcode Operation for `Opcode::PushPrivateEnvironment`
///
/// Operation:
/// - Push a private environment.
#[derive(Debug, Clone, Copy)]
pub(crate) struct PushPrivateEnvironment;
impl Operation for PushPrivateEnvironment {
const NAME: &'static str = "PushPrivateEnvironment";
const INSTRUCTION: &'static str = "INST - PushPrivateEnvironment";
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let class_value = context.vm.pop();
let class = class_value.to_object(context)?;
let count = context.vm.read::<u32>();
let mut names = Vec::with_capacity(count as usize);
for _ in 0..count {
let index = context.vm.read::<u32>();
let name = context.vm.frame().code_block.private_names[index as usize];
names.push(name.description());
}
let ptr: *const _ = class.as_ref();
let environment = Gc::new(PrivateEnvironment::new(ptr as usize, names));
class
.borrow_mut()
.as_function_mut()
.expect("class object must be function")
.push_private_environment(environment.clone());
context.vm.environments.push_private(environment);
context.vm.push(class_value);
Ok(CompletionType::Normal)
}
}
/// `PopPrivateEnvironment` implements the Opcode Operation for `Opcode::PopPrivateEnvironment`
///
/// Operation:
/// - Pop a private environment.
#[derive(Debug, Clone, Copy)]
pub(crate) struct PopPrivateEnvironment;
impl Operation for PopPrivateEnvironment {
const NAME: &'static str = "PopPrivateEnvironment";
const INSTRUCTION: &'static str = "INST - PopPrivateEnvironment";
fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
context.vm.environments.pop_private();
Ok(CompletionType::Normal)
}
}

27
boa_engine/src/vm/opcode/set/private.rs

@ -23,6 +23,13 @@ impl Operation for SetPrivateField {
let value = context.vm.pop(); let value = context.vm.pop();
let object = context.vm.pop(); let object = context.vm.pop();
let base_obj = object.to_object(context)?; let base_obj = object.to_object(context)?;
let name = context
.vm
.environments
.resolve_private_identifier(name.description())
.expect("private name must be in environment");
base_obj.private_set(&name, value.clone(), context)?; base_obj.private_set(&name, value.clone(), context)?;
context.vm.push(value); context.vm.push(value);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
@ -48,9 +55,11 @@ impl Operation for DefinePrivateField {
let object = object let object = object
.as_object() .as_object()
.expect("class prototype must be an object"); .expect("class prototype must be an object");
object
.borrow_mut() object.borrow_mut().append_private_element(
.append_private_element(name, PrivateElement::Field(value)); object.private_name(name.description()),
PrivateElement::Field(value),
);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }
@ -88,9 +97,11 @@ impl Operation for SetPrivateMethod {
let object = object let object = object
.as_object() .as_object()
.expect("class prototype must be an object"); .expect("class prototype must be an object");
object
.borrow_mut() object.borrow_mut().append_private_element(
.append_private_element(name, PrivateElement::Method(value.clone())); object.private_name(name.description()),
PrivateElement::Method(value.clone()),
);
let mut value_mut = value.borrow_mut(); let mut value_mut = value.borrow_mut();
let function = value_mut let function = value_mut
.as_function_mut() .as_function_mut()
@ -123,7 +134,7 @@ impl Operation for SetPrivateSetter {
.expect("class prototype must be an object"); .expect("class prototype must be an object");
object.borrow_mut().append_private_element( object.borrow_mut().append_private_element(
name, object.private_name(name.description()),
PrivateElement::Accessor { PrivateElement::Accessor {
getter: None, getter: None,
setter: Some(value.clone()), setter: Some(value.clone()),
@ -161,7 +172,7 @@ impl Operation for SetPrivateGetter {
.expect("class prototype must be an object"); .expect("class prototype must be an object");
object.borrow_mut().append_private_element( object.borrow_mut().append_private_element(
name, object.private_name(name.description()),
PrivateElement::Accessor { PrivateElement::Accessor {
getter: Some(value.clone()), getter: Some(value.clone()),
setter: None, setter: None,

36
boa_parser/src/parser/cursor/mod.rs

@ -25,12 +25,6 @@ pub(super) enum SemicolonResult<'s> {
pub(super) struct Cursor<R> { pub(super) struct Cursor<R> {
buffered_lexer: BufferedLexer<R>, buffered_lexer: BufferedLexer<R>,
/// Tracks the number of nested private environments that the cursor is in.
private_environment_nested_index: usize,
/// Tracks the number of private environments on the root level of the code that is parsed.
private_environment_root_index: usize,
/// Tracks if the cursor is in a arrow function declaration. /// Tracks if the cursor is in a arrow function declaration.
arrow: bool, arrow: bool,
@ -53,8 +47,6 @@ where
pub(super) fn new(reader: R) -> Self { pub(super) fn new(reader: R) -> Self {
Self { Self {
buffered_lexer: Lexer::new(reader).into(), buffered_lexer: Lexer::new(reader).into(),
private_environment_nested_index: 0,
private_environment_root_index: 0,
arrow: false, arrow: false,
json_parse: false, json_parse: false,
identifier: 0, identifier: 0,
@ -150,34 +142,6 @@ where
self.json_parse = json_parse; self.json_parse = json_parse;
} }
/// Push a new private environment.
#[inline]
pub(super) fn push_private_environment(&mut self) {
if !self.in_class() {
self.private_environment_root_index += 1;
}
self.private_environment_nested_index += 1;
}
/// Pop a private environment.
#[inline]
pub(super) fn pop_private_environment(&mut self) {
self.private_environment_nested_index -= 1;
}
/// Returns the current private environment root index.
#[inline]
pub(super) const fn private_environment_root_index(&self) -> usize {
self.private_environment_root_index
}
/// Returns if the cursor is in a class.
#[inline]
pub(super) const fn in_class(&self) -> bool {
self.private_environment_nested_index != 0
}
/// Set the identifier of the cursor. /// Set the identifier of the cursor.
#[inline] #[inline]
pub(super) fn set_identifier(&mut self, identifier: u32) { pub(super) fn set_identifier(&mut self, identifier: u32) {

6
boa_parser/src/parser/expression/left_hand_side/call.rs

@ -112,12 +112,6 @@ where
} }
TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(), TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(),
TokenKind::PrivateIdentifier(name) => { TokenKind::PrivateIdentifier(name) => {
if !cursor.in_class() {
return Err(Error::general(
"Private identifier outside of class",
token.span().start(),
));
}
PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into() PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into()
} }
_ => { _ => {

6
boa_parser/src/parser/expression/left_hand_side/member.rs

@ -201,12 +201,6 @@ where
} }
TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(), TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(),
TokenKind::PrivateIdentifier(name) => { TokenKind::PrivateIdentifier(name) => {
if !cursor.in_class() {
return Err(Error::general(
"Private identifier outside of class",
token.span().start(),
));
}
PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into() PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into()
} }
_ => { _ => {

14
boa_parser/src/parser/expression/left_hand_side/optional/mod.rs

@ -60,8 +60,7 @@ where
type Output = Optional; type Output = Optional;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
fn parse_const_access<R: Read>( fn parse_const_access(
cursor: &mut Cursor<R>,
token: &Token, token: &Token,
interner: &mut Interner, interner: &mut Interner,
) -> ParseResult<OptionalOperationKind> { ) -> ParseResult<OptionalOperationKind> {
@ -84,13 +83,6 @@ where
field: PropertyAccessField::Const(Sym::NULL), field: PropertyAccessField::Const(Sym::NULL),
}, },
TokenKind::PrivateIdentifier(name) => { TokenKind::PrivateIdentifier(name) => {
if !cursor.in_class() {
return Err(Error::general(
"Private identifier outside of class",
token.span().start(),
));
}
OptionalOperationKind::PrivatePropertyAccess { OptionalOperationKind::PrivatePropertyAccess {
field: PrivateName::new(*name), field: PrivateName::new(*name),
} }
@ -121,7 +113,7 @@ where
cursor.advance(interner); cursor.advance(interner);
let field = cursor.next(interner).or_abrupt()?; let field = cursor.next(interner).or_abrupt()?;
let item = parse_const_access(cursor, &field, interner)?; let item = parse_const_access(&field, interner)?;
items.push(OptionalOperation::new(item, false)); items.push(OptionalOperation::new(item, false));
continue; continue;
@ -162,7 +154,7 @@ where
} }
_ => { _ => {
let token = cursor.next(interner)?.expect("token disappeared"); let token = cursor.next(interner)?.expect("token disappeared");
parse_const_access(cursor, &token, interner)? parse_const_access(&token, interner)?
} }
}; };

8
boa_parser/src/parser/expression/mod.rs

@ -567,7 +567,6 @@ where
if self.allow_in.0 { if self.allow_in.0 {
let token = cursor.peek(0, interner).or_abrupt()?; let token = cursor.peek(0, interner).or_abrupt()?;
let span = token.span();
if let TokenKind::PrivateIdentifier(identifier) = token.kind() { if let TokenKind::PrivateIdentifier(identifier) = token.kind() {
let identifier = *identifier; let identifier = *identifier;
let token = cursor.peek(1, interner).or_abrupt()?; let token = cursor.peek(1, interner).or_abrupt()?;
@ -582,13 +581,6 @@ where
cursor.advance(interner); cursor.advance(interner);
cursor.advance(interner); cursor.advance(interner);
if !cursor.in_class() {
return Err(Error::general(
"Private identifier outside of class",
span.start(),
));
}
let rhs = let rhs =
ShiftExpression::new(self.name, self.allow_yield, self.allow_await) ShiftExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?; .parse(cursor, interner)?;

16
boa_parser/src/parser/mod.rs

@ -22,9 +22,9 @@ use boa_ast::{
expression::Identifier, expression::Identifier,
function::FormalParameterList, function::FormalParameterList,
operations::{ operations::{
check_labels, contains, contains_invalid_object_literal, lexically_declared_names, all_private_identifiers_valid, check_labels, contains, contains_invalid_object_literal,
top_level_lexically_declared_names, top_level_var_declared_names, var_declared_names, lexically_declared_names, top_level_lexically_declared_names, top_level_var_declared_names,
ContainsSymbol, var_declared_names, ContainsSymbol,
}, },
ModuleItemList, Position, StatementList, ModuleItemList, Position, StatementList,
}; };
@ -340,6 +340,16 @@ where
Position::new(1, 1), Position::new(1, 1),
)); ));
} }
// It is a Syntax Error if AllPrivateIdentifiersValid of StatementList with
// argument « » is false unless the source text containing ScriptBody is
// eval code that is being processed by a direct eval.
if !all_private_identifiers_valid(&body, Vec::new()) {
return Err(Error::general(
"invalid private identifier usage",
Position::new(1, 1),
));
}
} }
if let Err(error) = check_labels(&body) { if let Err(error) = check_labels(&body) {

22
boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs

@ -17,8 +17,7 @@ use crate::{
use ast::{ use ast::{
function::PrivateName, function::PrivateName,
operations::{ operations::{
check_labels, class_private_name_resolver, contains_invalid_object_literal, check_labels, contains_invalid_object_literal, lexically_declared_names, var_declared_names,
lexically_declared_names, var_declared_names,
}, },
}; };
use boa_ast::{ use boa_ast::{
@ -182,16 +181,12 @@ where
self.has_binding_identifier, self.has_binding_identifier,
)) ))
} else { } else {
cursor.push_private_environment();
let body_start = cursor.peek(0, interner).or_abrupt()?.span().start(); let body_start = cursor.peek(0, interner).or_abrupt()?.span().start();
let (constructor, elements) = let (constructor, elements) =
ClassBody::new(self.name, self.allow_yield, self.allow_await) ClassBody::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?; .parse(cursor, interner)?;
cursor.expect(Punctuator::CloseBlock, "class tail", interner)?; cursor.expect(Punctuator::CloseBlock, "class tail", interner)?;
cursor.pop_private_environment();
if super_ref.is_none() { if super_ref.is_none() {
if let Some(constructor) = &constructor { if let Some(constructor) = &constructor {
if contains(constructor, ContainsSymbol::SuperCall) { if contains(constructor, ContainsSymbol::SuperCall) {
@ -203,24 +198,13 @@ where
} }
} }
let mut class = Class::new( Ok(Class::new(
self.name, self.name,
super_ref, super_ref,
constructor, constructor,
elements.into(), elements.into(),
self.has_binding_identifier, self.has_binding_identifier,
); ))
if !cursor.in_class()
&& !class_private_name_resolver(&mut class, cursor.private_environment_root_index())
{
return Err(Error::lex(LexError::Syntax(
"invalid private name usage".into(),
body_start,
)));
}
Ok(class)
} }
} }
} }

14
boa_parser/src/parser/statement/mod.rs

@ -47,7 +47,7 @@ use crate::{
Error, Error,
}; };
use ast::{ use ast::{
operations::{check_labels, contains_invalid_object_literal}, operations::{all_private_identifiers_valid, check_labels, contains_invalid_object_literal},
Position, Position,
}; };
use boa_ast::{ use boa_ast::{
@ -929,7 +929,17 @@ where
list.push(item); list.push(item);
} }
Ok(list.into()) let list = list.into();
// It is a Syntax Error if AllPrivateIdentifiersValid of ModuleItemList with argument « » is false.
if !all_private_identifiers_valid(&list, Vec::new()) {
return Err(Error::general(
"invalid private identifier usage",
Position::new(1, 1),
));
}
Ok(list)
} }
} }

Loading…
Cancel
Save