feat: add dependency
This commit is contained in:
97
javascript-engine/external/boa/boa_ast/src/declaration/mod.rs
vendored
Normal file
97
javascript-engine/external/boa/boa_ast/src/declaration/mod.rs
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
//! The [`Declaration`] Parse Node, as defined by the [spec].
|
||||
//!
|
||||
//! ECMAScript declarations include:
|
||||
//! - [Lexical][lex] declarations (`let`, `const`).
|
||||
//! - [Function][fun] declarations (`function`, `async function`).
|
||||
//! - [Class][class] declarations.
|
||||
//!
|
||||
//! See [*Difference between statements and declarations*][diff] for an explanation on why `Declaration`s
|
||||
//! and `Statement`s are distinct nodes.
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#prod-Declaration
|
||||
//! [lex]: https://tc39.es/ecma262/#prod-LexicalDeclaration
|
||||
//! [fun]: https://tc39.es/ecma262/#prod-HoistableDeclaration
|
||||
//! [class]: https://tc39.es/ecma262/#prod-ClassDeclaration
|
||||
//! [diff]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements#difference_between_statements_and_declarations
|
||||
|
||||
use super::function::{AsyncFunction, AsyncGenerator, Class, Function, Generator};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
mod variable;
|
||||
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
pub use variable::*;
|
||||
|
||||
/// The `Declaration` Parse Node.
|
||||
///
|
||||
/// See the [module level documentation][self] for more information.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Declaration {
|
||||
/// See [`Function`]
|
||||
Function(Function),
|
||||
|
||||
/// See [`Generator`]
|
||||
Generator(Generator),
|
||||
|
||||
/// See [`AsyncFunction`]
|
||||
AsyncFunction(AsyncFunction),
|
||||
|
||||
/// See [`AsyncGenerator`]
|
||||
AsyncGenerator(AsyncGenerator),
|
||||
|
||||
/// See [`Class`]
|
||||
Class(Class),
|
||||
|
||||
/// See [`LexicalDeclaration`]
|
||||
Lexical(LexicalDeclaration),
|
||||
}
|
||||
|
||||
impl ToIndentedString for Declaration {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
match self {
|
||||
Self::Function(f) => f.to_indented_string(interner, indentation),
|
||||
Self::Generator(g) => g.to_indented_string(interner, indentation),
|
||||
Self::AsyncFunction(af) => af.to_indented_string(interner, indentation),
|
||||
Self::AsyncGenerator(ag) => ag.to_indented_string(interner, indentation),
|
||||
Self::Class(c) => c.to_indented_string(interner, indentation),
|
||||
Self::Lexical(l) => {
|
||||
let mut s = l.to_interned_string(interner);
|
||||
s.push(';');
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Declaration {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Function(f) => visitor.visit_function(f),
|
||||
Self::Generator(g) => visitor.visit_generator(g),
|
||||
Self::AsyncFunction(af) => visitor.visit_async_function(af),
|
||||
Self::AsyncGenerator(ag) => visitor.visit_async_generator(ag),
|
||||
Self::Class(c) => visitor.visit_class(c),
|
||||
Self::Lexical(ld) => visitor.visit_lexical_declaration(ld),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Function(f) => visitor.visit_function_mut(f),
|
||||
Self::Generator(g) => visitor.visit_generator_mut(g),
|
||||
Self::AsyncFunction(af) => visitor.visit_async_function_mut(af),
|
||||
Self::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag),
|
||||
Self::Class(c) => visitor.visit_class_mut(c),
|
||||
Self::Lexical(ld) => visitor.visit_lexical_declaration_mut(ld),
|
||||
}
|
||||
}
|
||||
}
|
||||
385
javascript-engine/external/boa/boa_ast/src/declaration/variable.rs
vendored
Normal file
385
javascript-engine/external/boa/boa_ast/src/declaration/variable.rs
vendored
Normal file
@@ -0,0 +1,385 @@
|
||||
//! Variable related declarations.
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{
|
||||
expression::{Expression, Identifier},
|
||||
join_nodes,
|
||||
pattern::Pattern,
|
||||
Statement,
|
||||
};
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
|
||||
use super::Declaration;
|
||||
|
||||
/// A [`var`][var] statement, also called [`VariableStatement`][varstmt] in the spec.
|
||||
///
|
||||
/// The scope of a variable declared with `var` is its current execution context, which is either
|
||||
/// the enclosing function or, for variables declared outside any function, global. If you
|
||||
/// re-declare a ECMAScript variable, it will not lose its value.
|
||||
///
|
||||
/// Although a bit confusing, `VarDeclaration`s are not considered [`Declaration`]s by the spec.
|
||||
/// This is partly because it has very different semantics from `let` and `const` declarations, but
|
||||
/// also because a `var` statement can be labelled just like any other [`Statement`]:
|
||||
///
|
||||
/// ```javascript
|
||||
/// label: var a = 5;
|
||||
/// a;
|
||||
/// ```
|
||||
///
|
||||
/// returns `5` as the value of the statement list, while:
|
||||
///
|
||||
/// ```javascript
|
||||
/// label: let a = 5;
|
||||
/// a;
|
||||
/// ```
|
||||
/// throws a `SyntaxError`.
|
||||
///
|
||||
/// `var` declarations, wherever they occur, are processed before any code is executed. This is
|
||||
/// called <code>[hoisting]</code>.
|
||||
///
|
||||
/// [var]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
|
||||
/// [varstmt]: https://tc39.es/ecma262/#prod-VariableStatement
|
||||
/// [hoisting]: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct VarDeclaration(pub VariableList);
|
||||
|
||||
impl From<VarDeclaration> for Statement {
|
||||
fn from(var: VarDeclaration) -> Self {
|
||||
Self::Var(var)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for VarDeclaration {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!("var {}", self.0.to_interned_string(interner))
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for VarDeclaration {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_variable_list(&self.0)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_variable_list_mut(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A **[lexical declaration]** defines variables that are scoped to the lexical environment of
|
||||
/// the variable declaration.
|
||||
///
|
||||
/// [lexical declaration]: https://tc39.es/ecma262/#sec-let-and-const-declarations
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum LexicalDeclaration {
|
||||
/// A <code>[const]</code> variable creates a constant whose scope can be either global or local
|
||||
/// to the block in which it is declared.
|
||||
///
|
||||
/// An initializer for a constant is required. You must specify its value in the same statement
|
||||
/// in which it's declared. (This makes sense, given that it can't be changed later)
|
||||
///
|
||||
/// [const]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
|
||||
Const(VariableList),
|
||||
|
||||
/// A <code>[let]</code> variable is limited to a scope of a block statement, or expression on
|
||||
/// which it is used, unlike the `var` keyword, which defines a variable globally, or locally to
|
||||
/// an entire function regardless of block scope.
|
||||
///
|
||||
/// Just like const, `let` does not create properties of the window object when declared
|
||||
/// globally (in the top-most scope).
|
||||
///
|
||||
/// If a let declaration does not have an initializer, the variable is assigned the value `undefined`.
|
||||
///
|
||||
/// [let]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
|
||||
Let(VariableList),
|
||||
}
|
||||
|
||||
impl LexicalDeclaration {
|
||||
/// Gets the inner variable list of the `LexicalDeclaration`
|
||||
#[must_use]
|
||||
pub const fn variable_list(&self) -> &VariableList {
|
||||
match self {
|
||||
Self::Const(list) | Self::Let(list) => list,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LexicalDeclaration> for Declaration {
|
||||
fn from(lex: LexicalDeclaration) -> Self {
|
||||
Self::Lexical(lex)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for LexicalDeclaration {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!(
|
||||
"{} {}",
|
||||
match &self {
|
||||
Self::Let(_) => "let",
|
||||
Self::Const(_) => "const",
|
||||
},
|
||||
self.variable_list().to_interned_string(interner)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for LexicalDeclaration {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Const(vars) | Self::Let(vars) => visitor.visit_variable_list(vars),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Const(vars) | Self::Let(vars) => visitor.visit_variable_list_mut(vars),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// List of variables in a variable declaration.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct VariableList {
|
||||
list: Box<[Variable]>,
|
||||
}
|
||||
|
||||
impl VariableList {
|
||||
/// Creates a variable list if the provided list of [`Variable`] is not empty.
|
||||
#[must_use]
|
||||
pub fn new(list: Box<[Variable]>) -> Option<Self> {
|
||||
if list.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Self { list })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[Variable]> for VariableList {
|
||||
fn as_ref(&self) -> &[Variable] {
|
||||
&self.list
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for VariableList {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
join_nodes(interner, self.list.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for VariableList {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
for variable in self.list.iter() {
|
||||
try_break!(visitor.visit_variable(variable));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
for variable in self.list.iter_mut() {
|
||||
try_break!(visitor.visit_variable_mut(variable));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The error returned by the [`VariableList::try_from`] function.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct TryFromVariableListError(());
|
||||
|
||||
impl std::fmt::Display for TryFromVariableListError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
"provided list of variables cannot be empty".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Box<[Variable]>> for VariableList {
|
||||
type Error = TryFromVariableListError;
|
||||
|
||||
fn try_from(value: Box<[Variable]>) -> Result<Self, Self::Error> {
|
||||
Self::new(value).ok_or(TryFromVariableListError(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<Variable>> for VariableList {
|
||||
type Error = TryFromVariableListError;
|
||||
|
||||
fn try_from(value: Vec<Variable>) -> Result<Self, Self::Error> {
|
||||
Self::try_from(value.into_boxed_slice())
|
||||
}
|
||||
}
|
||||
|
||||
/// Variable represents a variable declaration of some kind.
|
||||
///
|
||||
/// For `let` and `const` declarations this type represents a [`LexicalBinding`][spec1]
|
||||
///
|
||||
/// For `var` declarations this type represents a [`VariableDeclaration`][spec2]
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec3]
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-LexicalBinding
|
||||
/// [spec2]: https://tc39.es/ecma262/#prod-VariableDeclaration
|
||||
/// [spec3]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Variable {
|
||||
binding: Binding,
|
||||
init: Option<Expression>,
|
||||
}
|
||||
|
||||
impl ToInternedString for Variable {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let mut buf = self.binding.to_interned_string(interner);
|
||||
|
||||
if let Some(ref init) = self.init {
|
||||
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Variable {
|
||||
/// Creates a new variable declaration from a `BindingIdentifier`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_identifier(ident: Identifier, init: Option<Expression>) -> Self {
|
||||
Self {
|
||||
binding: Binding::Identifier(ident),
|
||||
init,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new variable declaration from a `Pattern`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_pattern(pattern: Pattern, init: Option<Expression>) -> Self {
|
||||
Self {
|
||||
binding: Binding::Pattern(pattern),
|
||||
init,
|
||||
}
|
||||
}
|
||||
/// Gets the variable declaration binding.
|
||||
#[must_use]
|
||||
pub const fn binding(&self) -> &Binding {
|
||||
&self.binding
|
||||
}
|
||||
|
||||
/// Gets the initialization expression for the variable declaration, if any.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn init(&self) -> Option<&Expression> {
|
||||
self.init.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Variable {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_binding(&self.binding));
|
||||
if let Some(init) = &self.init {
|
||||
try_break!(visitor.visit_expression(init));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_binding_mut(&mut self.binding));
|
||||
if let Some(init) = &mut self.init {
|
||||
try_break!(visitor.visit_expression_mut(init));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Binding represents either an individual binding or a binding pattern.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Binding {
|
||||
/// A single identifier binding.
|
||||
Identifier(Identifier),
|
||||
/// A pattern binding.
|
||||
Pattern(Pattern),
|
||||
}
|
||||
|
||||
impl From<Identifier> for Binding {
|
||||
fn from(id: Identifier) -> Self {
|
||||
Self::Identifier(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pattern> for Binding {
|
||||
fn from(pat: Pattern) -> Self {
|
||||
Self::Pattern(pat)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Binding {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
match self {
|
||||
Self::Identifier(id) => id.to_interned_string(interner),
|
||||
Self::Pattern(ref pattern) => pattern.to_interned_string(interner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Binding {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Identifier(id) => visitor.visit_identifier(id),
|
||||
Self::Pattern(pattern) => visitor.visit_pattern(pattern),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Identifier(id) => visitor.visit_identifier_mut(id),
|
||||
Self::Pattern(pattern) => visitor.visit_pattern_mut(pattern),
|
||||
}
|
||||
}
|
||||
}
|
||||
348
javascript-engine/external/boa/boa_ast/src/expression/access.rs
vendored
Normal file
348
javascript-engine/external/boa/boa_ast/src/expression/access.rs
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
//! Property access expressions, as defined by the [spec].
|
||||
//!
|
||||
//! [Property access expressions][access] provide two ways to access properties of an object: *dot notation*
|
||||
//! and *bracket notation*.
|
||||
//! - *Dot notation* is mostly used when the name of the property is static, and a valid Javascript
|
||||
//! identifier e.g. `obj.prop`, `arr.$val`.
|
||||
//! - *Bracket notation* is used when the name of the property is either variable, not a valid
|
||||
//! identifier or a symbol e.g. `arr[var]`, `arr[5]`, `arr[Symbol.iterator]`.
|
||||
//!
|
||||
//! A property access expression can be represented by a [`SimplePropertyAccess`] (`x.y`), a
|
||||
//! [`PrivatePropertyAccess`] (`x.#y`) or a [`SuperPropertyAccess`] (`super["y"]`), each of them with
|
||||
//! slightly different semantics overall.
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors
|
||||
//! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
|
||||
|
||||
use crate::expression::Expression;
|
||||
use crate::function::PrivateName;
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, Sym, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// A property access field.
|
||||
///
|
||||
/// See the [module level documentation][self] for more information.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PropertyAccessField {
|
||||
/// A constant property field, such as `x.prop`.
|
||||
Const(Sym),
|
||||
/// An expression property field, such as `x["val"]`.
|
||||
Expr(Box<Expression>),
|
||||
}
|
||||
|
||||
impl From<Sym> for PropertyAccessField {
|
||||
#[inline]
|
||||
fn from(id: Sym) -> Self {
|
||||
Self::Const(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expression> for PropertyAccessField {
|
||||
#[inline]
|
||||
fn from(expr: Expression) -> Self {
|
||||
Self::Expr(Box::new(expr))
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for PropertyAccessField {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Const(sym) => visitor.visit_sym(sym),
|
||||
Self::Expr(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::Const(sym) => visitor.visit_sym_mut(sym),
|
||||
Self::Expr(expr) => visitor.visit_expression_mut(&mut *expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A property access expression.
|
||||
///
|
||||
/// See the [module level documentation][self] for more information.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PropertyAccess {
|
||||
/// A simple property access (`x.prop`).
|
||||
Simple(SimplePropertyAccess),
|
||||
/// A property access of a private property (`x.#priv`).
|
||||
Private(PrivatePropertyAccess),
|
||||
/// A property access of a `super` reference. (`super["prop"]`).
|
||||
Super(SuperPropertyAccess),
|
||||
}
|
||||
|
||||
impl ToInternedString for PropertyAccess {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
match self {
|
||||
Self::Simple(s) => s.to_interned_string(interner),
|
||||
Self::Private(p) => p.to_interned_string(interner),
|
||||
Self::Super(s) => s.to_interned_string(interner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PropertyAccess> for Expression {
|
||||
#[inline]
|
||||
fn from(access: PropertyAccess) -> Self {
|
||||
Self::PropertyAccess(access)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for PropertyAccess {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Simple(spa) => visitor.visit_simple_property_access(spa),
|
||||
Self::Private(ppa) => visitor.visit_private_property_access(ppa),
|
||||
Self::Super(supa) => visitor.visit_super_property_access(supa),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Simple(spa) => visitor.visit_simple_property_access_mut(spa),
|
||||
Self::Private(ppa) => visitor.visit_private_property_access_mut(ppa),
|
||||
Self::Super(supa) => visitor.visit_super_property_access_mut(supa),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple property access, where the target object is an [`Expression`].
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SimplePropertyAccess {
|
||||
target: Box<Expression>,
|
||||
field: PropertyAccessField,
|
||||
}
|
||||
|
||||
impl SimplePropertyAccess {
|
||||
/// Gets the target object of the property access.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn target(&self) -> &Expression {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Gets the accessed field of the target object.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn field(&self) -> &PropertyAccessField {
|
||||
&self.field
|
||||
}
|
||||
|
||||
/// Creates a `PropertyAccess` AST Expression.
|
||||
pub fn new<F>(target: Expression, field: F) -> Self
|
||||
where
|
||||
F: Into<PropertyAccessField>,
|
||||
{
|
||||
Self {
|
||||
target: target.into(),
|
||||
field: field.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for SimplePropertyAccess {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let target = self.target.to_interned_string(interner);
|
||||
match self.field {
|
||||
PropertyAccessField::Const(sym) => format!("{target}.{}", interner.resolve_expect(sym)),
|
||||
PropertyAccessField::Expr(ref expr) => {
|
||||
format!("{target}[{}]", expr.to_interned_string(interner))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SimplePropertyAccess> for PropertyAccess {
|
||||
#[inline]
|
||||
fn from(access: SimplePropertyAccess) -> Self {
|
||||
Self::Simple(access)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for SimplePropertyAccess {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.target));
|
||||
visitor.visit_property_access_field(&self.field)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.target));
|
||||
visitor.visit_property_access_field_mut(&mut self.field)
|
||||
}
|
||||
}
|
||||
|
||||
/// An access expression to a class object's [private fields][mdn].
|
||||
///
|
||||
/// Private property accesses differ slightly from plain property accesses, since the accessed
|
||||
/// property must be prefixed by `#`, and the bracket notation is not allowed. For example,
|
||||
/// `this.#a` is a valid private property access.
|
||||
///
|
||||
/// This expression corresponds to the [`MemberExpression.PrivateIdentifier`][spec] production.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PrivatePropertyAccess {
|
||||
target: Box<Expression>,
|
||||
field: PrivateName,
|
||||
}
|
||||
|
||||
impl PrivatePropertyAccess {
|
||||
/// Creates a `GetPrivateField` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(value: Expression, field: PrivateName) -> Self {
|
||||
Self {
|
||||
target: value.into(),
|
||||
field,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the original object from where to get the field from.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn target(&self) -> &Expression {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Gets the name of the field to retrieve.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn field(&self) -> PrivateName {
|
||||
self.field
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for PrivatePropertyAccess {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!(
|
||||
"{}.#{}",
|
||||
self.target.to_interned_string(interner),
|
||||
interner.resolve_expect(self.field.description())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrivatePropertyAccess> for PropertyAccess {
|
||||
#[inline]
|
||||
fn from(access: PrivatePropertyAccess) -> Self {
|
||||
Self::Private(access)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for PrivatePropertyAccess {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.target));
|
||||
visitor.visit_private_name(&self.field)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.target));
|
||||
visitor.visit_private_name_mut(&mut self.field)
|
||||
}
|
||||
}
|
||||
|
||||
/// A property access of an object's parent, as defined by the [spec].
|
||||
///
|
||||
/// A `SuperPropertyAccess` is much like a regular [`PropertyAccess`], but where its `target` object
|
||||
/// is not a regular object, but a reference to the parent object of the current object ([`super`][mdn]).
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-SuperProperty
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SuperPropertyAccess {
|
||||
field: PropertyAccessField,
|
||||
}
|
||||
|
||||
impl SuperPropertyAccess {
|
||||
/// Creates a new property access field node.
|
||||
#[must_use]
|
||||
pub const fn new(field: PropertyAccessField) -> Self {
|
||||
Self { field }
|
||||
}
|
||||
|
||||
/// Gets the name of the field to retrieve.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn field(&self) -> &PropertyAccessField {
|
||||
&self.field
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for SuperPropertyAccess {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
match &self.field {
|
||||
PropertyAccessField::Const(field) => {
|
||||
format!("super.{}", interner.resolve_expect(*field))
|
||||
}
|
||||
PropertyAccessField::Expr(field) => {
|
||||
format!("super[{}]", field.to_interned_string(interner))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SuperPropertyAccess> for PropertyAccess {
|
||||
#[inline]
|
||||
fn from(access: SuperPropertyAccess) -> Self {
|
||||
Self::Super(access)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for SuperPropertyAccess {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_property_access_field(&self.field)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_property_access_field_mut(&mut self.field)
|
||||
}
|
||||
}
|
||||
71
javascript-engine/external/boa/boa_ast/src/expression/await.rs
vendored
Normal file
71
javascript-engine/external/boa/boa_ast/src/expression/await.rs
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
//! Await expression Expression.
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use super::Expression;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
|
||||
/// An await expression is used within an async function to pause execution and wait for a
|
||||
/// promise to resolve.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Await {
|
||||
target: Box<Expression>,
|
||||
}
|
||||
|
||||
impl Await {
|
||||
/// Return the target expression that should be awaited.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn target(&self) -> &Expression {
|
||||
&self.target
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Await
|
||||
where
|
||||
T: Into<Box<Expression>>,
|
||||
{
|
||||
fn from(e: T) -> Self {
|
||||
Self { target: e.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Await {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!("await {}", self.target.to_indented_string(interner, 0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Await> for Expression {
|
||||
#[inline]
|
||||
fn from(awaitexpr: Await) -> Self {
|
||||
Self::Await(awaitexpr)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Await {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_expression(&self.target)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_expression_mut(&mut self.target)
|
||||
}
|
||||
}
|
||||
164
javascript-engine/external/boa/boa_ast/src/expression/call.rs
vendored
Normal file
164
javascript-engine/external/boa/boa_ast/src/expression/call.rs
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
use crate::join_nodes;
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use super::Expression;
|
||||
|
||||
/// Calling the function actually performs the specified actions with the indicated parameters.
|
||||
///
|
||||
/// Defining a function does not execute it. Defining it simply names the function and
|
||||
/// specifies what to do when the function is called. Functions must be in scope when they are
|
||||
/// called, but the function declaration can be hoisted. The scope of a function is the
|
||||
/// function in which it is declared (or the entire program, if it is declared at the top
|
||||
/// level).
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-CallExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Call {
|
||||
function: Box<Expression>,
|
||||
args: Box<[Expression]>,
|
||||
}
|
||||
|
||||
impl Call {
|
||||
/// Creates a new `Call` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(function: Expression, args: Box<[Expression]>) -> Self {
|
||||
Self {
|
||||
function: function.into(),
|
||||
args,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the target function of this call expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn function(&self) -> &Expression {
|
||||
&self.function
|
||||
}
|
||||
|
||||
/// Retrieves the arguments passed to the function.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn args(&self) -> &[Expression] {
|
||||
&self.args
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Call {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!(
|
||||
"{}({})",
|
||||
self.function.to_interned_string(interner),
|
||||
join_nodes(interner, &self.args)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Call> for Expression {
|
||||
#[inline]
|
||||
fn from(call: Call) -> Self {
|
||||
Self::Call(call)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Call {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.function));
|
||||
for expr in self.args.iter() {
|
||||
try_break!(visitor.visit_expression(expr));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.function));
|
||||
for expr in self.args.iter_mut() {
|
||||
try_break!(visitor.visit_expression_mut(expr));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The `super` keyword is used to access and call functions on an object's parent.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-SuperCall
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SuperCall {
|
||||
args: Box<[Expression]>,
|
||||
}
|
||||
|
||||
impl SuperCall {
|
||||
/// Creates a new `SuperCall` AST node.
|
||||
pub fn new<A>(args: A) -> Self
|
||||
where
|
||||
A: Into<Box<[Expression]>>,
|
||||
{
|
||||
Self { args: args.into() }
|
||||
}
|
||||
|
||||
/// Retrieves the arguments of the super call.
|
||||
#[must_use]
|
||||
pub const fn arguments(&self) -> &[Expression] {
|
||||
&self.args
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for SuperCall {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!("super({})", join_nodes(interner, &self.args))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SuperCall> for Expression {
|
||||
#[inline]
|
||||
fn from(call: SuperCall) -> Self {
|
||||
Self::SuperCall(call)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for SuperCall {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
for expr in self.args.iter() {
|
||||
try_break!(visitor.visit_expression(expr));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
for expr in self.args.iter_mut() {
|
||||
try_break!(visitor.visit_expression_mut(expr));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
122
javascript-engine/external/boa/boa_ast/src/expression/identifier.rs
vendored
Normal file
122
javascript-engine/external/boa/boa_ast/src/expression/identifier.rs
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
//! Local identifier Expression.
|
||||
|
||||
use crate::{
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
ToStringEscaped,
|
||||
};
|
||||
use boa_interner::{Interner, Sym, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use super::Expression;
|
||||
|
||||
/// List of reserved keywords exclusive to strict mode.
|
||||
pub const RESERVED_IDENTIFIERS_STRICT: [Sym; 9] = [
|
||||
Sym::IMPLEMENTS,
|
||||
Sym::INTERFACE,
|
||||
Sym::LET,
|
||||
Sym::PACKAGE,
|
||||
Sym::PRIVATE,
|
||||
Sym::PROTECTED,
|
||||
Sym::PUBLIC,
|
||||
Sym::STATIC,
|
||||
Sym::YIELD,
|
||||
];
|
||||
|
||||
/// An `identifier` is a sequence of characters in the code that identifies a variable,
|
||||
/// function, or property.
|
||||
///
|
||||
/// In ECMAScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and
|
||||
/// digits (0-9), but may not start with a digit.
|
||||
///
|
||||
/// An identifier differs from a string in that a string is data, while an identifier is part
|
||||
/// of the code. In JavaScript, there is no way to convert identifiers to strings, but
|
||||
/// sometimes it is possible to parse strings into identifiers.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-Identifier
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(transparent)
|
||||
)]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct Identifier {
|
||||
ident: Sym,
|
||||
}
|
||||
|
||||
impl PartialEq<Sym> for Identifier {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Sym) -> bool {
|
||||
self.ident == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Identifier> for Sym {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Identifier) -> bool {
|
||||
*self == other.ident
|
||||
}
|
||||
}
|
||||
|
||||
impl Identifier {
|
||||
/// Creates a new identifier AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(ident: Sym) -> Self {
|
||||
Self { ident }
|
||||
}
|
||||
|
||||
/// Retrieves the identifier's string symbol in the interner.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn sym(self) -> Sym {
|
||||
self.ident
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Identifier {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
interner.resolve_expect(self.ident).join(
|
||||
String::from,
|
||||
ToStringEscaped::to_string_escaped,
|
||||
true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Sym> for Identifier {
|
||||
#[inline]
|
||||
fn from(sym: Sym) -> Self {
|
||||
Self { ident: sym }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Identifier> for Expression {
|
||||
#[inline]
|
||||
fn from(local: Identifier) -> Self {
|
||||
Self::Identifier(local)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Identifier {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_sym(&self.ident)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_sym_mut(&mut self.ident)
|
||||
}
|
||||
}
|
||||
223
javascript-engine/external/boa/boa_ast/src/expression/literal/array.rs
vendored
Normal file
223
javascript-engine/external/boa/boa_ast/src/expression/literal/array.rs
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
//! Array declaration Expression.
|
||||
|
||||
use crate::expression::operator::assign::AssignTarget;
|
||||
use crate::expression::Expression;
|
||||
use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern};
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, Sym, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// An array is an ordered collection of data (either primitive or object depending upon the
|
||||
/// language).
|
||||
///
|
||||
/// Arrays are used to store multiple values in a single variable.
|
||||
/// This is compared to a variable that can store only one value.
|
||||
///
|
||||
/// Each item in an array has a number attached to it, called a numeric index, that allows you
|
||||
/// to access it. In JavaScript, arrays start at index zero and can be manipulated with various
|
||||
/// methods.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ArrayLiteral {
|
||||
arr: Box<[Option<Expression>]>,
|
||||
has_trailing_comma_spread: bool,
|
||||
}
|
||||
|
||||
impl ArrayLiteral {
|
||||
/// Creates a new array literal.
|
||||
pub fn new<A>(array: A, has_trailing_comma_spread: bool) -> Self
|
||||
where
|
||||
A: Into<Box<[Option<Expression>]>>,
|
||||
{
|
||||
Self {
|
||||
arr: array.into(),
|
||||
has_trailing_comma_spread,
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates if a spread operator in the array literal has a trailing comma.
|
||||
/// This is a syntax error in some cases.
|
||||
#[must_use]
|
||||
pub const fn has_trailing_comma_spread(&self) -> bool {
|
||||
self.has_trailing_comma_spread
|
||||
}
|
||||
|
||||
/// Converts this `ArrayLiteral` into an [`ArrayPattern`].
|
||||
#[must_use]
|
||||
pub fn to_pattern(&self, strict: bool) -> Option<ArrayPattern> {
|
||||
if self.has_trailing_comma_spread() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut bindings = Vec::new();
|
||||
for (i, expr) in self.arr.iter().enumerate() {
|
||||
let expr = if let Some(expr) = expr {
|
||||
expr
|
||||
} else {
|
||||
bindings.push(ArrayPatternElement::Elision);
|
||||
continue;
|
||||
};
|
||||
match expr {
|
||||
Expression::Identifier(ident) => {
|
||||
if strict && *ident == Sym::ARGUMENTS {
|
||||
return None;
|
||||
}
|
||||
|
||||
bindings.push(ArrayPatternElement::SingleName {
|
||||
ident: *ident,
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
Expression::Spread(spread) => {
|
||||
match spread.target() {
|
||||
Expression::Identifier(ident) => {
|
||||
bindings.push(ArrayPatternElement::SingleNameRest { ident: *ident });
|
||||
}
|
||||
Expression::PropertyAccess(access) => {
|
||||
bindings.push(ArrayPatternElement::PropertyAccessRest {
|
||||
access: access.clone(),
|
||||
});
|
||||
}
|
||||
Expression::ArrayLiteral(array) => {
|
||||
let pattern = array.to_pattern(strict)?.into();
|
||||
bindings.push(ArrayPatternElement::PatternRest { pattern });
|
||||
}
|
||||
Expression::ObjectLiteral(object) => {
|
||||
let pattern = object.to_pattern(strict)?.into();
|
||||
bindings.push(ArrayPatternElement::PatternRest { pattern });
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
if i + 1 != self.arr.len() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Expression::Assign(assign) => match assign.lhs() {
|
||||
AssignTarget::Identifier(ident) => {
|
||||
bindings.push(ArrayPatternElement::SingleName {
|
||||
ident: *ident,
|
||||
default_init: Some(assign.rhs().clone()),
|
||||
});
|
||||
}
|
||||
AssignTarget::Access(access) => {
|
||||
bindings.push(ArrayPatternElement::PropertyAccess {
|
||||
access: access.clone(),
|
||||
});
|
||||
}
|
||||
AssignTarget::Pattern(pattern) => match pattern {
|
||||
Pattern::Object(pattern) => {
|
||||
bindings.push(ArrayPatternElement::Pattern {
|
||||
pattern: Pattern::Object(pattern.clone()),
|
||||
default_init: Some(assign.rhs().clone()),
|
||||
});
|
||||
}
|
||||
Pattern::Array(pattern) => {
|
||||
bindings.push(ArrayPatternElement::Pattern {
|
||||
pattern: Pattern::Array(pattern.clone()),
|
||||
default_init: Some(assign.rhs().clone()),
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
Expression::ArrayLiteral(array) => {
|
||||
let pattern = array.to_pattern(strict)?.into();
|
||||
bindings.push(ArrayPatternElement::Pattern {
|
||||
pattern,
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
Expression::ObjectLiteral(object) => {
|
||||
let pattern = object.to_pattern(strict)?.into();
|
||||
bindings.push(ArrayPatternElement::Pattern {
|
||||
pattern,
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
Expression::PropertyAccess(access) => {
|
||||
bindings.push(ArrayPatternElement::PropertyAccess {
|
||||
access: access.clone(),
|
||||
});
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
Some(ArrayPattern::new(bindings.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[Option<Expression>]> for ArrayLiteral {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[Option<Expression>] {
|
||||
&self.arr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for ArrayLiteral
|
||||
where
|
||||
T: Into<Box<[Option<Expression>]>>,
|
||||
{
|
||||
fn from(decl: T) -> Self {
|
||||
Self {
|
||||
arr: decl.into(),
|
||||
has_trailing_comma_spread: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for ArrayLiteral {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let mut buf = String::from("[");
|
||||
let mut first = true;
|
||||
for e in &*self.arr {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
buf.push_str(", ");
|
||||
}
|
||||
if let Some(e) = e {
|
||||
buf.push_str(&e.to_interned_string(interner));
|
||||
}
|
||||
}
|
||||
buf.push(']');
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArrayLiteral> for Expression {
|
||||
#[inline]
|
||||
fn from(arr: ArrayLiteral) -> Self {
|
||||
Self::ArrayLiteral(arr)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ArrayLiteral {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
for expr in self.arr.iter().flatten() {
|
||||
try_break!(visitor.visit_expression(expr));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
for expr in self.arr.iter_mut().flatten() {
|
||||
try_break!(visitor.visit_expression_mut(expr));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
202
javascript-engine/external/boa/boa_ast/src/expression/literal/mod.rs
vendored
Normal file
202
javascript-engine/external/boa/boa_ast/src/expression/literal/mod.rs
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
//! This module contains all literal expressions, which represents the primitive values in ECMAScript.
|
||||
//!
|
||||
//! More information:
|
||||
//! - [ECMAScript reference][spec]
|
||||
//! - [MDN documentation][mdn]
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
|
||||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
|
||||
|
||||
mod array;
|
||||
mod object;
|
||||
mod template;
|
||||
|
||||
pub use array::ArrayLiteral;
|
||||
use core::ops::ControlFlow;
|
||||
pub use object::ObjectLiteral;
|
||||
pub use template::{TemplateElement, TemplateLiteral};
|
||||
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, Sym, ToInternedString};
|
||||
use num_bigint::BigInt;
|
||||
|
||||
use super::Expression;
|
||||
|
||||
/// Literals represent values in ECMAScript.
|
||||
///
|
||||
/// These are fixed values **not variables** that you literally provide in your script.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Literal {
|
||||
/// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks.
|
||||
///
|
||||
/// A string must be delimited by quotation marks of the same type (that is, either both single quotation marks, or both double quotation marks).
|
||||
/// You can call any of the String object's methods on a string literal value.
|
||||
/// ECMAScript automatically converts the string literal to a temporary String object,
|
||||
/// calls the method, then discards the temporary String object.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-string-value
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#String_literals
|
||||
String(Sym),
|
||||
|
||||
/// A floating-point number literal.
|
||||
///
|
||||
/// The exponent part is an "`e`" or "`E`" followed by an integer, which can be signed (preceded by "`+`" or "`-`").
|
||||
/// A floating-point literal must have at least one digit, and either a decimal point or "`e`" (or "`E`").
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Floating-point_literals
|
||||
Num(f64),
|
||||
|
||||
/// Integer types can be expressed in decimal (base 10), hexadecimal (base 16), octal (base 8) and binary (base 2).
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals
|
||||
Int(i32),
|
||||
|
||||
/// BigInt provides a way to represent whole numbers larger than the largest number ECMAScript
|
||||
/// can reliably represent with the `Number` primitive.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-bigint-value
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals
|
||||
BigInt(Box<BigInt>),
|
||||
|
||||
/// The Boolean type has two literal values: `true` and `false`.
|
||||
///
|
||||
/// The Boolean object is a wrapper around the primitive Boolean data type.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-boolean-value
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Boolean_literals
|
||||
Bool(bool),
|
||||
|
||||
/// In JavaScript, `null` is marked as one of the primitive values, cause it's behaviour is seemingly primitive.
|
||||
///
|
||||
/// In computer science, a null value represents a reference that points,
|
||||
/// generally intentionally, to a nonexistent or invalid object or address.
|
||||
/// The meaning of a null reference varies among language implementations.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-null-value
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/null
|
||||
Null,
|
||||
}
|
||||
|
||||
impl From<Sym> for Literal {
|
||||
#[inline]
|
||||
fn from(string: Sym) -> Self {
|
||||
Self::String(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Literal {
|
||||
#[inline]
|
||||
fn from(num: f64) -> Self {
|
||||
Self::Num(num)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Literal {
|
||||
#[inline]
|
||||
fn from(i: i32) -> Self {
|
||||
Self::Int(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BigInt> for Literal {
|
||||
#[inline]
|
||||
fn from(i: BigInt) -> Self {
|
||||
Self::BigInt(Box::new(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<BigInt>> for Literal {
|
||||
#[inline]
|
||||
fn from(i: Box<BigInt>) -> Self {
|
||||
Self::BigInt(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Literal {
|
||||
#[inline]
|
||||
fn from(b: bool) -> Self {
|
||||
Self::Bool(b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Literal> for Expression {
|
||||
#[inline]
|
||||
fn from(lit: Literal) -> Self {
|
||||
Self::Literal(lit)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Literal {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
match *self {
|
||||
Self::String(st) => {
|
||||
format!("\"{}\"", interner.resolve_expect(st))
|
||||
}
|
||||
Self::Num(num) => num.to_string(),
|
||||
Self::Int(num) => num.to_string(),
|
||||
Self::BigInt(ref num) => num.to_string(),
|
||||
Self::Bool(v) => v.to_string(),
|
||||
Self::Null => "null".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Literal {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Self::String(sym) = self {
|
||||
visitor.visit_sym(sym)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Self::String(sym) = self {
|
||||
visitor.visit_sym_mut(sym)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
326
javascript-engine/external/boa/boa_ast/src/expression/literal/object.rs
vendored
Normal file
326
javascript-engine/external/boa/boa_ast/src/expression/literal/object.rs
vendored
Normal file
@@ -0,0 +1,326 @@
|
||||
//! Object Expression.
|
||||
|
||||
use crate::{
|
||||
block_to_string,
|
||||
expression::{operator::assign::AssignTarget, Expression, RESERVED_IDENTIFIERS_STRICT},
|
||||
function::Function,
|
||||
join_nodes,
|
||||
pattern::{ObjectPattern, ObjectPatternElement},
|
||||
property::{MethodDefinition, PropertyDefinition, PropertyName},
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// Objects in ECMAScript may be defined as an unordered collection of related data, of
|
||||
/// primitive or reference types, in the form of “key: value” pairs.
|
||||
///
|
||||
/// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal
|
||||
/// notation.
|
||||
///
|
||||
/// An object initializer is an expression that describes the initialization of an
|
||||
/// [`Object`][object]. Objects consist of properties, which are used to describe an object.
|
||||
/// Values of object properties can either contain [`primitive`][primitive] data types or other
|
||||
/// objects.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
|
||||
/// [object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||
/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ObjectLiteral {
|
||||
properties: Box<[PropertyDefinition]>,
|
||||
}
|
||||
|
||||
impl ObjectLiteral {
|
||||
/// Gets the object literal properties
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn properties(&self) -> &[PropertyDefinition] {
|
||||
&self.properties
|
||||
}
|
||||
|
||||
/// Converts the object literal into an [`ObjectPattern`].
|
||||
#[must_use]
|
||||
pub fn to_pattern(&self, strict: bool) -> Option<ObjectPattern> {
|
||||
let mut bindings = Vec::new();
|
||||
let mut excluded_keys = Vec::new();
|
||||
for (i, property) in self.properties.iter().enumerate() {
|
||||
match property {
|
||||
PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => {
|
||||
return None
|
||||
}
|
||||
PropertyDefinition::IdentifierReference(ident) => {
|
||||
if strict && RESERVED_IDENTIFIERS_STRICT.contains(&ident.sym()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
excluded_keys.push(*ident);
|
||||
bindings.push(ObjectPatternElement::SingleName {
|
||||
ident: *ident,
|
||||
name: PropertyName::Literal(ident.sym()),
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
PropertyDefinition::Property(name, expr) => match (name, expr) {
|
||||
(PropertyName::Literal(name), Expression::Identifier(ident))
|
||||
if *name == *ident =>
|
||||
{
|
||||
if strict && *name == Sym::EVAL {
|
||||
return None;
|
||||
}
|
||||
if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) {
|
||||
return None;
|
||||
}
|
||||
|
||||
excluded_keys.push(*ident);
|
||||
bindings.push(ObjectPatternElement::SingleName {
|
||||
ident: *ident,
|
||||
name: PropertyName::Literal(*name),
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
(PropertyName::Literal(name), Expression::Identifier(ident)) => {
|
||||
bindings.push(ObjectPatternElement::SingleName {
|
||||
ident: *ident,
|
||||
name: PropertyName::Literal(*name),
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
(PropertyName::Literal(name), Expression::ObjectLiteral(object)) => {
|
||||
let pattern = object.to_pattern(strict)?.into();
|
||||
bindings.push(ObjectPatternElement::Pattern {
|
||||
name: PropertyName::Literal(*name),
|
||||
pattern,
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
(PropertyName::Literal(name), Expression::ArrayLiteral(array)) => {
|
||||
let pattern = array.to_pattern(strict)?.into();
|
||||
bindings.push(ObjectPatternElement::Pattern {
|
||||
name: PropertyName::Literal(*name),
|
||||
pattern,
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
(_, Expression::Assign(assign)) => match assign.lhs() {
|
||||
AssignTarget::Identifier(ident) => {
|
||||
if let Some(name) = name.literal() {
|
||||
if name == *ident {
|
||||
if strict && name == Sym::EVAL {
|
||||
return None;
|
||||
}
|
||||
if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) {
|
||||
return None;
|
||||
}
|
||||
excluded_keys.push(*ident);
|
||||
}
|
||||
bindings.push(ObjectPatternElement::SingleName {
|
||||
ident: *ident,
|
||||
name: PropertyName::Literal(name),
|
||||
default_init: Some(assign.rhs().clone()),
|
||||
});
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
AssignTarget::Pattern(pattern) => {
|
||||
bindings.push(ObjectPatternElement::Pattern {
|
||||
name: name.clone(),
|
||||
pattern: pattern.clone(),
|
||||
default_init: Some(assign.rhs().clone()),
|
||||
});
|
||||
}
|
||||
AssignTarget::Access(access) => {
|
||||
bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
|
||||
name: name.clone(),
|
||||
access: access.clone(),
|
||||
default_init: Some(assign.rhs().clone()),
|
||||
});
|
||||
}
|
||||
},
|
||||
(_, Expression::PropertyAccess(access)) => {
|
||||
bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
|
||||
name: name.clone(),
|
||||
access: access.clone(),
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
(PropertyName::Computed(name), Expression::Identifier(ident)) => {
|
||||
bindings.push(ObjectPatternElement::SingleName {
|
||||
ident: *ident,
|
||||
name: PropertyName::Computed(name.clone()),
|
||||
default_init: None,
|
||||
});
|
||||
}
|
||||
_ => return None,
|
||||
},
|
||||
PropertyDefinition::SpreadObject(spread) => {
|
||||
match spread {
|
||||
Expression::Identifier(ident) => {
|
||||
bindings.push(ObjectPatternElement::RestProperty {
|
||||
ident: *ident,
|
||||
excluded_keys: excluded_keys.clone(),
|
||||
});
|
||||
}
|
||||
Expression::PropertyAccess(access) => {
|
||||
bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess {
|
||||
access: access.clone(),
|
||||
excluded_keys: excluded_keys.clone(),
|
||||
});
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
if i + 1 != self.properties.len() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
PropertyDefinition::MethodDefinition(_, _) => return None,
|
||||
PropertyDefinition::CoverInitializedName(ident, expr) => {
|
||||
if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
bindings.push(ObjectPatternElement::SingleName {
|
||||
ident: *ident,
|
||||
name: PropertyName::Literal(ident.sym()),
|
||||
default_init: Some(expr.clone()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(ObjectPattern::new(bindings.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for ObjectLiteral {
|
||||
fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
|
||||
let mut buf = "{\n".to_owned();
|
||||
let indentation = " ".repeat(indent_n + 1);
|
||||
for property in self.properties().iter() {
|
||||
buf.push_str(&match property {
|
||||
PropertyDefinition::IdentifierReference(ident) => {
|
||||
format!("{indentation}{},\n", interner.resolve_expect(ident.sym()))
|
||||
}
|
||||
PropertyDefinition::Property(key, value) => {
|
||||
let value = if let Expression::Function(f) = value {
|
||||
Function::new(None, f.parameters().clone(), f.body().clone()).into()
|
||||
} else {
|
||||
value.clone()
|
||||
};
|
||||
|
||||
format!(
|
||||
"{indentation}{}: {},\n",
|
||||
key.to_interned_string(interner),
|
||||
value.to_no_indent_string(interner, indent_n + 1)
|
||||
)
|
||||
}
|
||||
PropertyDefinition::SpreadObject(key) => {
|
||||
format!("{indentation}...{},\n", key.to_interned_string(interner))
|
||||
}
|
||||
PropertyDefinition::MethodDefinition(key, method) => {
|
||||
format!(
|
||||
"{indentation}{}{}({}) {},\n",
|
||||
match &method {
|
||||
MethodDefinition::Get(_) => "get ",
|
||||
MethodDefinition::Set(_) => "set ",
|
||||
_ => "",
|
||||
},
|
||||
key.to_interned_string(interner),
|
||||
match &method {
|
||||
MethodDefinition::Get(expression)
|
||||
| MethodDefinition::Set(expression)
|
||||
| MethodDefinition::Ordinary(expression) => {
|
||||
join_nodes(interner, expression.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Generator(expression) => {
|
||||
join_nodes(interner, expression.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expression) => {
|
||||
join_nodes(interner, expression.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Async(expression) => {
|
||||
join_nodes(interner, expression.parameters().as_ref())
|
||||
}
|
||||
},
|
||||
match &method {
|
||||
MethodDefinition::Get(expression)
|
||||
| MethodDefinition::Set(expression)
|
||||
| MethodDefinition::Ordinary(expression) => {
|
||||
block_to_string(expression.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Generator(expression) => {
|
||||
block_to_string(expression.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expression) => {
|
||||
block_to_string(expression.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Async(expression) => {
|
||||
block_to_string(expression.body(), interner, indent_n + 1)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
PropertyDefinition::CoverInitializedName(ident, expr) => {
|
||||
format!(
|
||||
"{indentation}{} = {},\n",
|
||||
interner.resolve_expect(ident.sym()),
|
||||
expr.to_no_indent_string(interner, indent_n + 1)
|
||||
)
|
||||
}
|
||||
});
|
||||
}
|
||||
buf.push_str(&format!("{}}}", " ".repeat(indent_n)));
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for ObjectLiteral
|
||||
where
|
||||
T: Into<Box<[PropertyDefinition]>>,
|
||||
{
|
||||
fn from(props: T) -> Self {
|
||||
Self {
|
||||
properties: props.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectLiteral> for Expression {
|
||||
#[inline]
|
||||
fn from(obj: ObjectLiteral) -> Self {
|
||||
Self::ObjectLiteral(obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ObjectLiteral {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
for pd in self.properties.iter() {
|
||||
try_break!(visitor.visit_property_definition(pd));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
for pd in self.properties.iter_mut() {
|
||||
try_break!(visitor.visit_property_definition_mut(pd));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
133
javascript-engine/external/boa/boa_ast/src/expression/literal/template.rs
vendored
Normal file
133
javascript-engine/external/boa/boa_ast/src/expression/literal/template.rs
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
//! Template literal Expression.
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use boa_interner::{Interner, Sym, ToInternedString};
|
||||
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
ToStringEscaped,
|
||||
};
|
||||
|
||||
/// Template literals are string literals allowing embedded expressions.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-template-literals
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TemplateLiteral {
|
||||
elements: Box<[TemplateElement]>,
|
||||
}
|
||||
|
||||
impl From<TemplateLiteral> for Expression {
|
||||
#[inline]
|
||||
fn from(tem: TemplateLiteral) -> Self {
|
||||
Self::TemplateLiteral(tem)
|
||||
}
|
||||
}
|
||||
|
||||
/// An element found within a [`TemplateLiteral`].
|
||||
///
|
||||
/// The [spec] doesn't define an element akin to `TemplateElement`. However, the AST defines this
|
||||
/// node as the equivalent of the components found in a template literal.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-template-literals
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum TemplateElement {
|
||||
/// A simple string.
|
||||
String(Sym),
|
||||
/// An expression that is evaluated and replaced by its string representation.
|
||||
Expr(Expression),
|
||||
}
|
||||
|
||||
impl TemplateLiteral {
|
||||
/// Creates a new `TemplateLiteral` from a list of [`TemplateElement`]s.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(elements: Box<[TemplateElement]>) -> Self {
|
||||
Self { elements }
|
||||
}
|
||||
|
||||
/// Gets the element list of this `TemplateLiteral`.
|
||||
#[must_use]
|
||||
pub const fn elements(&self) -> &[TemplateElement] {
|
||||
&self.elements
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for TemplateLiteral {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let mut buf = "`".to_owned();
|
||||
|
||||
for elt in self.elements.iter() {
|
||||
match elt {
|
||||
TemplateElement::String(s) => buf.push_str(&interner.resolve_expect(*s).join(
|
||||
Cow::Borrowed,
|
||||
|utf16| Cow::Owned(utf16.to_string_escaped()),
|
||||
true,
|
||||
)),
|
||||
TemplateElement::Expr(n) => {
|
||||
buf.push_str(&format!("${{{}}}", n.to_interned_string(interner)));
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.push('`');
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for TemplateLiteral {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
for element in self.elements.iter() {
|
||||
try_break!(visitor.visit_template_element(element));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
for element in self.elements.iter_mut() {
|
||||
try_break!(visitor.visit_template_element_mut(element));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for TemplateElement {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::String(sym) => visitor.visit_sym(sym),
|
||||
Self::Expr(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::String(sym) => visitor.visit_sym_mut(sym),
|
||||
Self::Expr(expr) => visitor.visit_expression_mut(expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
332
javascript-engine/external/boa/boa_ast/src/expression/mod.rs
vendored
Normal file
332
javascript-engine/external/boa/boa_ast/src/expression/mod.rs
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
//! The [`Expression`] Parse Node, as defined by the [spec].
|
||||
//!
|
||||
//! ECMAScript expressions include:
|
||||
//! - [Primary][primary] expressions (`this`, function expressions, literals).
|
||||
//! - [Left hand side][lhs] expressions (accessors, `new` operator, `super`).
|
||||
//! - [operator] expressions.
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#prod-Expression
|
||||
//! [primary]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#primary_expressions
|
||||
//! [lhs]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#left-hand-side_expressions
|
||||
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use self::{
|
||||
access::PropertyAccess,
|
||||
literal::{ArrayLiteral, Literal, ObjectLiteral, TemplateLiteral},
|
||||
operator::{Assign, Binary, Conditional, Unary},
|
||||
};
|
||||
|
||||
use super::{
|
||||
function::{ArrowFunction, AsyncFunction, AsyncGenerator, Class, Function, Generator},
|
||||
function::{AsyncArrowFunction, FormalParameterList},
|
||||
Statement,
|
||||
};
|
||||
|
||||
mod r#await;
|
||||
mod call;
|
||||
mod identifier;
|
||||
mod new;
|
||||
mod optional;
|
||||
mod spread;
|
||||
mod tagged_template;
|
||||
mod r#yield;
|
||||
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
pub use call::{Call, SuperCall};
|
||||
pub use identifier::{Identifier, RESERVED_IDENTIFIERS_STRICT};
|
||||
pub use new::New;
|
||||
pub use optional::{Optional, OptionalOperation, OptionalOperationKind};
|
||||
pub use r#await::Await;
|
||||
pub use r#yield::Yield;
|
||||
pub use spread::Spread;
|
||||
pub use tagged_template::TaggedTemplate;
|
||||
|
||||
pub mod access;
|
||||
pub mod literal;
|
||||
pub mod operator;
|
||||
|
||||
/// The `Expression` Parse Node.
|
||||
///
|
||||
/// See the [module level documentation][self] for more information.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expression {
|
||||
/// The ECMAScript `this` keyword refers to the object it belongs to.
|
||||
///
|
||||
/// A property of an execution context (global, function or eval) that,
|
||||
/// in non–strict mode, is always a reference to an object and in strict
|
||||
/// mode can be any value.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-this-keyword
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
|
||||
This,
|
||||
|
||||
/// See [`Identifier`].
|
||||
Identifier(Identifier),
|
||||
|
||||
/// See [`Literal`].
|
||||
Literal(Literal),
|
||||
|
||||
/// See [`ArrayLiteral`].
|
||||
ArrayLiteral(ArrayLiteral),
|
||||
|
||||
/// See [`ObjectLiteral`].
|
||||
ObjectLiteral(ObjectLiteral),
|
||||
|
||||
/// See [`Spread`],
|
||||
Spread(Spread),
|
||||
|
||||
/// See [`Function`].
|
||||
Function(Function),
|
||||
|
||||
/// See [`ArrowFunction`].
|
||||
ArrowFunction(ArrowFunction),
|
||||
|
||||
/// See [`AsyncArrowFunction`].
|
||||
AsyncArrowFunction(AsyncArrowFunction),
|
||||
|
||||
/// See [`Generator`].
|
||||
Generator(Generator),
|
||||
|
||||
/// See [`AsyncFunction`].
|
||||
AsyncFunction(AsyncFunction),
|
||||
|
||||
/// See [`AsyncGenerator`].
|
||||
AsyncGenerator(AsyncGenerator),
|
||||
|
||||
/// See [`Class`].
|
||||
Class(Box<Class>),
|
||||
|
||||
// TODO: Extract regexp literal Expression
|
||||
// RegExpLiteral,
|
||||
/// See [`TemplateLiteral`].
|
||||
TemplateLiteral(TemplateLiteral),
|
||||
|
||||
/// See [`PropertyAccess`].
|
||||
PropertyAccess(PropertyAccess),
|
||||
|
||||
/// See [`New`].
|
||||
New(New),
|
||||
|
||||
/// See [`Call`].
|
||||
Call(Call),
|
||||
|
||||
/// See [`SuperCall`].
|
||||
SuperCall(SuperCall),
|
||||
|
||||
/// See [`Optional`].
|
||||
Optional(Optional),
|
||||
|
||||
// TODO: Import calls
|
||||
/// See [`TaggedTemplate`].
|
||||
TaggedTemplate(TaggedTemplate),
|
||||
|
||||
/// The `new.target` pseudo-property expression.
|
||||
NewTarget,
|
||||
|
||||
// TODO: import.meta
|
||||
/// See [`Assign`].
|
||||
Assign(Assign),
|
||||
|
||||
/// See [`Unary`].
|
||||
Unary(Unary),
|
||||
|
||||
/// See [`Binary`].
|
||||
Binary(Binary),
|
||||
|
||||
/// See [`Conditional`].
|
||||
Conditional(Conditional),
|
||||
|
||||
/// See [`Await`].
|
||||
Await(Await),
|
||||
|
||||
/// See [`Yield`].
|
||||
Yield(Yield),
|
||||
|
||||
/// A FormalParameterList.
|
||||
///
|
||||
/// This is only used in the parser itself.
|
||||
/// It is not a valid expression node.
|
||||
#[doc(hidden)]
|
||||
FormalParameterList(FormalParameterList),
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
/// Implements the display formatting with indentation.
|
||||
///
|
||||
/// This will not prefix the value with any indentation. If you want to prefix this with proper
|
||||
/// indents, use [`to_indented_string()`](Self::to_indented_string).
|
||||
pub(crate) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
match self {
|
||||
Self::This => "this".to_owned(),
|
||||
Self::Identifier(id) => id.to_interned_string(interner),
|
||||
Self::Literal(lit) => lit.to_interned_string(interner),
|
||||
Self::ArrayLiteral(arr) => arr.to_interned_string(interner),
|
||||
Self::ObjectLiteral(o) => o.to_indented_string(interner, indentation),
|
||||
Self::Spread(sp) => sp.to_interned_string(interner),
|
||||
Self::Function(f) => f.to_indented_string(interner, indentation),
|
||||
Self::AsyncArrowFunction(f) => f.to_indented_string(interner, indentation),
|
||||
Self::ArrowFunction(arrf) => arrf.to_indented_string(interner, indentation),
|
||||
Self::Class(cl) => cl.to_indented_string(interner, indentation),
|
||||
Self::Generator(gen) => gen.to_indented_string(interner, indentation),
|
||||
Self::AsyncFunction(asf) => asf.to_indented_string(interner, indentation),
|
||||
Self::AsyncGenerator(asgen) => asgen.to_indented_string(interner, indentation),
|
||||
Self::TemplateLiteral(tem) => tem.to_interned_string(interner),
|
||||
Self::PropertyAccess(prop) => prop.to_interned_string(interner),
|
||||
Self::New(new) => new.to_interned_string(interner),
|
||||
Self::Call(call) => call.to_interned_string(interner),
|
||||
Self::SuperCall(supc) => supc.to_interned_string(interner),
|
||||
Self::Optional(opt) => opt.to_interned_string(interner),
|
||||
Self::NewTarget => "new.target".to_owned(),
|
||||
Self::TaggedTemplate(tag) => tag.to_interned_string(interner),
|
||||
Self::Assign(assign) => assign.to_interned_string(interner),
|
||||
Self::Unary(unary) => unary.to_interned_string(interner),
|
||||
Self::Binary(bin) => bin.to_interned_string(interner),
|
||||
Self::Conditional(cond) => cond.to_interned_string(interner),
|
||||
Self::Await(aw) => aw.to_interned_string(interner),
|
||||
Self::Yield(yi) => yi.to_interned_string(interner),
|
||||
Self::FormalParameterList(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns if the expression is a function definition according to the spec.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-isfunctiondefinition
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn is_function_definition(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::ArrowFunction(_)
|
||||
| Self::AsyncArrowFunction(_)
|
||||
| Self::Function(_)
|
||||
| Self::Generator(_)
|
||||
| Self::AsyncGenerator(_)
|
||||
| Self::AsyncFunction(_)
|
||||
| Self::Class(_)
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns if the expression is a function definition without a name.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-isanonymousfunctiondefinition
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn is_anonymous_function_definition(&self) -> bool {
|
||||
match self {
|
||||
Self::ArrowFunction(f) => f.name().is_none(),
|
||||
Self::AsyncArrowFunction(f) => f.name().is_none(),
|
||||
Self::Function(f) => f.name().is_none(),
|
||||
Self::Generator(f) => f.name().is_none(),
|
||||
Self::AsyncGenerator(f) => f.name().is_none(),
|
||||
Self::AsyncFunction(f) => f.name().is_none(),
|
||||
Self::Class(f) => f.name().is_none(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expression> for Statement {
|
||||
#[inline]
|
||||
fn from(expr: Expression) -> Self {
|
||||
Self::Expression(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Expression {
|
||||
#[inline]
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
self.to_no_indent_string(interner, indentation)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Expression {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Identifier(id) => visitor.visit_identifier(id),
|
||||
Self::Literal(lit) => visitor.visit_literal(lit),
|
||||
Self::ArrayLiteral(arlit) => visitor.visit_array_literal(arlit),
|
||||
Self::ObjectLiteral(olit) => visitor.visit_object_literal(olit),
|
||||
Self::Spread(sp) => visitor.visit_spread(sp),
|
||||
Self::Function(f) => visitor.visit_function(f),
|
||||
Self::ArrowFunction(af) => visitor.visit_arrow_function(af),
|
||||
Self::AsyncArrowFunction(af) => visitor.visit_async_arrow_function(af),
|
||||
Self::Generator(g) => visitor.visit_generator(g),
|
||||
Self::AsyncFunction(af) => visitor.visit_async_function(af),
|
||||
Self::AsyncGenerator(ag) => visitor.visit_async_generator(ag),
|
||||
Self::Class(c) => visitor.visit_class(c),
|
||||
Self::TemplateLiteral(tlit) => visitor.visit_template_literal(tlit),
|
||||
Self::PropertyAccess(pa) => visitor.visit_property_access(pa),
|
||||
Self::New(n) => visitor.visit_new(n),
|
||||
Self::Call(c) => visitor.visit_call(c),
|
||||
Self::SuperCall(sc) => visitor.visit_super_call(sc),
|
||||
Self::Optional(opt) => visitor.visit_optional(opt),
|
||||
Self::TaggedTemplate(tt) => visitor.visit_tagged_template(tt),
|
||||
Self::Assign(a) => visitor.visit_assign(a),
|
||||
Self::Unary(u) => visitor.visit_unary(u),
|
||||
Self::Binary(b) => visitor.visit_binary(b),
|
||||
Self::Conditional(c) => visitor.visit_conditional(c),
|
||||
Self::Await(a) => visitor.visit_await(a),
|
||||
Self::Yield(y) => visitor.visit_yield(y),
|
||||
Self::FormalParameterList(fpl) => visitor.visit_formal_parameter_list(fpl),
|
||||
Self::This | Self::NewTarget => {
|
||||
// do nothing; can be handled as special case by visitor
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Identifier(id) => visitor.visit_identifier_mut(id),
|
||||
Self::Literal(lit) => visitor.visit_literal_mut(lit),
|
||||
Self::ArrayLiteral(arlit) => visitor.visit_array_literal_mut(arlit),
|
||||
Self::ObjectLiteral(olit) => visitor.visit_object_literal_mut(olit),
|
||||
Self::Spread(sp) => visitor.visit_spread_mut(sp),
|
||||
Self::Function(f) => visitor.visit_function_mut(f),
|
||||
Self::ArrowFunction(af) => visitor.visit_arrow_function_mut(af),
|
||||
Self::AsyncArrowFunction(af) => visitor.visit_async_arrow_function_mut(af),
|
||||
Self::Generator(g) => visitor.visit_generator_mut(g),
|
||||
Self::AsyncFunction(af) => visitor.visit_async_function_mut(af),
|
||||
Self::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag),
|
||||
Self::Class(c) => visitor.visit_class_mut(c),
|
||||
Self::TemplateLiteral(tlit) => visitor.visit_template_literal_mut(tlit),
|
||||
Self::PropertyAccess(pa) => visitor.visit_property_access_mut(pa),
|
||||
Self::New(n) => visitor.visit_new_mut(n),
|
||||
Self::Call(c) => visitor.visit_call_mut(c),
|
||||
Self::SuperCall(sc) => visitor.visit_super_call_mut(sc),
|
||||
Self::Optional(opt) => visitor.visit_optional_mut(opt),
|
||||
Self::TaggedTemplate(tt) => visitor.visit_tagged_template_mut(tt),
|
||||
Self::Assign(a) => visitor.visit_assign_mut(a),
|
||||
Self::Unary(u) => visitor.visit_unary_mut(u),
|
||||
Self::Binary(b) => visitor.visit_binary_mut(b),
|
||||
Self::Conditional(c) => visitor.visit_conditional_mut(c),
|
||||
Self::Await(a) => visitor.visit_await_mut(a),
|
||||
Self::Yield(y) => visitor.visit_yield_mut(y),
|
||||
Self::FormalParameterList(fpl) => visitor.visit_formal_parameter_list_mut(fpl),
|
||||
Self::This | Self::NewTarget => {
|
||||
// do nothing; can be handled as special case by visitor
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
javascript-engine/external/boa/boa_ast/src/expression/new.rs
vendored
Normal file
87
javascript-engine/external/boa/boa_ast/src/expression/new.rs
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
use crate::expression::Call;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use super::Expression;
|
||||
|
||||
/// The `new` operator lets developers create an instance of a user-defined object type or of
|
||||
/// one of the built-in object types that has a constructor function.
|
||||
///
|
||||
/// The new keyword does the following things:
|
||||
/// - Creates a blank, plain JavaScript object;
|
||||
/// - Links (sets the constructor of) this object to another object;
|
||||
/// - Passes the newly created object from Step 1 as the this context;
|
||||
/// - Returns this if the function doesn't return its own object.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-NewExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct New {
|
||||
call: Call,
|
||||
}
|
||||
|
||||
impl New {
|
||||
/// Gets the constructor of the new expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn constructor(&self) -> &Expression {
|
||||
self.call.function()
|
||||
}
|
||||
|
||||
/// Retrieves the arguments passed to the constructor.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn arguments(&self) -> &[Expression] {
|
||||
self.call.args()
|
||||
}
|
||||
|
||||
/// Returns the inner call expression.
|
||||
#[must_use]
|
||||
pub const fn call(&self) -> &Call {
|
||||
&self.call
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Call> for New {
|
||||
#[inline]
|
||||
fn from(call: Call) -> Self {
|
||||
Self { call }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for New {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!("new {}", self.call.to_interned_string(interner))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<New> for Expression {
|
||||
#[inline]
|
||||
fn from(new: New) -> Self {
|
||||
Self::New(new)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for New {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_call(&self.call)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_call_mut(&mut self.call)
|
||||
}
|
||||
}
|
||||
191
javascript-engine/external/boa/boa_ast/src/expression/operator/assign/mod.rs
vendored
Normal file
191
javascript-engine/external/boa/boa_ast/src/expression/operator/assign/mod.rs
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
//! Assignment expression nodes, as defined by the [spec].
|
||||
//!
|
||||
//! An [assignment operator][mdn] assigns a value to its left operand based on the value of its right
|
||||
//! operand. Almost any [`LeftHandSideExpression`][lhs] Parse Node can be the target of a simple
|
||||
//! assignment expression (`=`). However, the compound assignment operations such as `%=` or `??=`
|
||||
//! only allow ["simple"][simple] left hand side expressions as an assignment target.
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
|
||||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators
|
||||
//! [lhs]: https://tc39.es/ecma262/#prod-LeftHandSideExpression
|
||||
//! [simple]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype
|
||||
|
||||
mod op;
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
pub use op::*;
|
||||
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
|
||||
use crate::{
|
||||
expression::{access::PropertyAccess, identifier::Identifier, Expression},
|
||||
pattern::Pattern,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
|
||||
/// An assignment operator expression.
|
||||
///
|
||||
/// See the [module level documentation][self] for more information.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Assign {
|
||||
op: AssignOp,
|
||||
lhs: Box<AssignTarget>,
|
||||
rhs: Box<Expression>,
|
||||
}
|
||||
|
||||
impl Assign {
|
||||
/// Creates an `Assign` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self {
|
||||
Self {
|
||||
op,
|
||||
lhs: Box::new(lhs),
|
||||
rhs: Box::new(rhs),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the operator of the assignment operation.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn op(&self) -> AssignOp {
|
||||
self.op
|
||||
}
|
||||
|
||||
/// Gets the left hand side of the assignment operation.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn lhs(&self) -> &AssignTarget {
|
||||
&self.lhs
|
||||
}
|
||||
|
||||
/// Gets the right hand side of the assignment operation.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn rhs(&self) -> &Expression {
|
||||
&self.rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Assign {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!(
|
||||
"{} {} {}",
|
||||
self.lhs.to_interned_string(interner),
|
||||
self.op,
|
||||
self.rhs.to_interned_string(interner)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Assign> for Expression {
|
||||
#[inline]
|
||||
fn from(op: Assign) -> Self {
|
||||
Self::Assign(op)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Assign {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_assign_target(&self.lhs));
|
||||
visitor.visit_expression(&self.rhs)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_assign_target_mut(&mut self.lhs));
|
||||
visitor.visit_expression_mut(&mut self.rhs)
|
||||
}
|
||||
}
|
||||
|
||||
/// The valid left-hand-side expressions of an assignment operator. Also called
|
||||
/// [`LeftHandSideExpression`][spec] in the spec.
|
||||
///
|
||||
/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum AssignTarget {
|
||||
/// A simple identifier, such as `a`.
|
||||
Identifier(Identifier),
|
||||
/// A property access, such as `a.prop`.
|
||||
Access(PropertyAccess),
|
||||
/// A pattern assignment, such as `{a, b, ...c}`.
|
||||
Pattern(Pattern),
|
||||
}
|
||||
|
||||
impl AssignTarget {
|
||||
/// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`].
|
||||
/// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression.
|
||||
#[must_use]
|
||||
pub fn from_expression(
|
||||
expression: &Expression,
|
||||
strict: bool,
|
||||
destructure: bool,
|
||||
) -> Option<Self> {
|
||||
match expression {
|
||||
Expression::Identifier(id) => Some(Self::Identifier(*id)),
|
||||
Expression::PropertyAccess(access) => Some(Self::Access(access.clone())),
|
||||
Expression::ObjectLiteral(object) if destructure => {
|
||||
let pattern = object.to_pattern(strict)?;
|
||||
Some(Self::Pattern(pattern.into()))
|
||||
}
|
||||
Expression::ArrayLiteral(array) if destructure => {
|
||||
let pattern = array.to_pattern(strict)?;
|
||||
Some(Self::Pattern(pattern.into()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for AssignTarget {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
match self {
|
||||
Self::Identifier(id) => id.to_interned_string(interner),
|
||||
Self::Access(access) => access.to_interned_string(interner),
|
||||
Self::Pattern(pattern) => pattern.to_interned_string(interner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Identifier> for AssignTarget {
|
||||
#[inline]
|
||||
fn from(target: Identifier) -> Self {
|
||||
Self::Identifier(target)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for AssignTarget {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Identifier(id) => visitor.visit_identifier(id),
|
||||
Self::Access(pa) => visitor.visit_property_access(pa),
|
||||
Self::Pattern(pat) => visitor.visit_pattern(pat),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Identifier(id) => visitor.visit_identifier_mut(id),
|
||||
Self::Access(pa) => visitor.visit_property_access_mut(pa),
|
||||
Self::Pattern(pat) => visitor.visit_pattern_mut(pat),
|
||||
}
|
||||
}
|
||||
}
|
||||
244
javascript-engine/external/boa/boa_ast/src/expression/operator/assign/op.rs
vendored
Normal file
244
javascript-engine/external/boa/boa_ast/src/expression/operator/assign/op.rs
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
/// An assignment operator assigns a value to its left operand based on the value of its right operand.
|
||||
///
|
||||
/// The simple assignment operator is equal (`=`), which assigns the value of its right operand to its
|
||||
/// left operand. That is, `x = y` assigns the value of `y to x`.
|
||||
///
|
||||
/// There are also compound assignment operators that are shorthand for the operations
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum AssignOp {
|
||||
/// The assignment operator assigns the value of the right operand to the left operand.
|
||||
///
|
||||
/// Syntax: `x = y`
|
||||
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment
|
||||
Assign,
|
||||
/// The addition assignment operator adds the value of the right operand to a variable and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x += y`
|
||||
///
|
||||
/// The types of the two operands determine the behavior of the addition assignment operator. Addition or concatenation is possible.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Addition_assignment
|
||||
Add,
|
||||
|
||||
/// The subtraction assignment operator subtracts the value of the right operand from a variable and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x -= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Subtraction_assignment
|
||||
Sub,
|
||||
|
||||
/// The multiplication assignment operator multiplies a variable by the value of the right operand and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x *= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Multiplication_assignment
|
||||
Mul,
|
||||
|
||||
/// The division assignment operator divides a variable by the value of the right operand and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x /= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Division_assignment
|
||||
Div,
|
||||
|
||||
/// The remainder assignment operator divides a variable by the value of the right operand and assigns the remainder to the variable.
|
||||
///
|
||||
/// Syntax: `x %= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Remainder_assignment
|
||||
Mod,
|
||||
|
||||
/// The exponentiation assignment operator raises the value of a variable to the power of the right operand.
|
||||
///
|
||||
/// Syntax: `x ** y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Exponentiation_assignment
|
||||
Exp,
|
||||
|
||||
/// The bitwise AND assignment operator uses the binary representation of both operands, does a bitwise AND operation on
|
||||
/// them and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x &= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_AND_assignment
|
||||
And,
|
||||
|
||||
/// The bitwise OR assignment operator uses the binary representation of both operands, does a bitwise OR operation on
|
||||
/// them and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x |= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_OR_assignment
|
||||
Or,
|
||||
|
||||
/// The bitwise XOR assignment operator uses the binary representation of both operands, does a bitwise XOR operation on
|
||||
/// them and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x ^= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_XOR_assignment
|
||||
Xor,
|
||||
|
||||
/// The left shift assignment operator moves the specified amount of bits to the left and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x <<= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Left_shift_assignment
|
||||
Shl,
|
||||
|
||||
/// The right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x >>= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Right_shift_assignment
|
||||
Shr,
|
||||
|
||||
/// The unsigned right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable.
|
||||
///
|
||||
/// Syntax: `x >>>= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift_assignment
|
||||
Ushr,
|
||||
|
||||
/// The logical and assignment operator only assigns if the target variable is truthy.
|
||||
///
|
||||
/// Syntax: `x &&= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND_assignment
|
||||
BoolAnd,
|
||||
|
||||
/// The logical or assignment operator only assigns if the target variable is falsy.
|
||||
///
|
||||
/// Syntax: `x ||= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment
|
||||
BoolOr,
|
||||
|
||||
/// The logical nullish assignment operator only assigns if the target variable is nullish (null or undefined).
|
||||
///
|
||||
/// Syntax: `x ??= y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_nullish_assignment
|
||||
Coalesce,
|
||||
}
|
||||
|
||||
impl AssignOp {
|
||||
/// Retrieves the operation as a static string.
|
||||
const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Assign => "=",
|
||||
Self::Add => "+=",
|
||||
Self::Sub => "-=",
|
||||
Self::Mul => "*=",
|
||||
Self::Exp => "**=",
|
||||
Self::Div => "/=",
|
||||
Self::Mod => "%=",
|
||||
Self::And => "&=",
|
||||
Self::Or => "|=",
|
||||
Self::Xor => "^=",
|
||||
Self::Shl => "<<=",
|
||||
Self::Shr => ">>=",
|
||||
Self::Ushr => ">>>=",
|
||||
Self::BoolAnd => "&&=",
|
||||
Self::BoolOr => "||=",
|
||||
Self::Coalesce => "??=",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AssignOp {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
111
javascript-engine/external/boa/boa_ast/src/expression/operator/binary/mod.rs
vendored
Normal file
111
javascript-engine/external/boa/boa_ast/src/expression/operator/binary/mod.rs
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
//! Binary expression nodes.
|
||||
//!
|
||||
//! A Binary expression comprises any operation between two expressions (excluding assignments),
|
||||
//! such as:
|
||||
//! - [Logic operations][logic] (`||`, `&&`).
|
||||
//! - [Relational math][relat] (`==`, `<`).
|
||||
//! - [Bit manipulation][bit] (`^`, `|`).
|
||||
//! - [Arithmetic][arith] (`+`, `%`).
|
||||
//! - The [comma operator][comma] (`,`)
|
||||
//!
|
||||
//! [logic]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#binary_logical_operators
|
||||
//! [relat]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#relational_operators
|
||||
//! [bit]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#binary_bitwise_operators
|
||||
//! [arith]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#arithmetic_operators
|
||||
//! [comma]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
|
||||
|
||||
mod op;
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
pub use op::*;
|
||||
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
|
||||
/// Binary operations require two operands, one before the operator and one after the operator.
|
||||
///
|
||||
/// See the [module level documentation][self] for more information.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Binary {
|
||||
op: BinaryOp,
|
||||
lhs: Box<Expression>,
|
||||
rhs: Box<Expression>,
|
||||
}
|
||||
|
||||
impl Binary {
|
||||
/// Creates a `BinOp` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(op: BinaryOp, lhs: Expression, rhs: Expression) -> Self {
|
||||
Self {
|
||||
op,
|
||||
lhs: Box::new(lhs),
|
||||
rhs: Box::new(rhs),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the binary operation of the Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn op(&self) -> BinaryOp {
|
||||
self.op
|
||||
}
|
||||
|
||||
/// Gets the left hand side of the binary operation.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn lhs(&self) -> &Expression {
|
||||
&self.lhs
|
||||
}
|
||||
|
||||
/// Gets the right hand side of the binary operation.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn rhs(&self) -> &Expression {
|
||||
&self.rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Binary {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!(
|
||||
"{} {} {}",
|
||||
self.lhs.to_interned_string(interner),
|
||||
self.op,
|
||||
self.rhs.to_interned_string(interner)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Binary> for Expression {
|
||||
#[inline]
|
||||
fn from(op: Binary) -> Self {
|
||||
Self::Binary(op)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Binary {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.lhs));
|
||||
visitor.visit_expression(&self.rhs)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.lhs));
|
||||
visitor.visit_expression_mut(&mut self.rhs)
|
||||
}
|
||||
}
|
||||
578
javascript-engine/external/boa/boa_ast/src/expression/operator/binary/op.rs
vendored
Normal file
578
javascript-engine/external/boa/boa_ast/src/expression/operator/binary/op.rs
vendored
Normal file
@@ -0,0 +1,578 @@
|
||||
//! This module implements various structure for logic handling.
|
||||
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
/// This represents a binary operation between two values.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum BinaryOp {
|
||||
/// Numeric operation.
|
||||
///
|
||||
/// see: [`NumOp`](enum.NumOp.html)
|
||||
Arithmetic(ArithmeticOp),
|
||||
|
||||
/// Bitwise operation.
|
||||
///
|
||||
/// see: [`BitOp`](enum.BitOp.html).
|
||||
Bitwise(BitwiseOp),
|
||||
|
||||
/// Comparative operation.
|
||||
///
|
||||
/// see: [`CompOp`](enum.CompOp.html).
|
||||
Relational(RelationalOp),
|
||||
|
||||
/// Logical operation.
|
||||
///
|
||||
/// see: [`LogOp`](enum.LogOp.html).
|
||||
Logical(LogicalOp),
|
||||
|
||||
/// Comma operation.
|
||||
Comma,
|
||||
}
|
||||
|
||||
impl From<ArithmeticOp> for BinaryOp {
|
||||
#[inline]
|
||||
fn from(op: ArithmeticOp) -> Self {
|
||||
Self::Arithmetic(op)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BitwiseOp> for BinaryOp {
|
||||
#[inline]
|
||||
fn from(op: BitwiseOp) -> Self {
|
||||
Self::Bitwise(op)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RelationalOp> for BinaryOp {
|
||||
#[inline]
|
||||
fn from(op: RelationalOp) -> Self {
|
||||
Self::Relational(op)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LogicalOp> for BinaryOp {
|
||||
#[inline]
|
||||
fn from(op: LogicalOp) -> Self {
|
||||
Self::Logical(op)
|
||||
}
|
||||
}
|
||||
|
||||
impl BinaryOp {
|
||||
/// Retrieves the operation as a static string.
|
||||
const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Arithmetic(ref op) => op.as_str(),
|
||||
Self::Bitwise(ref op) => op.as_str(),
|
||||
Self::Relational(ref op) => op.as_str(),
|
||||
Self::Logical(ref op) => op.as_str(),
|
||||
Self::Comma => ",",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BinaryOp {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Arithmetic operators take numerical values (either literals or variables)
|
||||
/// as their operands and return a single numerical value.
|
||||
///
|
||||
/// More information:
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ArithmeticOp {
|
||||
/// The addition operator produces the sum of numeric operands or string concatenation.
|
||||
///
|
||||
/// Syntax: `x + y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec].
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-addition-operator-plus
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Addition
|
||||
Add,
|
||||
|
||||
/// The subtraction operator subtracts the two operands, producing their difference.
|
||||
///
|
||||
/// Syntax: `x - y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec].
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-subtraction-operator-minus
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Subtraction
|
||||
Sub,
|
||||
|
||||
/// The division operator produces the quotient of its operands where the left operand
|
||||
/// is the dividend and the right operand is the divisor.
|
||||
///
|
||||
/// Syntax: `x / y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division
|
||||
Div,
|
||||
|
||||
/// The multiplication operator produces the product of the operands.
|
||||
///
|
||||
/// Syntax: `x * y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Multiplication
|
||||
Mul,
|
||||
|
||||
/// The exponentiation operator returns the result of raising the first operand to
|
||||
/// the power of the second operand.
|
||||
///
|
||||
/// Syntax: `x ** y`
|
||||
///
|
||||
/// The exponentiation operator is right-associative. a ** b ** c is equal to a ** (b ** c).
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-exp-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation
|
||||
Exp,
|
||||
|
||||
/// The remainder operator returns the remainder left over when one operand is divided by a second operand.
|
||||
///
|
||||
/// Syntax: `x % y`
|
||||
///
|
||||
/// The remainder operator always takes the sign of the dividend.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder
|
||||
Mod,
|
||||
}
|
||||
|
||||
impl ArithmeticOp {
|
||||
/// Retrieves the operation as a static string.
|
||||
const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Add => "+",
|
||||
Self::Sub => "-",
|
||||
Self::Div => "/",
|
||||
Self::Mul => "*",
|
||||
Self::Exp => "**",
|
||||
Self::Mod => "%",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ArithmeticOp {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// A bitwise operator is an operator used to perform bitwise operations
|
||||
/// on bit patterns or binary numerals that involve the manipulation of individual bits.
|
||||
///
|
||||
/// More information:
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum BitwiseOp {
|
||||
/// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1.
|
||||
///
|
||||
/// Syntax: `x & y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND
|
||||
And,
|
||||
|
||||
/// Performs the OR operation on each pair of bits. a OR b yields 1 if either a or b is 1.
|
||||
///
|
||||
/// Syntax: `x | y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR
|
||||
Or,
|
||||
|
||||
/// Performs the XOR operation on each pair of bits. a XOR b yields 1 if a and b are different.
|
||||
///
|
||||
/// Syntax: `x ^ y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR
|
||||
Xor,
|
||||
|
||||
/// This operator shifts the first operand the specified number of bits to the left.
|
||||
///
|
||||
/// Syntax: `x << y`
|
||||
///
|
||||
/// Excess bits shifted off to the left are discarded. Zero bits are shifted in from the right.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-left-shift-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Left_shift
|
||||
Shl,
|
||||
|
||||
/// This operator shifts the first operand the specified number of bits to the right.
|
||||
///
|
||||
/// Syntax: `x >> y`
|
||||
///
|
||||
/// Excess bits shifted off to the right are discarded. Copies of the leftmost bit
|
||||
/// are shifted in from the left. Since the new leftmost bit has the same value as
|
||||
/// the previous leftmost bit, the sign bit (the leftmost bit) does not change.
|
||||
/// Hence the name "sign-propagating".
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-signed-right-shift-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift
|
||||
Shr,
|
||||
|
||||
/// This operator shifts the first operand the specified number of bits to the right.
|
||||
///
|
||||
/// Syntax: `x >>> y`
|
||||
///
|
||||
/// Excess bits shifted off to the right are discarded. Zero bits are shifted in
|
||||
/// from the left. The sign bit becomes 0, so the result is always non-negative.
|
||||
/// Unlike the other bitwise operators, zero-fill right shift returns an unsigned 32-bit integer.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-unsigned-right-shift-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Unsigned_right_shift
|
||||
UShr,
|
||||
}
|
||||
|
||||
impl BitwiseOp {
|
||||
/// Retrieves the operation as a static string.
|
||||
const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::And => "&",
|
||||
Self::Or => "|",
|
||||
Self::Xor => "^",
|
||||
Self::Shl => "<<",
|
||||
Self::Shr => ">>",
|
||||
Self::UShr => ">>>",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BitwiseOp {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// A relational operator compares its operands and returns a logical value based on whether the relation is true.
|
||||
///
|
||||
/// The operands can be numerical, string, logical, or object values. Strings are compared based on standard
|
||||
/// lexicographical ordering, using Unicode values. In most cases, if the two operands are not of the same type,
|
||||
/// JavaScript attempts to convert them to an appropriate type for the comparison. This behavior generally results in
|
||||
/// comparing the operands numerically. The sole exceptions to type conversion within comparisons involve the `===` and `!==`
|
||||
/// operators, which perform strict equality and inequality comparisons. These operators do not attempt to convert the operands
|
||||
/// to compatible types before checking equality.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: tc39.es/ecma262/#sec-testing-and-comparison-operations
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Comparison
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RelationalOp {
|
||||
/// The equality operator converts the operands if they are not of the same type, then applies
|
||||
/// strict comparison.
|
||||
///
|
||||
/// Syntax: `y == y`
|
||||
///
|
||||
/// If both operands are objects, then JavaScript compares internal references which are equal
|
||||
/// when operands refer to the same object in memory.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-abstract-equality-comparison
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality
|
||||
Equal,
|
||||
|
||||
/// The inequality operator returns `true` if the operands are not equal.
|
||||
///
|
||||
/// Syntax: `x != y`
|
||||
///
|
||||
/// If the two operands are not of the same type, JavaScript attempts to convert the operands
|
||||
/// to an appropriate type for the comparison. If both operands are objects, then JavaScript
|
||||
/// compares internal references which are not equal when operands refer to different objects
|
||||
/// in memory.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Inequality
|
||||
NotEqual,
|
||||
|
||||
/// The identity operator returns `true` if the operands are strictly equal **with no type
|
||||
/// conversion**.
|
||||
///
|
||||
/// Syntax: `x === y`
|
||||
///
|
||||
/// Returns `true` if the operands are equal and of the same type.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-strict-equality-comparison
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity
|
||||
StrictEqual,
|
||||
|
||||
/// The non-identity operator returns `true` if the operands **are not equal and/or not of the
|
||||
/// same type**.
|
||||
///
|
||||
/// Syntax: `x !== y`
|
||||
///
|
||||
/// Returns `true` if the operands are of the same type but not equal, or are of different type.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Nonidentity>
|
||||
StrictNotEqual,
|
||||
|
||||
/// The greater than operator returns `true` if the left operand is greater than the right
|
||||
/// operand.
|
||||
///
|
||||
/// Syntax: `x > y`
|
||||
///
|
||||
/// Returns `true` if the left operand is greater than the right operand.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator
|
||||
GreaterThan,
|
||||
|
||||
/// The greater than or equal operator returns `true` if the left operand is greater than or
|
||||
/// equal to the right operand.
|
||||
///
|
||||
/// Syntax: `x >= y`
|
||||
///
|
||||
/// Returns `true` if the left operand is greater than the right operand.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator
|
||||
GreaterThanOrEqual,
|
||||
|
||||
/// The less than operator returns `true` if the left operand is less than the right operand.
|
||||
///
|
||||
/// Syntax: `x < y`
|
||||
///
|
||||
/// Returns `true` if the left operand is less than the right operand.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_operator
|
||||
LessThan,
|
||||
|
||||
/// The less than or equal operator returns `true` if the left operand is less than or equal to
|
||||
/// the right operand.
|
||||
///
|
||||
/// Syntax: `x <= y`
|
||||
///
|
||||
/// Returns `true` if the left operand is less than or equal to the right operand.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_or_equal_operator
|
||||
LessThanOrEqual,
|
||||
|
||||
/// The `in` operator returns `true` if the specified property is in the specified object or
|
||||
/// its prototype chain.
|
||||
///
|
||||
/// Syntax: `prop in object`
|
||||
///
|
||||
/// Returns `true` the specified property is in the specified object or its prototype chain.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in
|
||||
In,
|
||||
|
||||
/// The `instanceof` operator returns `true` if the specified object is an instance of the
|
||||
/// right hand side object.
|
||||
///
|
||||
/// Syntax: `obj instanceof Object`
|
||||
///
|
||||
/// Returns `true` the `prototype` property of the right hand side constructor appears anywhere
|
||||
/// in the prototype chain of the object.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof
|
||||
InstanceOf,
|
||||
}
|
||||
|
||||
impl RelationalOp {
|
||||
/// Retrieves the operation as a static string.
|
||||
const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Equal => "==",
|
||||
Self::NotEqual => "!=",
|
||||
Self::StrictEqual => "===",
|
||||
Self::StrictNotEqual => "!==",
|
||||
Self::GreaterThan => ">",
|
||||
Self::GreaterThanOrEqual => ">=",
|
||||
Self::LessThan => "<",
|
||||
Self::LessThanOrEqual => "<=",
|
||||
Self::In => "in",
|
||||
Self::InstanceOf => "instanceof",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RelationalOp {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Logical operators are typically used with Boolean (logical) values; when they are, they return a Boolean value.
|
||||
///
|
||||
/// However, the `&&` and `||` operators actually return the value of one of the specified operands,
|
||||
/// so if these operators are used with non-Boolean values, they may return a non-Boolean value.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-binary-logical-operators
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Logical
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum LogicalOp {
|
||||
/// The logical AND operator returns the value of the first operand if it can be coerced into `false`;
|
||||
/// otherwise, it returns the second operand.
|
||||
///
|
||||
/// Syntax: `x && y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-LogicalANDExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_AND
|
||||
And,
|
||||
|
||||
/// The logical OR operator returns the value the first operand if it can be coerced into `true`;
|
||||
/// otherwise, it returns the second operand.
|
||||
///
|
||||
/// Syntax: `x || y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-LogicalORExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR
|
||||
Or,
|
||||
|
||||
/// The nullish coalescing operator is a logical operator that returns the second operand
|
||||
/// when its first operand is null or undefined, and otherwise returns its first operand.
|
||||
///
|
||||
/// Syntax: `x ?? y`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-CoalesceExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
|
||||
Coalesce,
|
||||
}
|
||||
|
||||
impl LogicalOp {
|
||||
/// Retrieves the operation as a static string.
|
||||
const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::And => "&&",
|
||||
Self::Or => "||",
|
||||
Self::Coalesce => "??",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LogicalOp {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
103
javascript-engine/external/boa/boa_ast/src/expression/operator/conditional.rs
vendored
Normal file
103
javascript-engine/external/boa/boa_ast/src/expression/operator/conditional.rs
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The `conditional` (ternary) operation is the only ECMAScript operation that takes three
|
||||
/// operands.
|
||||
///
|
||||
/// This operation takes three operands: a condition followed by a question mark (`?`),
|
||||
/// then an expression to execute `if` the condition is truthy followed by a colon (`:`),
|
||||
/// and finally the expression to execute if the condition is `false`.
|
||||
/// This operator is frequently used as a shortcut for the `if` statement.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Conditional {
|
||||
condition: Box<Expression>,
|
||||
if_true: Box<Expression>,
|
||||
if_false: Box<Expression>,
|
||||
}
|
||||
|
||||
impl Conditional {
|
||||
/// Gets the condition of the `Conditional` expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn condition(&self) -> &Expression {
|
||||
&self.condition
|
||||
}
|
||||
|
||||
/// Gets the expression returned if `condition` is truthy.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn if_true(&self) -> &Expression {
|
||||
&self.if_true
|
||||
}
|
||||
|
||||
/// Gets the expression returned if `condition` is falsy.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn if_false(&self) -> &Expression {
|
||||
&self.if_false
|
||||
}
|
||||
|
||||
/// Creates a `Conditional` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(condition: Expression, if_true: Expression, if_false: Expression) -> Self {
|
||||
Self {
|
||||
condition: Box::new(condition),
|
||||
if_true: Box::new(if_true),
|
||||
if_false: Box::new(if_false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Conditional {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!(
|
||||
"{} ? {} : {}",
|
||||
self.condition().to_interned_string(interner),
|
||||
self.if_true().to_interned_string(interner),
|
||||
self.if_false().to_interned_string(interner)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Conditional> for Expression {
|
||||
#[inline]
|
||||
fn from(cond_op: Conditional) -> Self {
|
||||
Self::Conditional(cond_op)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Conditional {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.condition));
|
||||
try_break!(visitor.visit_expression(&self.if_true));
|
||||
visitor.visit_expression(&self.if_false)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.condition));
|
||||
try_break!(visitor.visit_expression_mut(&mut self.if_true));
|
||||
visitor.visit_expression_mut(&mut self.if_false)
|
||||
}
|
||||
}
|
||||
20
javascript-engine/external/boa/boa_ast/src/expression/operator/mod.rs
vendored
Normal file
20
javascript-engine/external/boa/boa_ast/src/expression/operator/mod.rs
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
//! Operator expression nodes.
|
||||
//!
|
||||
//! An [operator][op] expression is an expression that takes an operator (such as `+`, `typeof`, `+=`)
|
||||
//! and one or more expressions and returns an expression as a result.
|
||||
//! An operator expression can be any of the following:
|
||||
//!
|
||||
//! - A [`Unary`] expression.
|
||||
//! - An [`Assign`] expression.
|
||||
//! - A [`Binary`] expression.
|
||||
//! - A [`Conditional`] expression.
|
||||
//!
|
||||
//! [op]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators
|
||||
|
||||
mod conditional;
|
||||
|
||||
pub mod assign;
|
||||
pub mod binary;
|
||||
pub mod unary;
|
||||
|
||||
pub use self::{assign::Assign, binary::Binary, conditional::Conditional, unary::Unary};
|
||||
102
javascript-engine/external/boa/boa_ast/src/expression/operator/unary/mod.rs
vendored
Normal file
102
javascript-engine/external/boa/boa_ast/src/expression/operator/unary/mod.rs
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
//! Unary expression nodes.
|
||||
//!
|
||||
//! A Binary expression comprises any operation applied to a single expression. Some examples include:
|
||||
//!
|
||||
//! - [Increment and decrement operations][inc] (`++`, `--`).
|
||||
//! - The [`delete`][del] operator.
|
||||
//! - The [bitwise NOT][not] operator (`~`).
|
||||
//!
|
||||
//! The full list of valid unary operators is defined in [`UnaryOp`].
|
||||
//!
|
||||
//! [inc]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#increment_and_decrement
|
||||
//! [del]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete
|
||||
//! [not]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT
|
||||
mod op;
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
pub use op::*;
|
||||
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
|
||||
use crate::expression::Expression;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
|
||||
/// A unary expression is an operation with only one operand.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Unary {
|
||||
op: UnaryOp,
|
||||
target: Box<Expression>,
|
||||
}
|
||||
|
||||
impl Unary {
|
||||
/// Creates a new `UnaryOp` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(op: UnaryOp, target: Expression) -> Self {
|
||||
Self {
|
||||
op,
|
||||
target: Box::new(target),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the unary operation of the Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn op(&self) -> UnaryOp {
|
||||
self.op
|
||||
}
|
||||
|
||||
/// Gets the target of this unary operator.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn target(&self) -> &Expression {
|
||||
self.target.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Unary {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let space = match self.op {
|
||||
UnaryOp::TypeOf | UnaryOp::Delete | UnaryOp::Void => " ",
|
||||
_ => "",
|
||||
};
|
||||
format!(
|
||||
"{}{space}{}",
|
||||
self.op,
|
||||
self.target.to_interned_string(interner)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Unary> for Expression {
|
||||
#[inline]
|
||||
fn from(op: Unary) -> Self {
|
||||
Self::Unary(op)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Unary {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_expression(&self.target)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_expression_mut(&mut self.target)
|
||||
}
|
||||
}
|
||||
216
javascript-engine/external/boa/boa_ast/src/expression/operator/unary/op.rs
vendored
Normal file
216
javascript-engine/external/boa/boa_ast/src/expression/operator/unary/op.rs
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
/// A unary operator is one that takes a single operand/argument and performs an operation.
|
||||
///
|
||||
/// A unary operation is an operation with only one operand. This operand comes either
|
||||
/// before or after the operator. Unary operators are more efficient than standard JavaScript
|
||||
/// function calls.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum UnaryOp {
|
||||
/// The increment operator increments (adds one to) its operand and returns a value.
|
||||
///
|
||||
/// Syntax: `++x`
|
||||
///
|
||||
/// This operator increments and returns the value after incrementing.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-postfix-increment-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment
|
||||
IncrementPost,
|
||||
|
||||
/// The increment operator increments (adds one to) its operand and returns a value.
|
||||
///
|
||||
/// Syntax: `x++`
|
||||
///
|
||||
/// This operator increments and returns the value before incrementing.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-prefix-increment-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment
|
||||
IncrementPre,
|
||||
|
||||
/// The decrement operator decrements (subtracts one from) its operand and returns a value.
|
||||
///
|
||||
/// Syntax: `--x`
|
||||
///
|
||||
/// This operator decrements and returns the value before decrementing.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-postfix-decrement-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement
|
||||
DecrementPost,
|
||||
|
||||
/// The decrement operator decrements (subtracts one from) its operand and returns a value.
|
||||
///
|
||||
/// Syntax: `x--`
|
||||
///
|
||||
/// This operator decrements the operand and returns the value after decrementing.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-prefix-decrement-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement
|
||||
DecrementPre,
|
||||
|
||||
/// The unary negation operator precedes its operand and negates it.
|
||||
///
|
||||
/// Syntax: `-x`
|
||||
///
|
||||
/// Converts non-numbers data types to numbers like unary plus,
|
||||
/// however, it performs an additional operation, negation.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-unary-minus-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_negation
|
||||
Minus,
|
||||
|
||||
/// The unary plus operator attempts to convert the operand into a number, if it isn't already.
|
||||
///
|
||||
/// Syntax: `+x`
|
||||
///
|
||||
/// Although unary negation (`-`) also can convert non-numbers, unary plus is the fastest and preferred
|
||||
/// way of converting something into a number, because it does not perform any other operations on the number.
|
||||
/// It can convert `string` representations of integers and floats, as well as the non-string values `true`, `false`, and `null`.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-unary-plus-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus
|
||||
Plus,
|
||||
|
||||
/// Returns `false` if its single operand can be converted to `true`; otherwise, returns `true`.
|
||||
///
|
||||
/// Syntax: `!x`
|
||||
///
|
||||
/// Boolean values simply get inverted: `!true === false` and `!false === true`.
|
||||
/// Non-boolean values get converted to boolean values first, then are negated.
|
||||
/// This means that it is possible to use a couple of NOT operators in series to explicitly
|
||||
/// force the conversion of any value to the corresponding boolean primitive.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-logical-not-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_NOT
|
||||
Not,
|
||||
|
||||
/// Performs the NOT operator on each bit.
|
||||
///
|
||||
/// Syntax: `~x`
|
||||
///
|
||||
/// NOT `a` yields the inverted value (or one's complement) of `a`.
|
||||
/// Bitwise NOTing any number x yields -(x + 1). For example, ~-5 yields 4.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-bitwise-not-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT
|
||||
Tilde,
|
||||
|
||||
/// The `typeof` operator returns a string indicating the type of the unevaluated operand.
|
||||
///
|
||||
/// Syntax: `typeof x` or `typeof(x)`
|
||||
///
|
||||
/// The `typeof` is a JavaScript keyword that will return the type of a variable when you call it.
|
||||
/// You can use this to validate function parameters or check if variables are defined.
|
||||
/// There are other uses as well.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-typeof-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
|
||||
TypeOf,
|
||||
|
||||
/// The JavaScript `delete` operator removes a property from an object.
|
||||
///
|
||||
/// Syntax: `delete x`
|
||||
///
|
||||
/// Unlike what common belief suggests, the delete operator has nothing to do with
|
||||
/// directly freeing memory. Memory management is done indirectly via breaking references.
|
||||
/// If no more references to the same property are held, it is eventually released automatically.
|
||||
///
|
||||
/// The `delete` operator returns `true` for all cases except when the property is an
|
||||
/// [own](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty)
|
||||
/// [non-configurable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_delete)
|
||||
/// property, in which case, `false` is returned in non-strict mode.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-delete-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete
|
||||
Delete,
|
||||
|
||||
/// The `void` operator evaluates the given `expression` and then returns `undefined`.
|
||||
///
|
||||
/// Syntax: `void x`
|
||||
///
|
||||
/// This operator allows evaluating expressions that produce a value into places where an
|
||||
/// expression that evaluates to `undefined` is desired.
|
||||
/// The `void` operator is often used merely to obtain the `undefined` primitive value, usually using `void(0)`
|
||||
/// (which is equivalent to `void 0`). In these cases, the global variable undefined can be used.
|
||||
///
|
||||
/// When using an [immediately-invoked function expression](https://developer.mozilla.org/en-US/docs/Glossary/IIFE),
|
||||
/// `void` can be used to force the function keyword to be treated as an expression instead of a declaration.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-void-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
|
||||
Void,
|
||||
}
|
||||
|
||||
impl UnaryOp {
|
||||
/// Retrieves the operation as a static string.
|
||||
const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::IncrementPost | Self::IncrementPre => "++",
|
||||
Self::DecrementPost | Self::DecrementPre => "--",
|
||||
Self::Plus => "+",
|
||||
Self::Minus => "-",
|
||||
Self::Not => "!",
|
||||
Self::Tilde => "~",
|
||||
Self::Delete => "delete",
|
||||
Self::TypeOf => "typeof",
|
||||
Self::Void => "void",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UnaryOp {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
249
javascript-engine/external/boa/boa_ast/src/expression/optional.rs
vendored
Normal file
249
javascript-engine/external/boa/boa_ast/src/expression/optional.rs
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
use super::{access::PropertyAccessField, Expression};
|
||||
use crate::{
|
||||
function::PrivateName,
|
||||
join_nodes, try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// List of valid operations in an [`Optional`] chain.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum OptionalOperationKind {
|
||||
/// A property access (`a?.prop`).
|
||||
SimplePropertyAccess {
|
||||
/// The field accessed.
|
||||
field: PropertyAccessField,
|
||||
},
|
||||
/// A private property access (`a?.#prop`).
|
||||
PrivatePropertyAccess {
|
||||
/// The private property accessed.
|
||||
field: PrivateName,
|
||||
},
|
||||
/// A function call (`a?.(arg)`).
|
||||
Call {
|
||||
/// The args passed to the function call.
|
||||
args: Box<[Expression]>,
|
||||
},
|
||||
}
|
||||
|
||||
impl VisitWith for OptionalOperationKind {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::SimplePropertyAccess { field } => visitor.visit_property_access_field(field),
|
||||
Self::PrivatePropertyAccess { field } => visitor.visit_private_name(field),
|
||||
Self::Call { args } => {
|
||||
for arg in args.iter() {
|
||||
try_break!(visitor.visit_expression(arg));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::SimplePropertyAccess { field } => visitor.visit_property_access_field_mut(field),
|
||||
Self::PrivatePropertyAccess { field } => visitor.visit_private_name_mut(field),
|
||||
Self::Call { args } => {
|
||||
for arg in args.iter_mut() {
|
||||
try_break!(visitor.visit_expression_mut(arg));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Operation within an [`Optional`] chain.
|
||||
///
|
||||
/// An operation within an `Optional` chain can be either shorted or non-shorted. A shorted operation
|
||||
/// (`?.item`) will force the expression to return `undefined` if the target is `undefined` or `null`.
|
||||
/// In contrast, a non-shorted operation (`.prop`) will try to access the property, even if the target
|
||||
/// is `undefined` or `null`.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct OptionalOperation {
|
||||
kind: OptionalOperationKind,
|
||||
shorted: bool,
|
||||
}
|
||||
|
||||
impl OptionalOperation {
|
||||
/// Creates a new `OptionalOperation`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(kind: OptionalOperationKind, shorted: bool) -> Self {
|
||||
Self { kind, shorted }
|
||||
}
|
||||
/// Gets the kind of operation.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn kind(&self) -> &OptionalOperationKind {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
/// Returns `true` if the operation short-circuits the [`Optional`] chain when the target is
|
||||
/// `undefined` or `null`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn shorted(&self) -> bool {
|
||||
self.shorted
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for OptionalOperation {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let mut buf = if self.shorted {
|
||||
String::from("?.")
|
||||
} else {
|
||||
if let OptionalOperationKind::SimplePropertyAccess {
|
||||
field: PropertyAccessField::Const(name),
|
||||
} = &self.kind
|
||||
{
|
||||
return format!(".{}", interner.resolve_expect(*name));
|
||||
}
|
||||
|
||||
if let OptionalOperationKind::PrivatePropertyAccess { field } = &self.kind {
|
||||
return format!(".#{}", interner.resolve_expect(field.description()));
|
||||
}
|
||||
|
||||
String::new()
|
||||
};
|
||||
buf.push_str(&match &self.kind {
|
||||
OptionalOperationKind::SimplePropertyAccess { field } => match field {
|
||||
PropertyAccessField::Const(name) => interner.resolve_expect(*name).to_string(),
|
||||
PropertyAccessField::Expr(expr) => {
|
||||
format!("[{}]", expr.to_interned_string(interner))
|
||||
}
|
||||
},
|
||||
OptionalOperationKind::PrivatePropertyAccess { field } => {
|
||||
format!("#{}", interner.resolve_expect(field.description()))
|
||||
}
|
||||
OptionalOperationKind::Call { args } => format!("({})", join_nodes(interner, args)),
|
||||
});
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for OptionalOperation {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_optional_operation_kind(&self.kind)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_optional_operation_kind_mut(&mut self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
/// An optional chain expression, as defined by the [spec].
|
||||
///
|
||||
/// [Optional chaining][mdn] allows for short-circuiting property accesses and function calls, which
|
||||
/// will return `undefined` instead of returning an error if the access target or the call is
|
||||
/// either `undefined` or `null`.
|
||||
///
|
||||
/// An example of optional chaining:
|
||||
///
|
||||
/// ```Javascript
|
||||
/// const adventurer = {
|
||||
/// name: 'Alice',
|
||||
/// cat: {
|
||||
/// name: 'Dinah'
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// console.log(adventurer.cat?.name); // Dinah
|
||||
/// console.log(adventurer.dog?.name); // undefined
|
||||
/// ```
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-OptionalExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Optional {
|
||||
target: Box<Expression>,
|
||||
chain: Box<[OptionalOperation]>,
|
||||
}
|
||||
|
||||
impl VisitWith for Optional {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.target));
|
||||
for op in self.chain.iter() {
|
||||
try_break!(visitor.visit_optional_operation(op));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.target));
|
||||
for op in self.chain.iter_mut() {
|
||||
try_break!(visitor.visit_optional_operation_mut(op));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Optional {
|
||||
/// Creates a new `Optional` expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(target: Expression, chain: Box<[OptionalOperation]>) -> Self {
|
||||
Self {
|
||||
target: Box::new(target),
|
||||
chain,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the target of this `Optional` expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn target(&self) -> &Expression {
|
||||
self.target.as_ref()
|
||||
}
|
||||
|
||||
/// Gets the chain of accesses and calls that will be applied to the target at runtime.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn chain(&self) -> &[OptionalOperation] {
|
||||
self.chain.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Optional> for Expression {
|
||||
fn from(opt: Optional) -> Self {
|
||||
Self::Optional(opt)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Optional {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let mut buf = self.target.to_interned_string(interner);
|
||||
|
||||
for item in &*self.chain {
|
||||
buf.push_str(&item.to_interned_string(interner));
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
78
javascript-engine/external/boa/boa_ast/src/expression/spread.rs
vendored
Normal file
78
javascript-engine/external/boa/boa_ast/src/expression/spread.rs
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
|
||||
use super::Expression;
|
||||
|
||||
/// The `spread` operator allows an iterable such as an array expression or string to be
|
||||
/// expanded.
|
||||
///
|
||||
/// Syntax: `...x`
|
||||
///
|
||||
/// It expands array expressions or strings in places where zero or more arguments (for
|
||||
/// function calls) or elements (for array literals)
|
||||
/// are expected, or an object expression to be expanded in places where zero or more key-value
|
||||
/// pairs (for object literals) are expected.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-SpreadElement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Spread {
|
||||
target: Box<Expression>,
|
||||
}
|
||||
|
||||
impl Spread {
|
||||
/// Gets the target expression to be expanded by the spread operator.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn target(&self) -> &Expression {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Creates a `Spread` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(target: Expression) -> Self {
|
||||
Self {
|
||||
target: Box::new(target),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Spread {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!("...{}", self.target().to_interned_string(interner))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Spread> for Expression {
|
||||
#[inline]
|
||||
fn from(spread: Spread) -> Self {
|
||||
Self::Spread(spread)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Spread {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_expression(&self.target)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_expression_mut(&mut self.target)
|
||||
}
|
||||
}
|
||||
132
javascript-engine/external/boa/boa_ast/src/expression/tagged_template.rs
vendored
Normal file
132
javascript-engine/external/boa/boa_ast/src/expression/tagged_template.rs
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
use boa_interner::{Interner, Sym, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
|
||||
use super::Expression;
|
||||
|
||||
/// A [`TaggedTemplate`][moz] expression, as defined by the [spec].
|
||||
///
|
||||
/// `TaggedTemplate`s are a type of template literals that are parsed by a custom function to generate
|
||||
/// arbitrary objects from the inner strings and expressions.
|
||||
///
|
||||
/// [moz]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-tagged-templates
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TaggedTemplate {
|
||||
tag: Box<Expression>,
|
||||
raws: Box<[Sym]>,
|
||||
cookeds: Box<[Option<Sym>]>,
|
||||
exprs: Box<[Expression]>,
|
||||
}
|
||||
|
||||
impl TaggedTemplate {
|
||||
/// Creates a new tagged template with a tag, the list of raw strings, the cooked strings and
|
||||
/// the expressions.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
tag: Expression,
|
||||
raws: Box<[Sym]>,
|
||||
cookeds: Box<[Option<Sym>]>,
|
||||
exprs: Box<[Expression]>,
|
||||
) -> Self {
|
||||
Self {
|
||||
tag: tag.into(),
|
||||
raws,
|
||||
cookeds,
|
||||
exprs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the tag function of the template.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn tag(&self) -> &Expression {
|
||||
&self.tag
|
||||
}
|
||||
|
||||
/// Gets the inner raw strings of the template.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn raws(&self) -> &[Sym] {
|
||||
&self.raws
|
||||
}
|
||||
|
||||
/// Gets the cooked strings of the template.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn cookeds(&self) -> &[Option<Sym>] {
|
||||
&self.cookeds
|
||||
}
|
||||
|
||||
/// Gets the interpolated expressions of the template.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn exprs(&self) -> &[Expression] {
|
||||
&self.exprs
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for TaggedTemplate {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let mut buf = format!("{}`", self.tag.to_interned_string(interner));
|
||||
for (&raw, expr) in self.raws.iter().zip(self.exprs.iter()) {
|
||||
buf.push_str(&format!(
|
||||
"{}${{{}}}",
|
||||
interner.resolve_expect(raw),
|
||||
expr.to_interned_string(interner)
|
||||
));
|
||||
}
|
||||
buf.push('`');
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TaggedTemplate> for Expression {
|
||||
#[inline]
|
||||
fn from(template: TaggedTemplate) -> Self {
|
||||
Self::TaggedTemplate(template)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for TaggedTemplate {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.tag));
|
||||
for raw in self.raws.iter() {
|
||||
try_break!(visitor.visit_sym(raw));
|
||||
}
|
||||
for cooked in self.cookeds.iter().flatten() {
|
||||
try_break!(visitor.visit_sym(cooked));
|
||||
}
|
||||
for expr in self.exprs.iter() {
|
||||
try_break!(visitor.visit_expression(expr));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.tag));
|
||||
for raw in self.raws.iter_mut() {
|
||||
try_break!(visitor.visit_sym_mut(raw));
|
||||
}
|
||||
for cooked in self.cookeds.iter_mut().flatten() {
|
||||
try_break!(visitor.visit_sym_mut(cooked));
|
||||
}
|
||||
for expr in self.exprs.iter_mut() {
|
||||
try_break!(visitor.visit_expression_mut(expr));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
90
javascript-engine/external/boa/boa_ast/src/expression/yield.rs
vendored
Normal file
90
javascript-engine/external/boa/boa_ast/src/expression/yield.rs
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
|
||||
use super::Expression;
|
||||
|
||||
/// The `yield` keyword is used to pause and resume a generator function
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-YieldExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Yield {
|
||||
target: Option<Box<Expression>>,
|
||||
delegate: bool,
|
||||
}
|
||||
|
||||
impl Yield {
|
||||
/// Gets the target expression of this `Yield` statement.
|
||||
#[inline]
|
||||
pub fn target(&self) -> Option<&Expression> {
|
||||
self.target.as_ref().map(Box::as_ref)
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Yield` statement delegates to another generator or iterable object.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn delegate(&self) -> bool {
|
||||
self.delegate
|
||||
}
|
||||
|
||||
/// Creates a `Yield` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(expr: Option<Expression>, delegate: bool) -> Self {
|
||||
Self {
|
||||
target: expr.map(Box::new),
|
||||
delegate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Yield> for Expression {
|
||||
#[inline]
|
||||
fn from(r#yield: Yield) -> Self {
|
||||
Self::Yield(r#yield)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Yield {
|
||||
#[inline]
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let y = if self.delegate { "yield*" } else { "yield" };
|
||||
if let Some(ex) = self.target() {
|
||||
format!("{y} {}", ex.to_interned_string(interner))
|
||||
} else {
|
||||
y.to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Yield {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(expr) = &self.target {
|
||||
visitor.visit_expression(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(expr) = &mut self.target {
|
||||
visitor.visit_expression_mut(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
118
javascript-engine/external/boa/boa_ast/src/function/arrow_function.rs
vendored
Normal file
118
javascript-engine/external/boa/boa_ast/src/function/arrow_function.rs
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{
|
||||
expression::{Expression, Identifier},
|
||||
join_nodes, StatementList,
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use super::FormalParameterList;
|
||||
|
||||
/// An arrow function expression, as defined by the [spec].
|
||||
///
|
||||
/// An [arrow function][mdn] expression is a syntactically compact alternative to a regular function
|
||||
/// expression. Arrow function expressions are ill suited as methods, and they cannot be used as
|
||||
/// constructors. Arrow functions cannot be used as constructors and will throw an error when
|
||||
/// used with new.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ArrowFunction {
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
}
|
||||
|
||||
impl ArrowFunction {
|
||||
/// Creates a new `ArrowFunctionDecl` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
name: Option<Identifier>,
|
||||
params: FormalParameterList,
|
||||
body: StatementList,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
parameters: params,
|
||||
body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the name of the function declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> Option<Identifier> {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Sets the name of the function declaration.
|
||||
#[inline]
|
||||
pub fn set_name(&mut self, name: Option<Identifier>) {
|
||||
self.name = name;
|
||||
}
|
||||
|
||||
/// Gets the list of parameters of the arrow function.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn parameters(&self) -> &FormalParameterList {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
/// Gets the body of the arrow function.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &StatementList {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for ArrowFunction {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = format!("({}", join_nodes(interner, self.parameters.as_ref()));
|
||||
if self.body().statements().is_empty() {
|
||||
buf.push_str(") => {}");
|
||||
} else {
|
||||
buf.push_str(&format!(
|
||||
") => {{\n{}{}}}",
|
||||
self.body.to_indented_string(interner, indentation + 1),
|
||||
" ".repeat(indentation)
|
||||
));
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArrowFunction> for Expression {
|
||||
fn from(decl: ArrowFunction) -> Self {
|
||||
Self::ArrowFunction(decl)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ArrowFunction {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(ident) = &self.name {
|
||||
try_break!(visitor.visit_identifier(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
|
||||
visitor.visit_statement_list(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(ident) = &mut self.name {
|
||||
try_break!(visitor.visit_identifier_mut(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
|
||||
visitor.visit_statement_list_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
118
javascript-engine/external/boa/boa_ast/src/function/async_arrow_function.rs
vendored
Normal file
118
javascript-engine/external/boa/boa_ast/src/function/async_arrow_function.rs
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use super::FormalParameterList;
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{
|
||||
expression::{Expression, Identifier},
|
||||
join_nodes, StatementList,
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString};
|
||||
|
||||
/// An async arrow function expression, as defined by the [spec].
|
||||
///
|
||||
/// An [async arrow function][mdn] expression is a syntactically compact alternative to a regular function
|
||||
/// expression. Arrow function expressions are ill suited as methods, and they cannot be used as
|
||||
/// constructors. Arrow functions cannot be used as constructors and will throw an error when
|
||||
/// used with new.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AsyncArrowFunction {
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
}
|
||||
|
||||
impl AsyncArrowFunction {
|
||||
/// Creates a new `AsyncArrowFunction` AST Expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
parameters,
|
||||
body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the name of the function declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> Option<Identifier> {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Sets the name of the function declaration.
|
||||
#[inline]
|
||||
pub fn set_name(&mut self, name: Option<Identifier>) {
|
||||
self.name = name;
|
||||
}
|
||||
|
||||
/// Gets the list of parameters of the arrow function.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn parameters(&self) -> &FormalParameterList {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
/// Gets the body of the arrow function.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &StatementList {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for AsyncArrowFunction {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = format!("async ({}", join_nodes(interner, self.parameters.as_ref()));
|
||||
if self.body().statements().is_empty() {
|
||||
buf.push_str(") => {}");
|
||||
} else {
|
||||
buf.push_str(&format!(
|
||||
") => {{\n{}{}}}",
|
||||
self.body.to_indented_string(interner, indentation + 1),
|
||||
" ".repeat(indentation)
|
||||
));
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AsyncArrowFunction> for Expression {
|
||||
fn from(decl: AsyncArrowFunction) -> Self {
|
||||
Self::AsyncArrowFunction(decl)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for AsyncArrowFunction {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(ident) = &self.name {
|
||||
try_break!(visitor.visit_identifier(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
|
||||
visitor.visit_statement_list(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(ident) = &mut self.name {
|
||||
try_break!(visitor.visit_identifier_mut(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
|
||||
visitor.visit_statement_list_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
138
javascript-engine/external/boa/boa_ast/src/function/async_function.rs
vendored
Normal file
138
javascript-engine/external/boa/boa_ast/src/function/async_function.rs
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
//! Async Function Expression.
|
||||
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{
|
||||
expression::{Expression, Identifier},
|
||||
join_nodes, Declaration, StatementList,
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use super::FormalParameterList;
|
||||
|
||||
/// An async function definition, as defined by the [spec].
|
||||
///
|
||||
/// An [async function][mdn] is a function where await expressions are allowed within it.
|
||||
/// The async and await keywords enable asynchronous programming on Javascript without the use
|
||||
/// of promise chains.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-async-function-definitions
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AsyncFunction {
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
has_binding_identifier: bool,
|
||||
}
|
||||
|
||||
impl AsyncFunction {
|
||||
/// Creates a new function expression
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
has_binding_identifier: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
parameters,
|
||||
body,
|
||||
has_binding_identifier,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the name of the function declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> Option<Identifier> {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Gets the list of parameters of the function declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn parameters(&self) -> &FormalParameterList {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
/// Gets the body of the function declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &StatementList {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Returns whether the function expression has a binding identifier.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn has_binding_identifier(&self) -> bool {
|
||||
self.has_binding_identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for AsyncFunction {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = "async function".to_owned();
|
||||
if let Some(name) = self.name {
|
||||
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
|
||||
}
|
||||
buf.push_str(&format!(
|
||||
"({}",
|
||||
join_nodes(interner, self.parameters.as_ref())
|
||||
));
|
||||
if self.body().statements().is_empty() {
|
||||
buf.push_str(") {}");
|
||||
} else {
|
||||
buf.push_str(&format!(
|
||||
") {{\n{}{}}}",
|
||||
self.body.to_indented_string(interner, indentation + 1),
|
||||
" ".repeat(indentation)
|
||||
));
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AsyncFunction> for Expression {
|
||||
#[inline]
|
||||
fn from(expr: AsyncFunction) -> Self {
|
||||
Self::AsyncFunction(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AsyncFunction> for Declaration {
|
||||
#[inline]
|
||||
fn from(f: AsyncFunction) -> Self {
|
||||
Self::AsyncFunction(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for AsyncFunction {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(ident) = &self.name {
|
||||
try_break!(visitor.visit_identifier(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
|
||||
visitor.visit_statement_list(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(ident) = &mut self.name {
|
||||
try_break!(visitor.visit_identifier_mut(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
|
||||
visitor.visit_statement_list_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
130
javascript-engine/external/boa/boa_ast/src/function/async_generator.rs
vendored
Normal file
130
javascript-engine/external/boa/boa_ast/src/function/async_generator.rs
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
//! Async Generator Expression
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{
|
||||
block_to_string,
|
||||
expression::{Expression, Identifier},
|
||||
join_nodes, Declaration, StatementList,
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use super::FormalParameterList;
|
||||
|
||||
/// An async generator definition, as defined by the [spec].
|
||||
///
|
||||
/// An [async generator][mdn] combines async functions with generators, making it possible to use
|
||||
/// `await` and `yield` expressions within the definition of the function.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-async-generator-function-definitions
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AsyncGenerator {
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
has_binding_identifier: bool,
|
||||
}
|
||||
|
||||
impl AsyncGenerator {
|
||||
/// Creates a new async generator expression
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
has_binding_identifier: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
parameters,
|
||||
body,
|
||||
has_binding_identifier,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the name of the async generator expression
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> Option<Identifier> {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Gets the list of parameters of the async generator expression
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn parameters(&self) -> &FormalParameterList {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
/// Gets the body of the async generator expression
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &StatementList {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Returns whether the function expression has a binding identifier.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn has_binding_identifier(&self) -> bool {
|
||||
self.has_binding_identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for AsyncGenerator {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = "async function*".to_owned();
|
||||
if let Some(name) = self.name {
|
||||
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
|
||||
}
|
||||
buf.push_str(&format!(
|
||||
"({}) {}",
|
||||
join_nodes(interner, self.parameters.as_ref()),
|
||||
block_to_string(&self.body, interner, indentation)
|
||||
));
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AsyncGenerator> for Expression {
|
||||
#[inline]
|
||||
fn from(expr: AsyncGenerator) -> Self {
|
||||
Self::AsyncGenerator(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AsyncGenerator> for Declaration {
|
||||
#[inline]
|
||||
fn from(f: AsyncGenerator) -> Self {
|
||||
Self::AsyncGenerator(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for AsyncGenerator {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(ident) = &self.name {
|
||||
try_break!(visitor.visit_identifier(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
|
||||
visitor.visit_statement_list(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(ident) = &mut self.name {
|
||||
try_break!(visitor.visit_identifier_mut(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
|
||||
visitor.visit_statement_list_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
579
javascript-engine/external/boa/boa_ast/src/function/class.rs
vendored
Normal file
579
javascript-engine/external/boa/boa_ast/src/function/class.rs
vendored
Normal file
@@ -0,0 +1,579 @@
|
||||
use super::Function;
|
||||
use crate::{
|
||||
block_to_string,
|
||||
expression::{Expression, Identifier},
|
||||
join_nodes,
|
||||
property::{MethodDefinition, PropertyName},
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
Declaration, StatementList, ToStringEscaped,
|
||||
};
|
||||
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
use std::borrow::Cow;
|
||||
use std::hash::Hash;
|
||||
|
||||
/// A class declaration, as defined by the [spec].
|
||||
///
|
||||
/// A [class][mdn] declaration defines a class with the specified methods, fields, and optional constructor.
|
||||
/// Classes can be used to create objects, which can also be created through literals (using `{}`).
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-class-definitions
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Class {
|
||||
name: Option<Identifier>,
|
||||
super_ref: Option<Expression>,
|
||||
pub(crate) constructor: Option<Function>,
|
||||
pub(crate) elements: Box<[ClassElement]>,
|
||||
has_binding_identifier: bool,
|
||||
}
|
||||
|
||||
impl Class {
|
||||
/// Creates a new class declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
name: Option<Identifier>,
|
||||
super_ref: Option<Expression>,
|
||||
constructor: Option<Function>,
|
||||
elements: Box<[ClassElement]>,
|
||||
has_binding_identifier: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
super_ref,
|
||||
constructor,
|
||||
elements,
|
||||
has_binding_identifier,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the name of the class.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> Option<Identifier> {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Returns the super class ref of the class.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn super_ref(&self) -> Option<&Expression> {
|
||||
self.super_ref.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the constructor of the class.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn constructor(&self) -> Option<&Function> {
|
||||
self.constructor.as_ref()
|
||||
}
|
||||
|
||||
/// Gets the list of all fields defined on the class.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn elements(&self) -> &[ClassElement] {
|
||||
&self.elements
|
||||
}
|
||||
|
||||
/// Returns whether the class has a binding identifier.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn has_binding_identifier(&self) -> bool {
|
||||
self.has_binding_identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Class {
|
||||
fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
|
||||
let class_name = self.name.map_or(Cow::Borrowed(""), |s| {
|
||||
interner.resolve_expect(s.sym()).join(
|
||||
Cow::Borrowed,
|
||||
|utf16| Cow::Owned(utf16.to_string_escaped()),
|
||||
true,
|
||||
)
|
||||
});
|
||||
if self.elements.is_empty() && self.constructor().is_none() {
|
||||
return format!(
|
||||
"class {class_name}{} {{}}",
|
||||
self.super_ref
|
||||
.as_ref()
|
||||
.map_or_else(String::new, |sup| format!(
|
||||
" extends {}",
|
||||
sup.to_interned_string(interner)
|
||||
))
|
||||
);
|
||||
}
|
||||
let indentation = " ".repeat(indent_n + 1);
|
||||
let mut buf = format!(
|
||||
"class {class_name}{} {{\n",
|
||||
self.super_ref
|
||||
.as_ref()
|
||||
.map_or_else(String::new, |sup| format!(
|
||||
"extends {}",
|
||||
sup.to_interned_string(interner)
|
||||
))
|
||||
);
|
||||
if let Some(expr) = &self.constructor {
|
||||
buf.push_str(&format!(
|
||||
"{indentation}constructor({}) {}\n",
|
||||
join_nodes(interner, expr.parameters().as_ref()),
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
));
|
||||
}
|
||||
for element in self.elements.iter() {
|
||||
buf.push_str(&match element {
|
||||
ClassElement::MethodDefinition(name, method) => {
|
||||
format!(
|
||||
"{indentation}{}{}({}) {}\n",
|
||||
match &method {
|
||||
MethodDefinition::Get(_) => "get ",
|
||||
MethodDefinition::Set(_) => "set ",
|
||||
_ => "",
|
||||
},
|
||||
name.to_interned_string(interner),
|
||||
match &method {
|
||||
MethodDefinition::Get(expr)
|
||||
| MethodDefinition::Set(expr)
|
||||
| MethodDefinition::Ordinary(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Generator(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Async(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
},
|
||||
match &method {
|
||||
MethodDefinition::Get(expr)
|
||||
| MethodDefinition::Set(expr)
|
||||
| MethodDefinition::Ordinary(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Generator(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Async(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
ClassElement::StaticMethodDefinition(name, method) => {
|
||||
format!(
|
||||
"{indentation}static {}{}({}) {}\n",
|
||||
match &method {
|
||||
MethodDefinition::Get(_) => "get ",
|
||||
MethodDefinition::Set(_) => "set ",
|
||||
_ => "",
|
||||
},
|
||||
name.to_interned_string(interner),
|
||||
match &method {
|
||||
MethodDefinition::Get(expr)
|
||||
| MethodDefinition::Set(expr)
|
||||
| MethodDefinition::Ordinary(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Generator(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Async(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
},
|
||||
match &method {
|
||||
MethodDefinition::Get(expr)
|
||||
| MethodDefinition::Set(expr)
|
||||
| MethodDefinition::Ordinary(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Generator(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Async(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
ClassElement::FieldDefinition(name, field) => match field {
|
||||
Some(expr) => {
|
||||
format!(
|
||||
"{indentation}{} = {};\n",
|
||||
name.to_interned_string(interner),
|
||||
expr.to_no_indent_string(interner, indent_n + 1)
|
||||
)
|
||||
}
|
||||
None => {
|
||||
format!("{indentation}{};\n", name.to_interned_string(interner),)
|
||||
}
|
||||
},
|
||||
ClassElement::StaticFieldDefinition(name, field) => match field {
|
||||
Some(expr) => {
|
||||
format!(
|
||||
"{indentation}static {} = {};\n",
|
||||
name.to_interned_string(interner),
|
||||
expr.to_no_indent_string(interner, indent_n + 1)
|
||||
)
|
||||
}
|
||||
None => {
|
||||
format!(
|
||||
"{indentation}static {};\n",
|
||||
name.to_interned_string(interner),
|
||||
)
|
||||
}
|
||||
},
|
||||
ClassElement::PrivateMethodDefinition(name, method) => {
|
||||
format!(
|
||||
"{indentation}{}#{}({}) {}\n",
|
||||
match &method {
|
||||
MethodDefinition::Get(_) => "get ",
|
||||
MethodDefinition::Set(_) => "set ",
|
||||
_ => "",
|
||||
},
|
||||
interner.resolve_expect(name.description()),
|
||||
match &method {
|
||||
MethodDefinition::Get(expr)
|
||||
| MethodDefinition::Set(expr)
|
||||
| MethodDefinition::Ordinary(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Generator(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Async(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
},
|
||||
match &method {
|
||||
MethodDefinition::Get(expr)
|
||||
| MethodDefinition::Set(expr)
|
||||
| MethodDefinition::Ordinary(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Generator(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Async(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
ClassElement::PrivateStaticMethodDefinition(name, method) => {
|
||||
format!(
|
||||
"{indentation}static {}#{}({}) {}\n",
|
||||
match &method {
|
||||
MethodDefinition::Get(_) => "get ",
|
||||
MethodDefinition::Set(_) => "set ",
|
||||
_ => "",
|
||||
},
|
||||
interner.resolve_expect(name.description()),
|
||||
match &method {
|
||||
MethodDefinition::Get(expr)
|
||||
| MethodDefinition::Set(expr)
|
||||
| MethodDefinition::Ordinary(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Generator(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
MethodDefinition::Async(expr) => {
|
||||
join_nodes(interner, expr.parameters().as_ref())
|
||||
}
|
||||
},
|
||||
match &method {
|
||||
MethodDefinition::Get(expr)
|
||||
| MethodDefinition::Set(expr)
|
||||
| MethodDefinition::Ordinary(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Generator(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::AsyncGenerator(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
MethodDefinition::Async(expr) => {
|
||||
block_to_string(expr.body(), interner, indent_n + 1)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
ClassElement::PrivateFieldDefinition(name, field) => match field {
|
||||
Some(expr) => {
|
||||
format!(
|
||||
"{indentation}#{} = {};\n",
|
||||
interner.resolve_expect(name.description()),
|
||||
expr.to_no_indent_string(interner, indent_n + 1)
|
||||
)
|
||||
}
|
||||
None => {
|
||||
format!(
|
||||
"{indentation}#{};\n",
|
||||
interner.resolve_expect(name.description()),
|
||||
)
|
||||
}
|
||||
},
|
||||
ClassElement::PrivateStaticFieldDefinition(name, field) => match field {
|
||||
Some(expr) => {
|
||||
format!(
|
||||
"{indentation}static #{} = {};\n",
|
||||
interner.resolve_expect(name.description()),
|
||||
expr.to_no_indent_string(interner, indent_n + 1)
|
||||
)
|
||||
}
|
||||
None => {
|
||||
format!(
|
||||
"{indentation}static #{};\n",
|
||||
interner.resolve_expect(name.description()),
|
||||
)
|
||||
}
|
||||
},
|
||||
ClassElement::StaticBlock(statement_list) => {
|
||||
format!(
|
||||
"{indentation}static {}\n",
|
||||
block_to_string(statement_list, interner, indent_n + 1)
|
||||
)
|
||||
}
|
||||
});
|
||||
}
|
||||
buf.push('}');
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Class> for Expression {
|
||||
fn from(expr: Class) -> Self {
|
||||
Self::Class(Box::new(expr))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Class> for Declaration {
|
||||
fn from(f: Class) -> Self {
|
||||
Self::Class(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Class {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(ident) = &self.name {
|
||||
try_break!(visitor.visit_identifier(ident));
|
||||
}
|
||||
if let Some(expr) = &self.super_ref {
|
||||
try_break!(visitor.visit_expression(expr));
|
||||
}
|
||||
if let Some(func) = &self.constructor {
|
||||
try_break!(visitor.visit_function(func));
|
||||
}
|
||||
for elem in self.elements.iter() {
|
||||
try_break!(visitor.visit_class_element(elem));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(ident) = &mut self.name {
|
||||
try_break!(visitor.visit_identifier_mut(ident));
|
||||
}
|
||||
if let Some(expr) = &mut self.super_ref {
|
||||
try_break!(visitor.visit_expression_mut(expr));
|
||||
}
|
||||
if let Some(func) = &mut self.constructor {
|
||||
try_break!(visitor.visit_function_mut(func));
|
||||
}
|
||||
for elem in self.elements.iter_mut() {
|
||||
try_break!(visitor.visit_class_element_mut(elem));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An element that can be within a [`Class`], as defined by the [spec].
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ClassElement
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ClassElement {
|
||||
/// A method definition, including `get` and `set` accessors.
|
||||
MethodDefinition(PropertyName, MethodDefinition),
|
||||
|
||||
/// A static method definition, accessible from the class constructor object.
|
||||
StaticMethodDefinition(PropertyName, MethodDefinition),
|
||||
|
||||
/// A field definition.
|
||||
FieldDefinition(PropertyName, Option<Expression>),
|
||||
|
||||
/// A static field definition, accessible from the class constructor object
|
||||
StaticFieldDefinition(PropertyName, Option<Expression>),
|
||||
|
||||
/// A private method definition, only accessible inside the class declaration.
|
||||
PrivateMethodDefinition(PrivateName, MethodDefinition),
|
||||
|
||||
/// A private static method definition, only accessible from static methods and fields inside
|
||||
/// the class declaration.
|
||||
PrivateStaticMethodDefinition(PrivateName, MethodDefinition),
|
||||
|
||||
/// A private field definition, only accessible inside the class declaration.
|
||||
PrivateFieldDefinition(PrivateName, Option<Expression>),
|
||||
|
||||
/// A private static field definition, only accessible from static methods and fields inside the
|
||||
/// class declaration.
|
||||
PrivateStaticFieldDefinition(PrivateName, Option<Expression>),
|
||||
|
||||
/// A static block, where a class can have initialization logic for its static fields.
|
||||
StaticBlock(StatementList),
|
||||
}
|
||||
|
||||
impl VisitWith for ClassElement {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::MethodDefinition(pn, md) | Self::StaticMethodDefinition(pn, md) => {
|
||||
try_break!(visitor.visit_property_name(pn));
|
||||
visitor.visit_method_definition(md)
|
||||
}
|
||||
Self::FieldDefinition(pn, maybe_expr) | Self::StaticFieldDefinition(pn, maybe_expr) => {
|
||||
try_break!(visitor.visit_property_name(pn));
|
||||
if let Some(expr) = maybe_expr {
|
||||
visitor.visit_expression(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::PrivateMethodDefinition(name, md)
|
||||
| Self::PrivateStaticMethodDefinition(name, md) => {
|
||||
try_break!(visitor.visit_private_name(name));
|
||||
visitor.visit_method_definition(md)
|
||||
}
|
||||
Self::PrivateFieldDefinition(name, maybe_expr)
|
||||
| Self::PrivateStaticFieldDefinition(name, maybe_expr) => {
|
||||
try_break!(visitor.visit_private_name(name));
|
||||
if let Some(expr) = maybe_expr {
|
||||
visitor.visit_expression(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::StaticBlock(sl) => visitor.visit_statement_list(sl),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::MethodDefinition(pn, md) | Self::StaticMethodDefinition(pn, md) => {
|
||||
try_break!(visitor.visit_property_name_mut(pn));
|
||||
visitor.visit_method_definition_mut(md)
|
||||
}
|
||||
Self::FieldDefinition(pn, maybe_expr) | Self::StaticFieldDefinition(pn, maybe_expr) => {
|
||||
try_break!(visitor.visit_property_name_mut(pn));
|
||||
if let Some(expr) = maybe_expr {
|
||||
visitor.visit_expression_mut(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::PrivateMethodDefinition(name, md)
|
||||
| Self::PrivateStaticMethodDefinition(name, md) => {
|
||||
try_break!(visitor.visit_private_name_mut(name));
|
||||
visitor.visit_method_definition_mut(md)
|
||||
}
|
||||
Self::PrivateFieldDefinition(name, maybe_expr)
|
||||
| Self::PrivateStaticFieldDefinition(name, maybe_expr) => {
|
||||
try_break!(visitor.visit_private_name_mut(name));
|
||||
if let Some(expr) = maybe_expr {
|
||||
visitor.visit_expression_mut(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::StaticBlock(sl) => visitor.visit_statement_list_mut(sl),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A private name as defined by the [spec].
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-private-names
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct PrivateName {
|
||||
/// The `[[Description]]` internal slot of the private name.
|
||||
description: Sym,
|
||||
|
||||
/// The indices of the private name are included to ensure that private names are unique.
|
||||
pub(crate) indices: (usize, usize),
|
||||
}
|
||||
|
||||
impl PrivateName {
|
||||
/// Create a new private name.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(description: Sym) -> Self {
|
||||
Self {
|
||||
description,
|
||||
indices: (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the description of the private name.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn description(&self) -> Sym {
|
||||
self.description
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for PrivateName {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_sym(&self.description)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_sym_mut(&mut self.description)
|
||||
}
|
||||
}
|
||||
132
javascript-engine/external/boa/boa_ast/src/function/generator.rs
vendored
Normal file
132
javascript-engine/external/boa/boa_ast/src/function/generator.rs
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
use crate::{
|
||||
block_to_string,
|
||||
expression::{Expression, Identifier},
|
||||
join_nodes, Declaration, StatementList,
|
||||
};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, ToIndentedString};
|
||||
|
||||
use super::FormalParameterList;
|
||||
|
||||
/// A generator definition, as defined by the [spec].
|
||||
///
|
||||
/// [Generators][mdn] are "resumable functions", which can be suspended during execution and
|
||||
/// resumed at any later time. The main feature of a generator are `yield` expressions, which
|
||||
/// specifies the value returned when a generator is suspended, and the point from which
|
||||
/// the execution will resume.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-generator-function-definitions
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Generator {
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
has_binding_identifier: bool,
|
||||
}
|
||||
|
||||
impl Generator {
|
||||
/// Creates a new generator expression
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
has_binding_identifier: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
parameters,
|
||||
body,
|
||||
has_binding_identifier,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the name of the generator declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> Option<Identifier> {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Gets the list of parameters of the generator declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn parameters(&self) -> &FormalParameterList {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
/// Gets the body of the generator declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &StatementList {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Returns whether the function expression has a binding identifier.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn has_binding_identifier(&self) -> bool {
|
||||
self.has_binding_identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Generator {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = "function*".to_owned();
|
||||
if let Some(name) = self.name {
|
||||
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
|
||||
}
|
||||
buf.push_str(&format!(
|
||||
"({}) {}",
|
||||
join_nodes(interner, self.parameters.as_ref()),
|
||||
block_to_string(&self.body, interner, indentation)
|
||||
));
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Generator> for Expression {
|
||||
#[inline]
|
||||
fn from(expr: Generator) -> Self {
|
||||
Self::Generator(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Generator> for Declaration {
|
||||
#[inline]
|
||||
fn from(f: Generator) -> Self {
|
||||
Self::Generator(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Generator {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(ident) = &self.name {
|
||||
try_break!(visitor.visit_identifier(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
|
||||
visitor.visit_statement_list(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(ident) = &mut self.name {
|
||||
try_break!(visitor.visit_identifier_mut(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
|
||||
visitor.visit_statement_list_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
184
javascript-engine/external/boa/boa_ast/src/function/mod.rs
vendored
Normal file
184
javascript-engine/external/boa/boa_ast/src/function/mod.rs
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
//! Functions and classes nodes, as defined by the [spec].
|
||||
//!
|
||||
//! [Functions][func] are mainly subprograms that can be called by external code to execute a sequence of
|
||||
//! statements (the *body* of the function). Javascript functions fall in several categories:
|
||||
//!
|
||||
//! - [`Function`]s.
|
||||
//! - [`ArrowFunction`]s.
|
||||
//! - [`AsyncArrowFunction`]s.
|
||||
//! - [`Generator`]s.
|
||||
//! - [`AsyncFunction`]s.
|
||||
//! - [`AsyncGenerator`]s.
|
||||
//!
|
||||
//! All of them can be declared in either [declaration][decl] form or [expression][expr] form,
|
||||
//! except from `ArrowFunction`s and `AsyncArrowFunction`s, which can only be declared in expression form.
|
||||
//!
|
||||
//! This module also contains [`Class`]es, which are templates for creating objects. Classes
|
||||
//! can also be declared in either declaration or expression form.
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-functions-and-classes
|
||||
//! [func]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
|
||||
//! [decl]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
|
||||
//! [expr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
|
||||
|
||||
mod arrow_function;
|
||||
mod async_arrow_function;
|
||||
mod async_function;
|
||||
mod async_generator;
|
||||
mod class;
|
||||
mod generator;
|
||||
mod parameters;
|
||||
|
||||
pub use arrow_function::ArrowFunction;
|
||||
pub use async_arrow_function::AsyncArrowFunction;
|
||||
pub use async_function::AsyncFunction;
|
||||
pub use async_generator::AsyncGenerator;
|
||||
pub use class::{Class, ClassElement, PrivateName};
|
||||
use core::ops::ControlFlow;
|
||||
pub use generator::Generator;
|
||||
pub use parameters::{FormalParameter, FormalParameterList, FormalParameterListFlags};
|
||||
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{block_to_string, join_nodes, StatementList};
|
||||
use boa_interner::{Interner, ToIndentedString};
|
||||
|
||||
use super::expression::{Expression, Identifier};
|
||||
use super::Declaration;
|
||||
|
||||
/// A function definition, as defined by the [spec].
|
||||
///
|
||||
/// By default, functions return `undefined`. To return any other value, the function must have
|
||||
/// a return statement that specifies the value to return.
|
||||
///
|
||||
/// More information:
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-function-definitions
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Function {
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
has_binding_identifier: bool,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
/// Creates a new function expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
parameters,
|
||||
body,
|
||||
has_binding_identifier: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new function expression with an expression binding identifier.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new_with_binding_identifier(
|
||||
name: Option<Identifier>,
|
||||
parameters: FormalParameterList,
|
||||
body: StatementList,
|
||||
has_binding_identifier: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
parameters,
|
||||
body,
|
||||
has_binding_identifier,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the name of the function declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> Option<Identifier> {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Gets the list of parameters of the function declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn parameters(&self) -> &FormalParameterList {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
/// Gets the body of the function declaration.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &StatementList {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Returns whether the function expression has a binding identifier.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn has_binding_identifier(&self) -> bool {
|
||||
self.has_binding_identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Function {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = "function".to_owned();
|
||||
if let Some(name) = self.name {
|
||||
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
|
||||
}
|
||||
buf.push_str(&format!(
|
||||
"({}) {}",
|
||||
join_nodes(interner, self.parameters.as_ref()),
|
||||
block_to_string(&self.body, interner, indentation)
|
||||
));
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Function> for Expression {
|
||||
#[inline]
|
||||
fn from(expr: Function) -> Self {
|
||||
Self::Function(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Function> for Declaration {
|
||||
#[inline]
|
||||
fn from(f: Function) -> Self {
|
||||
Self::Function(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Function {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(ident) = &self.name {
|
||||
try_break!(visitor.visit_identifier(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
|
||||
visitor.visit_statement_list(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(ident) = &mut self.name {
|
||||
try_break!(visitor.visit_identifier_mut(ident));
|
||||
}
|
||||
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
|
||||
visitor.visit_statement_list_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
287
javascript-engine/external/boa/boa_ast/src/function/parameters.rs
vendored
Normal file
287
javascript-engine/external/boa/boa_ast/src/function/parameters.rs
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
use crate::{
|
||||
declaration::{Binding, Variable},
|
||||
expression::Expression,
|
||||
operations::bound_names,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
use boa_interner::{Interner, Sym, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
/// A list of `FormalParameter`s that describes the parameters of a function, as defined by the [spec].
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-FormalParameterList
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct FormalParameterList {
|
||||
parameters: Box<[FormalParameter]>,
|
||||
flags: FormalParameterListFlags,
|
||||
length: u32,
|
||||
}
|
||||
|
||||
impl FormalParameterList {
|
||||
/// Creates a new empty formal parameter list.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
parameters: Box::new([]),
|
||||
flags: FormalParameterListFlags::default(),
|
||||
length: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `FormalParameterList` from a list of [`FormalParameter`]s.
|
||||
#[must_use]
|
||||
pub fn from_parameters(parameters: Vec<FormalParameter>) -> Self {
|
||||
let mut flags = FormalParameterListFlags::default();
|
||||
let mut length = 0;
|
||||
let mut names = FxHashSet::default();
|
||||
|
||||
for parameter in ¶meters {
|
||||
let parameter_names = bound_names(parameter);
|
||||
|
||||
for name in parameter_names {
|
||||
if name == Sym::ARGUMENTS {
|
||||
flags |= FormalParameterListFlags::HAS_ARGUMENTS;
|
||||
}
|
||||
if names.contains(&name) {
|
||||
flags |= FormalParameterListFlags::HAS_DUPLICATES;
|
||||
} else {
|
||||
names.insert(name);
|
||||
}
|
||||
}
|
||||
|
||||
if parameter.is_rest_param() {
|
||||
flags |= FormalParameterListFlags::HAS_REST_PARAMETER;
|
||||
}
|
||||
if parameter.init().is_some() {
|
||||
flags |= FormalParameterListFlags::HAS_EXPRESSIONS;
|
||||
}
|
||||
if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier()
|
||||
{
|
||||
flags.remove(FormalParameterListFlags::IS_SIMPLE);
|
||||
}
|
||||
if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
|
||||
|| parameter.is_rest_param()
|
||||
|| parameter.init().is_some())
|
||||
{
|
||||
length += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
parameters: parameters.into(),
|
||||
flags,
|
||||
length,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the length of the parameter list.
|
||||
/// Note that this is not equal to the length of the parameters slice.
|
||||
#[must_use]
|
||||
pub const fn length(&self) -> u32 {
|
||||
self.length
|
||||
}
|
||||
|
||||
/// Returns the parameter list flags.
|
||||
#[must_use]
|
||||
pub const fn flags(&self) -> FormalParameterListFlags {
|
||||
self.flags
|
||||
}
|
||||
|
||||
/// Indicates if the parameter list is simple.
|
||||
#[must_use]
|
||||
pub const fn is_simple(&self) -> bool {
|
||||
self.flags.contains(FormalParameterListFlags::IS_SIMPLE)
|
||||
}
|
||||
|
||||
/// Indicates if the parameter list has duplicate parameters.
|
||||
#[must_use]
|
||||
pub const fn has_duplicates(&self) -> bool {
|
||||
self.flags
|
||||
.contains(FormalParameterListFlags::HAS_DUPLICATES)
|
||||
}
|
||||
|
||||
/// Indicates if the parameter list has a rest parameter.
|
||||
#[must_use]
|
||||
pub const fn has_rest_parameter(&self) -> bool {
|
||||
self.flags
|
||||
.contains(FormalParameterListFlags::HAS_REST_PARAMETER)
|
||||
}
|
||||
|
||||
/// Indicates if the parameter list has expressions in it's parameters.
|
||||
#[must_use]
|
||||
pub const fn has_expressions(&self) -> bool {
|
||||
self.flags
|
||||
.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
|
||||
}
|
||||
|
||||
/// Indicates if the parameter list has parameters named 'arguments'.
|
||||
#[must_use]
|
||||
pub const fn has_arguments(&self) -> bool {
|
||||
self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<FormalParameter>> for FormalParameterList {
|
||||
fn from(parameters: Vec<FormalParameter>) -> Self {
|
||||
Self::from_parameters(parameters)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FormalParameter> for FormalParameterList {
|
||||
fn from(parameter: FormalParameter) -> Self {
|
||||
Self::from_parameters(vec![parameter])
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[FormalParameter]> for FormalParameterList {
|
||||
fn as_ref(&self) -> &[FormalParameter] {
|
||||
&self.parameters
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for FormalParameterList {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
for parameter in self.parameters.iter() {
|
||||
try_break!(visitor.visit_formal_parameter(parameter));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
for parameter in self.parameters.iter_mut() {
|
||||
try_break!(visitor.visit_formal_parameter_mut(parameter));
|
||||
}
|
||||
// TODO recompute flags
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fuzz")]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for FormalParameterList {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let params: Vec<FormalParameter> = u.arbitrary()?;
|
||||
Ok(Self::from(params))
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags for a [`FormalParameterList`].
|
||||
#[allow(clippy::unsafe_derive_deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct FormalParameterListFlags: u8 {
|
||||
/// Has only identifier parameters with no initialization expressions.
|
||||
const IS_SIMPLE = 0b0000_0001;
|
||||
/// Has any duplicate parameters.
|
||||
const HAS_DUPLICATES = 0b0000_0010;
|
||||
/// Has a rest parameter.
|
||||
const HAS_REST_PARAMETER = 0b0000_0100;
|
||||
/// Has any initialization expression.
|
||||
const HAS_EXPRESSIONS = 0b0000_1000;
|
||||
/// Has an argument with the name `arguments`.
|
||||
const HAS_ARGUMENTS = 0b0001_0000;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FormalParameterListFlags {
|
||||
fn default() -> Self {
|
||||
Self::empty().union(Self::IS_SIMPLE)
|
||||
}
|
||||
}
|
||||
|
||||
/// "Formal parameter" is a fancy way of saying "function parameter".
|
||||
///
|
||||
/// In the declaration of a function, the parameters must be identifiers,
|
||||
/// not any value like numbers, strings, or objects.
|
||||
/// ```text
|
||||
/// function foo(formalParameter1, formalParameter2) {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-FormalParameter
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FormalParameter {
|
||||
variable: Variable,
|
||||
is_rest_param: bool,
|
||||
}
|
||||
|
||||
impl FormalParameter {
|
||||
/// Creates a new formal parameter.
|
||||
pub fn new<D>(variable: D, is_rest_param: bool) -> Self
|
||||
where
|
||||
D: Into<Variable>,
|
||||
{
|
||||
Self {
|
||||
variable: variable.into(),
|
||||
is_rest_param,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the variable of the formal parameter
|
||||
#[must_use]
|
||||
pub const fn variable(&self) -> &Variable {
|
||||
&self.variable
|
||||
}
|
||||
|
||||
/// Gets the initialization node of the formal parameter, if any.
|
||||
#[must_use]
|
||||
pub const fn init(&self) -> Option<&Expression> {
|
||||
self.variable.init()
|
||||
}
|
||||
|
||||
/// Returns `true` if the parameter is a rest parameter.
|
||||
#[must_use]
|
||||
pub const fn is_rest_param(&self) -> bool {
|
||||
self.is_rest_param
|
||||
}
|
||||
|
||||
/// Returns `true` if the parameter is an identifier.
|
||||
#[must_use]
|
||||
pub const fn is_identifier(&self) -> bool {
|
||||
matches!(&self.variable.binding(), Binding::Identifier(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for FormalParameter {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let mut buf = if self.is_rest_param {
|
||||
"...".to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
buf.push_str(&self.variable.to_interned_string(interner));
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for FormalParameter {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_variable(&self.variable)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_variable_mut(&mut self.variable)
|
||||
}
|
||||
}
|
||||
625
javascript-engine/external/boa/boa_ast/src/keyword.rs
vendored
Normal file
625
javascript-engine/external/boa/boa_ast/src/keyword.rs
vendored
Normal file
@@ -0,0 +1,625 @@
|
||||
//! The `Keyword` AST node, which represents reserved words of the ECMAScript language.
|
||||
//!
|
||||
//! The [specification][spec] defines keywords as tokens that match an `IdentifierName`, but also
|
||||
//! have special meaning in ECMAScript. In ECMAScript, you cannot use these reserved words as variables,
|
||||
//! labels, or function names.
|
||||
//!
|
||||
//! The [MDN documentation][mdn] contains a more extensive explanation about keywords.
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-keywords-and-reserved-words
|
||||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords
|
||||
|
||||
use crate::expression::operator::binary::{BinaryOp, RelationalOp};
|
||||
use boa_interner::{Interner, Sym};
|
||||
use boa_macros::utf16;
|
||||
use std::{convert::TryFrom, error, fmt, str::FromStr};
|
||||
|
||||
/// List of keywords recognized by the JavaScript grammar.
|
||||
///
|
||||
/// See the [module-level documentation][self] for more details.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Keyword {
|
||||
/// The `await` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
|
||||
Await,
|
||||
|
||||
/// The `async` keyword.
|
||||
///
|
||||
/// 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/Statements/async_function
|
||||
Async,
|
||||
|
||||
/// The `break` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [break `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement
|
||||
/// [node]: ../node/enum.Node.html#variant.Break
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
|
||||
Break,
|
||||
|
||||
/// The `case` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [switch `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-CaseClause
|
||||
/// [node]: ../node/enum.Node.html#variant.Switch
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
|
||||
Case,
|
||||
|
||||
/// The `catch` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [try `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-Catch
|
||||
/// [node]: ../node/enum.Node.html#variant.Try
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
|
||||
Catch,
|
||||
|
||||
/// The `class` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ClassDeclaration
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class
|
||||
Class,
|
||||
|
||||
/// The `continue` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [continue `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
|
||||
/// [node]: ../node/enum.Node.html#variant.Continue
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
|
||||
Continue,
|
||||
|
||||
/// The `const` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [const `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
|
||||
/// [node]: ../node/enum.Node.html#variant.ConstDecl
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
|
||||
Const,
|
||||
|
||||
/// The `debugger` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-debugger-statement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger
|
||||
Debugger,
|
||||
|
||||
/// The `default` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [switch `Node` documentation][node]
|
||||
/// - [ECMAScript reference default clause][spec-clause]
|
||||
/// - [ECMAScript reference default export][spec-export]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.Switch
|
||||
/// [spec-clause]: https://tc39.es/ecma262/#prod-DefaultClause
|
||||
/// [spec-export]: https://tc39.es/ecma262/#prod-ImportedDefaultBinding
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/default
|
||||
Default,
|
||||
|
||||
/// The `delete` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [delete `UnaryOp` documentation][unary]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-delete-operator
|
||||
/// [unary]: ../op/enum.UnaryOp.html#variant.Delete
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete
|
||||
Delete,
|
||||
|
||||
/// The `do` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
|
||||
Do,
|
||||
|
||||
/// The `else` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [if `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.If
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-IfStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
|
||||
Else,
|
||||
|
||||
/// The `enum` keyword.
|
||||
///
|
||||
/// Future reserved keyword.
|
||||
Enum,
|
||||
|
||||
/// The `export` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-exports
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
|
||||
Export,
|
||||
|
||||
/// The `extends` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ClassHeritage
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends
|
||||
Extends,
|
||||
|
||||
/// The `false` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-BooleanLiteral
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
False,
|
||||
|
||||
/// The `finally` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [try `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.Try
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-Finally
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
|
||||
Finally,
|
||||
|
||||
/// The `for` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [for loop `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.ForLoop
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
|
||||
For,
|
||||
|
||||
/// The `function` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [function `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.FunctionDecl
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
|
||||
Function,
|
||||
|
||||
/// The `if` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [if `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.If
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-IfStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
|
||||
If,
|
||||
|
||||
/// The `in` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in
|
||||
In,
|
||||
|
||||
/// The `instanceof` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-instanceofoperator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof
|
||||
InstanceOf,
|
||||
|
||||
/// The `import` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-imports
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
|
||||
Import,
|
||||
|
||||
/// The `let` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [let `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.LetDecl
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
|
||||
Let,
|
||||
|
||||
/// The `new` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [new `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.New
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-NewExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
|
||||
New,
|
||||
|
||||
/// The `null` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-NullLiteral
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null
|
||||
Null,
|
||||
|
||||
/// The `of` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-for-in-and-for-of-statements
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
|
||||
Of,
|
||||
|
||||
/// The `return` keyword
|
||||
///
|
||||
/// More information:
|
||||
/// - [return `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.Return
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return
|
||||
Return,
|
||||
|
||||
/// The `super` keyword
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-super-keyword
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
|
||||
Super,
|
||||
|
||||
/// The `switch` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [switch `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.Switch
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
|
||||
Switch,
|
||||
|
||||
/// The `this` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [this `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.This
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-this-keyword
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
|
||||
This,
|
||||
|
||||
/// The `throw` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [throw `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.Throw
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-throw-statement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
|
||||
Throw,
|
||||
|
||||
/// The `true` keyword
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-BooleanLiteral
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
True,
|
||||
|
||||
/// The `try` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [try `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.Try
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-TryStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
|
||||
Try,
|
||||
|
||||
/// The `typeof` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [typeof `UnaryOp` documentation][unary]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [unary]: ../op/enum.UnaryOp.html#variant.TypeOf
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-typeof-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
|
||||
TypeOf,
|
||||
|
||||
/// The `var` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [var `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.VarDecl
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-VariableStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
|
||||
Var,
|
||||
|
||||
/// The `void` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [void `UnaryOp` documentation][unary]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [unary]: ../op/enum.UnaryOp.html#variant.Void
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-void-operator
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
|
||||
Void,
|
||||
|
||||
/// The `while` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [while `Node` documentation][node]
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [node]: ../node/enum.Node.html#variant.While
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while
|
||||
While,
|
||||
|
||||
/// The `with` keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-WithStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
|
||||
With,
|
||||
|
||||
/// The 'yield' keyword.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-YieldExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
|
||||
Yield,
|
||||
}
|
||||
|
||||
impl Keyword {
|
||||
/// Gets the keyword as a binary operation, if this keyword is the `in` or the `instanceof`
|
||||
/// keywords.
|
||||
#[must_use]
|
||||
pub const fn as_binary_op(self) -> Option<BinaryOp> {
|
||||
match self {
|
||||
Self::In => Some(BinaryOp::Relational(RelationalOp::In)),
|
||||
Self::InstanceOf => Some(BinaryOp::Relational(RelationalOp::InstanceOf)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the keyword as a tuple of strings.
|
||||
#[must_use]
|
||||
pub const fn as_str(self) -> (&'static str, &'static [u16]) {
|
||||
match self {
|
||||
Self::Await => ("await", utf16!("await")),
|
||||
Self::Async => ("async", utf16!("async")),
|
||||
Self::Break => ("break", utf16!("break")),
|
||||
Self::Case => ("case", utf16!("case")),
|
||||
Self::Catch => ("catch", utf16!("catch")),
|
||||
Self::Class => ("class", utf16!("class")),
|
||||
Self::Continue => ("continue", utf16!("continue")),
|
||||
Self::Const => ("const", utf16!("const")),
|
||||
Self::Debugger => ("debugger", utf16!("debugger")),
|
||||
Self::Default => ("default", utf16!("default")),
|
||||
Self::Delete => ("delete", utf16!("delete")),
|
||||
Self::Do => ("do", utf16!("do")),
|
||||
Self::Else => ("else", utf16!("else")),
|
||||
Self::Enum => ("enum", utf16!("enum")),
|
||||
Self::Extends => ("extends", utf16!("extends")),
|
||||
Self::Export => ("export", utf16!("export")),
|
||||
Self::False => ("false", utf16!("false")),
|
||||
Self::Finally => ("finally", utf16!("finally")),
|
||||
Self::For => ("for", utf16!("for")),
|
||||
Self::Function => ("function", utf16!("function")),
|
||||
Self::If => ("if", utf16!("if")),
|
||||
Self::In => ("in", utf16!("in")),
|
||||
Self::InstanceOf => ("instanceof", utf16!("instanceof")),
|
||||
Self::Import => ("import", utf16!("import")),
|
||||
Self::Let => ("let", utf16!("let")),
|
||||
Self::New => ("new", utf16!("new")),
|
||||
Self::Null => ("null", utf16!("null")),
|
||||
Self::Of => ("of", utf16!("of")),
|
||||
Self::Return => ("return", utf16!("return")),
|
||||
Self::Super => ("super", utf16!("super")),
|
||||
Self::Switch => ("switch", utf16!("switch")),
|
||||
Self::This => ("this", utf16!("this")),
|
||||
Self::Throw => ("throw", utf16!("throw")),
|
||||
Self::True => ("true", utf16!("true")),
|
||||
Self::Try => ("try", utf16!("try")),
|
||||
Self::TypeOf => ("typeof", utf16!("typeof")),
|
||||
Self::Var => ("var", utf16!("var")),
|
||||
Self::Void => ("void", utf16!("void")),
|
||||
Self::While => ("while", utf16!("while")),
|
||||
Self::With => ("with", utf16!("with")),
|
||||
Self::Yield => ("yield", utf16!("yield")),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: promote all keywords to statics inside Interner
|
||||
/// Converts the keyword to a symbol in the given interner.
|
||||
pub fn to_sym(self, interner: &mut Interner) -> Sym {
|
||||
let (utf8, utf16) = self.as_str();
|
||||
interner.get_or_intern_static(utf8, utf16)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should use a proper Error
|
||||
impl TryFrom<Keyword> for BinaryOp {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: Keyword) -> Result<Self, Self::Error> {
|
||||
value
|
||||
.as_binary_op()
|
||||
.ok_or_else(|| format!("No binary operation for {value}"))
|
||||
}
|
||||
}
|
||||
|
||||
/// The error type which is returned from parsing a [`str`] into a [`Keyword`].
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct KeywordError;
|
||||
impl fmt::Display for KeywordError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "invalid token")
|
||||
}
|
||||
}
|
||||
|
||||
// This is important for other errors to wrap this one.
|
||||
impl error::Error for KeywordError {
|
||||
fn description(&self) -> &str {
|
||||
"invalid token"
|
||||
}
|
||||
}
|
||||
impl FromStr for Keyword {
|
||||
type Err = KeywordError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"await" => Ok(Self::Await),
|
||||
"async" => Ok(Self::Async),
|
||||
"break" => Ok(Self::Break),
|
||||
"case" => Ok(Self::Case),
|
||||
"catch" => Ok(Self::Catch),
|
||||
"class" => Ok(Self::Class),
|
||||
"continue" => Ok(Self::Continue),
|
||||
"const" => Ok(Self::Const),
|
||||
"debugger" => Ok(Self::Debugger),
|
||||
"default" => Ok(Self::Default),
|
||||
"delete" => Ok(Self::Delete),
|
||||
"do" => Ok(Self::Do),
|
||||
"else" => Ok(Self::Else),
|
||||
"enum" => Ok(Self::Enum),
|
||||
"extends" => Ok(Self::Extends),
|
||||
"export" => Ok(Self::Export),
|
||||
"false" => Ok(Self::False),
|
||||
"finally" => Ok(Self::Finally),
|
||||
"for" => Ok(Self::For),
|
||||
"function" => Ok(Self::Function),
|
||||
"if" => Ok(Self::If),
|
||||
"in" => Ok(Self::In),
|
||||
"instanceof" => Ok(Self::InstanceOf),
|
||||
"import" => Ok(Self::Import),
|
||||
"let" => Ok(Self::Let),
|
||||
"new" => Ok(Self::New),
|
||||
"null" => Ok(Self::Null),
|
||||
"of" => Ok(Self::Of),
|
||||
"return" => Ok(Self::Return),
|
||||
"super" => Ok(Self::Super),
|
||||
"switch" => Ok(Self::Switch),
|
||||
"this" => Ok(Self::This),
|
||||
"throw" => Ok(Self::Throw),
|
||||
"true" => Ok(Self::True),
|
||||
"try" => Ok(Self::Try),
|
||||
"typeof" => Ok(Self::TypeOf),
|
||||
"var" => Ok(Self::Var),
|
||||
"void" => Ok(Self::Void),
|
||||
"while" => Ok(Self::While),
|
||||
"with" => Ok(Self::With),
|
||||
"yield" => Ok(Self::Yield),
|
||||
_ => Err(KeywordError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Keyword {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self.as_str().0, f)
|
||||
}
|
||||
}
|
||||
174
javascript-engine/external/boa/boa_ast/src/lib.rs
vendored
Normal file
174
javascript-engine/external/boa/boa_ast/src/lib.rs
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
//! Boa's **`boa_ast`** crate implements an ECMAScript abstract syntax tree.
|
||||
//!
|
||||
//! # Crate Overview
|
||||
//! **`boa_ast`** contains representations of [**Parse Nodes**][grammar] as defined by the ECMAScript
|
||||
//! spec. Some `Parse Node`s are not represented by Boa's AST, because a lot of grammar productions
|
||||
//! are only used to throw [**Early Errors**][early], and don't influence the evaluation of the AST
|
||||
//! itself.
|
||||
//!
|
||||
//! Boa's AST is mainly split in three main components: [`Declaration`]s, [`Expression`]s and
|
||||
//! [`Statement`]s, with [`StatementList`] being the primordial Parse Node that combines
|
||||
//! all of them to create a proper AST.
|
||||
//!
|
||||
//! # About Boa
|
||||
//! Boa is an open-source, experimental ECMAScript Engine written in Rust for lexing, parsing and executing ECMAScript/JavaScript. Currently, Boa
|
||||
//! supports some of the [language][boa-conformance]. More information can be viewed at [Boa's website][boa-web].
|
||||
//!
|
||||
//! Try out the most recent release with Boa's live demo [playground][boa-playground].
|
||||
//!
|
||||
//! # Boa Crates
|
||||
//! - **`boa_ast`** - Boa's ECMAScript Abstract Syntax Tree.
|
||||
//! - **`boa_engine`** - Boa's implementation of ECMAScript builtin objects and execution.
|
||||
//! - **`boa_gc`** - Boa's garbage collector.
|
||||
//! - **`boa_interner`** - Boa's string interner.
|
||||
//! - **`boa_parser`** - Boa's lexer and parser.
|
||||
//! - **`boa_profiler`** - Boa's code profiler.
|
||||
//! - **`boa_unicode`** - Boa's Unicode identifier.
|
||||
//! - **`boa_icu_provider`** - Boa's ICU4X data provider.
|
||||
//!
|
||||
//! [grammar]: https://tc39.es/ecma262/#sec-syntactic-grammar
|
||||
//! [early]: https://tc39.es/ecma262/#sec-static-semantic-rules
|
||||
//! [boa-conformance]: https://boa-dev.github.io/boa/test262/
|
||||
//! [boa-web]: https://boa-dev.github.io/
|
||||
//! [boa-playground]: https://boa-dev.github.io/boa/playground/
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
|
||||
html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg"
|
||||
)]
|
||||
#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
|
||||
#![warn(missing_docs, clippy::dbg_macro)]
|
||||
#![deny(
|
||||
// rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html
|
||||
warnings,
|
||||
future_incompatible,
|
||||
let_underscore,
|
||||
nonstandard_style,
|
||||
rust_2018_compatibility,
|
||||
rust_2018_idioms,
|
||||
rust_2021_compatibility,
|
||||
unused,
|
||||
|
||||
// rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html
|
||||
macro_use_extern_crate,
|
||||
meta_variable_misuse,
|
||||
missing_abi,
|
||||
missing_copy_implementations,
|
||||
missing_debug_implementations,
|
||||
non_ascii_idents,
|
||||
noop_method_call,
|
||||
single_use_lifetimes,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unreachable_pub,
|
||||
unsafe_op_in_unsafe_fn,
|
||||
unused_crate_dependencies,
|
||||
unused_import_braces,
|
||||
unused_lifetimes,
|
||||
unused_qualifications,
|
||||
unused_tuple_struct_fields,
|
||||
variant_size_differences,
|
||||
|
||||
// rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html
|
||||
rustdoc::broken_intra_doc_links,
|
||||
rustdoc::private_intra_doc_links,
|
||||
rustdoc::missing_crate_level_docs,
|
||||
rustdoc::private_doc_tests,
|
||||
rustdoc::invalid_codeblock_attributes,
|
||||
rustdoc::invalid_rust_codeblocks,
|
||||
rustdoc::bare_urls,
|
||||
|
||||
// clippy categories https://doc.rust-lang.org/clippy/
|
||||
clippy::all,
|
||||
clippy::correctness,
|
||||
clippy::suspicious,
|
||||
clippy::style,
|
||||
clippy::complexity,
|
||||
clippy::perf,
|
||||
clippy::pedantic,
|
||||
clippy::nursery,
|
||||
)]
|
||||
#![allow(
|
||||
clippy::module_name_repetitions,
|
||||
clippy::too_many_lines,
|
||||
clippy::option_if_let_else,
|
||||
clippy::use_self
|
||||
)]
|
||||
|
||||
mod position;
|
||||
mod punctuator;
|
||||
mod statement_list;
|
||||
|
||||
pub mod declaration;
|
||||
pub mod expression;
|
||||
pub mod function;
|
||||
pub mod keyword;
|
||||
pub mod operations;
|
||||
pub mod pattern;
|
||||
pub mod property;
|
||||
pub mod statement;
|
||||
pub mod visitor;
|
||||
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
|
||||
pub use self::{
|
||||
declaration::Declaration,
|
||||
expression::Expression,
|
||||
keyword::Keyword,
|
||||
position::{Position, Span},
|
||||
punctuator::Punctuator,
|
||||
statement::Statement,
|
||||
statement_list::{StatementList, StatementListItem},
|
||||
};
|
||||
|
||||
/// Utility to join multiple Nodes into a single string.
|
||||
fn join_nodes<N>(interner: &Interner, nodes: &[N]) -> String
|
||||
where
|
||||
N: ToInternedString,
|
||||
{
|
||||
let mut first = true;
|
||||
let mut buf = String::new();
|
||||
for e in nodes {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
buf.push_str(", ");
|
||||
}
|
||||
buf.push_str(&e.to_interned_string(interner));
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
/// Displays the body of a block or statement list.
|
||||
///
|
||||
/// This includes the curly braces at the start and end. This will not indent the first brace,
|
||||
/// but will indent the last brace.
|
||||
fn block_to_string(body: &StatementList, interner: &Interner, indentation: usize) -> String {
|
||||
if body.statements().is_empty() {
|
||||
"{}".to_owned()
|
||||
} else {
|
||||
format!(
|
||||
"{{\n{}{}}}",
|
||||
body.to_indented_string(interner, indentation + 1),
|
||||
" ".repeat(indentation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility trait that adds a `UTF-16` escaped representation to every [`[u16]`][slice].
|
||||
trait ToStringEscaped {
|
||||
/// Decodes `self` as an `UTF-16` encoded string, escaping any unpaired surrogates by its
|
||||
/// codepoint value.
|
||||
fn to_string_escaped(&self) -> String;
|
||||
}
|
||||
|
||||
impl ToStringEscaped for [u16] {
|
||||
fn to_string_escaped(&self) -> String {
|
||||
char::decode_utf16(self.iter().copied())
|
||||
.map(|r| match r {
|
||||
Ok(c) => String::from(c),
|
||||
Err(e) => format!("\\u{:04X}", e.unpaired_surrogate()),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
769
javascript-engine/external/boa/boa_ast/src/operations.rs
vendored
Normal file
769
javascript-engine/external/boa/boa_ast/src/operations.rs
vendored
Normal file
@@ -0,0 +1,769 @@
|
||||
//! Definitions of various **Syntax-Directed Operations** used in the [spec].
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-syntax-directed-operations
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
use std::convert::Infallible;
|
||||
|
||||
use boa_interner::Sym;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use crate::{
|
||||
declaration::VarDeclaration,
|
||||
expression::{access::SuperPropertyAccess, Await, Identifier, SuperCall, Yield},
|
||||
function::{
|
||||
ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement,
|
||||
Function, Generator, PrivateName,
|
||||
},
|
||||
property::{MethodDefinition, PropertyDefinition},
|
||||
statement::LabelledItem,
|
||||
try_break,
|
||||
visitor::{NodeRef, VisitWith, Visitor, VisitorMut},
|
||||
Declaration, Expression, Statement, StatementList, StatementListItem,
|
||||
};
|
||||
|
||||
/// Represents all the possible symbols searched for by the [`Contains`][contains] operation.
|
||||
///
|
||||
/// [contains]: https://tc39.es/ecma262/#sec-syntax-directed-operations-contains
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum ContainsSymbol {
|
||||
/// A node with the `super` keyword (`super(args)` or `super.prop`).
|
||||
Super,
|
||||
/// A super property access (`super.prop`).
|
||||
SuperProperty,
|
||||
/// A super constructor call (`super(args)`).
|
||||
SuperCall,
|
||||
/// A yield expression (`yield 5`).
|
||||
YieldExpression,
|
||||
/// An await expression (`await 4`).
|
||||
AwaitExpression,
|
||||
/// The new target expression (`new.target`).
|
||||
NewTarget,
|
||||
/// The body of a class definition.
|
||||
ClassBody,
|
||||
/// The super class of a class definition.
|
||||
ClassHeritage,
|
||||
/// A this expression (`this`).
|
||||
This,
|
||||
/// A method definition.
|
||||
MethodDefinition,
|
||||
/// The BindingIdentifier "eval" or "arguments".
|
||||
EvalOrArguments,
|
||||
}
|
||||
|
||||
/// Returns `true` if the node contains the given symbol.
|
||||
///
|
||||
/// This is equivalent to the [`Contains`][spec] syntax operation in the spec.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
|
||||
#[must_use]
|
||||
pub fn contains<N>(node: &N, symbol: ContainsSymbol) -> bool
|
||||
where
|
||||
N: VisitWith,
|
||||
{
|
||||
/// Visitor used by the function to search for a specific symbol in a node.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ContainsVisitor(ContainsSymbol);
|
||||
|
||||
impl<'ast> Visitor<'ast> for ContainsVisitor {
|
||||
type BreakTy = ();
|
||||
|
||||
fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {
|
||||
if self.0 == ContainsSymbol::EvalOrArguments
|
||||
&& (node.sym() == Sym::EVAL || node.sym() == Sym::ARGUMENTS)
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_function(&mut self, _: &'ast Function) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_async_function(&mut self, _: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_generator(&mut self, _: &'ast Generator) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_async_generator(&mut self, _: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_class(&mut self, node: &'ast Class) -> ControlFlow<Self::BreakTy> {
|
||||
if !node.elements().is_empty() && self.0 == ContainsSymbol::ClassBody {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
if node.super_ref().is_some() && self.0 == ContainsSymbol::ClassHeritage {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
// `ComputedPropertyContains`: https://tc39.es/ecma262/#sec-static-semantics-computedpropertycontains
|
||||
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
|
||||
match node {
|
||||
ClassElement::MethodDefinition(name, _)
|
||||
| ClassElement::StaticMethodDefinition(name, _)
|
||||
| ClassElement::FieldDefinition(name, _)
|
||||
| ClassElement::StaticFieldDefinition(name, _) => name.visit_with(self),
|
||||
_ => ControlFlow::Continue(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_property_definition(
|
||||
&mut self,
|
||||
node: &'ast PropertyDefinition,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
if let PropertyDefinition::MethodDefinition(name, _) = node {
|
||||
if self.0 == ContainsSymbol::MethodDefinition {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
return name.visit_with(self);
|
||||
}
|
||||
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_arrow_function(
|
||||
&mut self,
|
||||
node: &'ast ArrowFunction,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
if ![
|
||||
ContainsSymbol::NewTarget,
|
||||
ContainsSymbol::SuperProperty,
|
||||
ContainsSymbol::SuperCall,
|
||||
ContainsSymbol::Super,
|
||||
ContainsSymbol::This,
|
||||
]
|
||||
.contains(&self.0)
|
||||
{
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_async_arrow_function(
|
||||
&mut self,
|
||||
node: &'ast AsyncArrowFunction,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
if ![
|
||||
ContainsSymbol::NewTarget,
|
||||
ContainsSymbol::SuperProperty,
|
||||
ContainsSymbol::SuperCall,
|
||||
ContainsSymbol::Super,
|
||||
ContainsSymbol::This,
|
||||
]
|
||||
.contains(&self.0)
|
||||
{
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_super_property_access(
|
||||
&mut self,
|
||||
node: &'ast SuperPropertyAccess,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
if [ContainsSymbol::SuperProperty, ContainsSymbol::Super].contains(&self.0) {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_super_call(&mut self, node: &'ast SuperCall) -> ControlFlow<Self::BreakTy> {
|
||||
if [ContainsSymbol::SuperCall, ContainsSymbol::Super].contains(&self.0) {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_yield(&mut self, node: &'ast Yield) -> ControlFlow<Self::BreakTy> {
|
||||
if self.0 == ContainsSymbol::YieldExpression {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_await(&mut self, node: &'ast Await) -> ControlFlow<Self::BreakTy> {
|
||||
if self.0 == ContainsSymbol::AwaitExpression {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_expression(&mut self, node: &'ast Expression) -> ControlFlow<Self::BreakTy> {
|
||||
if node == &Expression::This && self.0 == ContainsSymbol::This {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
if node == &Expression::NewTarget && self.0 == ContainsSymbol::NewTarget {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
node.visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
node.visit_with(&mut ContainsVisitor(symbol)).is_break()
|
||||
}
|
||||
|
||||
/// Returns true if the node contains an identifier reference with name `arguments`.
|
||||
///
|
||||
/// This is equivalent to the [`ContainsArguments`][spec] syntax operation in the spec.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
|
||||
#[must_use]
|
||||
pub fn contains_arguments<N>(node: &N) -> bool
|
||||
where
|
||||
N: VisitWith,
|
||||
{
|
||||
/// Visitor used by the function to search for an identifier with the name `arguments`.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ContainsArgsVisitor;
|
||||
|
||||
impl<'ast> Visitor<'ast> for ContainsArgsVisitor {
|
||||
type BreakTy = ();
|
||||
|
||||
fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {
|
||||
if node.sym() == Sym::ARGUMENTS {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_function(&mut self, _: &'ast Function) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_async_function(&mut self, _: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_generator(&mut self, _: &'ast Generator) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_async_generator(&mut self, _: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
|
||||
match node {
|
||||
ClassElement::MethodDefinition(name, _)
|
||||
| ClassElement::StaticMethodDefinition(name, _) => return name.visit_with(self),
|
||||
_ => {}
|
||||
}
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_property_definition(
|
||||
&mut self,
|
||||
node: &'ast PropertyDefinition,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
if let PropertyDefinition::MethodDefinition(name, _) = node {
|
||||
name.visit_with(self)
|
||||
} else {
|
||||
node.visit_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
node.visit_with(&mut ContainsArgsVisitor).is_break()
|
||||
}
|
||||
|
||||
/// Returns `true` if `method` has a super call in its parameters or body.
|
||||
///
|
||||
/// This is equivalent to the [`HasDirectSuper`][spec] syntax operation in the spec.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn has_direct_super(method: &MethodDefinition) -> bool {
|
||||
match method {
|
||||
MethodDefinition::Get(f) | MethodDefinition::Set(f) | MethodDefinition::Ordinary(f) => {
|
||||
contains(f, ContainsSymbol::SuperCall)
|
||||
}
|
||||
MethodDefinition::Generator(f) => contains(f, ContainsSymbol::SuperCall),
|
||||
MethodDefinition::AsyncGenerator(f) => contains(f, ContainsSymbol::SuperCall),
|
||||
MethodDefinition::Async(f) => contains(f, ContainsSymbol::SuperCall),
|
||||
}
|
||||
}
|
||||
|
||||
/// A container that [`BoundNamesVisitor`] can use to push the found identifiers.
|
||||
trait IdentList {
|
||||
fn add(&mut self, value: Identifier, function: bool);
|
||||
}
|
||||
|
||||
impl IdentList for Vec<Identifier> {
|
||||
fn add(&mut self, value: Identifier, _function: bool) {
|
||||
self.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
impl IdentList for Vec<(Identifier, bool)> {
|
||||
fn add(&mut self, value: Identifier, function: bool) {
|
||||
self.push((value, function));
|
||||
}
|
||||
}
|
||||
|
||||
impl IdentList for FxHashSet<Identifier> {
|
||||
fn add(&mut self, value: Identifier, _function: bool) {
|
||||
self.insert(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`Visitor`] used to obtain the bound names of a node.
|
||||
#[derive(Debug)]
|
||||
struct BoundNamesVisitor<'a, T: IdentList>(&'a mut T);
|
||||
|
||||
impl<'ast, T: IdentList> Visitor<'ast> for BoundNamesVisitor<'_, T> {
|
||||
type BreakTy = Infallible;
|
||||
|
||||
fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {
|
||||
self.0.add(*node, false);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
// TODO: add "*default" for module default functions without name
|
||||
fn visit_function(&mut self, node: &'ast Function) -> ControlFlow<Self::BreakTy> {
|
||||
if let Some(ident) = node.name() {
|
||||
self.0.add(ident, true);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_generator(&mut self, node: &'ast Generator) -> ControlFlow<Self::BreakTy> {
|
||||
if let Some(ident) = node.name() {
|
||||
self.0.add(ident, false);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_async_function(&mut self, node: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
|
||||
if let Some(ident) = node.name() {
|
||||
self.0.add(ident, false);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_async_generator(&mut self, node: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
|
||||
if let Some(ident) = node.name() {
|
||||
self.0.add(ident, false);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_class(&mut self, node: &'ast Class) -> ControlFlow<Self::BreakTy> {
|
||||
if let Some(ident) = node.name() {
|
||||
self.0.add(ident, false);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list with the bound names of an AST node, which may contain duplicates.
|
||||
///
|
||||
/// This is equivalent to the [`BoundNames`][spec] syntax operation in the spec.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames
|
||||
#[must_use]
|
||||
pub fn bound_names<'a, N>(node: &'a N) -> Vec<Identifier>
|
||||
where
|
||||
&'a N: Into<NodeRef<'a>>,
|
||||
{
|
||||
let mut names = Vec::new();
|
||||
BoundNamesVisitor(&mut names).visit(node.into());
|
||||
|
||||
names
|
||||
}
|
||||
|
||||
/// The [`Visitor`] used to obtain the lexically declared names of a node.
|
||||
#[derive(Debug)]
|
||||
struct LexicallyDeclaredNamesVisitor<'a, T: IdentList>(&'a mut T);
|
||||
|
||||
impl<'ast, T: IdentList> Visitor<'ast> for LexicallyDeclaredNamesVisitor<'_, T> {
|
||||
type BreakTy = Infallible;
|
||||
|
||||
fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {
|
||||
if let Statement::Labelled(labelled) = node {
|
||||
return self.visit_labelled(labelled);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
fn visit_declaration(&mut self, node: &'ast Declaration) -> ControlFlow<Self::BreakTy> {
|
||||
BoundNamesVisitor(self.0).visit_declaration(node)
|
||||
}
|
||||
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
|
||||
match node {
|
||||
LabelledItem::Function(f) => BoundNamesVisitor(self.0).visit_function(f),
|
||||
LabelledItem::Statement(_) => ControlFlow::Continue(()),
|
||||
}
|
||||
}
|
||||
fn visit_function(&mut self, node: &'ast Function) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_lexicals(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
fn visit_async_function(&mut self, node: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_lexicals(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
fn visit_generator(&mut self, node: &'ast Generator) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_lexicals(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
fn visit_async_generator(&mut self, node: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_lexicals(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
fn visit_arrow_function(&mut self, node: &'ast ArrowFunction) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_lexicals(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
fn visit_async_arrow_function(
|
||||
&mut self,
|
||||
node: &'ast AsyncArrowFunction,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_lexicals(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
|
||||
if let ClassElement::StaticBlock(stmts) = node {
|
||||
top_level_lexicals(stmts, self.0);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
// TODO: ScriptBody : StatementList
|
||||
// 1. Return TopLevelLexicallyDeclaredNames of StatementList.
|
||||
// But we don't have that node yet. In the meantime, use `top_level_lexically_declared_names` directly.
|
||||
}
|
||||
|
||||
/// Returns a list with the lexical bindings of a node, which may contain duplicates.
|
||||
///
|
||||
/// This is equivalent to the [`LexicallyDeclaredNames`][spec] syntax operation in the spec.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames
|
||||
#[must_use]
|
||||
pub fn lexically_declared_names<'a, N>(node: &'a N) -> Vec<Identifier>
|
||||
where
|
||||
&'a N: Into<NodeRef<'a>>,
|
||||
{
|
||||
let mut names = Vec::new();
|
||||
LexicallyDeclaredNamesVisitor(&mut names).visit(node.into());
|
||||
names
|
||||
}
|
||||
|
||||
/// Returns a list with the lexical bindings of a node, which may contain duplicates.
|
||||
///
|
||||
/// If a declared name originates from a function declaration it is flagged as `true` in the returned
|
||||
/// list. (See [B.3.2.4 Changes to Block Static Semantics: Early Errors])
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames
|
||||
/// [changes]: https://tc39.es/ecma262/#sec-block-duplicates-allowed-static-semantics
|
||||
#[must_use]
|
||||
pub fn lexically_declared_names_legacy<'a, N>(node: &'a N) -> Vec<(Identifier, bool)>
|
||||
where
|
||||
&'a N: Into<NodeRef<'a>>,
|
||||
{
|
||||
let mut names = Vec::new();
|
||||
LexicallyDeclaredNamesVisitor(&mut names).visit(node.into());
|
||||
names
|
||||
}
|
||||
|
||||
/// The [`Visitor`] used to obtain the var declared names of a node.
|
||||
#[derive(Debug)]
|
||||
struct VarDeclaredNamesVisitor<'a>(&'a mut FxHashSet<Identifier>);
|
||||
|
||||
impl<'ast> Visitor<'ast> for VarDeclaredNamesVisitor<'_> {
|
||||
type BreakTy = Infallible;
|
||||
|
||||
fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_declaration(&mut self, _: &'ast Declaration) -> ControlFlow<Self::BreakTy> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_var_declaration(&mut self, node: &'ast VarDeclaration) -> ControlFlow<Self::BreakTy> {
|
||||
BoundNamesVisitor(self.0).visit_var_declaration(node)
|
||||
}
|
||||
|
||||
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
|
||||
match node {
|
||||
LabelledItem::Function(_) => ControlFlow::Continue(()),
|
||||
LabelledItem::Statement(stmt) => stmt.visit_with(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_function(&mut self, node: &'ast Function) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_vars(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_async_function(&mut self, node: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_vars(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_generator(&mut self, node: &'ast Generator) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_vars(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_async_generator(&mut self, node: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_vars(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_arrow_function(&mut self, node: &'ast ArrowFunction) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_vars(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_async_arrow_function(
|
||||
&mut self,
|
||||
node: &'ast AsyncArrowFunction,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
top_level_vars(node.body(), self.0);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
|
||||
if let ClassElement::StaticBlock(stmts) = node {
|
||||
top_level_vars(stmts, self.0);
|
||||
}
|
||||
node.visit_with(self)
|
||||
}
|
||||
|
||||
// TODO: ScriptBody : StatementList
|
||||
// 1. Return TopLevelVarDeclaredNames of StatementList.
|
||||
// But we don't have that node yet. In the meantime, use `top_level_var_declared_names` directly.
|
||||
}
|
||||
|
||||
/// Returns a set with the var bindings of a node, with no duplicates.
|
||||
///
|
||||
/// This is equivalent to the [`VarDeclaredNames`][spec] syntax operation in the spec.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-vardeclarednames
|
||||
#[must_use]
|
||||
pub fn var_declared_names<'a, N>(node: &'a N) -> FxHashSet<Identifier>
|
||||
where
|
||||
&'a N: Into<NodeRef<'a>>,
|
||||
{
|
||||
let mut names = FxHashSet::default();
|
||||
VarDeclaredNamesVisitor(&mut names).visit(node.into());
|
||||
names
|
||||
}
|
||||
|
||||
/// Utility function that collects the top level lexicals of a statement list into `names`.
|
||||
fn top_level_lexicals<T: IdentList>(stmts: &StatementList, names: &mut T) {
|
||||
for stmt in stmts.statements() {
|
||||
if let StatementListItem::Declaration(decl) = stmt {
|
||||
match decl {
|
||||
// Note
|
||||
// At the top level of a function, or script, function declarations are treated like
|
||||
// var declarations rather than like lexical declarations.
|
||||
Declaration::Function(_)
|
||||
| Declaration::Generator(_)
|
||||
| Declaration::AsyncFunction(_)
|
||||
| Declaration::AsyncGenerator(_) => {}
|
||||
Declaration::Class(class) => {
|
||||
BoundNamesVisitor(names).visit_class(class);
|
||||
}
|
||||
Declaration::Lexical(decl) => {
|
||||
BoundNamesVisitor(names).visit_lexical_declaration(decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list with the lexical bindings of a top-level statement list, which may contain duplicates.
|
||||
///
|
||||
/// This is equivalent to the [`TopLevelLexicallyDeclaredNames`][spec] syntax operation in the spec.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevellexicallydeclarednames
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn top_level_lexically_declared_names(stmts: &StatementList) -> Vec<Identifier> {
|
||||
let mut names = Vec::new();
|
||||
top_level_lexicals(stmts, &mut names);
|
||||
names
|
||||
}
|
||||
|
||||
/// Utility function that collects the top level vars of a statement list into `names`.
|
||||
fn top_level_vars(stmts: &StatementList, names: &mut FxHashSet<Identifier>) {
|
||||
for stmt in stmts.statements() {
|
||||
match stmt {
|
||||
StatementListItem::Declaration(decl) => {
|
||||
match decl {
|
||||
// Note
|
||||
// At the top level of a function, or script, function declarations are treated like
|
||||
// var declarations rather than like lexical declarations.
|
||||
Declaration::Function(f) => {
|
||||
BoundNamesVisitor(names).visit_function(f);
|
||||
}
|
||||
Declaration::Generator(f) => {
|
||||
BoundNamesVisitor(names).visit_generator(f);
|
||||
}
|
||||
Declaration::AsyncFunction(f) => {
|
||||
BoundNamesVisitor(names).visit_async_function(f);
|
||||
}
|
||||
Declaration::AsyncGenerator(f) => {
|
||||
BoundNamesVisitor(names).visit_async_generator(f);
|
||||
}
|
||||
Declaration::Class(_) | Declaration::Lexical(_) => {}
|
||||
}
|
||||
}
|
||||
StatementListItem::Statement(stmt) => {
|
||||
let mut stmt = Some(stmt);
|
||||
while let Some(Statement::Labelled(labelled)) = stmt {
|
||||
match labelled.item() {
|
||||
LabelledItem::Function(f) => {
|
||||
BoundNamesVisitor(names).visit_function(f);
|
||||
stmt = None;
|
||||
}
|
||||
LabelledItem::Statement(s) => stmt = Some(s),
|
||||
}
|
||||
}
|
||||
if let Some(stmt) = stmt {
|
||||
VarDeclaredNamesVisitor(names).visit(stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list with the var bindings of a top-level statement list, with no duplicates.
|
||||
///
|
||||
/// This is equivalent to the [`TopLevelVarDeclaredNames`][spec] syntax operation in the spec.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevelvardeclarednames
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn top_level_var_declared_names(stmts: &StatementList) -> FxHashSet<Identifier> {
|
||||
let mut names = FxHashSet::default();
|
||||
top_level_vars(stmts, &mut names);
|
||||
names
|
||||
}
|
||||
|
||||
/// Resolves the private names of a class and all of the contained classes and private identifiers.
|
||||
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`.
|
||||
#[derive(Debug, Clone)]
|
||||
struct ClassPrivateNameResolver {
|
||||
private_environments_stack: Vec<FxHashMap<Sym, PrivateName>>,
|
||||
top_level_class_index: usize,
|
||||
}
|
||||
|
||||
impl<'ast> VisitorMut<'ast> for ClassPrivateNameResolver {
|
||||
type BreakTy = ();
|
||||
|
||||
#[inline]
|
||||
fn visit_class_mut(&mut self, node: &'ast mut Class) -> ControlFlow<Self::BreakTy> {
|
||||
let mut names = FxHashMap::default();
|
||||
|
||||
for element in node.elements.iter_mut() {
|
||||
match element {
|
||||
ClassElement::PrivateMethodDefinition(name, _)
|
||||
| ClassElement::PrivateStaticMethodDefinition(name, _)
|
||||
| ClassElement::PrivateFieldDefinition(name, _)
|
||||
| ClassElement::PrivateStaticFieldDefinition(name, _) => {
|
||||
name.indices = (
|
||||
self.top_level_class_index,
|
||||
self.private_environments_stack.len(),
|
||||
);
|
||||
names.insert(name.description(), *name);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.private_environments_stack.push(names);
|
||||
|
||||
for element in node.elements.iter_mut() {
|
||||
match element {
|
||||
ClassElement::MethodDefinition(name, method)
|
||||
| ClassElement::StaticMethodDefinition(name, method) => {
|
||||
try_break!(self.visit_property_name_mut(name));
|
||||
try_break!(self.visit_method_definition_mut(method));
|
||||
}
|
||||
ClassElement::PrivateMethodDefinition(_, method)
|
||||
| ClassElement::PrivateStaticMethodDefinition(_, method) => {
|
||||
try_break!(self.visit_method_definition_mut(method));
|
||||
}
|
||||
ClassElement::FieldDefinition(name, expression)
|
||||
| ClassElement::StaticFieldDefinition(name, expression) => {
|
||||
try_break!(self.visit_property_name_mut(name));
|
||||
if let Some(expression) = expression {
|
||||
try_break!(self.visit_expression_mut(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(function) = &mut node.constructor {
|
||||
try_break!(self.visit_function_mut(function));
|
||||
}
|
||||
|
||||
self.private_environments_stack.pop();
|
||||
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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 {
|
||||
ControlFlow::Continue(())
|
||||
} else {
|
||||
ControlFlow::Break(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = ClassPrivateNameResolver {
|
||||
private_environments_stack: Vec::new(),
|
||||
top_level_class_index,
|
||||
};
|
||||
|
||||
visitor.visit_class_mut(node).is_continue()
|
||||
}
|
||||
787
javascript-engine/external/boa/boa_ast/src/pattern.rs
vendored
Normal file
787
javascript-engine/external/boa/boa_ast/src/pattern.rs
vendored
Normal file
@@ -0,0 +1,787 @@
|
||||
//! A pattern binding or assignment node.
|
||||
//!
|
||||
//! A [`Pattern`] Corresponds to the [`BindingPattern`][spec1] and the [`AssignmentPattern`][spec2]
|
||||
//! nodes, each of which is used in different situations and have slightly different grammars.
|
||||
//! For example, a variable declaration combined with a destructuring expression is a `BindingPattern`:
|
||||
//!
|
||||
//! ```Javascript
|
||||
//! const obj = { a: 1, b: 2 };
|
||||
//! const { a, b } = obj; // BindingPattern
|
||||
//! ```
|
||||
//!
|
||||
//! On the other hand, a simple destructuring expression with already declared variables is called
|
||||
//! an `AssignmentPattern`:
|
||||
//!
|
||||
//! ```Javascript
|
||||
//! let a = 1;
|
||||
//! let b = 3;
|
||||
//! [a, b] = [b, a]; // AssignmentPattern
|
||||
//! ```
|
||||
//!
|
||||
//! [spec1]: https://tc39.es/ecma262/#prod-BindingPattern
|
||||
//! [spec2]: https://tc39.es/ecma262/#prod-AssignmentPattern
|
||||
//! [destr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
|
||||
|
||||
use crate::{
|
||||
expression::{access::PropertyAccess, Identifier},
|
||||
property::PropertyName,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
Expression,
|
||||
};
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// An object or array pattern binding or assignment.
|
||||
///
|
||||
/// See the [module level documentation][self] for more information.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Pattern {
|
||||
/// An object pattern (`let {a, b, c} = object`).
|
||||
Object(ObjectPattern),
|
||||
/// An array pattern (`[a, b, c] = array`).
|
||||
Array(ArrayPattern),
|
||||
}
|
||||
|
||||
impl From<ObjectPattern> for Pattern {
|
||||
fn from(obj: ObjectPattern) -> Self {
|
||||
Self::Object(obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArrayPattern> for Pattern {
|
||||
fn from(obj: ArrayPattern) -> Self {
|
||||
Self::Array(obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<ObjectPatternElement>> for Pattern {
|
||||
fn from(elements: Vec<ObjectPatternElement>) -> Self {
|
||||
ObjectPattern::new(elements.into()).into()
|
||||
}
|
||||
}
|
||||
impl From<Vec<ArrayPatternElement>> for Pattern {
|
||||
fn from(elements: Vec<ArrayPatternElement>) -> Self {
|
||||
ArrayPattern::new(elements.into()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Pattern {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
match &self {
|
||||
Self::Object(o) => o.to_interned_string(interner),
|
||||
Self::Array(a) => a.to_interned_string(interner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Pattern {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Object(op) => visitor.visit_object_pattern(op),
|
||||
Self::Array(ap) => visitor.visit_array_pattern(ap),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Object(op) => visitor.visit_object_pattern_mut(op),
|
||||
Self::Array(ap) => visitor.visit_array_pattern_mut(ap),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An object binding or assignment pattern.
|
||||
///
|
||||
/// Corresponds to the [`ObjectBindingPattern`][spec1] and the [`ObjectAssignmentPattern`][spec2]
|
||||
/// Parse Nodes.
|
||||
///
|
||||
/// For more information on what is a valid binding in an object pattern, see [`ObjectPatternElement`].
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern
|
||||
/// [spec2]: https://tc39.es/ecma262/#prod-ObjectAssignmentPattern
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ObjectPattern(Box<[ObjectPatternElement]>);
|
||||
|
||||
impl From<Vec<ObjectPatternElement>> for ObjectPattern {
|
||||
fn from(elements: Vec<ObjectPatternElement>) -> Self {
|
||||
Self(elements.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for ObjectPattern {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let mut buf = "{".to_owned();
|
||||
for (i, binding) in self.0.iter().enumerate() {
|
||||
let binding = binding.to_interned_string(interner);
|
||||
let str = if i == self.0.len() - 1 {
|
||||
format!("{binding} ")
|
||||
} else {
|
||||
format!("{binding},")
|
||||
};
|
||||
|
||||
buf.push_str(&str);
|
||||
}
|
||||
if self.0.is_empty() {
|
||||
buf.push(' ');
|
||||
}
|
||||
buf.push('}');
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectPattern {
|
||||
/// Creates a new object binding pattern.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(bindings: Box<[ObjectPatternElement]>) -> Self {
|
||||
Self(bindings)
|
||||
}
|
||||
|
||||
/// Gets the bindings for the object binding pattern.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn bindings(&self) -> &[ObjectPatternElement] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns true if the object binding pattern has a rest element.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn has_rest(&self) -> bool {
|
||||
matches!(
|
||||
self.0.last(),
|
||||
Some(ObjectPatternElement::RestProperty { .. })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ObjectPattern {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
for elem in self.0.iter() {
|
||||
try_break!(visitor.visit_object_pattern_element(elem));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
for elem in self.0.iter_mut() {
|
||||
try_break!(visitor.visit_object_pattern_element_mut(elem));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An array binding or assignment pattern.
|
||||
///
|
||||
/// Corresponds to the [`ArrayBindingPattern`][spec1] and the [`ArrayAssignmentPattern`][spec2]
|
||||
/// Parse Nodes.
|
||||
///
|
||||
/// For more information on what is a valid binding in an array pattern, see [`ArrayPatternElement`].
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern
|
||||
/// [spec2]: https://tc39.es/ecma262/#prod-ArrayAssignmentPattern
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ArrayPattern(Box<[ArrayPatternElement]>);
|
||||
|
||||
impl From<Vec<ArrayPatternElement>> for ArrayPattern {
|
||||
fn from(elements: Vec<ArrayPatternElement>) -> Self {
|
||||
Self(elements.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for ArrayPattern {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let mut buf = "[".to_owned();
|
||||
for (i, binding) in self.0.iter().enumerate() {
|
||||
if i == self.0.len() - 1 {
|
||||
match binding {
|
||||
ArrayPatternElement::Elision => {
|
||||
buf.push_str(&format!("{}, ", binding.to_interned_string(interner)));
|
||||
}
|
||||
_ => buf.push_str(&format!("{} ", binding.to_interned_string(interner))),
|
||||
}
|
||||
} else {
|
||||
buf.push_str(&format!("{},", binding.to_interned_string(interner)));
|
||||
}
|
||||
}
|
||||
buf.push(']');
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayPattern {
|
||||
/// Creates a new array binding pattern.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(bindings: Box<[ArrayPatternElement]>) -> Self {
|
||||
Self(bindings)
|
||||
}
|
||||
|
||||
/// Gets the bindings for the array binding pattern.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn bindings(&self) -> &[ArrayPatternElement] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ArrayPattern {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
for elem in self.0.iter() {
|
||||
try_break!(visitor.visit_array_pattern_element(elem));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
for elem in self.0.iter_mut() {
|
||||
try_break!(visitor.visit_array_pattern_element_mut(elem));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The different types of bindings that an [`ObjectPattern`] may contain.
|
||||
///
|
||||
/// Corresponds to the [`BindingProperty`][spec1] and the [`AssignmentProperty`][spec2] nodes.
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty
|
||||
/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentProperty
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ObjectPatternElement {
|
||||
/// SingleName represents one of the following properties:
|
||||
///
|
||||
/// - `SingleName` with an identifier and an optional default initializer.
|
||||
/// - `BindingProperty` with an property name and a `SingleNameBinding` as the `BindingElement`.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1]
|
||||
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec2]
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding
|
||||
/// [spec2]: https://tc39.es/ecma262/#prod-BindingProperty
|
||||
SingleName {
|
||||
/// The identifier name of the property to be destructured.
|
||||
name: PropertyName,
|
||||
/// The variable name where the property value will be stored.
|
||||
ident: Identifier,
|
||||
/// An optional default value for the variable, in case the property doesn't exist.
|
||||
default_init: Option<Expression>,
|
||||
},
|
||||
|
||||
/// RestProperty represents a `BindingRestProperty` with an identifier.
|
||||
///
|
||||
/// It also includes a list of the property keys that should be excluded from the rest,
|
||||
/// because they where already assigned.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestProperty][spec1]
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty
|
||||
RestProperty {
|
||||
/// The variable name where the unassigned properties will be stored.
|
||||
ident: Identifier,
|
||||
/// A list of the excluded property keys that were already destructured.
|
||||
excluded_keys: Vec<Identifier>,
|
||||
},
|
||||
|
||||
/// AssignmentGetField represents an AssignmentProperty with an expression field member expression AssignmentElement.
|
||||
///
|
||||
/// Note: According to the spec this is not part of an ObjectBindingPattern.
|
||||
/// This is only used when a object literal is used to cover an AssignmentPattern.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentProperty
|
||||
AssignmentPropertyAccess {
|
||||
/// The identifier name of the property to be destructured.
|
||||
name: PropertyName,
|
||||
/// The property access where the property value will be destructured.
|
||||
access: PropertyAccess,
|
||||
/// An optional default value for the variable, in case the property doesn't exist.
|
||||
default_init: Option<Expression>,
|
||||
},
|
||||
|
||||
/// AssignmentRestProperty represents a rest property with a DestructuringAssignmentTarget.
|
||||
///
|
||||
/// Note: According to the spec this is not part of an ObjectBindingPattern.
|
||||
/// This is only used when a object literal is used to cover an AssignmentPattern.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentRestProperty
|
||||
AssignmentRestPropertyAccess {
|
||||
/// The property access where the unassigned properties will be stored.
|
||||
access: PropertyAccess,
|
||||
/// A list of the excluded property keys that were already destructured.
|
||||
excluded_keys: Vec<Identifier>,
|
||||
},
|
||||
|
||||
/// Pattern represents a property with a `Pattern` as the element.
|
||||
///
|
||||
/// Additionally to the identifier of the new property and the nested pattern,
|
||||
/// this may also include an optional default initializer.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec1]
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty
|
||||
Pattern {
|
||||
/// The identifier name of the property to be destructured.
|
||||
name: PropertyName,
|
||||
/// The pattern where the property value will be destructured.
|
||||
pattern: Pattern,
|
||||
/// An optional default value for the variable, in case the property doesn't exist.
|
||||
default_init: Option<Expression>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ToInternedString for ObjectPatternElement {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
match self {
|
||||
Self::SingleName {
|
||||
ident,
|
||||
name,
|
||||
default_init,
|
||||
} => {
|
||||
let mut buf = match name {
|
||||
PropertyName::Literal(name) if name == ident => {
|
||||
format!(" {}", interner.resolve_expect(ident.sym()))
|
||||
}
|
||||
PropertyName::Literal(name) => {
|
||||
format!(
|
||||
" {} : {}",
|
||||
interner.resolve_expect(*name),
|
||||
interner.resolve_expect(ident.sym())
|
||||
)
|
||||
}
|
||||
PropertyName::Computed(node) => {
|
||||
format!(
|
||||
" [{}] : {}",
|
||||
node.to_interned_string(interner),
|
||||
interner.resolve_expect(ident.sym())
|
||||
)
|
||||
}
|
||||
};
|
||||
if let Some(ref init) = default_init {
|
||||
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
|
||||
}
|
||||
buf
|
||||
}
|
||||
Self::RestProperty {
|
||||
ident,
|
||||
excluded_keys: _,
|
||||
} => {
|
||||
format!(" ... {}", interner.resolve_expect(ident.sym()))
|
||||
}
|
||||
Self::AssignmentRestPropertyAccess { access, .. } => {
|
||||
format!(" ... {}", access.to_interned_string(interner))
|
||||
}
|
||||
Self::AssignmentPropertyAccess {
|
||||
name,
|
||||
access,
|
||||
default_init,
|
||||
} => {
|
||||
let mut buf = match name {
|
||||
PropertyName::Literal(name) => {
|
||||
format!(
|
||||
" {} : {}",
|
||||
interner.resolve_expect(*name),
|
||||
access.to_interned_string(interner)
|
||||
)
|
||||
}
|
||||
PropertyName::Computed(node) => {
|
||||
format!(
|
||||
" [{}] : {}",
|
||||
node.to_interned_string(interner),
|
||||
access.to_interned_string(interner)
|
||||
)
|
||||
}
|
||||
};
|
||||
if let Some(init) = &default_init {
|
||||
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
|
||||
}
|
||||
buf
|
||||
}
|
||||
Self::Pattern {
|
||||
name,
|
||||
pattern,
|
||||
default_init,
|
||||
} => {
|
||||
let mut buf = match name {
|
||||
PropertyName::Literal(name) => {
|
||||
format!(
|
||||
" {} : {}",
|
||||
interner.resolve_expect(*name),
|
||||
pattern.to_interned_string(interner),
|
||||
)
|
||||
}
|
||||
PropertyName::Computed(node) => {
|
||||
format!(
|
||||
" [{}] : {}",
|
||||
node.to_interned_string(interner),
|
||||
pattern.to_interned_string(interner),
|
||||
)
|
||||
}
|
||||
};
|
||||
if let Some(ref init) = default_init {
|
||||
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ObjectPatternElement {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::SingleName {
|
||||
name,
|
||||
ident,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_property_name(name));
|
||||
try_break!(visitor.visit_identifier(ident));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::RestProperty { ident, .. } => visitor.visit_identifier(ident),
|
||||
Self::AssignmentPropertyAccess {
|
||||
name,
|
||||
access,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_property_name(name));
|
||||
try_break!(visitor.visit_property_access(access));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::AssignmentRestPropertyAccess { access, .. } => {
|
||||
visitor.visit_property_access(access)
|
||||
}
|
||||
Self::Pattern {
|
||||
name,
|
||||
pattern,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_property_name(name));
|
||||
try_break!(visitor.visit_pattern(pattern));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::SingleName {
|
||||
name,
|
||||
ident,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_property_name_mut(name));
|
||||
try_break!(visitor.visit_identifier_mut(ident));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression_mut(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::RestProperty { ident, .. } => visitor.visit_identifier_mut(ident),
|
||||
Self::AssignmentPropertyAccess {
|
||||
name,
|
||||
access,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_property_name_mut(name));
|
||||
try_break!(visitor.visit_property_access_mut(access));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression_mut(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::AssignmentRestPropertyAccess { access, .. } => {
|
||||
visitor.visit_property_access_mut(access)
|
||||
}
|
||||
Self::Pattern {
|
||||
name,
|
||||
pattern,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_property_name_mut(name));
|
||||
try_break!(visitor.visit_pattern_mut(pattern));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression_mut(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The different types of bindings that an array binding pattern may contain.
|
||||
///
|
||||
/// Corresponds to the [`BindingElement`][spec1] and the [`AssignmentElement`][spec2] nodes.
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-BindingElement
|
||||
/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentElement
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ArrayPatternElement {
|
||||
/// Elision represents the elision of an item in the array binding pattern.
|
||||
///
|
||||
/// An `Elision` may occur at multiple points in the pattern and may be multiple elisions.
|
||||
/// This variant strictly represents one elision. If there are multiple, this should be used multiple times.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 13.2.4 Array Initializer - Elision][spec1]
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-Elision
|
||||
Elision,
|
||||
|
||||
/// SingleName represents a `SingleName` with an identifier and an optional default initializer.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1]
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding
|
||||
SingleName {
|
||||
/// The variable name where the index element will be stored.
|
||||
ident: Identifier,
|
||||
/// An optional default value for the variable, in case the index element doesn't exist.
|
||||
default_init: Option<Expression>,
|
||||
},
|
||||
|
||||
/// PropertyAccess represents a binding with a property accessor.
|
||||
///
|
||||
/// Note: According to the spec this is not part of an ArrayBindingPattern.
|
||||
/// This is only used when a array literal is used as the left-hand-side of an assignment expression.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
|
||||
PropertyAccess {
|
||||
/// The property access where the index element will be stored.
|
||||
access: PropertyAccess,
|
||||
},
|
||||
|
||||
/// Pattern represents a `Pattern` in an `Element` of an array pattern.
|
||||
///
|
||||
/// The pattern and the optional default initializer are both stored in the DeclarationPattern.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingElement][spec1]
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-BindingElement
|
||||
Pattern {
|
||||
/// The pattern where the index element will be stored.
|
||||
pattern: Pattern,
|
||||
/// An optional default value for the pattern, in case the index element doesn't exist.
|
||||
default_init: Option<Expression>,
|
||||
},
|
||||
|
||||
/// SingleNameRest represents a `BindingIdentifier` in a `BindingRestElement` of an array pattern.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1]
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement
|
||||
SingleNameRest {
|
||||
/// The variable where the unassigned index elements will be stored.
|
||||
ident: Identifier,
|
||||
},
|
||||
|
||||
/// PropertyAccess represents a rest (spread operator) with a property accessor.
|
||||
///
|
||||
/// Note: According to the spec this is not part of an ArrayBindingPattern.
|
||||
/// This is only used when a array literal is used as the left-hand-side of an assignment expression.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
|
||||
PropertyAccessRest {
|
||||
/// The property access where the unassigned index elements will be stored.
|
||||
access: PropertyAccess,
|
||||
},
|
||||
|
||||
/// PatternRest represents a `Pattern` in a `RestElement` of an array pattern.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1]
|
||||
///
|
||||
/// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement
|
||||
PatternRest {
|
||||
/// The pattern where the unassigned index elements will be stored.
|
||||
pattern: Pattern,
|
||||
},
|
||||
}
|
||||
|
||||
impl ToInternedString for ArrayPatternElement {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
match self {
|
||||
Self::Elision => " ".to_owned(),
|
||||
Self::SingleName {
|
||||
ident,
|
||||
default_init,
|
||||
} => {
|
||||
let mut buf = format!(" {}", interner.resolve_expect(ident.sym()));
|
||||
if let Some(ref init) = default_init {
|
||||
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
|
||||
}
|
||||
buf
|
||||
}
|
||||
Self::PropertyAccess { access } => {
|
||||
format!(" {}", access.to_interned_string(interner))
|
||||
}
|
||||
Self::Pattern {
|
||||
pattern,
|
||||
default_init,
|
||||
} => {
|
||||
let mut buf = format!(" {}", pattern.to_interned_string(interner));
|
||||
if let Some(init) = default_init {
|
||||
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
|
||||
}
|
||||
buf
|
||||
}
|
||||
Self::SingleNameRest { ident } => {
|
||||
format!(" ... {}", interner.resolve_expect(ident.sym()))
|
||||
}
|
||||
Self::PropertyAccessRest { access } => {
|
||||
format!(" ... {}", access.to_interned_string(interner))
|
||||
}
|
||||
Self::PatternRest { pattern } => {
|
||||
format!(" ... {}", pattern.to_interned_string(interner))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ArrayPatternElement {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::SingleName {
|
||||
ident,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_identifier(ident));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => {
|
||||
visitor.visit_property_access(access)
|
||||
}
|
||||
Self::Pattern {
|
||||
pattern,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_pattern(pattern));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::SingleNameRest { ident } => visitor.visit_identifier(ident),
|
||||
Self::PatternRest { pattern } => visitor.visit_pattern(pattern),
|
||||
Self::Elision => {
|
||||
// special case to be handled by user
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::SingleName {
|
||||
ident,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_identifier_mut(ident));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression_mut(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => {
|
||||
visitor.visit_property_access_mut(access)
|
||||
}
|
||||
Self::Pattern {
|
||||
pattern,
|
||||
default_init,
|
||||
} => {
|
||||
try_break!(visitor.visit_pattern_mut(pattern));
|
||||
if let Some(expr) = default_init {
|
||||
visitor.visit_expression_mut(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Self::SingleNameRest { ident } => visitor.visit_identifier_mut(ident),
|
||||
Self::PatternRest { pattern } => visitor.visit_pattern_mut(pattern),
|
||||
Self::Elision => {
|
||||
// special case to be handled by user
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
302
javascript-engine/external/boa/boa_ast/src/position.rs
vendored
Normal file
302
javascript-engine/external/boa/boa_ast/src/position.rs
vendored
Normal file
@@ -0,0 +1,302 @@
|
||||
use std::{cmp::Ordering, fmt, num::NonZeroU32};
|
||||
|
||||
/// A position in the ECMAScript source code.
|
||||
///
|
||||
/// Stores both the column number and the line number.
|
||||
///
|
||||
/// ## Similar Implementations
|
||||
/// [V8: Location](https://cs.chromium.org/chromium/src/v8/src/parsing/scanner.h?type=cs&q=isValid+Location&g=0&l=216)
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Position {
|
||||
/// Line number.
|
||||
line_number: NonZeroU32,
|
||||
/// Column number.
|
||||
column_number: NonZeroU32,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// Creates a new `Position`.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[must_use]
|
||||
pub fn new(line_number: u32, column_number: u32) -> Self {
|
||||
Self {
|
||||
line_number: NonZeroU32::new(line_number).expect("line number cannot be 0"),
|
||||
column_number: NonZeroU32::new(column_number).expect("column number cannot be 0"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the line number of the position.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn line_number(self) -> u32 {
|
||||
self.line_number.get()
|
||||
}
|
||||
|
||||
/// Gets the column number of the position.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn column_number(self) -> u32 {
|
||||
self.column_number.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.line_number, self.column_number)
|
||||
}
|
||||
}
|
||||
|
||||
/// A span in the ECMAScript source code.
|
||||
///
|
||||
/// Stores a start position and an end position.
|
||||
///
|
||||
/// Note that spans are of the form [start, end) i.e. that the start position is inclusive
|
||||
/// and the end position is exclusive.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Span {
|
||||
start: Position,
|
||||
end: Position,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// Creates a new `Span`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the start position is bigger than the end position.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[must_use]
|
||||
pub fn new(start: Position, end: Position) -> Self {
|
||||
assert!(start <= end, "a span cannot start after its end");
|
||||
|
||||
Self { start, end }
|
||||
}
|
||||
|
||||
/// Gets the starting position of the span.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn start(self) -> Position {
|
||||
self.start
|
||||
}
|
||||
|
||||
/// Gets the final position of the span.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn end(self) -> Position {
|
||||
self.end
|
||||
}
|
||||
|
||||
/// Checks if this span inclusively contains another span or position.
|
||||
pub fn contains<S>(self, other: S) -> bool
|
||||
where
|
||||
S: Into<Self>,
|
||||
{
|
||||
let other = other.into();
|
||||
self.start <= other.start && self.end >= other.end
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Position> for Span {
|
||||
fn from(pos: Position) -> Self {
|
||||
Self {
|
||||
start: pos,
|
||||
end: pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Span {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
if self == other {
|
||||
Some(Ordering::Equal)
|
||||
} else if self.end < other.start {
|
||||
Some(Ordering::Less)
|
||||
} else if self.start > other.end {
|
||||
Some(Ordering::Greater)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[{}..{}]", self.start, self.end)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::similar_names)]
|
||||
#![allow(unused_must_use)]
|
||||
use super::{Position, Span};
|
||||
|
||||
/// Checks that we cannot create a position with 0 as the column.
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn invalid_position_column() {
|
||||
Position::new(10, 0);
|
||||
}
|
||||
|
||||
/// Checks that we cannot create a position with 0 as the line.
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn invalid_position_line() {
|
||||
Position::new(0, 10);
|
||||
}
|
||||
|
||||
/// Checks that the `PartialEq` implementation of `Position` is consistent.
|
||||
#[test]
|
||||
fn position_equality() {
|
||||
assert_eq!(Position::new(10, 50), Position::new(10, 50));
|
||||
assert_ne!(Position::new(10, 50), Position::new(10, 51));
|
||||
assert_ne!(Position::new(10, 50), Position::new(11, 50));
|
||||
assert_ne!(Position::new(10, 50), Position::new(11, 51));
|
||||
}
|
||||
|
||||
/// Checks that the `PartialOrd` implementation of `Position` is consistent.
|
||||
#[test]
|
||||
fn position_order() {
|
||||
assert!(Position::new(10, 50) < Position::new(10, 51));
|
||||
assert!(Position::new(9, 50) < Position::new(10, 50));
|
||||
assert!(Position::new(10, 50) < Position::new(11, 51));
|
||||
assert!(Position::new(10, 50) < Position::new(11, 49));
|
||||
|
||||
assert!(Position::new(10, 51) > Position::new(10, 50));
|
||||
assert!(Position::new(10, 50) > Position::new(9, 50));
|
||||
assert!(Position::new(11, 51) > Position::new(10, 50));
|
||||
assert!(Position::new(11, 49) > Position::new(10, 50));
|
||||
}
|
||||
|
||||
/// Checks that the position getters actually retrieve correct values.
|
||||
#[test]
|
||||
fn position_getters() {
|
||||
let pos = Position::new(10, 50);
|
||||
assert_eq!(pos.line_number(), 10);
|
||||
assert_eq!(pos.column_number(), 50);
|
||||
}
|
||||
|
||||
/// Checks that the string representation of a position is correct.
|
||||
#[test]
|
||||
fn position_to_string() {
|
||||
let pos = Position::new(10, 50);
|
||||
|
||||
assert_eq!("10:50", pos.to_string());
|
||||
assert_eq!("10:50", pos.to_string());
|
||||
}
|
||||
|
||||
/// Checks that we cannot create an invalid span.
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn invalid_span() {
|
||||
let a = Position::new(10, 30);
|
||||
let b = Position::new(10, 50);
|
||||
Span::new(b, a);
|
||||
}
|
||||
|
||||
/// Checks that we can create valid spans.
|
||||
#[test]
|
||||
fn span_creation() {
|
||||
let a = Position::new(10, 30);
|
||||
let b = Position::new(10, 50);
|
||||
|
||||
let _ = Span::new(a, b);
|
||||
let _ = Span::new(a, a);
|
||||
let _ = Span::from(a);
|
||||
}
|
||||
|
||||
/// Checks that the `PartialEq` implementation of `Span` is consistent.
|
||||
#[test]
|
||||
fn span_equality() {
|
||||
let a = Position::new(10, 50);
|
||||
let b = Position::new(10, 52);
|
||||
let c = Position::new(11, 20);
|
||||
|
||||
let span_ab = Span::new(a, b);
|
||||
let span_ab_2 = Span::new(a, b);
|
||||
let span_ac = Span::new(a, c);
|
||||
let span_bc = Span::new(b, c);
|
||||
|
||||
assert_eq!(span_ab, span_ab_2);
|
||||
assert_ne!(span_ab, span_ac);
|
||||
assert_ne!(span_ab, span_bc);
|
||||
assert_ne!(span_bc, span_ac);
|
||||
|
||||
let span_a = Span::from(a);
|
||||
let span_aa = Span::new(a, a);
|
||||
|
||||
assert_eq!(span_a, span_aa);
|
||||
}
|
||||
|
||||
/// Checks that the getters retrieve the correct value.
|
||||
#[test]
|
||||
fn span_getters() {
|
||||
let a = Position::new(10, 50);
|
||||
let b = Position::new(10, 52);
|
||||
|
||||
let span = Span::new(a, b);
|
||||
|
||||
assert_eq!(span.start(), a);
|
||||
assert_eq!(span.end(), b);
|
||||
}
|
||||
|
||||
/// Checks that the `Span::contains()` method works properly.
|
||||
#[test]
|
||||
fn span_contains() {
|
||||
let a = Position::new(10, 50);
|
||||
let b = Position::new(10, 52);
|
||||
let c = Position::new(11, 20);
|
||||
let d = Position::new(12, 5);
|
||||
|
||||
let span_ac = Span::new(a, c);
|
||||
assert!(span_ac.contains(b));
|
||||
|
||||
let span_ab = Span::new(a, b);
|
||||
let span_cd = Span::new(c, d);
|
||||
|
||||
assert!(!span_ab.contains(span_cd));
|
||||
assert!(span_ab.contains(b));
|
||||
|
||||
let span_ad = Span::new(a, d);
|
||||
let span_bc = Span::new(b, c);
|
||||
|
||||
assert!(span_ad.contains(span_bc));
|
||||
assert!(!span_bc.contains(span_ad));
|
||||
|
||||
let span_ac = Span::new(a, c);
|
||||
let span_bd = Span::new(b, d);
|
||||
|
||||
assert!(!span_ac.contains(span_bd));
|
||||
assert!(!span_bd.contains(span_ac));
|
||||
}
|
||||
|
||||
/// Checks that the string representation of a span is correct.
|
||||
#[test]
|
||||
fn span_to_string() {
|
||||
let a = Position::new(10, 50);
|
||||
let b = Position::new(11, 20);
|
||||
let span = Span::new(a, b);
|
||||
|
||||
assert_eq!("[10:50..11:20]", span.to_string());
|
||||
assert_eq!("[10:50..11:20]", span.to_string());
|
||||
}
|
||||
|
||||
/// Checks that the ordering of spans is correct.
|
||||
#[test]
|
||||
fn span_ordering() {
|
||||
let a = Position::new(10, 50);
|
||||
let b = Position::new(10, 52);
|
||||
let c = Position::new(11, 20);
|
||||
let d = Position::new(12, 5);
|
||||
|
||||
let span_ab = Span::new(a, b);
|
||||
let span_cd = Span::new(c, d);
|
||||
|
||||
assert!(span_ab < span_cd);
|
||||
assert!(span_cd > span_ab);
|
||||
}
|
||||
}
|
||||
371
javascript-engine/external/boa/boa_ast/src/property.rs
vendored
Normal file
371
javascript-engine/external/boa/boa_ast/src/property.rs
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
//! 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 = "fuzz", 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 = "fuzz", 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 = "fuzz", 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
|
||||
}
|
||||
}
|
||||
}
|
||||
285
javascript-engine/external/boa/boa_ast/src/punctuator.rs
vendored
Normal file
285
javascript-engine/external/boa/boa_ast/src/punctuator.rs
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
//! The `Punctuator` enum, which contains all punctuators used in ECMAScript.
|
||||
//!
|
||||
//! More information:
|
||||
//! - [ECMAScript Reference][spec]
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#prod-Punctuator
|
||||
|
||||
use crate::expression::operator::{
|
||||
assign::AssignOp,
|
||||
binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp},
|
||||
};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
fmt::{Display, Error, Formatter},
|
||||
};
|
||||
|
||||
/// All of the punctuators used in ECMAScript.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript Reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-Punctuator
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Punctuator {
|
||||
/// `+`
|
||||
Add,
|
||||
/// `&`
|
||||
And,
|
||||
/// `=>`
|
||||
Arrow,
|
||||
/// `=`
|
||||
Assign,
|
||||
/// `+=`
|
||||
AssignAdd,
|
||||
/// `&=`
|
||||
AssignAnd,
|
||||
/// `&&=`
|
||||
AssignBoolAnd,
|
||||
/// `||=`
|
||||
AssignBoolOr,
|
||||
/// `??=`,
|
||||
AssignCoalesce,
|
||||
/// `/=`
|
||||
AssignDiv,
|
||||
/// `<<=`
|
||||
AssignLeftSh,
|
||||
/// `%=`
|
||||
AssignMod,
|
||||
/// `*=`
|
||||
AssignMul,
|
||||
/// `|=`
|
||||
AssignOr,
|
||||
/// `**=`
|
||||
AssignPow,
|
||||
/// `>>=`
|
||||
AssignRightSh,
|
||||
/// `-=`
|
||||
AssignSub,
|
||||
/// `>>>=`
|
||||
AssignURightSh,
|
||||
/// `^=`
|
||||
AssignXor,
|
||||
/// `&&`
|
||||
BoolAnd,
|
||||
/// `||`
|
||||
BoolOr,
|
||||
/// `}`
|
||||
CloseBlock,
|
||||
/// `]`
|
||||
CloseBracket,
|
||||
/// `)`
|
||||
CloseParen,
|
||||
/// `??`
|
||||
Coalesce,
|
||||
/// `:`
|
||||
Colon,
|
||||
/// `,`
|
||||
Comma,
|
||||
/// `--`
|
||||
Dec,
|
||||
/// `/`
|
||||
Div,
|
||||
/// `.`
|
||||
Dot,
|
||||
/// `==`
|
||||
Eq,
|
||||
/// `>`
|
||||
GreaterThan,
|
||||
/// `>=`
|
||||
GreaterThanOrEq,
|
||||
/// `++`
|
||||
Inc,
|
||||
/// `<<`
|
||||
LeftSh,
|
||||
/// `<`
|
||||
LessThan,
|
||||
/// `<=`
|
||||
LessThanOrEq,
|
||||
/// `%`
|
||||
Mod,
|
||||
/// `*`
|
||||
Mul,
|
||||
/// `~`
|
||||
Neg,
|
||||
/// `!`
|
||||
Not,
|
||||
/// `!=`
|
||||
NotEq,
|
||||
/// `{`
|
||||
OpenBlock,
|
||||
/// `[`
|
||||
OpenBracket,
|
||||
/// `(`
|
||||
OpenParen,
|
||||
/// `?.`
|
||||
Optional,
|
||||
/// `|`
|
||||
Or,
|
||||
/// `**`
|
||||
Exp,
|
||||
/// `?`
|
||||
Question,
|
||||
/// `>>`
|
||||
RightSh,
|
||||
/// `;`
|
||||
Semicolon,
|
||||
/// `...`
|
||||
Spread,
|
||||
/// `===`
|
||||
StrictEq,
|
||||
/// `!==`
|
||||
StrictNotEq,
|
||||
/// `-`
|
||||
Sub,
|
||||
/// `>>>`
|
||||
URightSh,
|
||||
/// `^`
|
||||
Xor,
|
||||
}
|
||||
|
||||
impl Punctuator {
|
||||
/// Attempts to convert a punctuator (`+`, `=`...) to an Assign Operator
|
||||
///
|
||||
/// If there is no match, `None` will be returned.
|
||||
#[must_use]
|
||||
pub const fn as_assign_op(self) -> Option<AssignOp> {
|
||||
match self {
|
||||
Self::Assign => Some(AssignOp::Assign),
|
||||
Self::AssignAdd => Some(AssignOp::Add),
|
||||
Self::AssignAnd => Some(AssignOp::And),
|
||||
Self::AssignBoolAnd => Some(AssignOp::BoolAnd),
|
||||
Self::AssignBoolOr => Some(AssignOp::BoolOr),
|
||||
Self::AssignCoalesce => Some(AssignOp::Coalesce),
|
||||
Self::AssignDiv => Some(AssignOp::Div),
|
||||
Self::AssignLeftSh => Some(AssignOp::Shl),
|
||||
Self::AssignMod => Some(AssignOp::Mod),
|
||||
Self::AssignMul => Some(AssignOp::Mul),
|
||||
Self::AssignOr => Some(AssignOp::Or),
|
||||
Self::AssignPow => Some(AssignOp::Exp),
|
||||
Self::AssignRightSh => Some(AssignOp::Shr),
|
||||
Self::AssignSub => Some(AssignOp::Sub),
|
||||
Self::AssignURightSh => Some(AssignOp::Ushr),
|
||||
Self::AssignXor => Some(AssignOp::Xor),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to convert a punctuator (`+`, `=`...) to a Binary Operator
|
||||
///
|
||||
/// If there is no match, `None` will be returned.
|
||||
#[must_use]
|
||||
pub const fn as_binary_op(self) -> Option<BinaryOp> {
|
||||
match self {
|
||||
Self::Add => Some(BinaryOp::Arithmetic(ArithmeticOp::Add)),
|
||||
Self::Sub => Some(BinaryOp::Arithmetic(ArithmeticOp::Sub)),
|
||||
Self::Mul => Some(BinaryOp::Arithmetic(ArithmeticOp::Mul)),
|
||||
Self::Div => Some(BinaryOp::Arithmetic(ArithmeticOp::Div)),
|
||||
Self::Mod => Some(BinaryOp::Arithmetic(ArithmeticOp::Mod)),
|
||||
Self::And => Some(BinaryOp::Bitwise(BitwiseOp::And)),
|
||||
Self::Or => Some(BinaryOp::Bitwise(BitwiseOp::Or)),
|
||||
Self::Xor => Some(BinaryOp::Bitwise(BitwiseOp::Xor)),
|
||||
Self::BoolAnd => Some(BinaryOp::Logical(LogicalOp::And)),
|
||||
Self::BoolOr => Some(BinaryOp::Logical(LogicalOp::Or)),
|
||||
Self::Coalesce => Some(BinaryOp::Logical(LogicalOp::Coalesce)),
|
||||
Self::Eq => Some(BinaryOp::Relational(RelationalOp::Equal)),
|
||||
Self::NotEq => Some(BinaryOp::Relational(RelationalOp::NotEqual)),
|
||||
Self::StrictEq => Some(BinaryOp::Relational(RelationalOp::StrictEqual)),
|
||||
Self::StrictNotEq => Some(BinaryOp::Relational(RelationalOp::StrictNotEqual)),
|
||||
Self::LessThan => Some(BinaryOp::Relational(RelationalOp::LessThan)),
|
||||
Self::GreaterThan => Some(BinaryOp::Relational(RelationalOp::GreaterThan)),
|
||||
Self::GreaterThanOrEq => Some(BinaryOp::Relational(RelationalOp::GreaterThanOrEqual)),
|
||||
Self::LessThanOrEq => Some(BinaryOp::Relational(RelationalOp::LessThanOrEqual)),
|
||||
Self::LeftSh => Some(BinaryOp::Bitwise(BitwiseOp::Shl)),
|
||||
Self::RightSh => Some(BinaryOp::Bitwise(BitwiseOp::Shr)),
|
||||
Self::URightSh => Some(BinaryOp::Bitwise(BitwiseOp::UShr)),
|
||||
Self::Comma => Some(BinaryOp::Comma),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the punctuator as a static string.
|
||||
#[must_use]
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Add => "+",
|
||||
Self::And => "&",
|
||||
Self::Arrow => "=>",
|
||||
Self::Assign => "=",
|
||||
Self::AssignAdd => "+=",
|
||||
Self::AssignAnd => "&=",
|
||||
Self::AssignBoolAnd => "&&=",
|
||||
Self::AssignBoolOr => "||=",
|
||||
Self::AssignCoalesce => "??=",
|
||||
Self::AssignDiv => "/=",
|
||||
Self::AssignLeftSh => "<<=",
|
||||
Self::AssignMod => "%=",
|
||||
Self::AssignMul => "*=",
|
||||
Self::AssignOr => "|=",
|
||||
Self::AssignPow => "**=",
|
||||
Self::AssignRightSh => ">>=",
|
||||
Self::AssignSub => "-=",
|
||||
Self::AssignURightSh => ">>>=",
|
||||
Self::AssignXor => "^=",
|
||||
Self::BoolAnd => "&&",
|
||||
Self::BoolOr => "||",
|
||||
Self::Coalesce => "??",
|
||||
Self::CloseBlock => "}",
|
||||
Self::CloseBracket => "]",
|
||||
Self::CloseParen => ")",
|
||||
Self::Colon => ":",
|
||||
Self::Comma => ",",
|
||||
Self::Dec => "--",
|
||||
Self::Div => "/",
|
||||
Self::Dot => ".",
|
||||
Self::Eq => "==",
|
||||
Self::GreaterThan => ">",
|
||||
Self::GreaterThanOrEq => ">=",
|
||||
Self::Inc => "++",
|
||||
Self::LeftSh => "<<",
|
||||
Self::LessThan => "<",
|
||||
Self::LessThanOrEq => "<=",
|
||||
Self::Mod => "%",
|
||||
Self::Mul => "*",
|
||||
Self::Neg => "~",
|
||||
Self::Not => "!",
|
||||
Self::NotEq => "!=",
|
||||
Self::OpenBlock => "{",
|
||||
Self::OpenBracket => "[",
|
||||
Self::OpenParen => "(",
|
||||
Self::Optional => "?.",
|
||||
Self::Or => "|",
|
||||
Self::Exp => "**",
|
||||
Self::Question => "?",
|
||||
Self::RightSh => ">>",
|
||||
Self::Semicolon => ";",
|
||||
Self::Spread => "...",
|
||||
Self::StrictEq => "===",
|
||||
Self::StrictNotEq => "!==",
|
||||
Self::Sub => "-",
|
||||
Self::URightSh => ">>>",
|
||||
Self::Xor => "^",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryInto<BinaryOp> for Punctuator {
|
||||
type Error = String;
|
||||
fn try_into(self) -> Result<BinaryOp, Self::Error> {
|
||||
self.as_binary_op()
|
||||
.ok_or_else(|| format!("No binary operation for {self}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Punctuator {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Punctuator> for Box<str> {
|
||||
fn from(p: Punctuator) -> Self {
|
||||
p.as_str().into()
|
||||
}
|
||||
}
|
||||
85
javascript-engine/external/boa/boa_ast/src/statement/block.rs
vendored
Normal file
85
javascript-engine/external/boa/boa_ast/src/statement/block.rs
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
//! Block AST node.
|
||||
|
||||
use crate::{
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
Statement, StatementList,
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// A `block` statement (or compound statement in other languages) is used to group zero or
|
||||
/// more statements.
|
||||
///
|
||||
/// The block statement is often called compound statement in other languages.
|
||||
/// It allows you to use multiple statements where ECMAScript expects only one statement.
|
||||
/// Combining statements into blocks is a common practice in ECMAScript. The opposite behavior
|
||||
/// is possible using an empty statement, where you provide no statement, although one is
|
||||
/// required.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-BlockStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq, Default)]
|
||||
pub struct Block {
|
||||
#[cfg_attr(feature = "serde", serde(flatten))]
|
||||
statements: StatementList,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
/// Gets the list of statements and declarations in this block.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn statement_list(&self) -> &StatementList {
|
||||
&self.statements
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Block
|
||||
where
|
||||
T: Into<StatementList>,
|
||||
{
|
||||
fn from(list: T) -> Self {
|
||||
Self {
|
||||
statements: list.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Block {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
format!(
|
||||
"{{\n{}{}}}",
|
||||
self.statements
|
||||
.to_indented_string(interner, indentation + 1),
|
||||
" ".repeat(indentation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Block> for Statement {
|
||||
#[inline]
|
||||
fn from(block: Block) -> Self {
|
||||
Self::Block(block)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Block {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_statement_list(&self.statements)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_statement_list_mut(&mut self.statements)
|
||||
}
|
||||
}
|
||||
119
javascript-engine/external/boa/boa_ast/src/statement/if.rs
vendored
Normal file
119
javascript-engine/external/boa/boa_ast/src/statement/if.rs
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
//! If statement
|
||||
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
statement::Statement,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If
|
||||
/// the condition is [`falsy`][falsy], another statement can be executed.
|
||||
///
|
||||
/// Multiple `if...else` statements can be nested to create an else if clause.
|
||||
///
|
||||
/// Note that there is no elseif (in one word) keyword in JavaScript.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-IfStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
|
||||
/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/truthy
|
||||
/// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/falsy
|
||||
/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct If {
|
||||
condition: Expression,
|
||||
body: Box<Statement>,
|
||||
else_node: Option<Box<Statement>>,
|
||||
}
|
||||
|
||||
impl If {
|
||||
/// Gets the condition of the if statement.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn cond(&self) -> &Expression {
|
||||
&self.condition
|
||||
}
|
||||
|
||||
/// Gets the body to execute if the condition is true.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &Statement {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Gets the `else` node, if it has one.
|
||||
#[inline]
|
||||
pub fn else_node(&self) -> Option<&Statement> {
|
||||
self.else_node.as_ref().map(Box::as_ref)
|
||||
}
|
||||
|
||||
/// Creates an `If` AST node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(condition: Expression, body: Statement, else_node: Option<Statement>) -> Self {
|
||||
Self {
|
||||
condition,
|
||||
body: body.into(),
|
||||
else_node: else_node.map(Box::new),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for If {
|
||||
fn to_indented_string(&self, interner: &Interner, indent: usize) -> String {
|
||||
let mut buf = format!("if ({}) ", self.cond().to_interned_string(interner));
|
||||
match self.else_node() {
|
||||
Some(else_e) => {
|
||||
buf.push_str(&format!(
|
||||
"{} else {}",
|
||||
self.body().to_indented_string(interner, indent),
|
||||
else_e.to_indented_string(interner, indent)
|
||||
));
|
||||
}
|
||||
None => {
|
||||
buf.push_str(&self.body().to_indented_string(interner, indent));
|
||||
}
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<If> for Statement {
|
||||
fn from(if_stm: If) -> Self {
|
||||
Self::If(if_stm)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for If {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.condition));
|
||||
try_break!(visitor.visit_statement(&self.body));
|
||||
if let Some(stmt) = &self.else_node {
|
||||
try_break!(visitor.visit_statement(stmt));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.condition));
|
||||
try_break!(visitor.visit_statement_mut(&mut self.body));
|
||||
if let Some(stmt) = &mut self.else_node {
|
||||
try_break!(visitor.visit_statement_mut(stmt));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
79
javascript-engine/external/boa/boa_ast/src/statement/iteration/break.rs
vendored
Normal file
79
javascript-engine/external/boa/boa_ast/src/statement/iteration/break.rs
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
use boa_interner::{Interner, Sym, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::Statement;
|
||||
|
||||
/// The `break` statement terminates the current loop, switch, or label statement and transfers
|
||||
/// program control to the statement following the terminated statement.
|
||||
///
|
||||
/// The break statement includes an optional label that allows the program to break out of a
|
||||
/// labeled statement. The break statement needs to be nested within the referenced label. The
|
||||
/// labeled statement can be any block statement; it does not have to be preceded by a loop
|
||||
/// statement.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Break {
|
||||
label: Option<Sym>,
|
||||
}
|
||||
|
||||
impl Break {
|
||||
/// Creates a `Break` AST node.
|
||||
#[must_use]
|
||||
pub const fn new(label: Option<Sym>) -> Self {
|
||||
Self { label }
|
||||
}
|
||||
|
||||
/// Gets the label of the break statement, if any.
|
||||
#[must_use]
|
||||
pub const fn label(&self) -> Option<Sym> {
|
||||
self.label
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Break {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
self.label.map_or_else(
|
||||
|| "break".to_owned(),
|
||||
|label| format!("break {}", interner.resolve_expect(label)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Break> for Statement {
|
||||
fn from(break_smt: Break) -> Self {
|
||||
Self::Break(break_smt)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Break {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(sym) = &self.label {
|
||||
visitor.visit_sym(sym)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(sym) = &mut self.label {
|
||||
visitor.visit_sym_mut(sym)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
77
javascript-engine/external/boa/boa_ast/src/statement/iteration/continue.rs
vendored
Normal file
77
javascript-engine/external/boa/boa_ast/src/statement/iteration/continue.rs
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
use crate::statement::Statement;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, Sym, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The `continue` statement terminates execution of the statements in the current iteration of
|
||||
/// the current or labeled loop, and continues execution of the loop with the next iteration.
|
||||
///
|
||||
/// The continue statement can include an optional label that allows the program to jump to the
|
||||
/// next iteration of a labeled loop statement instead of the current loop. In this case, the
|
||||
/// continue statement needs to be nested within this labeled statement.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Continue {
|
||||
label: Option<Sym>,
|
||||
}
|
||||
|
||||
impl Continue {
|
||||
/// Creates a `Continue` AST node.
|
||||
#[must_use]
|
||||
pub const fn new(label: Option<Sym>) -> Self {
|
||||
Self { label }
|
||||
}
|
||||
|
||||
/// Gets the label of this `Continue` statement.
|
||||
#[must_use]
|
||||
pub const fn label(&self) -> Option<Sym> {
|
||||
self.label
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Continue {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
self.label.map_or_else(
|
||||
|| "continue".to_owned(),
|
||||
|label| format!("continue {}", interner.resolve_expect(label)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Continue> for Statement {
|
||||
fn from(cont: Continue) -> Self {
|
||||
Self::Continue(cont)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Continue {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(sym) = &self.label {
|
||||
visitor.visit_sym(sym)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(sym) = &mut self.label {
|
||||
visitor.visit_sym_mut(sym)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
87
javascript-engine/external/boa/boa_ast/src/statement/iteration/do_while_loop.rs
vendored
Normal file
87
javascript-engine/external/boa/boa_ast/src/statement/iteration/do_while_loop.rs
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
statement::Statement,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The `do...while` statement creates a loop that executes a specified statement until the
|
||||
/// test condition evaluates to false.
|
||||
///
|
||||
/// The condition is evaluated after executing the statement, resulting in the specified
|
||||
/// statement executing at least once.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct DoWhileLoop {
|
||||
body: Box<Statement>,
|
||||
condition: Expression,
|
||||
}
|
||||
|
||||
impl DoWhileLoop {
|
||||
/// Gets the body of the do-while loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &Statement {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Gets the condition of the do-while loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn cond(&self) -> &Expression {
|
||||
&self.condition
|
||||
}
|
||||
/// Creates a `DoWhileLoop` AST node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(body: Statement, condition: Expression) -> Self {
|
||||
Self {
|
||||
body: body.into(),
|
||||
condition,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for DoWhileLoop {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
format!(
|
||||
"do {} while ({})",
|
||||
self.body().to_indented_string(interner, indentation),
|
||||
self.cond().to_interned_string(interner)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DoWhileLoop> for Statement {
|
||||
fn from(do_while: DoWhileLoop) -> Self {
|
||||
Self::DoWhileLoop(do_while)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for DoWhileLoop {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_statement(&self.body));
|
||||
visitor.visit_expression(&self.condition)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_statement_mut(&mut self.body));
|
||||
visitor.visit_expression_mut(&mut self.condition)
|
||||
}
|
||||
}
|
||||
98
javascript-engine/external/boa/boa_ast/src/statement/iteration/for_in_loop.rs
vendored
Normal file
98
javascript-engine/external/boa/boa_ast/src/statement/iteration/for_in_loop.rs
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
statement::{iteration::IterableLoopInitializer, Statement},
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// A `for...in` loop statement, as defined by the [spec].
|
||||
///
|
||||
/// [`for...in`][forin] statements loop over all enumerable string properties of an object, including
|
||||
/// inherited properties.
|
||||
///
|
||||
/// [forin]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ForInLoop {
|
||||
initializer: IterableLoopInitializer,
|
||||
target: Expression,
|
||||
body: Box<Statement>,
|
||||
}
|
||||
|
||||
impl ForInLoop {
|
||||
/// Creates a new `ForInLoop`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(initializer: IterableLoopInitializer, target: Expression, body: Statement) -> Self {
|
||||
Self {
|
||||
initializer,
|
||||
target,
|
||||
body: body.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the initializer of the for...in loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn initializer(&self) -> &IterableLoopInitializer {
|
||||
&self.initializer
|
||||
}
|
||||
|
||||
/// Gets the target object of the for...in loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn target(&self) -> &Expression {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Gets the body of the for...in loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &Statement {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for ForInLoop {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = format!(
|
||||
"for ({} in {}) ",
|
||||
self.initializer.to_interned_string(interner),
|
||||
self.target.to_interned_string(interner)
|
||||
);
|
||||
buf.push_str(&self.body().to_indented_string(interner, indentation));
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ForInLoop> for Statement {
|
||||
#[inline]
|
||||
fn from(for_in: ForInLoop) -> Self {
|
||||
Self::ForInLoop(for_in)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ForInLoop {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_iterable_loop_initializer(&self.initializer));
|
||||
try_break!(visitor.visit_expression(&self.target));
|
||||
visitor.visit_statement(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_iterable_loop_initializer_mut(&mut self.initializer));
|
||||
try_break!(visitor.visit_expression_mut(&mut self.target));
|
||||
visitor.visit_statement_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
263
javascript-engine/external/boa/boa_ast/src/statement/iteration/for_loop.rs
vendored
Normal file
263
javascript-engine/external/boa/boa_ast/src/statement/iteration/for_loop.rs
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{
|
||||
declaration::{LexicalDeclaration, VarDeclaration},
|
||||
statement::Statement,
|
||||
Expression,
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The `for` statement creates a loop that consists of three optional expressions.
|
||||
///
|
||||
/// A [`for`][mdn] loop repeats until a specified condition evaluates to `false`.
|
||||
/// The JavaScript for loop is similar to the Java and C for loop.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ForLoop {
|
||||
#[cfg_attr(feature = "serde", serde(flatten))]
|
||||
inner: Box<InnerForLoop>,
|
||||
}
|
||||
|
||||
impl ForLoop {
|
||||
/// Creates a new for loop AST node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
init: Option<ForLoopInitializer>,
|
||||
condition: Option<Expression>,
|
||||
final_expr: Option<Expression>,
|
||||
body: Statement,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the initialization node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn init(&self) -> Option<&ForLoopInitializer> {
|
||||
self.inner.init()
|
||||
}
|
||||
|
||||
/// Gets the loop condition node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn condition(&self) -> Option<&Expression> {
|
||||
self.inner.condition()
|
||||
}
|
||||
|
||||
/// Gets the final expression node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn final_expr(&self) -> Option<&Expression> {
|
||||
self.inner.final_expr()
|
||||
}
|
||||
|
||||
/// Gets the body of the for loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &Statement {
|
||||
self.inner.body()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for ForLoop {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = String::from("for (");
|
||||
if let Some(init) = self.init() {
|
||||
buf.push_str(&init.to_interned_string(interner));
|
||||
}
|
||||
buf.push_str("; ");
|
||||
if let Some(condition) = self.condition() {
|
||||
buf.push_str(&condition.to_interned_string(interner));
|
||||
}
|
||||
buf.push_str("; ");
|
||||
if let Some(final_expr) = self.final_expr() {
|
||||
buf.push_str(&final_expr.to_interned_string(interner));
|
||||
}
|
||||
buf.push_str(&format!(
|
||||
") {}",
|
||||
self.inner.body().to_indented_string(interner, indentation)
|
||||
));
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ForLoop> for Statement {
|
||||
#[inline]
|
||||
fn from(for_loop: ForLoop) -> Self {
|
||||
Self::ForLoop(for_loop)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ForLoop {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(fli) = &self.inner.init {
|
||||
try_break!(visitor.visit_for_loop_initializer(fli));
|
||||
}
|
||||
if let Some(expr) = &self.inner.condition {
|
||||
try_break!(visitor.visit_expression(expr));
|
||||
}
|
||||
if let Some(expr) = &self.inner.final_expr {
|
||||
try_break!(visitor.visit_expression(expr));
|
||||
}
|
||||
visitor.visit_statement(&self.inner.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(fli) = &mut self.inner.init {
|
||||
try_break!(visitor.visit_for_loop_initializer_mut(fli));
|
||||
}
|
||||
if let Some(expr) = &mut self.inner.condition {
|
||||
try_break!(visitor.visit_expression_mut(expr));
|
||||
}
|
||||
if let Some(expr) = &mut self.inner.final_expr {
|
||||
try_break!(visitor.visit_expression_mut(expr));
|
||||
}
|
||||
visitor.visit_statement_mut(&mut self.inner.body)
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner structure to avoid multiple indirections in the heap.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct InnerForLoop {
|
||||
init: Option<ForLoopInitializer>,
|
||||
condition: Option<Expression>,
|
||||
final_expr: Option<Expression>,
|
||||
body: Statement,
|
||||
}
|
||||
|
||||
impl InnerForLoop {
|
||||
/// Creates a new inner for loop.
|
||||
#[inline]
|
||||
const fn new(
|
||||
init: Option<ForLoopInitializer>,
|
||||
condition: Option<Expression>,
|
||||
final_expr: Option<Expression>,
|
||||
body: Statement,
|
||||
) -> Self {
|
||||
Self {
|
||||
init,
|
||||
condition,
|
||||
final_expr,
|
||||
body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the initialization node.
|
||||
#[inline]
|
||||
const fn init(&self) -> Option<&ForLoopInitializer> {
|
||||
self.init.as_ref()
|
||||
}
|
||||
|
||||
/// Gets the loop condition node.
|
||||
#[inline]
|
||||
const fn condition(&self) -> Option<&Expression> {
|
||||
self.condition.as_ref()
|
||||
}
|
||||
|
||||
/// Gets the final expression node.
|
||||
#[inline]
|
||||
const fn final_expr(&self) -> Option<&Expression> {
|
||||
self.final_expr.as_ref()
|
||||
}
|
||||
|
||||
/// Gets the body of the for loop.
|
||||
#[inline]
|
||||
const fn body(&self) -> &Statement {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`ForLoop`] initializer, as defined by the [spec].
|
||||
///
|
||||
/// A `ForLoop` initializer differs a lot from an
|
||||
/// [`IterableLoopInitializer`][super::IterableLoopInitializer], since it can contain any arbitrary
|
||||
/// expression instead of only accessors and patterns. Additionally, it can also contain many variable
|
||||
/// declarations instead of only one.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ForStatement
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ForLoopInitializer {
|
||||
/// An expression initializer.
|
||||
Expression(Expression),
|
||||
/// A var declaration initializer.
|
||||
Var(VarDeclaration),
|
||||
/// A lexical declaration initializer.
|
||||
Lexical(LexicalDeclaration),
|
||||
}
|
||||
|
||||
impl ToInternedString for ForLoopInitializer {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
match self {
|
||||
Self::Var(var) => var.to_interned_string(interner),
|
||||
Self::Lexical(lex) => lex.to_interned_string(interner),
|
||||
Self::Expression(expr) => expr.to_interned_string(interner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expression> for ForLoopInitializer {
|
||||
#[inline]
|
||||
fn from(expr: Expression) -> Self {
|
||||
Self::Expression(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LexicalDeclaration> for ForLoopInitializer {
|
||||
#[inline]
|
||||
fn from(list: LexicalDeclaration) -> Self {
|
||||
Self::Lexical(list)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VarDeclaration> for ForLoopInitializer {
|
||||
#[inline]
|
||||
fn from(list: VarDeclaration) -> Self {
|
||||
Self::Var(list)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ForLoopInitializer {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Expression(expr) => visitor.visit_expression(expr),
|
||||
Self::Var(vd) => visitor.visit_var_declaration(vd),
|
||||
Self::Lexical(ld) => visitor.visit_lexical_declaration(ld),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Expression(expr) => visitor.visit_expression_mut(expr),
|
||||
Self::Var(vd) => visitor.visit_var_declaration_mut(vd),
|
||||
Self::Lexical(ld) => visitor.visit_lexical_declaration_mut(ld),
|
||||
}
|
||||
}
|
||||
}
|
||||
115
javascript-engine/external/boa/boa_ast/src/statement/iteration/for_of_loop.rs
vendored
Normal file
115
javascript-engine/external/boa/boa_ast/src/statement/iteration/for_of_loop.rs
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
statement::{iteration::IterableLoopInitializer, Statement},
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// A `for...of` loop statement, as defined by the [spec].
|
||||
///
|
||||
/// [`for..of`][forof] statements loop over a sequence of values obtained from an iterable object (Array,
|
||||
/// String, Map, generators).
|
||||
///
|
||||
/// This type combines `for..of` and [`for await...of`][forawait] statements in a single structure,
|
||||
/// since `for await...of` is essentially the same statement but with async iterable objects
|
||||
/// as the source of iteration.
|
||||
///
|
||||
/// [forof]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
|
||||
/// [forawait]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ForOfLoop {
|
||||
init: IterableLoopInitializer,
|
||||
iterable: Expression,
|
||||
body: Box<Statement>,
|
||||
r#await: bool,
|
||||
}
|
||||
|
||||
impl ForOfLoop {
|
||||
/// Creates a new "for of" loop AST node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
init: IterableLoopInitializer,
|
||||
iterable: Expression,
|
||||
body: Statement,
|
||||
r#await: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
init,
|
||||
iterable,
|
||||
body: body.into(),
|
||||
r#await,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the initializer of the for...of loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn initializer(&self) -> &IterableLoopInitializer {
|
||||
&self.init
|
||||
}
|
||||
|
||||
/// Gets the iterable expression of the for...of loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn iterable(&self) -> &Expression {
|
||||
&self.iterable
|
||||
}
|
||||
|
||||
/// Gets the body to execute in the for...of loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &Statement {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Returns true if this "for...of" loop is an "for await...of" loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn r#await(&self) -> bool {
|
||||
self.r#await
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for ForOfLoop {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
format!(
|
||||
"for ({} of {}) {}",
|
||||
self.init.to_interned_string(interner),
|
||||
self.iterable.to_interned_string(interner),
|
||||
self.body().to_indented_string(interner, indentation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ForOfLoop> for Statement {
|
||||
#[inline]
|
||||
fn from(for_of: ForOfLoop) -> Self {
|
||||
Self::ForOfLoop(for_of)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for ForOfLoop {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_iterable_loop_initializer(&self.init));
|
||||
try_break!(visitor.visit_expression(&self.iterable));
|
||||
visitor.visit_statement(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_iterable_loop_initializer_mut(&mut self.init));
|
||||
try_break!(visitor.visit_expression_mut(&mut self.iterable));
|
||||
visitor.visit_statement_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
94
javascript-engine/external/boa/boa_ast/src/statement/iteration/mod.rs
vendored
Normal file
94
javascript-engine/external/boa/boa_ast/src/statement/iteration/mod.rs
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
//! Iteration nodes
|
||||
|
||||
mod r#break;
|
||||
mod r#continue;
|
||||
mod do_while_loop;
|
||||
mod for_in_loop;
|
||||
mod for_loop;
|
||||
mod for_of_loop;
|
||||
mod while_loop;
|
||||
|
||||
use crate::{
|
||||
declaration::Binding,
|
||||
expression::{access::PropertyAccess, Identifier},
|
||||
pattern::Pattern,
|
||||
};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
pub use self::{
|
||||
do_while_loop::DoWhileLoop,
|
||||
for_in_loop::ForInLoop,
|
||||
for_loop::{ForLoop, ForLoopInitializer},
|
||||
for_of_loop::ForOfLoop,
|
||||
r#break::Break,
|
||||
r#continue::Continue,
|
||||
while_loop::WhileLoop,
|
||||
};
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
|
||||
/// A `for-in`, `for-of` and `for-await-of` loop initializer.
|
||||
///
|
||||
/// The [spec] specifies only single bindings for the listed types of loops, which makes us
|
||||
/// unable to use plain `LexicalDeclaration`s or `VarStatement`s as initializers, since those
|
||||
/// can have more than one binding.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum IterableLoopInitializer {
|
||||
/// An already declared variable.
|
||||
Identifier(Identifier),
|
||||
/// A property access.
|
||||
Access(PropertyAccess),
|
||||
/// A new var declaration.
|
||||
Var(Binding),
|
||||
/// A new let declaration.
|
||||
Let(Binding),
|
||||
/// A new const declaration.
|
||||
Const(Binding),
|
||||
/// A pattern with already declared variables.
|
||||
Pattern(Pattern),
|
||||
}
|
||||
|
||||
impl ToInternedString for IterableLoopInitializer {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
let (binding, pre) = match self {
|
||||
Self::Identifier(ident) => return ident.to_interned_string(interner),
|
||||
Self::Pattern(pattern) => return pattern.to_interned_string(interner),
|
||||
Self::Access(access) => return access.to_interned_string(interner),
|
||||
Self::Var(binding) => (binding, "var"),
|
||||
Self::Let(binding) => (binding, "let"),
|
||||
Self::Const(binding) => (binding, "const"),
|
||||
};
|
||||
|
||||
format!("{pre} {}", binding.to_interned_string(interner))
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for IterableLoopInitializer {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Identifier(id) => visitor.visit_identifier(id),
|
||||
Self::Access(pa) => visitor.visit_property_access(pa),
|
||||
Self::Var(b) | Self::Let(b) | Self::Const(b) => visitor.visit_binding(b),
|
||||
Self::Pattern(p) => visitor.visit_pattern(p),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Identifier(id) => visitor.visit_identifier_mut(id),
|
||||
Self::Access(pa) => visitor.visit_property_access_mut(pa),
|
||||
Self::Var(b) | Self::Let(b) | Self::Const(b) => visitor.visit_binding_mut(b),
|
||||
Self::Pattern(p) => visitor.visit_pattern_mut(p),
|
||||
}
|
||||
}
|
||||
}
|
||||
88
javascript-engine/external/boa/boa_ast/src/statement/iteration/while_loop.rs
vendored
Normal file
88
javascript-engine/external/boa/boa_ast/src/statement/iteration/while_loop.rs
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
statement::Statement,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The `while` statement creates a loop that executes a specified statement as long as the
|
||||
/// test condition evaluates to `true`.
|
||||
///
|
||||
/// The condition is evaluated before executing the statement.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WhileLoop {
|
||||
condition: Expression,
|
||||
body: Box<Statement>,
|
||||
}
|
||||
|
||||
impl WhileLoop {
|
||||
/// Creates a `WhileLoop` AST node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(condition: Expression, body: Statement) -> Self {
|
||||
Self {
|
||||
condition,
|
||||
body: body.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the condition of the while loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn condition(&self) -> &Expression {
|
||||
&self.condition
|
||||
}
|
||||
|
||||
/// Gets the body of the while loop.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &Statement {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for WhileLoop {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
format!(
|
||||
"while ({}) {}",
|
||||
self.condition().to_interned_string(interner),
|
||||
self.body().to_indented_string(interner, indentation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WhileLoop> for Statement {
|
||||
#[inline]
|
||||
fn from(while_loop: WhileLoop) -> Self {
|
||||
Self::WhileLoop(while_loop)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for WhileLoop {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.condition));
|
||||
visitor.visit_statement(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.condition));
|
||||
visitor.visit_statement_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
154
javascript-engine/external/boa/boa_ast/src/statement/labelled.rs
vendored
Normal file
154
javascript-engine/external/boa/boa_ast/src/statement/labelled.rs
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
use crate::{
|
||||
function::Function,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
Statement,
|
||||
};
|
||||
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The set of Parse Nodes that can be preceded by a label, as defined by the [spec].
|
||||
///
|
||||
/// Semantically, a [`Labelled`] statement should only wrap [`Statement`] nodes. However,
|
||||
/// old ECMAScript implementations supported [labelled function declarations][label-fn] as an extension
|
||||
/// of the grammar. In the ECMAScript 2015 spec, the production of `LabelledStatement` was
|
||||
/// modified to include labelled [`Function`]s as a valid node.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-LabelledItem
|
||||
/// [label-fn]: https://tc39.es/ecma262/#sec-labelled-function-declarations
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum LabelledItem {
|
||||
/// A labelled [`Function`].
|
||||
Function(Function),
|
||||
/// A labelled [`Statement`].
|
||||
Statement(Statement),
|
||||
}
|
||||
|
||||
impl LabelledItem {
|
||||
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
match self {
|
||||
Self::Function(f) => f.to_indented_string(interner, indentation),
|
||||
Self::Statement(stmt) => stmt.to_indented_string(interner, indentation),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for LabelledItem {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
self.to_indented_string(interner, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Function> for LabelledItem {
|
||||
fn from(f: Function) -> Self {
|
||||
Self::Function(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Statement> for LabelledItem {
|
||||
fn from(stmt: Statement) -> Self {
|
||||
Self::Statement(stmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for LabelledItem {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Function(f) => visitor.visit_function(f),
|
||||
Self::Statement(s) => visitor.visit_statement(s),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Function(f) => visitor.visit_function_mut(f),
|
||||
Self::Statement(s) => visitor.visit_statement_mut(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Labelled statement nodes, as defined by the [spec].
|
||||
///
|
||||
/// The method [`Labelled::item`] doesn't return a [`Statement`] for compatibility reasons.
|
||||
/// See [`LabelledItem`] for more information.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-labelled-statements
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Labelled {
|
||||
item: Box<LabelledItem>,
|
||||
label: Sym,
|
||||
}
|
||||
|
||||
impl Labelled {
|
||||
/// Creates a new `Labelled` statement.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(item: LabelledItem, label: Sym) -> Self {
|
||||
Self {
|
||||
item: Box::new(item),
|
||||
label,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the labelled item.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn item(&self) -> &LabelledItem {
|
||||
&self.item
|
||||
}
|
||||
|
||||
/// Gets the label name.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn label(&self) -> Sym {
|
||||
self.label
|
||||
}
|
||||
|
||||
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
format!(
|
||||
"{}: {}",
|
||||
interner.resolve_expect(self.label),
|
||||
self.item.to_indented_string(interner, indentation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Labelled {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
self.to_indented_string(interner, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Labelled> for Statement {
|
||||
fn from(labelled: Labelled) -> Self {
|
||||
Self::Labelled(labelled)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Labelled {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_labelled_item(&self.item));
|
||||
visitor.visit_sym(&self.label)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_labelled_item_mut(&mut self.item));
|
||||
visitor.visit_sym_mut(&mut self.label)
|
||||
}
|
||||
}
|
||||
241
javascript-engine/external/boa/boa_ast/src/statement/mod.rs
vendored
Normal file
241
javascript-engine/external/boa/boa_ast/src/statement/mod.rs
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
//! The [`Statement`] Parse Node, as defined by the [spec].
|
||||
//!
|
||||
//! ECMAScript [statements] are mainly composed of control flow operations, such as [`If`],
|
||||
//! [`WhileLoop`], and [`Break`]. However, it also contains statements such as [`VarDeclaration`],
|
||||
//! [`Block`] or [`Expression`] which are not strictly used for control flow.
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#prod-Statement
|
||||
//! [statements]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements
|
||||
|
||||
mod block;
|
||||
mod r#if;
|
||||
mod labelled;
|
||||
mod r#return;
|
||||
mod switch;
|
||||
mod throw;
|
||||
mod r#try;
|
||||
|
||||
pub mod iteration;
|
||||
|
||||
pub use self::{
|
||||
block::Block,
|
||||
iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop},
|
||||
labelled::{Labelled, LabelledItem},
|
||||
r#if::If,
|
||||
r#return::Return,
|
||||
r#try::{Catch, ErrorHandler, Finally, Try},
|
||||
switch::{Case, Switch},
|
||||
throw::Throw,
|
||||
};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
|
||||
use super::{declaration::VarDeclaration, expression::Expression};
|
||||
|
||||
/// The `Statement` Parse Node.
|
||||
///
|
||||
/// See the [module level documentation][self] for more information.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Statement {
|
||||
/// See [`Block`].
|
||||
Block(Block),
|
||||
|
||||
/// See [`VarDeclaration`]
|
||||
Var(VarDeclaration),
|
||||
|
||||
/// An empty statement.
|
||||
///
|
||||
/// Empty statements do nothing, just return undefined.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-EmptyStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/Empty
|
||||
Empty,
|
||||
|
||||
/// See [`Expression`].
|
||||
Expression(Expression),
|
||||
|
||||
/// See [`If`].
|
||||
If(If),
|
||||
|
||||
/// See [`DoWhileLoop`].
|
||||
DoWhileLoop(DoWhileLoop),
|
||||
|
||||
/// See [`WhileLoop`].
|
||||
WhileLoop(WhileLoop),
|
||||
|
||||
/// See [`ForLoop`].
|
||||
ForLoop(ForLoop),
|
||||
|
||||
/// See [`ForInLoop`].
|
||||
ForInLoop(ForInLoop),
|
||||
|
||||
/// See [`ForOfLoop`].
|
||||
ForOfLoop(ForOfLoop),
|
||||
|
||||
/// See[`Switch`].
|
||||
Switch(Switch),
|
||||
|
||||
/// See [`Continue`].
|
||||
Continue(Continue),
|
||||
|
||||
/// See [`Break`].
|
||||
Break(Break),
|
||||
|
||||
/// See [`Return`].
|
||||
Return(Return),
|
||||
|
||||
// TODO: Possibly add `with` statements.
|
||||
/// See [`Labelled`].
|
||||
Labelled(Labelled),
|
||||
|
||||
/// See [`Throw`].
|
||||
Throw(Throw),
|
||||
|
||||
/// See [`Try`].
|
||||
Try(Try),
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
/// Implements the display formatting with indentation.
|
||||
///
|
||||
/// This will not prefix the value with any indentation. If you want to prefix this with proper
|
||||
/// indents, use [`to_indented_string()`](Self::to_indented_string).
|
||||
pub(super) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut s = match self {
|
||||
Self::Block(block) => return block.to_indented_string(interner, indentation),
|
||||
Self::Var(var) => var.to_interned_string(interner),
|
||||
Self::Empty => return ";".to_owned(),
|
||||
Self::Expression(expr) => expr.to_indented_string(interner, indentation),
|
||||
Self::If(if_smt) => return if_smt.to_indented_string(interner, indentation),
|
||||
Self::DoWhileLoop(do_while) => do_while.to_indented_string(interner, indentation),
|
||||
Self::WhileLoop(while_loop) => {
|
||||
return while_loop.to_indented_string(interner, indentation)
|
||||
}
|
||||
Self::ForLoop(for_loop) => return for_loop.to_indented_string(interner, indentation),
|
||||
Self::ForInLoop(for_in) => return for_in.to_indented_string(interner, indentation),
|
||||
Self::ForOfLoop(for_of) => return for_of.to_indented_string(interner, indentation),
|
||||
Self::Switch(switch) => return switch.to_indented_string(interner, indentation),
|
||||
Self::Continue(cont) => cont.to_interned_string(interner),
|
||||
Self::Break(break_smt) => break_smt.to_interned_string(interner),
|
||||
Self::Return(ret) => ret.to_interned_string(interner),
|
||||
Self::Labelled(labelled) => return labelled.to_interned_string(interner),
|
||||
Self::Throw(throw) => throw.to_interned_string(interner),
|
||||
Self::Try(try_catch) => return try_catch.to_indented_string(interner, indentation),
|
||||
};
|
||||
s.push(';');
|
||||
s
|
||||
}
|
||||
|
||||
/// Abstract operation [`IsLabelledFunction`][spec].
|
||||
///
|
||||
/// This recursively checks if this `Statement` is a labelled function, since adding
|
||||
/// several labels in a function should not change the return value of the abstract operation:
|
||||
///
|
||||
/// ```Javascript
|
||||
/// l1: l2: l3: l4: function f(){ }
|
||||
/// ```
|
||||
///
|
||||
/// This should return `true` for that snippet.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-islabelledfunction
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_labelled_function(&self) -> bool {
|
||||
match self {
|
||||
Self::Labelled(stmt) => match stmt.item() {
|
||||
LabelledItem::Function(_) => true,
|
||||
LabelledItem::Statement(stmt) => stmt.is_labelled_function(),
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Statement {
|
||||
/// Creates a string of the value of the node with the given indentation. For example, an
|
||||
/// indent level of 2 would produce this:
|
||||
///
|
||||
/// ```js
|
||||
/// function hello() {
|
||||
/// console.log("hello");
|
||||
/// }
|
||||
/// hello();
|
||||
/// a = 2;
|
||||
/// ```
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = match *self {
|
||||
Self::Block(_) => String::new(),
|
||||
_ => " ".repeat(indentation),
|
||||
};
|
||||
|
||||
buf.push_str(&self.to_no_indent_string(interner, indentation));
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Statement {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Block(b) => visitor.visit_block(b),
|
||||
Self::Var(v) => visitor.visit_var_declaration(v),
|
||||
Self::Empty => {
|
||||
// do nothing; there is nothing to visit here
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
Self::Expression(e) => visitor.visit_expression(e),
|
||||
Self::If(i) => visitor.visit_if(i),
|
||||
Self::DoWhileLoop(dw) => visitor.visit_do_while_loop(dw),
|
||||
Self::WhileLoop(w) => visitor.visit_while_loop(w),
|
||||
Self::ForLoop(f) => visitor.visit_for_loop(f),
|
||||
Self::ForInLoop(fi) => visitor.visit_for_in_loop(fi),
|
||||
Self::ForOfLoop(fo) => visitor.visit_for_of_loop(fo),
|
||||
Self::Switch(s) => visitor.visit_switch(s),
|
||||
Self::Continue(c) => visitor.visit_continue(c),
|
||||
Self::Break(b) => visitor.visit_break(b),
|
||||
Self::Return(r) => visitor.visit_return(r),
|
||||
Self::Labelled(l) => visitor.visit_labelled(l),
|
||||
Self::Throw(th) => visitor.visit_throw(th),
|
||||
Self::Try(tr) => visitor.visit_try(tr),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Block(b) => visitor.visit_block_mut(b),
|
||||
Self::Var(v) => visitor.visit_var_declaration_mut(v),
|
||||
Self::Empty => {
|
||||
// do nothing; there is nothing to visit here
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
Self::Expression(e) => visitor.visit_expression_mut(e),
|
||||
Self::If(i) => visitor.visit_if_mut(i),
|
||||
Self::DoWhileLoop(dw) => visitor.visit_do_while_loop_mut(dw),
|
||||
Self::WhileLoop(w) => visitor.visit_while_loop_mut(w),
|
||||
Self::ForLoop(f) => visitor.visit_for_loop_mut(f),
|
||||
Self::ForInLoop(fi) => visitor.visit_for_in_loop_mut(fi),
|
||||
Self::ForOfLoop(fo) => visitor.visit_for_of_loop_mut(fo),
|
||||
Self::Switch(s) => visitor.visit_switch_mut(s),
|
||||
Self::Continue(c) => visitor.visit_continue_mut(c),
|
||||
Self::Break(b) => visitor.visit_break_mut(b),
|
||||
Self::Return(r) => visitor.visit_return_mut(r),
|
||||
Self::Labelled(l) => visitor.visit_labelled_mut(l),
|
||||
Self::Throw(th) => visitor.visit_throw_mut(th),
|
||||
Self::Try(tr) => visitor.visit_try_mut(tr),
|
||||
}
|
||||
}
|
||||
}
|
||||
84
javascript-engine/external/boa/boa_ast/src/statement/return.rs
vendored
Normal file
84
javascript-engine/external/boa/boa_ast/src/statement/return.rs
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
statement::Statement,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The `return` statement ends function execution and specifies a value to be returned to the
|
||||
/// function caller.
|
||||
///
|
||||
/// Syntax: `return [expression];`
|
||||
///
|
||||
/// `expression`:
|
||||
/// > The expression whose value is to be returned. If omitted, `undefined` is returned instead.
|
||||
///
|
||||
/// When a `return` statement is used in a function body, the execution of the function is
|
||||
/// stopped. If specified, a given value is returned to the function caller.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Return {
|
||||
target: Option<Expression>,
|
||||
}
|
||||
|
||||
impl Return {
|
||||
/// Gets the target expression value of this `Return` statement.
|
||||
#[must_use]
|
||||
pub const fn target(&self) -> Option<&Expression> {
|
||||
self.target.as_ref()
|
||||
}
|
||||
|
||||
/// Creates a `Return` AST node.
|
||||
#[must_use]
|
||||
pub const fn new(expression: Option<Expression>) -> Self {
|
||||
Self { target: expression }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Return> for Statement {
|
||||
fn from(return_smt: Return) -> Self {
|
||||
Self::Return(return_smt)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Return {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
self.target().map_or_else(
|
||||
|| "return".to_owned(),
|
||||
|ex| format!("return {}", ex.to_interned_string(interner)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Return {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(expr) = &self.target {
|
||||
visitor.visit_expression(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(expr) = &mut self.target {
|
||||
visitor.visit_expression_mut(expr)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
189
javascript-engine/external/boa/boa_ast/src/statement/switch.rs
vendored
Normal file
189
javascript-engine/external/boa/boa_ast/src/statement/switch.rs
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
//! Switch node.
|
||||
//!
|
||||
use crate::{
|
||||
expression::Expression,
|
||||
statement::Statement,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
StatementList,
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// A case clause inside a [`Switch`] statement, as defined by the [spec].
|
||||
///
|
||||
/// Even though every [`Case`] body is a [`StatementList`], it doesn't create a new lexical
|
||||
/// environment. This means any variable declared in a `Case` will be considered as part of the
|
||||
/// lexical environment of the parent [`Switch`] block.
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-CaseClause
|
||||
/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Case {
|
||||
condition: Expression,
|
||||
body: StatementList,
|
||||
}
|
||||
|
||||
impl Case {
|
||||
/// Creates a `Case` AST node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(condition: Expression, body: StatementList) -> Self {
|
||||
Self { condition, body }
|
||||
}
|
||||
|
||||
/// Gets the condition of the case.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn condition(&self) -> &Expression {
|
||||
&self.condition
|
||||
}
|
||||
|
||||
/// Gets the statement listin the body of the case.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn body(&self) -> &StatementList {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Case {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.condition));
|
||||
visitor.visit_statement_list(&self.body)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.condition));
|
||||
visitor.visit_statement_list_mut(&mut self.body)
|
||||
}
|
||||
}
|
||||
|
||||
/// The `switch` statement evaluates an expression, matching the expression's value to a case
|
||||
/// clause, and executes statements associated with that case, as well as statements in cases
|
||||
/// that follow the matching case.
|
||||
///
|
||||
/// A `switch` statement first evaluates its expression. It then looks for the first case
|
||||
/// clause whose expression evaluates to the same value as the result of the input expression
|
||||
/// (using the strict comparison, `===`) and transfers control to that clause, executing the
|
||||
/// associated statements. (If multiple cases match the provided value, the first case that
|
||||
/// matches is selected, even if the cases are not equal to each other.)
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Switch {
|
||||
val: Expression,
|
||||
cases: Box<[Case]>,
|
||||
default: Option<StatementList>,
|
||||
}
|
||||
|
||||
impl Switch {
|
||||
/// Creates a `Switch` AST node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(val: Expression, cases: Box<[Case]>, default: Option<StatementList>) -> Self {
|
||||
Self {
|
||||
val,
|
||||
cases,
|
||||
default,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the value to switch.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn val(&self) -> &Expression {
|
||||
&self.val
|
||||
}
|
||||
|
||||
/// Gets the list of cases for the switch statement.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn cases(&self) -> &[Case] {
|
||||
&self.cases
|
||||
}
|
||||
|
||||
/// Gets the default statement list, if any.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn default(&self) -> Option<&StatementList> {
|
||||
self.default.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Switch {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let indent = " ".repeat(indentation);
|
||||
let mut buf = format!("switch ({}) {{\n", self.val().to_interned_string(interner));
|
||||
for e in self.cases().iter() {
|
||||
buf.push_str(&format!(
|
||||
"{} case {}:\n{}",
|
||||
indent,
|
||||
e.condition().to_interned_string(interner),
|
||||
e.body().to_indented_string(interner, indentation + 2)
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(ref default) = self.default {
|
||||
buf.push_str(&format!(
|
||||
"{indent} default:\n{}",
|
||||
default.to_indented_string(interner, indentation + 2)
|
||||
));
|
||||
}
|
||||
buf.push_str(&format!("{indent}}}"));
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Switch> for Statement {
|
||||
#[inline]
|
||||
fn from(switch: Switch) -> Self {
|
||||
Self::Switch(switch)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Switch {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression(&self.val));
|
||||
for case in self.cases.iter() {
|
||||
try_break!(visitor.visit_case(case));
|
||||
}
|
||||
if let Some(sl) = &self.default {
|
||||
try_break!(visitor.visit_statement_list(sl));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_expression_mut(&mut self.val));
|
||||
for case in self.cases.iter_mut() {
|
||||
try_break!(visitor.visit_case_mut(case));
|
||||
}
|
||||
if let Some(sl) = &mut self.default {
|
||||
try_break!(visitor.visit_statement_list_mut(sl));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
70
javascript-engine/external/boa/boa_ast/src/statement/throw.rs
vendored
Normal file
70
javascript-engine/external/boa/boa_ast/src/statement/throw.rs
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::{
|
||||
statement::Statement,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
Expression,
|
||||
};
|
||||
use boa_interner::{Interner, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The `throw` statement throws a user-defined exception.
|
||||
///
|
||||
/// Syntax: `throw expression;`
|
||||
///
|
||||
/// Execution of the current function will stop (the statements after throw won't be executed),
|
||||
/// and control will be passed to the first catch block in the call stack. If no catch block
|
||||
/// exists among caller functions, the program will terminate.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Throw {
|
||||
target: Expression,
|
||||
}
|
||||
|
||||
impl Throw {
|
||||
/// Gets the target expression of this `Throw` statement.
|
||||
#[must_use]
|
||||
pub const fn target(&self) -> &Expression {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Creates a `Throw` AST node.
|
||||
#[must_use]
|
||||
pub const fn new(target: Expression) -> Self {
|
||||
Self { target }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToInternedString for Throw {
|
||||
fn to_interned_string(&self, interner: &Interner) -> String {
|
||||
format!("throw {}", self.target.to_interned_string(interner))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Throw> for Statement {
|
||||
fn from(trw: Throw) -> Self {
|
||||
Self::Throw(trw)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Throw {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_expression(&self.target)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_expression_mut(&mut self.target)
|
||||
}
|
||||
}
|
||||
256
javascript-engine/external/boa/boa_ast/src/statement/try.rs
vendored
Normal file
256
javascript-engine/external/boa/boa_ast/src/statement/try.rs
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
//! Error handling statements
|
||||
|
||||
use crate::try_break;
|
||||
use crate::visitor::{VisitWith, Visitor, VisitorMut};
|
||||
use crate::{
|
||||
declaration::Binding,
|
||||
statement::{Block, Statement},
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString, ToInternedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// The `try...catch` statement marks a block of statements to try and specifies a response
|
||||
/// should an exception be thrown.
|
||||
///
|
||||
/// The `try` statement consists of a `try`-block, which contains one or more statements. `{}`
|
||||
/// must always be used, even for single statements. At least one `catch`-block, or a
|
||||
/// `finally`-block, must be present.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-TryStatement
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Try {
|
||||
block: Block,
|
||||
handler: ErrorHandler,
|
||||
}
|
||||
|
||||
/// The type of error handler in a [`Try`] statement.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ErrorHandler {
|
||||
/// A [`Catch`] error handler.
|
||||
Catch(Catch),
|
||||
/// A [`Finally`] error handler.
|
||||
Finally(Finally),
|
||||
/// A [`Catch`] and [`Finally`] error handler.
|
||||
Full(Catch, Finally),
|
||||
}
|
||||
|
||||
impl Try {
|
||||
/// Creates a new `Try` AST node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(block: Block, handler: ErrorHandler) -> Self {
|
||||
Self { block, handler }
|
||||
}
|
||||
|
||||
/// Gets the `try` block.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn block(&self) -> &Block {
|
||||
&self.block
|
||||
}
|
||||
|
||||
/// Gets the `catch` block, if any.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn catch(&self) -> Option<&Catch> {
|
||||
match &self.handler {
|
||||
ErrorHandler::Catch(c) | ErrorHandler::Full(c, _) => Some(c),
|
||||
ErrorHandler::Finally(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the `finally` block, if any.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn finally(&self) -> Option<&Finally> {
|
||||
match &self.handler {
|
||||
ErrorHandler::Finally(f) | ErrorHandler::Full(_, f) => Some(f),
|
||||
ErrorHandler::Catch(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Try {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = format!(
|
||||
"{}try {}",
|
||||
" ".repeat(indentation),
|
||||
self.block.to_indented_string(interner, indentation)
|
||||
);
|
||||
|
||||
if let Some(catch) = self.catch() {
|
||||
buf.push_str(&catch.to_indented_string(interner, indentation));
|
||||
}
|
||||
|
||||
if let Some(finally) = self.finally() {
|
||||
buf.push_str(&finally.to_indented_string(interner, indentation));
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Try> for Statement {
|
||||
#[inline]
|
||||
fn from(try_catch: Try) -> Self {
|
||||
Self::Try(try_catch)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Try {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_block(&self.block));
|
||||
if let Some(catch) = &self.catch() {
|
||||
try_break!(visitor.visit_catch(catch));
|
||||
}
|
||||
if let Some(finally) = &self.finally() {
|
||||
try_break!(visitor.visit_finally(finally));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
try_break!(visitor.visit_block_mut(&mut self.block));
|
||||
match &mut self.handler {
|
||||
ErrorHandler::Catch(c) => try_break!(visitor.visit_catch_mut(c)),
|
||||
ErrorHandler::Finally(f) => try_break!(visitor.visit_finally_mut(f)),
|
||||
ErrorHandler::Full(c, f) => {
|
||||
try_break!(visitor.visit_catch_mut(c));
|
||||
try_break!(visitor.visit_finally_mut(f));
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Catch block.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Catch {
|
||||
parameter: Option<Binding>,
|
||||
block: Block,
|
||||
}
|
||||
|
||||
impl Catch {
|
||||
/// Creates a new catch block.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(parameter: Option<Binding>, block: Block) -> Self {
|
||||
Self { parameter, block }
|
||||
}
|
||||
|
||||
/// Gets the parameter of the catch block.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn parameter(&self) -> Option<&Binding> {
|
||||
self.parameter.as_ref()
|
||||
}
|
||||
|
||||
/// Retrieves the catch execution block.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn block(&self) -> &Block {
|
||||
&self.block
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Catch {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = " catch".to_owned();
|
||||
if let Some(ref param) = self.parameter {
|
||||
buf.push_str(&format!("({})", param.to_interned_string(interner)));
|
||||
}
|
||||
buf.push_str(&format!(
|
||||
" {}",
|
||||
self.block.to_indented_string(interner, indentation)
|
||||
));
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Catch {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
if let Some(binding) = &self.parameter {
|
||||
try_break!(visitor.visit_binding(binding));
|
||||
}
|
||||
visitor.visit_block(&self.block)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
if let Some(binding) = &mut self.parameter {
|
||||
try_break!(visitor.visit_binding_mut(binding));
|
||||
}
|
||||
visitor.visit_block_mut(&mut self.block)
|
||||
}
|
||||
}
|
||||
|
||||
/// Finally block.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Finally {
|
||||
block: Block,
|
||||
}
|
||||
|
||||
impl Finally {
|
||||
/// Gets the finally block.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn block(&self) -> &Block {
|
||||
&self.block
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for Finally {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
format!(
|
||||
" finally {}",
|
||||
self.block.to_indented_string(interner, indentation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Block> for Finally {
|
||||
#[inline]
|
||||
fn from(block: Block) -> Self {
|
||||
Self { block }
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for Finally {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
visitor.visit_block(&self.block)
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
visitor.visit_block_mut(&mut self.block)
|
||||
}
|
||||
}
|
||||
215
javascript-engine/external/boa/boa_ast/src/statement_list.rs
vendored
Normal file
215
javascript-engine/external/boa/boa_ast/src/statement_list.rs
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
//! Statement list node.
|
||||
|
||||
use super::Declaration;
|
||||
use crate::{
|
||||
statement::Statement,
|
||||
try_break,
|
||||
visitor::{VisitWith, Visitor, VisitorMut},
|
||||
};
|
||||
use boa_interner::{Interner, ToIndentedString};
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// An item inside a [`StatementList`] Parse Node, as defined by the [spec].
|
||||
///
|
||||
/// Items in a `StatementList` can be either [`Declaration`]s (functions, classes, let/const declarations)
|
||||
/// or [`Statement`]s (if, while, var statement).
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-StatementListItem
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum StatementListItem {
|
||||
/// See [`Statement`].
|
||||
Statement(Statement),
|
||||
/// See [`Declaration`].
|
||||
Declaration(Declaration),
|
||||
}
|
||||
|
||||
impl StatementListItem {
|
||||
/// Returns a node ordering based on the hoistability of each statement.
|
||||
#[must_use]
|
||||
pub const fn hoistable_order(a: &Self, b: &Self) -> Ordering {
|
||||
match (a, b) {
|
||||
(
|
||||
Self::Declaration(Declaration::Function(_)),
|
||||
Self::Declaration(Declaration::Function(_)),
|
||||
) => Ordering::Equal,
|
||||
(_, Self::Declaration(Declaration::Function(_))) => Ordering::Greater,
|
||||
(Self::Declaration(Declaration::Function(_)), _) => Ordering::Less,
|
||||
|
||||
(_, _) => Ordering::Equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for StatementListItem {
|
||||
/// Creates a string of the value of the node with the given indentation. For example, an
|
||||
/// indent level of 2 would produce this:
|
||||
///
|
||||
/// ```js
|
||||
/// function hello() {
|
||||
/// console.log("hello");
|
||||
/// }
|
||||
/// hello();
|
||||
/// a = 2;
|
||||
/// ```
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = " ".repeat(indentation);
|
||||
|
||||
match self {
|
||||
Self::Statement(stmt) => {
|
||||
buf.push_str(&stmt.to_no_indent_string(interner, indentation));
|
||||
}
|
||||
Self::Declaration(decl) => {
|
||||
buf.push_str(&decl.to_indented_string(interner, indentation));
|
||||
}
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Statement> for StatementListItem {
|
||||
#[inline]
|
||||
fn from(stmt: Statement) -> Self {
|
||||
Self::Statement(stmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Declaration> for StatementListItem {
|
||||
#[inline]
|
||||
fn from(decl: Declaration) -> Self {
|
||||
Self::Declaration(decl)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for StatementListItem {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Statement(statement) => visitor.visit_statement(statement),
|
||||
Self::Declaration(declaration) => visitor.visit_declaration(declaration),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
match self {
|
||||
Self::Statement(statement) => visitor.visit_statement_mut(statement),
|
||||
Self::Declaration(declaration) => visitor.visit_declaration_mut(declaration),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// List of statements.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-StatementList
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct StatementList {
|
||||
statements: Box<[StatementListItem]>,
|
||||
strict: bool,
|
||||
}
|
||||
|
||||
impl StatementList {
|
||||
/// Creates a new `StatementList` AST node.
|
||||
#[must_use]
|
||||
pub fn new<S>(statements: S, strict: bool) -> Self
|
||||
where
|
||||
S: Into<Box<[StatementListItem]>>,
|
||||
{
|
||||
Self {
|
||||
statements: statements.into(),
|
||||
strict,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the list of statements.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn statements(&self) -> &[StatementListItem] {
|
||||
&self.statements
|
||||
}
|
||||
|
||||
/// Get the strict mode.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn strict(&self) -> bool {
|
||||
self.strict
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<[StatementListItem]>> for StatementList {
|
||||
#[inline]
|
||||
fn from(stm: Box<[StatementListItem]>) -> Self {
|
||||
Self {
|
||||
statements: stm,
|
||||
strict: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<StatementListItem>> for StatementList {
|
||||
#[inline]
|
||||
fn from(stm: Vec<StatementListItem>) -> Self {
|
||||
Self {
|
||||
statements: stm.into(),
|
||||
strict: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToIndentedString for StatementList {
|
||||
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
|
||||
let mut buf = String::new();
|
||||
// Print statements
|
||||
for item in self.statements.iter() {
|
||||
// We rely on the node to add the correct indent.
|
||||
buf.push_str(&item.to_indented_string(interner, indentation));
|
||||
|
||||
buf.push('\n');
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitWith for StatementList {
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
for statement in self.statements.iter() {
|
||||
try_break!(visitor.visit_statement_list_item(statement));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
for statement in self.statements.iter_mut() {
|
||||
try_break!(visitor.visit_statement_list_item_mut(statement));
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fuzz")]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for StatementList {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
Ok(Self {
|
||||
statements: u.arbitrary()?,
|
||||
strict: false, // disable strictness; this is *not* in source data
|
||||
})
|
||||
}
|
||||
}
|
||||
569
javascript-engine/external/boa/boa_ast/src/visitor.rs
vendored
Normal file
569
javascript-engine/external/boa/boa_ast/src/visitor.rs
vendored
Normal file
@@ -0,0 +1,569 @@
|
||||
//! ECMAScript Abstract Syntax Tree visitors.
|
||||
//!
|
||||
//! This module contains visitors which can be used to inspect or modify AST nodes. This allows for
|
||||
//! fine-grained manipulation of ASTs for analysis, rewriting, or instrumentation.
|
||||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::{
|
||||
declaration::{
|
||||
Binding, Declaration, LexicalDeclaration, VarDeclaration, Variable, VariableList,
|
||||
},
|
||||
expression::{
|
||||
access::{
|
||||
PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SimplePropertyAccess,
|
||||
SuperPropertyAccess,
|
||||
},
|
||||
literal::{ArrayLiteral, Literal, ObjectLiteral, TemplateElement, TemplateLiteral},
|
||||
operator::{
|
||||
assign::{Assign, AssignTarget},
|
||||
Binary, Conditional, Unary,
|
||||
},
|
||||
Await, Call, Expression, Identifier, New, Optional, OptionalOperation,
|
||||
OptionalOperationKind, Spread, SuperCall, TaggedTemplate, Yield,
|
||||
},
|
||||
function::{
|
||||
ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement,
|
||||
FormalParameter, FormalParameterList, Function, Generator, PrivateName,
|
||||
},
|
||||
pattern::{ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern},
|
||||
property::{MethodDefinition, PropertyDefinition, PropertyName},
|
||||
statement::{
|
||||
iteration::{
|
||||
Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForLoopInitializer, ForOfLoop,
|
||||
IterableLoopInitializer, WhileLoop,
|
||||
},
|
||||
Block, Case, Catch, Finally, If, Labelled, LabelledItem, Return, Statement, Switch, Throw,
|
||||
Try,
|
||||
},
|
||||
StatementList, StatementListItem,
|
||||
};
|
||||
use boa_interner::Sym;
|
||||
|
||||
/// `Try`-like conditional unwrapping of `ControlFlow`.
|
||||
#[macro_export]
|
||||
macro_rules! try_break {
|
||||
($expr:expr) => {
|
||||
match $expr {
|
||||
core::ops::ControlFlow::Continue(c) => c,
|
||||
core::ops::ControlFlow::Break(b) => return core::ops::ControlFlow::Break(b),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates the default visit function implementation for a particular type
|
||||
macro_rules! define_visit {
|
||||
($fn_name:ident, $type_name:ident) => {
|
||||
#[doc = concat!("Visits a `", stringify!($type_name), "` with this visitor")]
|
||||
fn $fn_name(&mut self, node: &'ast $type_name) -> ControlFlow<Self::BreakTy> {
|
||||
node.visit_with(self)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates the default mutable visit function implementation for a particular type
|
||||
macro_rules! define_visit_mut {
|
||||
($fn_name:ident, $type_name:ident) => {
|
||||
#[doc = concat!("Visits a `", stringify!($type_name), "` with this visitor, mutably")]
|
||||
fn $fn_name(&mut self, node: &'ast mut $type_name) -> ControlFlow<Self::BreakTy> {
|
||||
node.visit_with_mut(self)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Generates the `NodeRef` and `NodeMutRef` enums from a list of variants.
|
||||
macro_rules! node_ref {
|
||||
(
|
||||
$(
|
||||
$Variant:ident
|
||||
),*
|
||||
$(,)?
|
||||
) => {
|
||||
/// A reference to a node visitable by a [`Visitor`].
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum NodeRef<'a> {
|
||||
$(
|
||||
$Variant(&'a $Variant)
|
||||
),*
|
||||
}
|
||||
|
||||
$(
|
||||
impl<'a> From<&'a $Variant> for NodeRef<'a> {
|
||||
fn from(node: &'a $Variant) -> NodeRef<'a> {
|
||||
Self::$Variant(node)
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
/// A mutable reference to a node visitable by a [`VisitorMut`].
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum NodeRefMut<'a> {
|
||||
$(
|
||||
$Variant(&'a mut $Variant)
|
||||
),*
|
||||
}
|
||||
|
||||
$(
|
||||
impl<'a> From<&'a mut $Variant> for NodeRefMut<'a> {
|
||||
fn from(node: &'a mut $Variant) -> NodeRefMut<'a> {
|
||||
Self::$Variant(node)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
node_ref! {
|
||||
StatementList,
|
||||
StatementListItem,
|
||||
Statement,
|
||||
Declaration,
|
||||
Function,
|
||||
Generator,
|
||||
AsyncFunction,
|
||||
AsyncGenerator,
|
||||
Class,
|
||||
LexicalDeclaration,
|
||||
Block,
|
||||
VarDeclaration,
|
||||
Expression,
|
||||
If,
|
||||
DoWhileLoop,
|
||||
WhileLoop,
|
||||
ForLoop,
|
||||
ForInLoop,
|
||||
ForOfLoop,
|
||||
Switch,
|
||||
Continue,
|
||||
Break,
|
||||
Return,
|
||||
Labelled,
|
||||
Throw,
|
||||
Try,
|
||||
Identifier,
|
||||
FormalParameterList,
|
||||
ClassElement,
|
||||
PrivateName,
|
||||
VariableList,
|
||||
Variable,
|
||||
Binding,
|
||||
Pattern,
|
||||
Literal,
|
||||
ArrayLiteral,
|
||||
ObjectLiteral,
|
||||
Spread,
|
||||
ArrowFunction,
|
||||
AsyncArrowFunction,
|
||||
TemplateLiteral,
|
||||
PropertyAccess,
|
||||
New,
|
||||
Call,
|
||||
SuperCall,
|
||||
Optional,
|
||||
TaggedTemplate,
|
||||
Assign,
|
||||
Unary,
|
||||
Binary,
|
||||
Conditional,
|
||||
Await,
|
||||
Yield,
|
||||
ForLoopInitializer,
|
||||
IterableLoopInitializer,
|
||||
Case,
|
||||
Sym,
|
||||
LabelledItem,
|
||||
Catch,
|
||||
Finally,
|
||||
FormalParameter,
|
||||
PropertyName,
|
||||
MethodDefinition,
|
||||
ObjectPattern,
|
||||
ArrayPattern,
|
||||
PropertyDefinition,
|
||||
TemplateElement,
|
||||
SimplePropertyAccess,
|
||||
PrivatePropertyAccess,
|
||||
SuperPropertyAccess,
|
||||
OptionalOperation,
|
||||
AssignTarget,
|
||||
ObjectPatternElement,
|
||||
ArrayPatternElement,
|
||||
PropertyAccessField,
|
||||
OptionalOperationKind,
|
||||
}
|
||||
|
||||
/// Represents an AST visitor.
|
||||
///
|
||||
/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s
|
||||
/// visitor pattern.
|
||||
pub trait Visitor<'ast>: Sized {
|
||||
/// Type which will be propagated from the visitor if completing early.
|
||||
type BreakTy;
|
||||
|
||||
define_visit!(visit_statement_list, StatementList);
|
||||
define_visit!(visit_statement_list_item, StatementListItem);
|
||||
define_visit!(visit_statement, Statement);
|
||||
define_visit!(visit_declaration, Declaration);
|
||||
define_visit!(visit_function, Function);
|
||||
define_visit!(visit_generator, Generator);
|
||||
define_visit!(visit_async_function, AsyncFunction);
|
||||
define_visit!(visit_async_generator, AsyncGenerator);
|
||||
define_visit!(visit_class, Class);
|
||||
define_visit!(visit_lexical_declaration, LexicalDeclaration);
|
||||
define_visit!(visit_block, Block);
|
||||
define_visit!(visit_var_declaration, VarDeclaration);
|
||||
define_visit!(visit_expression, Expression);
|
||||
define_visit!(visit_if, If);
|
||||
define_visit!(visit_do_while_loop, DoWhileLoop);
|
||||
define_visit!(visit_while_loop, WhileLoop);
|
||||
define_visit!(visit_for_loop, ForLoop);
|
||||
define_visit!(visit_for_in_loop, ForInLoop);
|
||||
define_visit!(visit_for_of_loop, ForOfLoop);
|
||||
define_visit!(visit_switch, Switch);
|
||||
define_visit!(visit_continue, Continue);
|
||||
define_visit!(visit_break, Break);
|
||||
define_visit!(visit_return, Return);
|
||||
define_visit!(visit_labelled, Labelled);
|
||||
define_visit!(visit_throw, Throw);
|
||||
define_visit!(visit_try, Try);
|
||||
define_visit!(visit_identifier, Identifier);
|
||||
define_visit!(visit_formal_parameter_list, FormalParameterList);
|
||||
define_visit!(visit_class_element, ClassElement);
|
||||
define_visit!(visit_private_name, PrivateName);
|
||||
define_visit!(visit_variable_list, VariableList);
|
||||
define_visit!(visit_variable, Variable);
|
||||
define_visit!(visit_binding, Binding);
|
||||
define_visit!(visit_pattern, Pattern);
|
||||
define_visit!(visit_literal, Literal);
|
||||
define_visit!(visit_array_literal, ArrayLiteral);
|
||||
define_visit!(visit_object_literal, ObjectLiteral);
|
||||
define_visit!(visit_spread, Spread);
|
||||
define_visit!(visit_arrow_function, ArrowFunction);
|
||||
define_visit!(visit_async_arrow_function, AsyncArrowFunction);
|
||||
define_visit!(visit_template_literal, TemplateLiteral);
|
||||
define_visit!(visit_property_access, PropertyAccess);
|
||||
define_visit!(visit_new, New);
|
||||
define_visit!(visit_call, Call);
|
||||
define_visit!(visit_super_call, SuperCall);
|
||||
define_visit!(visit_optional, Optional);
|
||||
define_visit!(visit_tagged_template, TaggedTemplate);
|
||||
define_visit!(visit_assign, Assign);
|
||||
define_visit!(visit_unary, Unary);
|
||||
define_visit!(visit_binary, Binary);
|
||||
define_visit!(visit_conditional, Conditional);
|
||||
define_visit!(visit_await, Await);
|
||||
define_visit!(visit_yield, Yield);
|
||||
define_visit!(visit_for_loop_initializer, ForLoopInitializer);
|
||||
define_visit!(visit_iterable_loop_initializer, IterableLoopInitializer);
|
||||
define_visit!(visit_case, Case);
|
||||
define_visit!(visit_sym, Sym);
|
||||
define_visit!(visit_labelled_item, LabelledItem);
|
||||
define_visit!(visit_catch, Catch);
|
||||
define_visit!(visit_finally, Finally);
|
||||
define_visit!(visit_formal_parameter, FormalParameter);
|
||||
define_visit!(visit_property_name, PropertyName);
|
||||
define_visit!(visit_method_definition, MethodDefinition);
|
||||
define_visit!(visit_object_pattern, ObjectPattern);
|
||||
define_visit!(visit_array_pattern, ArrayPattern);
|
||||
define_visit!(visit_property_definition, PropertyDefinition);
|
||||
define_visit!(visit_template_element, TemplateElement);
|
||||
define_visit!(visit_simple_property_access, SimplePropertyAccess);
|
||||
define_visit!(visit_private_property_access, PrivatePropertyAccess);
|
||||
define_visit!(visit_super_property_access, SuperPropertyAccess);
|
||||
define_visit!(visit_optional_operation, OptionalOperation);
|
||||
define_visit!(visit_assign_target, AssignTarget);
|
||||
define_visit!(visit_object_pattern_element, ObjectPatternElement);
|
||||
define_visit!(visit_array_pattern_element, ArrayPatternElement);
|
||||
define_visit!(visit_property_access_field, PropertyAccessField);
|
||||
define_visit!(visit_optional_operation_kind, OptionalOperationKind);
|
||||
|
||||
/// Generic entry point for a node that is visitable by a `Visitor`.
|
||||
///
|
||||
/// This is usually used for generic functions that need to visit an unnamed AST node.
|
||||
fn visit<N: Into<NodeRef<'ast>>>(&mut self, node: N) -> ControlFlow<Self::BreakTy> {
|
||||
let node = node.into();
|
||||
match node {
|
||||
NodeRef::StatementList(n) => self.visit_statement_list(n),
|
||||
NodeRef::StatementListItem(n) => self.visit_statement_list_item(n),
|
||||
NodeRef::Statement(n) => self.visit_statement(n),
|
||||
NodeRef::Declaration(n) => self.visit_declaration(n),
|
||||
NodeRef::Function(n) => self.visit_function(n),
|
||||
NodeRef::Generator(n) => self.visit_generator(n),
|
||||
NodeRef::AsyncFunction(n) => self.visit_async_function(n),
|
||||
NodeRef::AsyncGenerator(n) => self.visit_async_generator(n),
|
||||
NodeRef::Class(n) => self.visit_class(n),
|
||||
NodeRef::LexicalDeclaration(n) => self.visit_lexical_declaration(n),
|
||||
NodeRef::Block(n) => self.visit_block(n),
|
||||
NodeRef::VarDeclaration(n) => self.visit_var_declaration(n),
|
||||
NodeRef::Expression(n) => self.visit_expression(n),
|
||||
NodeRef::If(n) => self.visit_if(n),
|
||||
NodeRef::DoWhileLoop(n) => self.visit_do_while_loop(n),
|
||||
NodeRef::WhileLoop(n) => self.visit_while_loop(n),
|
||||
NodeRef::ForLoop(n) => self.visit_for_loop(n),
|
||||
NodeRef::ForInLoop(n) => self.visit_for_in_loop(n),
|
||||
NodeRef::ForOfLoop(n) => self.visit_for_of_loop(n),
|
||||
NodeRef::Switch(n) => self.visit_switch(n),
|
||||
NodeRef::Continue(n) => self.visit_continue(n),
|
||||
NodeRef::Break(n) => self.visit_break(n),
|
||||
NodeRef::Return(n) => self.visit_return(n),
|
||||
NodeRef::Labelled(n) => self.visit_labelled(n),
|
||||
NodeRef::Throw(n) => self.visit_throw(n),
|
||||
NodeRef::Try(n) => self.visit_try(n),
|
||||
NodeRef::Identifier(n) => self.visit_identifier(n),
|
||||
NodeRef::FormalParameterList(n) => self.visit_formal_parameter_list(n),
|
||||
NodeRef::ClassElement(n) => self.visit_class_element(n),
|
||||
NodeRef::PrivateName(n) => self.visit_private_name(n),
|
||||
NodeRef::VariableList(n) => self.visit_variable_list(n),
|
||||
NodeRef::Variable(n) => self.visit_variable(n),
|
||||
NodeRef::Binding(n) => self.visit_binding(n),
|
||||
NodeRef::Pattern(n) => self.visit_pattern(n),
|
||||
NodeRef::Literal(n) => self.visit_literal(n),
|
||||
NodeRef::ArrayLiteral(n) => self.visit_array_literal(n),
|
||||
NodeRef::ObjectLiteral(n) => self.visit_object_literal(n),
|
||||
NodeRef::Spread(n) => self.visit_spread(n),
|
||||
NodeRef::ArrowFunction(n) => self.visit_arrow_function(n),
|
||||
NodeRef::AsyncArrowFunction(n) => self.visit_async_arrow_function(n),
|
||||
NodeRef::TemplateLiteral(n) => self.visit_template_literal(n),
|
||||
NodeRef::PropertyAccess(n) => self.visit_property_access(n),
|
||||
NodeRef::New(n) => self.visit_new(n),
|
||||
NodeRef::Call(n) => self.visit_call(n),
|
||||
NodeRef::SuperCall(n) => self.visit_super_call(n),
|
||||
NodeRef::Optional(n) => self.visit_optional(n),
|
||||
NodeRef::TaggedTemplate(n) => self.visit_tagged_template(n),
|
||||
NodeRef::Assign(n) => self.visit_assign(n),
|
||||
NodeRef::Unary(n) => self.visit_unary(n),
|
||||
NodeRef::Binary(n) => self.visit_binary(n),
|
||||
NodeRef::Conditional(n) => self.visit_conditional(n),
|
||||
NodeRef::Await(n) => self.visit_await(n),
|
||||
NodeRef::Yield(n) => self.visit_yield(n),
|
||||
NodeRef::ForLoopInitializer(n) => self.visit_for_loop_initializer(n),
|
||||
NodeRef::IterableLoopInitializer(n) => self.visit_iterable_loop_initializer(n),
|
||||
NodeRef::Case(n) => self.visit_case(n),
|
||||
NodeRef::Sym(n) => self.visit_sym(n),
|
||||
NodeRef::LabelledItem(n) => self.visit_labelled_item(n),
|
||||
NodeRef::Catch(n) => self.visit_catch(n),
|
||||
NodeRef::Finally(n) => self.visit_finally(n),
|
||||
NodeRef::FormalParameter(n) => self.visit_formal_parameter(n),
|
||||
NodeRef::PropertyName(n) => self.visit_property_name(n),
|
||||
NodeRef::MethodDefinition(n) => self.visit_method_definition(n),
|
||||
NodeRef::ObjectPattern(n) => self.visit_object_pattern(n),
|
||||
NodeRef::ArrayPattern(n) => self.visit_array_pattern(n),
|
||||
NodeRef::PropertyDefinition(n) => self.visit_property_definition(n),
|
||||
NodeRef::TemplateElement(n) => self.visit_template_element(n),
|
||||
NodeRef::SimplePropertyAccess(n) => self.visit_simple_property_access(n),
|
||||
NodeRef::PrivatePropertyAccess(n) => self.visit_private_property_access(n),
|
||||
NodeRef::SuperPropertyAccess(n) => self.visit_super_property_access(n),
|
||||
NodeRef::OptionalOperation(n) => self.visit_optional_operation(n),
|
||||
NodeRef::AssignTarget(n) => self.visit_assign_target(n),
|
||||
NodeRef::ObjectPatternElement(n) => self.visit_object_pattern_element(n),
|
||||
NodeRef::ArrayPatternElement(n) => self.visit_array_pattern_element(n),
|
||||
NodeRef::PropertyAccessField(n) => self.visit_property_access_field(n),
|
||||
NodeRef::OptionalOperationKind(n) => self.visit_optional_operation_kind(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an AST visitor which can modify AST content.
|
||||
///
|
||||
/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s
|
||||
/// visitor pattern.
|
||||
pub trait VisitorMut<'ast>: Sized {
|
||||
/// Type which will be propagated from the visitor if completing early.
|
||||
type BreakTy;
|
||||
|
||||
define_visit_mut!(visit_statement_list_mut, StatementList);
|
||||
define_visit_mut!(visit_statement_list_item_mut, StatementListItem);
|
||||
define_visit_mut!(visit_statement_mut, Statement);
|
||||
define_visit_mut!(visit_declaration_mut, Declaration);
|
||||
define_visit_mut!(visit_function_mut, Function);
|
||||
define_visit_mut!(visit_generator_mut, Generator);
|
||||
define_visit_mut!(visit_async_function_mut, AsyncFunction);
|
||||
define_visit_mut!(visit_async_generator_mut, AsyncGenerator);
|
||||
define_visit_mut!(visit_class_mut, Class);
|
||||
define_visit_mut!(visit_lexical_declaration_mut, LexicalDeclaration);
|
||||
define_visit_mut!(visit_block_mut, Block);
|
||||
define_visit_mut!(visit_var_declaration_mut, VarDeclaration);
|
||||
define_visit_mut!(visit_expression_mut, Expression);
|
||||
define_visit_mut!(visit_if_mut, If);
|
||||
define_visit_mut!(visit_do_while_loop_mut, DoWhileLoop);
|
||||
define_visit_mut!(visit_while_loop_mut, WhileLoop);
|
||||
define_visit_mut!(visit_for_loop_mut, ForLoop);
|
||||
define_visit_mut!(visit_for_in_loop_mut, ForInLoop);
|
||||
define_visit_mut!(visit_for_of_loop_mut, ForOfLoop);
|
||||
define_visit_mut!(visit_switch_mut, Switch);
|
||||
define_visit_mut!(visit_continue_mut, Continue);
|
||||
define_visit_mut!(visit_break_mut, Break);
|
||||
define_visit_mut!(visit_return_mut, Return);
|
||||
define_visit_mut!(visit_labelled_mut, Labelled);
|
||||
define_visit_mut!(visit_throw_mut, Throw);
|
||||
define_visit_mut!(visit_try_mut, Try);
|
||||
define_visit_mut!(visit_identifier_mut, Identifier);
|
||||
define_visit_mut!(visit_formal_parameter_list_mut, FormalParameterList);
|
||||
define_visit_mut!(visit_class_element_mut, ClassElement);
|
||||
define_visit_mut!(visit_private_name_mut, PrivateName);
|
||||
define_visit_mut!(visit_variable_list_mut, VariableList);
|
||||
define_visit_mut!(visit_variable_mut, Variable);
|
||||
define_visit_mut!(visit_binding_mut, Binding);
|
||||
define_visit_mut!(visit_pattern_mut, Pattern);
|
||||
define_visit_mut!(visit_literal_mut, Literal);
|
||||
define_visit_mut!(visit_array_literal_mut, ArrayLiteral);
|
||||
define_visit_mut!(visit_object_literal_mut, ObjectLiteral);
|
||||
define_visit_mut!(visit_spread_mut, Spread);
|
||||
define_visit_mut!(visit_arrow_function_mut, ArrowFunction);
|
||||
define_visit_mut!(visit_async_arrow_function_mut, AsyncArrowFunction);
|
||||
define_visit_mut!(visit_template_literal_mut, TemplateLiteral);
|
||||
define_visit_mut!(visit_property_access_mut, PropertyAccess);
|
||||
define_visit_mut!(visit_new_mut, New);
|
||||
define_visit_mut!(visit_call_mut, Call);
|
||||
define_visit_mut!(visit_super_call_mut, SuperCall);
|
||||
define_visit_mut!(visit_optional_mut, Optional);
|
||||
define_visit_mut!(visit_tagged_template_mut, TaggedTemplate);
|
||||
define_visit_mut!(visit_assign_mut, Assign);
|
||||
define_visit_mut!(visit_unary_mut, Unary);
|
||||
define_visit_mut!(visit_binary_mut, Binary);
|
||||
define_visit_mut!(visit_conditional_mut, Conditional);
|
||||
define_visit_mut!(visit_await_mut, Await);
|
||||
define_visit_mut!(visit_yield_mut, Yield);
|
||||
define_visit_mut!(visit_for_loop_initializer_mut, ForLoopInitializer);
|
||||
define_visit_mut!(visit_iterable_loop_initializer_mut, IterableLoopInitializer);
|
||||
define_visit_mut!(visit_case_mut, Case);
|
||||
define_visit_mut!(visit_sym_mut, Sym);
|
||||
define_visit_mut!(visit_labelled_item_mut, LabelledItem);
|
||||
define_visit_mut!(visit_catch_mut, Catch);
|
||||
define_visit_mut!(visit_finally_mut, Finally);
|
||||
define_visit_mut!(visit_formal_parameter_mut, FormalParameter);
|
||||
define_visit_mut!(visit_property_name_mut, PropertyName);
|
||||
define_visit_mut!(visit_method_definition_mut, MethodDefinition);
|
||||
define_visit_mut!(visit_object_pattern_mut, ObjectPattern);
|
||||
define_visit_mut!(visit_array_pattern_mut, ArrayPattern);
|
||||
define_visit_mut!(visit_property_definition_mut, PropertyDefinition);
|
||||
define_visit_mut!(visit_template_element_mut, TemplateElement);
|
||||
define_visit_mut!(visit_simple_property_access_mut, SimplePropertyAccess);
|
||||
define_visit_mut!(visit_private_property_access_mut, PrivatePropertyAccess);
|
||||
define_visit_mut!(visit_super_property_access_mut, SuperPropertyAccess);
|
||||
define_visit_mut!(visit_optional_operation_mut, OptionalOperation);
|
||||
define_visit_mut!(visit_assign_target_mut, AssignTarget);
|
||||
define_visit_mut!(visit_object_pattern_element_mut, ObjectPatternElement);
|
||||
define_visit_mut!(visit_array_pattern_element_mut, ArrayPatternElement);
|
||||
define_visit_mut!(visit_property_access_field_mut, PropertyAccessField);
|
||||
define_visit_mut!(visit_optional_operation_kind_mut, OptionalOperationKind);
|
||||
|
||||
/// Generic entry point for a node that is visitable by a `VisitorMut`.
|
||||
///
|
||||
/// This is usually used for generic functions that need to visit an unnamed AST node.
|
||||
fn visit<N: Into<NodeRefMut<'ast>>>(&mut self, node: N) -> ControlFlow<Self::BreakTy> {
|
||||
let node = node.into();
|
||||
match node {
|
||||
NodeRefMut::StatementList(n) => self.visit_statement_list_mut(n),
|
||||
NodeRefMut::StatementListItem(n) => self.visit_statement_list_item_mut(n),
|
||||
NodeRefMut::Statement(n) => self.visit_statement_mut(n),
|
||||
NodeRefMut::Declaration(n) => self.visit_declaration_mut(n),
|
||||
NodeRefMut::Function(n) => self.visit_function_mut(n),
|
||||
NodeRefMut::Generator(n) => self.visit_generator_mut(n),
|
||||
NodeRefMut::AsyncFunction(n) => self.visit_async_function_mut(n),
|
||||
NodeRefMut::AsyncGenerator(n) => self.visit_async_generator_mut(n),
|
||||
NodeRefMut::Class(n) => self.visit_class_mut(n),
|
||||
NodeRefMut::LexicalDeclaration(n) => self.visit_lexical_declaration_mut(n),
|
||||
NodeRefMut::Block(n) => self.visit_block_mut(n),
|
||||
NodeRefMut::VarDeclaration(n) => self.visit_var_declaration_mut(n),
|
||||
NodeRefMut::Expression(n) => self.visit_expression_mut(n),
|
||||
NodeRefMut::If(n) => self.visit_if_mut(n),
|
||||
NodeRefMut::DoWhileLoop(n) => self.visit_do_while_loop_mut(n),
|
||||
NodeRefMut::WhileLoop(n) => self.visit_while_loop_mut(n),
|
||||
NodeRefMut::ForLoop(n) => self.visit_for_loop_mut(n),
|
||||
NodeRefMut::ForInLoop(n) => self.visit_for_in_loop_mut(n),
|
||||
NodeRefMut::ForOfLoop(n) => self.visit_for_of_loop_mut(n),
|
||||
NodeRefMut::Switch(n) => self.visit_switch_mut(n),
|
||||
NodeRefMut::Continue(n) => self.visit_continue_mut(n),
|
||||
NodeRefMut::Break(n) => self.visit_break_mut(n),
|
||||
NodeRefMut::Return(n) => self.visit_return_mut(n),
|
||||
NodeRefMut::Labelled(n) => self.visit_labelled_mut(n),
|
||||
NodeRefMut::Throw(n) => self.visit_throw_mut(n),
|
||||
NodeRefMut::Try(n) => self.visit_try_mut(n),
|
||||
NodeRefMut::Identifier(n) => self.visit_identifier_mut(n),
|
||||
NodeRefMut::FormalParameterList(n) => self.visit_formal_parameter_list_mut(n),
|
||||
NodeRefMut::ClassElement(n) => self.visit_class_element_mut(n),
|
||||
NodeRefMut::PrivateName(n) => self.visit_private_name_mut(n),
|
||||
NodeRefMut::VariableList(n) => self.visit_variable_list_mut(n),
|
||||
NodeRefMut::Variable(n) => self.visit_variable_mut(n),
|
||||
NodeRefMut::Binding(n) => self.visit_binding_mut(n),
|
||||
NodeRefMut::Pattern(n) => self.visit_pattern_mut(n),
|
||||
NodeRefMut::Literal(n) => self.visit_literal_mut(n),
|
||||
NodeRefMut::ArrayLiteral(n) => self.visit_array_literal_mut(n),
|
||||
NodeRefMut::ObjectLiteral(n) => self.visit_object_literal_mut(n),
|
||||
NodeRefMut::Spread(n) => self.visit_spread_mut(n),
|
||||
NodeRefMut::ArrowFunction(n) => self.visit_arrow_function_mut(n),
|
||||
NodeRefMut::AsyncArrowFunction(n) => self.visit_async_arrow_function_mut(n),
|
||||
NodeRefMut::TemplateLiteral(n) => self.visit_template_literal_mut(n),
|
||||
NodeRefMut::PropertyAccess(n) => self.visit_property_access_mut(n),
|
||||
NodeRefMut::New(n) => self.visit_new_mut(n),
|
||||
NodeRefMut::Call(n) => self.visit_call_mut(n),
|
||||
NodeRefMut::SuperCall(n) => self.visit_super_call_mut(n),
|
||||
NodeRefMut::Optional(n) => self.visit_optional_mut(n),
|
||||
NodeRefMut::TaggedTemplate(n) => self.visit_tagged_template_mut(n),
|
||||
NodeRefMut::Assign(n) => self.visit_assign_mut(n),
|
||||
NodeRefMut::Unary(n) => self.visit_unary_mut(n),
|
||||
NodeRefMut::Binary(n) => self.visit_binary_mut(n),
|
||||
NodeRefMut::Conditional(n) => self.visit_conditional_mut(n),
|
||||
NodeRefMut::Await(n) => self.visit_await_mut(n),
|
||||
NodeRefMut::Yield(n) => self.visit_yield_mut(n),
|
||||
NodeRefMut::ForLoopInitializer(n) => self.visit_for_loop_initializer_mut(n),
|
||||
NodeRefMut::IterableLoopInitializer(n) => self.visit_iterable_loop_initializer_mut(n),
|
||||
NodeRefMut::Case(n) => self.visit_case_mut(n),
|
||||
NodeRefMut::Sym(n) => self.visit_sym_mut(n),
|
||||
NodeRefMut::LabelledItem(n) => self.visit_labelled_item_mut(n),
|
||||
NodeRefMut::Catch(n) => self.visit_catch_mut(n),
|
||||
NodeRefMut::Finally(n) => self.visit_finally_mut(n),
|
||||
NodeRefMut::FormalParameter(n) => self.visit_formal_parameter_mut(n),
|
||||
NodeRefMut::PropertyName(n) => self.visit_property_name_mut(n),
|
||||
NodeRefMut::MethodDefinition(n) => self.visit_method_definition_mut(n),
|
||||
NodeRefMut::ObjectPattern(n) => self.visit_object_pattern_mut(n),
|
||||
NodeRefMut::ArrayPattern(n) => self.visit_array_pattern_mut(n),
|
||||
NodeRefMut::PropertyDefinition(n) => self.visit_property_definition_mut(n),
|
||||
NodeRefMut::TemplateElement(n) => self.visit_template_element_mut(n),
|
||||
NodeRefMut::SimplePropertyAccess(n) => self.visit_simple_property_access_mut(n),
|
||||
NodeRefMut::PrivatePropertyAccess(n) => self.visit_private_property_access_mut(n),
|
||||
NodeRefMut::SuperPropertyAccess(n) => self.visit_super_property_access_mut(n),
|
||||
NodeRefMut::OptionalOperation(n) => self.visit_optional_operation_mut(n),
|
||||
NodeRefMut::AssignTarget(n) => self.visit_assign_target_mut(n),
|
||||
NodeRefMut::ObjectPatternElement(n) => self.visit_object_pattern_element_mut(n),
|
||||
NodeRefMut::ArrayPatternElement(n) => self.visit_array_pattern_element_mut(n),
|
||||
NodeRefMut::PropertyAccessField(n) => self.visit_property_access_field_mut(n),
|
||||
NodeRefMut::OptionalOperationKind(n) => self.visit_optional_operation_kind_mut(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Denotes that a type may be visited, providing a method which allows a visitor to traverse its
|
||||
/// private fields.
|
||||
pub trait VisitWith {
|
||||
/// Visit this node with the provided visitor.
|
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>;
|
||||
|
||||
/// Visit this node with the provided visitor mutably, allowing the visitor to modify private
|
||||
/// fields.
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>;
|
||||
}
|
||||
|
||||
// implementation for Sym as it is out-of-crate
|
||||
impl VisitWith for Sym {
|
||||
fn visit_with<'a, V>(&'a self, _visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
core::ops::ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, _visitor: &mut V) -> ControlFlow<V::BreakTy>
|
||||
where
|
||||
V: VisitorMut<'a>,
|
||||
{
|
||||
core::ops::ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user