use crate::{
builtins::{iterable::get_iterator, Array},
environment::lexical_environment::VariableScope,
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, Identifier, Node},
Context, JsResult, JsValue,
};
use std::fmt;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
pub mod arrow_function_decl;
pub mod async_function_decl;
pub mod async_function_expr;
pub mod function_decl;
pub mod function_expr;
pub use self::{
arrow_function_decl::ArrowFunctionDecl, async_function_decl::AsyncFunctionDecl,
async_function_expr::AsyncFunctionExpr, function_decl::FunctionDecl,
function_expr::FunctionExpr,
};
#[cfg(test)]
mod tests;
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum DeclarationList {
Const(Box<[Declaration]>),
Let(Box<[Declaration]>),
Var(Box<[Declaration]>),
}
impl Executable for DeclarationList {
fn run(&self, context: &mut Context) -> JsResult<JsValue> {
for decl in self.as_ref() {
use DeclarationList::*;
let val = match decl.init() {
None if self.is_const() => {
return context.throw_syntax_error("missing = in const declaration")
}
Some(init) => init.run(context)?,
None => JsValue::undefined(),
};
match &decl {
Declaration::Identifier { ident, init } => {
if self.is_var() && context.has_binding(ident.as_ref())? {
if init.is_some() {
context.set_mutable_binding(ident.as_ref(), val, context.strict())?;
}
continue;
}
match &self {
Const(_) => context.create_immutable_binding(
ident.as_ref(),
false,
VariableScope::Block,
)?,
Let(_) => context.create_mutable_binding(
ident.as_ref(),
false,
VariableScope::Block,
)?,
Var(_) => context.create_mutable_binding(
ident.as_ref(),
false,
VariableScope::Function,
)?,
}
context.initialize_binding(ident.as_ref(), val)?;
}
Declaration::Pattern(p) => {
for (ident, value) in p.run(None, context)? {
if self.is_var() && context.has_binding(ident.as_ref())? {
if !value.is_undefined() {
context.set_mutable_binding(
ident.as_ref(),
value,
context.strict(),
)?;
}
continue;
}
match &self {
Const(_) => context.create_immutable_binding(
ident.as_ref(),
false,
VariableScope::Block,
)?,
Let(_) => context.create_mutable_binding(
ident.as_ref(),
false,
VariableScope::Block,
)?,
Var(_) => context.create_mutable_binding(
ident.as_ref(),
false,
VariableScope::Function,
)?,
}
context.initialize_binding(ident.as_ref(), value)?;
}
}
}
}
Ok(JsValue::undefined())
}
}
impl DeclarationList {
#[allow(dead_code)]
pub(in crate::syntax) fn is_let(&self) -> bool {
matches!(self, Self::Let(_))
}
pub(in crate::syntax) fn is_const(&self) -> bool {
matches!(self, Self::Const(_))
}
pub(in crate::syntax) fn is_var(&self) -> bool {
matches!(self, Self::Var(_))
}
}
impl AsRef<[Declaration]> for DeclarationList {
fn as_ref(&self) -> &[Declaration] {
use DeclarationList::*;
match self {
Var(list) | Const(list) | Let(list) => list,
}
}
}
impl fmt::Display for DeclarationList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.as_ref().is_empty() {
use DeclarationList::*;
match &self {
Let(_) => write!(f, "let ")?,
Const(_) => write!(f, "const ")?,
Var(_) => write!(f, "var ")?,
}
join_nodes(f, self.as_ref())
} else {
Ok(())
}
}
}
impl From<DeclarationList> for Node {
fn from(list: DeclarationList) -> Self {
use DeclarationList::*;
match &list {
Let(_) => Node::LetDeclList(list),
Const(_) => Node::ConstDeclList(list),
Var(_) => Node::VarDeclList(list),
}
}
}
impl From<Declaration> for Box<[Declaration]> {
fn from(d: Declaration) -> Self {
Box::new([d])
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum Declaration {
Identifier {
ident: Identifier,
init: Option<Node>,
},
Pattern(DeclarationPattern),
}
impl fmt::Display for Declaration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Self::Identifier { ident, init } => {
fmt::Display::fmt(&ident, f)?;
if let Some(ref init) = &init {
write!(f, " = {}", init)?;
}
}
Self::Pattern(pattern) => {
fmt::Display::fmt(&pattern, f)?;
}
}
Ok(())
}
}
impl Declaration {
#[inline]
pub(in crate::syntax) fn new_with_identifier<N, I>(ident: N, init: I) -> Self
where
N: Into<Identifier>,
I: Into<Option<Node>>,
{
Self::Identifier {
ident: ident.into(),
init: init.into(),
}
}
#[inline]
pub(in crate::syntax) fn new_with_object_pattern<I>(
bindings: Vec<BindingPatternTypeObject>,
init: I,
) -> Self
where
I: Into<Option<Node>>,
{
Self::Pattern(DeclarationPattern::Object(DeclarationPatternObject::new(
bindings,
init.into(),
)))
}
#[inline]
pub(in crate::syntax) fn new_with_array_pattern<I>(
bindings: Vec<BindingPatternTypeArray>,
init: I,
) -> Self
where
I: Into<Option<Node>>,
{
Self::Pattern(DeclarationPattern::Array(DeclarationPatternArray::new(
bindings,
init.into(),
)))
}
#[inline]
pub(crate) fn init(&self) -> Option<&Node> {
match &self {
Self::Identifier { init, .. } => init.as_ref(),
Self::Pattern(pattern) => pattern.init(),
}
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum DeclarationPattern {
Object(DeclarationPatternObject),
Array(DeclarationPatternArray),
}
impl fmt::Display for DeclarationPattern {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
DeclarationPattern::Object(o) => {
fmt::Display::fmt(o, f)?;
}
DeclarationPattern::Array(a) => {
fmt::Display::fmt(a, f)?;
}
}
Ok(())
}
}
impl DeclarationPattern {
#[inline]
pub(in crate::syntax) fn run(
&self,
init: Option<JsValue>,
context: &mut Context,
) -> JsResult<Vec<(Box<str>, JsValue)>> {
match &self {
DeclarationPattern::Object(pattern) => pattern.run(init, context),
DeclarationPattern::Array(pattern) => pattern.run(init, context),
}
}
#[inline]
pub fn idents(&self) -> Vec<&str> {
match &self {
DeclarationPattern::Object(pattern) => pattern.idents(),
DeclarationPattern::Array(pattern) => pattern.idents(),
}
}
#[inline]
pub fn init(&self) -> Option<&Node> {
match &self {
DeclarationPattern::Object(pattern) => pattern.init(),
DeclarationPattern::Array(pattern) => pattern.init(),
}
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct DeclarationPatternObject {
bindings: Vec<BindingPatternTypeObject>,
init: Option<Node>,
}
impl fmt::Display for DeclarationPatternObject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt("{", f)?;
for (i, binding) in self.bindings.iter().enumerate() {
if i == self.bindings.len() - 1 {
write!(f, "{} ", binding)?;
} else {
write!(f, "{},", binding)?;
}
}
fmt::Display::fmt("}", f)?;
if let Some(ref init) = self.init {
write!(f, " = {}", init)?;
}
Ok(())
}
}
impl DeclarationPatternObject {
#[inline]
pub(in crate::syntax) fn new(
bindings: Vec<BindingPatternTypeObject>,
init: Option<Node>,
) -> Self {
Self { bindings, init }
}
#[inline]
pub(in crate::syntax) fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
pub(in crate::syntax) fn run(
&self,
init: Option<JsValue>,
context: &mut Context,
) -> JsResult<Vec<(Box<str>, JsValue)>> {
let value = if let Some(value) = init {
value
} else if let Some(node) = &self.init {
node.run(context)?
} else {
JsValue::undefined()
};
if value.is_null() {
return Err(context.construct_type_error("Cannot destructure 'null' value"));
}
if value.is_undefined() {
return Err(context.construct_type_error("Cannot destructure 'undefined' value"));
}
let value = value.require_object_coercible(context)?;
let mut results = Vec::new();
for binding in &self.bindings {
use BindingPatternTypeObject::*;
match binding {
Empty => {
}
SingleName {
ident,
property_name,
default_init,
} => {
let mut v = value.get_field(property_name.as_ref(), context)?;
if let Some(init) = default_init {
if v.is_undefined() {
v = init.run(context)?;
}
}
results.push((ident.clone(), v));
}
RestProperty {
ident,
excluded_keys,
} => {
let mut rest_obj = context.construct_object();
rest_obj.copy_data_properties(value, excluded_keys.clone(), context)?;
results.push((ident.clone(), rest_obj.into()));
}
BindingPattern {
ident,
pattern,
default_init,
} => {
let mut v = value.get_field(ident.as_ref(), context)?;
if let Some(init) = default_init {
if v.is_undefined() {
v = init.run(context)?;
}
}
results.append(&mut pattern.run(Some(v), context)?);
}
}
}
Ok(results)
}
#[inline]
pub(in crate::syntax) fn idents(&self) -> Vec<&str> {
let mut idents = Vec::new();
for binding in &self.bindings {
use BindingPatternTypeObject::*;
match binding {
Empty => {}
SingleName {
ident,
property_name: _,
default_init: _,
} => {
idents.push(ident.as_ref());
}
RestProperty {
ident: property_name,
excluded_keys: _,
} => {
idents.push(property_name.as_ref());
}
BindingPattern {
ident: _,
pattern,
default_init: _,
} => {
for ident in pattern.idents() {
idents.push(ident);
}
}
}
}
idents
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct DeclarationPatternArray {
bindings: Vec<BindingPatternTypeArray>,
init: Option<Node>,
}
impl fmt::Display for DeclarationPatternArray {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt("[", f)?;
for (i, binding) in self.bindings.iter().enumerate() {
if i == self.bindings.len() - 1 {
match binding {
BindingPatternTypeArray::Elision => write!(f, "{}, ", binding)?,
_ => write!(f, "{} ", binding)?,
}
} else {
write!(f, "{},", binding)?;
}
}
fmt::Display::fmt("]", f)?;
if let Some(ref init) = self.init {
write!(f, " = {}", init)?;
}
Ok(())
}
}
impl DeclarationPatternArray {
#[inline]
pub(in crate::syntax) fn new(
bindings: Vec<BindingPatternTypeArray>,
init: Option<Node>,
) -> Self {
Self { bindings, init }
}
#[inline]
pub(in crate::syntax) fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
pub(in crate::syntax) fn run(
&self,
init: Option<JsValue>,
context: &mut Context,
) -> JsResult<Vec<(Box<str>, JsValue)>> {
let value = if let Some(value) = init {
value
} else if let Some(node) = &self.init {
node.run(context)?
} else {
JsValue::undefined()
};
if value.is_null() {
return Err(context.construct_type_error("Cannot destructure 'null' value"));
}
if value.is_undefined() {
return Err(context.construct_type_error("Cannot destructure 'undefined' value"));
}
let iterator = get_iterator(&value, context)?;
let mut result = Vec::new();
for binding in &self.bindings {
use BindingPatternTypeArray::*;
match binding {
Empty => {
}
Elision => {
let _ = iterator.next(context)?;
}
SingleName {
ident,
default_init,
} => {
let next = iterator.next(context)?;
let mut v = if !next.done {
next.value
} else {
JsValue::undefined()
};
if let Some(init) = default_init {
if v.is_undefined() {
v = init.run(context)?
}
}
result.push((ident.clone(), v));
}
BindingPattern { pattern } => {
let next = iterator.next(context)?;
let v = if !next.done {
Some(next.value)
} else {
None
};
result.append(&mut pattern.run(v, context)?);
}
SingleNameRest { ident } => {
let a = Array::array_create(0, None, context)
.expect("Array creation with 0 length should never fail");
loop {
let next = iterator.next(context)?;
if next.done {
break result.push((ident.clone(), a.clone().into()));
}
Array::add_to_array_object(&a.clone().into(), &[next.value], context)?;
}
}
BindingPatternRest { pattern } => {
let a = Array::array_create(0, None, context)
.expect("Array creation with 0 length should never fail");
loop {
let next = iterator.next(context)?;
if next.done {
break result
.append(&mut pattern.run(Some(a.clone().into()), context)?);
}
Array::add_to_array_object(&a.clone().into(), &[next.value], context)?;
}
}
}
}
Ok(result)
}
#[inline]
pub(in crate::syntax) fn idents(&self) -> Vec<&str> {
let mut idents = Vec::new();
for binding in &self.bindings {
use BindingPatternTypeArray::*;
match binding {
Empty => {}
Elision => {}
SingleName {
ident,
default_init: _,
} => {
idents.push(ident.as_ref());
}
BindingPattern { pattern } | BindingPatternRest { pattern } => {
let mut i = pattern.idents();
idents.append(&mut i)
}
SingleNameRest { ident } => idents.push(ident),
}
}
idents
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum BindingPatternTypeObject {
Empty,
SingleName {
ident: Box<str>,
property_name: Box<str>,
default_init: Option<Node>,
},
RestProperty {
ident: Box<str>,
excluded_keys: Vec<Box<str>>,
},
BindingPattern {
ident: Box<str>,
pattern: DeclarationPattern,
default_init: Option<Node>,
},
}
impl fmt::Display for BindingPatternTypeObject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
BindingPatternTypeObject::Empty => {}
BindingPatternTypeObject::SingleName {
ident,
property_name,
default_init,
} => {
if ident == property_name {
write!(f, " {}", ident)?;
} else {
write!(f, " {} : {}", property_name, ident)?;
}
if let Some(ref init) = default_init {
write!(f, " = {}", init)?;
}
}
BindingPatternTypeObject::RestProperty {
ident: property_name,
excluded_keys: _,
} => {
write!(f, " ... {}", property_name)?;
}
BindingPatternTypeObject::BindingPattern {
ident: property_name,
pattern,
default_init,
} => {
write!(f, " {} : {}", property_name, pattern)?;
if let Some(ref init) = default_init {
write!(f, " = {}", init)?;
}
}
}
Ok(())
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum BindingPatternTypeArray {
Empty,
Elision,
SingleName {
ident: Box<str>,
default_init: Option<Node>,
},
BindingPattern { pattern: DeclarationPattern },
SingleNameRest { ident: Box<str> },
BindingPatternRest { pattern: DeclarationPattern },
}
impl fmt::Display for BindingPatternTypeArray {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
BindingPatternTypeArray::Empty => {}
BindingPatternTypeArray::Elision => {
fmt::Display::fmt(" ", f)?;
}
BindingPatternTypeArray::SingleName {
ident,
default_init,
} => {
write!(f, " {}", ident)?;
if let Some(ref init) = default_init {
write!(f, " = {}", init)?;
}
}
BindingPatternTypeArray::BindingPattern { pattern } => {
write!(f, " {}", pattern)?;
}
BindingPatternTypeArray::SingleNameRest { ident } => {
write!(f, " ... {}", ident)?;
}
BindingPatternTypeArray::BindingPatternRest { pattern } => {
write!(f, " ... {}", pattern)?;
}
}
Ok(())
}
}