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