1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::{
    environment::lexical_environment::VariableScope,
    exec::Executable,
    gc::{Finalize, Trace},
    syntax::ast::node::{join_nodes, Identifier, Node},
    Context, Result, Value,
};
use std::fmt;

#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};

/// The `let` statement declares a block scope local variable, optionally initializing it to a
/// value.
///
///
/// `let` allows you to declare variables that are 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 the `let` does not create properties of the window object when declared
/// globally (in the top-most scope).
///
/// More information:
///  - [ECMAScript reference][spec]
///  - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct LetDeclList {
    #[cfg_attr(feature = "deser", serde(flatten))]
    list: Box<[LetDecl]>,
}

impl Executable for LetDeclList {
    fn run(&self, context: &mut Context) -> Result<Value> {
        for var in self.as_ref() {
            let val = match var.init() {
                Some(v) => v.run(context)?,
                None => Value::undefined(),
            };
            context
                .realm_mut()
                .environment
                .create_mutable_binding(var.name().to_owned(), false, VariableScope::Block)
                .map_err(|e| e.to_error(context))?;
            context
                .realm_mut()
                .environment
                .initialize_binding(var.name(), val)
                .map_err(|e| e.to_error(context))?;
        }
        Ok(Value::undefined())
    }
}

impl<T> From<T> for LetDeclList
where
    T: Into<Box<[LetDecl]>>,
{
    fn from(list: T) -> Self {
        Self { list: list.into() }
    }
}

impl From<LetDecl> for LetDeclList {
    fn from(decl: LetDecl) -> Self {
        Self {
            list: Box::new([decl]),
        }
    }
}

impl AsRef<[LetDecl]> for LetDeclList {
    fn as_ref(&self) -> &[LetDecl] {
        &self.list
    }
}

impl fmt::Display for LetDeclList {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if !self.list.is_empty() {
            write!(f, "let ")?;
            join_nodes(f, &self.list)
        } else {
            Ok(())
        }
    }
}

impl From<LetDeclList> for Node {
    fn from(list: LetDeclList) -> Self {
        Self::LetDeclList(list)
    }
}

/// Individual constant declaration.
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct LetDecl {
    name: Identifier,
    init: Option<Node>,
}

impl fmt::Display for LetDecl {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.name, f)?;
        if let Some(ref init) = self.init {
            write!(f, " = {}", init)?;
        }
        Ok(())
    }
}

impl LetDecl {
    /// Creates a new variable declaration.
    pub(in crate::syntax) fn new<N, I>(name: N, init: I) -> Self
    where
        N: Into<Identifier>,
        I: Into<Option<Node>>,
    {
        Self {
            name: name.into(),
            init: init.into(),
        }
    }

    /// Gets the name of the variable.
    pub fn name(&self) -> &str {
        self.name.as_ref()
    }

    /// Gets the initialization node for the variable, if any.
    pub fn init(&self) -> Option<&Node> {
        self.init.as_ref()
    }
}