mirror of https://github.com/boa-dev/boa.git
Browse Source
This Pull Request replaces `contains`, `contains_arguments`, `has_direct_super` and `function_contains_super` with visitors. (~1000 removed lines!) Also, the new visitor implementation caught a bug where we weren't setting the home object of async functions, generators and async generators for methods of classes, which caused a stack overflow on `super` calls, and I think that's pretty cool! Next is `var_declared_names`, `lexically_declared_names` and friends, which will be on another PR.pull/2393/head
José Julián Espina
2 years ago
65 changed files with 539 additions and 1519 deletions
@ -0,0 +1,262 @@ |
|||||||
|
//! Definitions of various **Syntax-Directed Operations** used in the [spec].
|
||||||
|
//!
|
||||||
|
//! [spec]: https://tc39.es/ecma262/#sec-syntax-directed-operations
|
||||||
|
|
||||||
|
use core::ops::ControlFlow; |
||||||
|
|
||||||
|
use boa_interner::Sym; |
||||||
|
|
||||||
|
use crate::{ |
||||||
|
expression::{access::SuperPropertyAccess, Await, Identifier, SuperCall, Yield}, |
||||||
|
function::{ |
||||||
|
ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, Function, Generator, |
||||||
|
}, |
||||||
|
property::{MethodDefinition, PropertyDefinition}, |
||||||
|
visitor::{VisitWith, Visitor}, |
||||||
|
Expression, |
||||||
|
}; |
||||||
|
|
||||||
|
/// 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, |
||||||
|
} |
||||||
|
|
||||||
|
/// 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_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_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] |
||||||
|
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), |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue