boa/syntax/ast/node/operator/unary_op/
mod.rs

1use crate::{
2    exec::Executable,
3    gc::{Finalize, Trace},
4    syntax::ast::{node::Node, op},
5    Context, JsBigInt, JsResult, JsValue,
6};
7use std::fmt;
8
9use crate::builtins::Number;
10use crate::value::Numeric;
11#[cfg(feature = "deser")]
12use serde::{Deserialize, Serialize};
13
14/// A unary operation is an operation with only one operand.
15///
16/// More information:
17///  - [ECMAScript reference][spec]
18///  - [MDN documentation][mdn]
19///
20/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
21/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators
22#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
23#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
24pub struct UnaryOp {
25    op: op::UnaryOp,
26    target: Box<Node>,
27}
28
29impl UnaryOp {
30    /// Creates a new `UnaryOp` AST node.
31    pub(in crate::syntax) fn new<V>(op: op::UnaryOp, target: V) -> Self
32    where
33        V: Into<Node>,
34    {
35        Self {
36            op,
37            target: Box::new(target.into()),
38        }
39    }
40
41    /// Gets the unary operation of the node.
42    pub fn op(&self) -> op::UnaryOp {
43        self.op
44    }
45
46    /// Gets the target of this unary operator.
47    pub fn target(&self) -> &Node {
48        self.target.as_ref()
49    }
50}
51
52impl Executable for UnaryOp {
53    fn run(&self, context: &mut Context) -> JsResult<JsValue> {
54        Ok(match self.op() {
55            op::UnaryOp::Minus => self.target().run(context)?.neg(context)?,
56            op::UnaryOp::Plus => JsValue::new(self.target().run(context)?.to_number(context)?),
57            op::UnaryOp::IncrementPost => {
58                let x = self.target().run(context)?;
59                let ret = x.clone();
60                let result = x.to_number(context)? + 1.0;
61                context.set_value(self.target(), result.into())?;
62                ret
63            }
64            op::UnaryOp::IncrementPre => {
65                let result = self.target().run(context)?.to_number(context)? + 1.0;
66                context.set_value(self.target(), result.into())?
67            }
68            op::UnaryOp::DecrementPost => {
69                let x = self.target().run(context)?;
70                let ret = x.clone();
71                let result = x.to_number(context)? - 1.0;
72                context.set_value(self.target(), result.into())?;
73                ret
74            }
75            op::UnaryOp::DecrementPre => {
76                let result = self.target().run(context)?.to_number(context)? - 1.0;
77                context.set_value(self.target(), result.into())?
78            }
79            op::UnaryOp::Not => self.target().run(context)?.not(context)?.into(),
80            op::UnaryOp::Tilde => {
81                let expr = self.target().run(context)?;
82                let old_v = expr.to_numeric(context)?;
83                match old_v {
84                    Numeric::Number(x) => JsValue::new(Number::not(x)),
85                    Numeric::BigInt(x) => JsValue::new(JsBigInt::not(&x)),
86                }
87            }
88            op::UnaryOp::Void => {
89                self.target().run(context)?;
90                JsValue::undefined()
91            }
92            op::UnaryOp::Delete => match *self.target() {
93                Node::GetConstField(ref get_const_field) => {
94                    let delete_status = get_const_field
95                        .obj()
96                        .run(context)?
97                        .to_object(context)?
98                        .__delete__(&get_const_field.field().into(), context)?;
99
100                    if !delete_status && context.strict() {
101                        return context.throw_type_error("Cannot delete property");
102                    } else {
103                        JsValue::new(delete_status)
104                    }
105                }
106                Node::GetField(ref get_field) => {
107                    let obj = get_field.obj().run(context)?;
108                    let field = &get_field.field().run(context)?;
109                    let res = obj
110                        .to_object(context)?
111                        .__delete__(&field.to_property_key(context)?, context)?;
112                    return Ok(JsValue::new(res));
113                }
114                Node::Identifier(_) => JsValue::new(false),
115                Node::ArrayDecl(_)
116                | Node::Block(_)
117                | Node::Const(_)
118                | Node::FunctionDecl(_)
119                | Node::FunctionExpr(_)
120                | Node::New(_)
121                | Node::Object(_)
122                | Node::UnaryOp(_) => JsValue::new(true),
123                _ => return context.throw_syntax_error(format!("wrong delete argument {}", self)),
124            },
125            op::UnaryOp::TypeOf => JsValue::new(self.target().run(context)?.type_of()),
126        })
127    }
128}
129
130impl fmt::Display for UnaryOp {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        write!(f, "{}{}", self.op, self.target)
133    }
134}
135
136impl From<UnaryOp> for Node {
137    fn from(op: UnaryOp) -> Self {
138        Self::UnaryOp(op)
139    }
140}