mirror of https://github.com/boa-dev/boa.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
371 lines
14 KiB
371 lines
14 KiB
//! Property definition related types, used in object literals and class definitions. |
|
|
|
use crate::function::PrivateName; |
|
use crate::try_break; |
|
use crate::visitor::{VisitWith, Visitor, VisitorMut}; |
|
use boa_interner::{Interner, Sym, ToInternedString}; |
|
use core::ops::ControlFlow; |
|
|
|
use super::{ |
|
expression::{literal::Literal, Identifier}, |
|
function::{AsyncFunction, AsyncGenerator, Function, Generator}, |
|
Expression, |
|
}; |
|
|
|
/// Describes the definition of a property within an object literal. |
|
/// |
|
/// A property has a name (a string) and a value (primitive, method, or object reference). |
|
/// Note that when we say that "a property holds an object", that is shorthand for "a property holds an object reference". |
|
/// This distinction matters because the original referenced object remains unchanged when you change the property's value. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript |
|
// TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] |
|
#[derive(Clone, Debug, PartialEq)] |
|
pub enum PropertyDefinition { |
|
/// Puts a variable into an object. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions |
|
IdentifierReference(Identifier), |
|
|
|
/// Binds a property name to a JavaScript value. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions |
|
Property(PropertyName, Expression), |
|
|
|
/// A property of an object can also refer to a function or a getter or setter method. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions |
|
MethodDefinition(PropertyName, MethodDefinition), |
|
|
|
/// The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals. |
|
/// It copies own enumerable properties from a provided object onto a new object. |
|
/// |
|
/// Shallow-cloning (excluding `prototype`) or merging objects is now possible using a shorter syntax than `Object.assign()`. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties |
|
SpreadObject(Expression), |
|
|
|
/// Cover grammar for when an object literal is used as an object binding pattern. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-CoverInitializedName |
|
CoverInitializedName(Identifier, Expression), |
|
} |
|
|
|
impl VisitWith for PropertyDefinition { |
|
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
|
where |
|
V: Visitor<'a>, |
|
{ |
|
match self { |
|
Self::IdentifierReference(id) => visitor.visit_identifier(id), |
|
Self::Property(pn, expr) => { |
|
try_break!(visitor.visit_property_name(pn)); |
|
visitor.visit_expression(expr) |
|
} |
|
Self::MethodDefinition(pn, md) => { |
|
try_break!(visitor.visit_property_name(pn)); |
|
visitor.visit_method_definition(md) |
|
} |
|
Self::SpreadObject(expr) => visitor.visit_expression(expr), |
|
Self::CoverInitializedName(id, expr) => { |
|
try_break!(visitor.visit_identifier(id)); |
|
visitor.visit_expression(expr) |
|
} |
|
} |
|
} |
|
|
|
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
|
where |
|
V: VisitorMut<'a>, |
|
{ |
|
match self { |
|
Self::IdentifierReference(id) => visitor.visit_identifier_mut(id), |
|
Self::Property(pn, expr) => { |
|
try_break!(visitor.visit_property_name_mut(pn)); |
|
visitor.visit_expression_mut(expr) |
|
} |
|
Self::MethodDefinition(pn, md) => { |
|
try_break!(visitor.visit_property_name_mut(pn)); |
|
visitor.visit_method_definition_mut(md) |
|
} |
|
Self::SpreadObject(expr) => visitor.visit_expression_mut(expr), |
|
Self::CoverInitializedName(id, expr) => { |
|
try_break!(visitor.visit_identifier_mut(id)); |
|
visitor.visit_expression_mut(expr) |
|
} |
|
} |
|
} |
|
} |
|
|
|
/// Method definition. |
|
/// |
|
/// Starting with ECMAScript 2015, a shorter syntax for method definitions on objects initializers is introduced. |
|
/// It is a shorthand for a function assigned to the method's name. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] |
|
#[derive(Clone, Debug, PartialEq)] |
|
pub enum MethodDefinition { |
|
/// The `get` syntax binds an object property to a function that will be called when that property is looked up. |
|
/// |
|
/// Sometimes it is desirable to allow access to a property that returns a dynamically computed value, |
|
/// or you may want to reflect the status of an internal variable without requiring the use of explicit method calls. |
|
/// In JavaScript, this can be accomplished with the use of a getter. |
|
/// |
|
/// It is not possible to simultaneously have a getter bound to a property and have that property actually hold a value, |
|
/// although it is possible to use a getter and a setter in conjunction to create a type of pseudo-property. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get |
|
Get(Function), |
|
|
|
/// The `set` syntax binds an object property to a function to be called when there is an attempt to set that property. |
|
/// |
|
/// In JavaScript, a setter can be used to execute a function whenever a specified property is attempted to be changed. |
|
/// Setters are most often used in conjunction with getters to create a type of pseudo-property. |
|
/// It is not possible to simultaneously have a setter on a property that holds an actual value. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set |
|
Set(Function), |
|
|
|
/// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Method_definition_syntax |
|
Ordinary(Function), |
|
|
|
/// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#generator_methods |
|
Generator(Generator), |
|
|
|
/// Async generators can be used to define a method |
|
/// |
|
/// More information |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorMethod |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#async_generator_methods |
|
AsyncGenerator(AsyncGenerator), |
|
|
|
/// Async function can be used to define a method |
|
/// |
|
/// More information |
|
/// - [ECMAScript reference][spec] |
|
/// - [MDN documentation][mdn] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-AsyncMethod |
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#async_methods |
|
Async(AsyncFunction), |
|
} |
|
|
|
impl VisitWith for MethodDefinition { |
|
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
|
where |
|
V: Visitor<'a>, |
|
{ |
|
match self { |
|
Self::Get(f) | Self::Set(f) | Self::Ordinary(f) => visitor.visit_function(f), |
|
Self::Generator(g) => visitor.visit_generator(g), |
|
Self::AsyncGenerator(ag) => visitor.visit_async_generator(ag), |
|
Self::Async(af) => visitor.visit_async_function(af), |
|
} |
|
} |
|
|
|
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
|
where |
|
V: VisitorMut<'a>, |
|
{ |
|
match self { |
|
Self::Get(f) | Self::Set(f) | Self::Ordinary(f) => visitor.visit_function_mut(f), |
|
Self::Generator(g) => visitor.visit_generator_mut(g), |
|
Self::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag), |
|
Self::Async(af) => visitor.visit_async_function_mut(af), |
|
} |
|
} |
|
} |
|
|
|
/// `PropertyName` can be either a literal or computed. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-PropertyName |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] |
|
#[derive(Clone, Debug, PartialEq)] |
|
pub enum PropertyName { |
|
/// A `Literal` property name can be either an identifier, a string or a numeric literal. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-LiteralPropertyName |
|
Literal(Sym), |
|
|
|
/// A `Computed` property name is an expression that gets evaluated and converted into a property name. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-ComputedPropertyName |
|
Computed(Expression), |
|
} |
|
|
|
impl PropertyName { |
|
/// Returns the literal property name if it exists. |
|
#[must_use] |
|
pub const fn literal(&self) -> Option<Sym> { |
|
if let Self::Literal(sym) = self { |
|
Some(*sym) |
|
} else { |
|
None |
|
} |
|
} |
|
|
|
/// Returns the expression if the property name is computed. |
|
#[must_use] |
|
pub const fn computed(&self) -> Option<&Expression> { |
|
if let Self::Computed(expr) = self { |
|
Some(expr) |
|
} else { |
|
None |
|
} |
|
} |
|
|
|
/// Returns either the literal property name or the computed const string property name. |
|
#[must_use] |
|
pub const fn prop_name(&self) -> Option<Sym> { |
|
match self { |
|
Self::Literal(sym) | Self::Computed(Expression::Literal(Literal::String(sym))) => { |
|
Some(*sym) |
|
} |
|
Self::Computed(_) => None, |
|
} |
|
} |
|
} |
|
|
|
impl ToInternedString for PropertyName { |
|
fn to_interned_string(&self, interner: &Interner) -> String { |
|
match self { |
|
Self::Literal(key) => interner.resolve_expect(*key).to_string(), |
|
Self::Computed(key) => key.to_interned_string(interner), |
|
} |
|
} |
|
} |
|
|
|
impl From<Sym> for PropertyName { |
|
fn from(name: Sym) -> Self { |
|
Self::Literal(name) |
|
} |
|
} |
|
|
|
impl From<Expression> for PropertyName { |
|
fn from(name: Expression) -> Self { |
|
Self::Computed(name) |
|
} |
|
} |
|
|
|
impl VisitWith for PropertyName { |
|
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
|
where |
|
V: Visitor<'a>, |
|
{ |
|
match self { |
|
Self::Literal(sym) => visitor.visit_sym(sym), |
|
Self::Computed(expr) => visitor.visit_expression(expr), |
|
} |
|
} |
|
|
|
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
|
where |
|
V: VisitorMut<'a>, |
|
{ |
|
match self { |
|
Self::Literal(sym) => visitor.visit_sym_mut(sym), |
|
Self::Computed(expr) => visitor.visit_expression_mut(expr), |
|
} |
|
} |
|
} |
|
|
|
/// `ClassElementName` can be either a property name or a private identifier. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-ClassElementName |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[derive(Clone, Debug, PartialEq)] |
|
pub enum ClassElementName { |
|
/// A public property. |
|
PropertyName(PropertyName), |
|
/// A private property. |
|
PrivateIdentifier(PrivateName), |
|
} |
|
|
|
impl ClassElementName { |
|
/// Returns the property name if it exists. |
|
#[must_use] |
|
pub const fn literal(&self) -> Option<Sym> { |
|
if let Self::PropertyName(name) = self { |
|
name.literal() |
|
} else { |
|
None |
|
} |
|
} |
|
}
|
|
|