boa/syntax/ast/node/operator/bin_op/
mod.rs1use crate::{
2 exec::Executable,
3 gc::{Finalize, Trace},
4 symbol::WellKnownSymbols,
5 syntax::ast::{
6 node::Node,
7 op::{self, AssignOp, BitOp, CompOp, LogOp, NumOp},
8 },
9 Context, JsResult, JsValue,
10};
11use std::fmt;
12
13#[cfg(feature = "deser")]
14use serde::{Deserialize, Serialize};
15
16#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
23#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
24pub struct BinOp {
25 op: op::BinOp,
26 lhs: Box<Node>,
27 rhs: Box<Node>,
28}
29
30impl BinOp {
31 pub(in crate::syntax) fn new<O, L, R>(op: O, lhs: L, rhs: R) -> Self
33 where
34 O: Into<op::BinOp>,
35 L: Into<Node>,
36 R: Into<Node>,
37 {
38 Self {
39 op: op.into(),
40 lhs: Box::new(lhs.into()),
41 rhs: Box::new(rhs.into()),
42 }
43 }
44
45 pub fn op(&self) -> op::BinOp {
47 self.op
48 }
49
50 pub fn lhs(&self) -> &Node {
52 &self.lhs
53 }
54
55 pub fn rhs(&self) -> &Node {
57 &self.rhs
58 }
59
60 fn run_assign(op: AssignOp, x: JsValue, y: &Node, context: &mut Context) -> JsResult<JsValue> {
62 match op {
63 AssignOp::Add => x.add(&y.run(context)?, context),
64 AssignOp::Sub => x.sub(&y.run(context)?, context),
65 AssignOp::Mul => x.mul(&y.run(context)?, context),
66 AssignOp::Exp => x.pow(&y.run(context)?, context),
67 AssignOp::Div => x.div(&y.run(context)?, context),
68 AssignOp::Mod => x.rem(&y.run(context)?, context),
69 AssignOp::And => x.bitand(&y.run(context)?, context),
70 AssignOp::Or => x.bitor(&y.run(context)?, context),
71 AssignOp::Xor => x.bitxor(&y.run(context)?, context),
72 AssignOp::Shl => x.shl(&y.run(context)?, context),
73 AssignOp::Shr => x.shr(&y.run(context)?, context),
74 AssignOp::Ushr => x.ushr(&y.run(context)?, context),
75 AssignOp::BoolAnd => {
76 if x.to_boolean() {
77 Ok(y.run(context)?)
78 } else {
79 Ok(x)
80 }
81 }
82 AssignOp::BoolOr => {
83 if x.to_boolean() {
84 Ok(x)
85 } else {
86 Ok(y.run(context)?)
87 }
88 }
89 AssignOp::Coalesce => {
90 if x.is_null_or_undefined() {
91 Ok(y.run(context)?)
92 } else {
93 Ok(x)
94 }
95 }
96 }
97 }
98}
99
100impl Executable for BinOp {
101 fn run(&self, context: &mut Context) -> JsResult<JsValue> {
102 match self.op() {
103 op::BinOp::Num(op) => {
104 let x = self.lhs().run(context)?;
105 let y = self.rhs().run(context)?;
106 match op {
107 NumOp::Add => x.add(&y, context),
108 NumOp::Sub => x.sub(&y, context),
109 NumOp::Mul => x.mul(&y, context),
110 NumOp::Exp => x.pow(&y, context),
111 NumOp::Div => x.div(&y, context),
112 NumOp::Mod => x.rem(&y, context),
113 }
114 }
115 op::BinOp::Bit(op) => {
116 let x = self.lhs().run(context)?;
117 let y = self.rhs().run(context)?;
118 match op {
119 BitOp::And => x.bitand(&y, context),
120 BitOp::Or => x.bitor(&y, context),
121 BitOp::Xor => x.bitxor(&y, context),
122 BitOp::Shl => x.shl(&y, context),
123 BitOp::Shr => x.shr(&y, context),
124 BitOp::UShr => x.ushr(&y, context),
125 }
126 }
127 op::BinOp::Comp(op) => {
128 let x = self.lhs().run(context)?;
129 let y = self.rhs().run(context)?;
130 Ok(JsValue::new(match op {
131 CompOp::Equal => x.equals(&y, context)?,
132 CompOp::NotEqual => !x.equals(&y, context)?,
133 CompOp::StrictEqual => x.strict_equals(&y),
134 CompOp::StrictNotEqual => !x.strict_equals(&y),
135 CompOp::GreaterThan => x.gt(&y, context)?,
136 CompOp::GreaterThanOrEqual => x.ge(&y, context)?,
137 CompOp::LessThan => x.lt(&y, context)?,
138 CompOp::LessThanOrEqual => x.le(&y, context)?,
139 CompOp::In => {
140 if !y.is_object() {
141 return context.throw_type_error(format!(
142 "right-hand side of 'in' should be an object, got {}",
143 y.type_of()
144 ));
145 }
146 let key = x.to_property_key(context)?;
147 context.has_property(&y, &key)?
148 }
149 CompOp::InstanceOf => {
150 if let Some(object) = y.as_object() {
151 let key = WellKnownSymbols::has_instance();
152
153 match object.get_method(context, key)? {
154 Some(instance_of_handler) => {
155 instance_of_handler.call(&y, &[x], context)?.to_boolean()
156 }
157 None if object.is_callable() => {
158 object.ordinary_has_instance(context, &x)?
159 }
160 None => {
161 return context.throw_type_error(
162 "right-hand side of 'instanceof' is not callable",
163 );
164 }
165 }
166 } else {
167 return context.throw_type_error(format!(
168 "right-hand side of 'instanceof' should be an object, got {}",
169 y.type_of()
170 ));
171 }
172 }
173 }))
174 }
175 op::BinOp::Log(op) => Ok(match op {
176 LogOp::And => {
177 let left = self.lhs().run(context)?;
178 if !left.to_boolean() {
179 left
180 } else {
181 self.rhs().run(context)?
182 }
183 }
184 LogOp::Or => {
185 let left = self.lhs().run(context)?;
186 if left.to_boolean() {
187 left
188 } else {
189 self.rhs().run(context)?
190 }
191 }
192 LogOp::Coalesce => {
193 let left = self.lhs.run(context)?;
194 if left.is_null_or_undefined() {
195 self.rhs().run(context)?
196 } else {
197 left
198 }
199 }
200 }),
201 op::BinOp::Assign(op) => match self.lhs() {
202 Node::Identifier(ref name) => {
203 let v_a = context.get_binding_value(name.as_ref())?;
204
205 let value = Self::run_assign(op, v_a, self.rhs(), context)?;
206 context.set_mutable_binding(name.as_ref(), value.clone(), context.strict())?;
207 Ok(value)
208 }
209 Node::GetConstField(ref get_const_field) => {
210 let v_r_a = get_const_field.obj().run(context)?;
211 let v_a = v_r_a.get_field(get_const_field.field(), context)?;
212 let value = Self::run_assign(op, v_a, self.rhs(), context)?;
213 v_r_a.set_field(get_const_field.field(), value.clone(), false, context)?;
214 Ok(value)
215 }
216 _ => Ok(JsValue::undefined()),
217 },
218 op::BinOp::Comma => {
219 self.lhs().run(context)?;
220 Ok(self.rhs().run(context)?)
221 }
222 }
223 }
224}
225
226impl fmt::Display for BinOp {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 write!(f, "{} {} {}", self.lhs, self.op, self.rhs)
229 }
230}
231
232impl From<BinOp> for Node {
233 fn from(op: BinOp) -> Self {
234 Self::BinOp(op)
235 }
236}