1use super::*;
5
6impl Interp {
7 pub(crate) fn eval_unary(
8 &mut self,
9 op: &UnaryOp,
10 expr: &Expression,
11 scope: &Rc<RefCell<Scope>>,
12 this: &Value,
13 ) -> EvalResult {
14 if let UnaryOp::TypeOf = op {
16 if let Expression::Identifier(name) = expr {
17 if scope_get(scope, name).is_none() {
18 return Ok(Value::str("undefined"));
19 }
20 }
21 }
22 if let UnaryOp::Delete = op {
25 return self.eval_delete(expr, scope, this);
26 }
27 let v = self.eval(expr, scope, this)?;
28 Ok(match op {
29 UnaryOp::Neg => match &v {
30 Value::BigInt(b) => Value::bigint(b.neg()),
31 _ => Value::Number(-v.to_number()),
32 },
33 UnaryOp::Pos => {
34 if matches!(v, Value::BigInt(_)) {
36 return Err(self.throw("Cannot convert a BigInt to a number"));
37 }
38 Value::Number(v.to_number())
39 }
40 UnaryOp::Not => Value::Bool(!v.truthy()),
41 UnaryOp::BitNot => match &v {
42 Value::BigInt(b) => Value::bigint(b.bitnot()),
43 _ => Value::Number(!(to_i32(v.to_number())) as f64),
44 },
45 UnaryOp::TypeOf => Value::str(v.type_of()),
46 UnaryOp::Void => Value::Undefined,
47 UnaryOp::Delete => Value::Bool(true),
49 })
50 }
51
52 pub(crate) fn eval_delete(
57 &mut self,
58 expr: &Expression,
59 scope: &Rc<RefCell<Scope>>,
60 this: &Value,
61 ) -> EvalResult {
62 match expr {
63 Expression::Member {
64 object, property, ..
65 } => {
66 let obj = self.eval(object, scope, this)?;
67 self.delete_property(&obj, property)
68 }
69 Expression::Index { object, index, .. } => {
70 let obj = self.eval(object, scope, this)?;
71 let key = self.eval(index, scope, this)?;
72 let k = to_property_key(&key);
73 self.delete_property(&obj, &k)
74 }
75 _ => Ok(Value::Bool(true)),
76 }
77 }
78
79 pub(crate) fn delete_property(&mut self, obj: &Value, property: &str) -> EvalResult {
84 if let Value::Object(o) = obj {
85 let proxy = match &o.borrow().kind {
91 ObjKind::Proxy { target, handler } => Some((target.clone(), handler.clone())),
92 _ => None,
93 };
94 if let Some((target, handler)) = proxy {
95 let trap = handler.borrow().props.get("deleteProperty").cloned();
96 if let Some(trap) =
97 trap.filter(|t| matches!(t, Value::Object(f) if f.borrow().is_callable()))
98 {
99 let args = [Value::Object(target), Value::str(property)];
100 let res = self.call_value(&trap, Value::Object(handler), &args)?;
101 return Ok(Value::Bool(res.truthy()));
102 }
103 return self.delete_property(&Value::Object(target), property);
104 }
105 let dataset_idx = match &o.borrow().kind {
109 ObjKind::Host(t) if t.starts_with("dataset:") => {
110 t.strip_prefix("dataset:").and_then(|s| s.parse::<usize>().ok())
111 }
112 _ => None,
113 };
114 if let Some(idx) = dataset_idx {
115 let attr = camel_to_data_attr(property);
116 self.dom.borrow_mut().remove_attr(idx, &attr);
117 return Ok(Value::Bool(true));
118 }
119 let style_idx = match &o.borrow().kind {
123 ObjKind::Host(t) if t.starts_with("style:") => {
124 t.strip_prefix("style:").and_then(|s| s.parse::<usize>().ok())
125 }
126 _ => None,
127 };
128 if let Some(idx) = style_idx {
129 self.dom.borrow_mut().set_style(idx, property, "");
130 return Ok(Value::Bool(true));
131 }
132 let storage_tag = match &o.borrow().kind {
135 ObjKind::Host(t) if t.starts_with("storage:") => Some(t.clone()),
136 _ => None,
137 };
138 if let Some(tag) = storage_tag {
139 super::super::builtins::storage_remove_prop(&tag, property);
140 return Ok(Value::Bool(true));
141 }
142 if let Some(attr) = o.borrow().attrs.get(property) {
144 if !attr.configurable {
145 return Ok(Value::Bool(false));
146 }
147 }
148 if o.borrow().frozen || o.borrow().sealed {
150 return Ok(Value::Bool(false));
151 }
152 let is_array = matches!(&o.borrow().kind, ObjKind::Array(_));
153 if is_array {
154 if let Ok(idx) = property.parse::<usize>() {
158 if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
159 if idx < items.len() {
160 items[idx] = Value::Undefined;
161 }
162 }
163 }
164 } else {
165 let mut b = o.borrow_mut();
166 if let Some(attr) = b.attrs.get(property) {
167 if !attr.configurable {
168 return Ok(Value::Bool(false));
169 }
170 }
171 b.props.shift_remove(property);
172 b.accessors.shift_remove(property);
173 b.attrs.shift_remove(property);
174 }
175 }
176 Ok(Value::Bool(true))
177 }
178
179 pub(crate) fn eval_update(
180 &mut self,
181 op: &str,
182 prefix: bool,
183 target: &Expression,
184 scope: &Rc<RefCell<Scope>>,
185 this: &Value,
186 ) -> EvalResult {
187 let old = self.eval(target, scope, this)?.to_number();
188 let new = if op == "++" { old + 1.0 } else { old - 1.0 };
189 self.assign_to(target, Value::Number(new), scope, this)?;
190 Ok(Value::Number(if prefix { new } else { old }))
191 }
192
193 pub(crate) fn eval_assign(
194 &mut self,
195 op: &str,
196 target: &Expression,
197 value: &Expression,
198 scope: &Rc<RefCell<Scope>>,
199 this: &Value,
200 ) -> EvalResult {
201 match op {
208 "&&=" => {
209 let cur = self.eval(target, scope, this)?;
210 return if cur.truthy() {
211 let v = self.eval(value, scope, this)?;
212 self.assign_to(target, v.clone(), scope, this)?;
213 Ok(v)
214 } else {
215 Ok(cur)
216 };
217 }
218 "||=" => {
219 let cur = self.eval(target, scope, this)?;
220 return if !cur.truthy() {
221 let v = self.eval(value, scope, this)?;
222 self.assign_to(target, v.clone(), scope, this)?;
223 Ok(v)
224 } else {
225 Ok(cur)
226 };
227 }
228 "??=" => {
229 let cur = self.eval(target, scope, this)?;
230 return if matches!(cur, Value::Undefined | Value::Null) {
231 let v = self.eval(value, scope, this)?;
232 self.assign_to(target, v.clone(), scope, this)?;
233 Ok(v)
234 } else {
235 Ok(cur)
236 };
237 }
238 _ => {}
239 }
240 let rhs = self.eval(value, scope, this)?;
241 let final_val = if op == "=" {
242 rhs
243 } else {
244 let cur = self.eval(target, scope, this)?;
245 let bop = match op {
246 "+=" => BinaryOp::Add,
247 "-=" => BinaryOp::Sub,
248 "*=" => BinaryOp::Mul,
249 "/=" => BinaryOp::Div,
250 "%=" => BinaryOp::Mod,
251 "**=" => BinaryOp::Pow,
252 "&=" => BinaryOp::BitAnd,
253 "|=" => BinaryOp::BitOr,
254 "^=" => BinaryOp::BitXor,
255 "<<=" => BinaryOp::Shl,
256 ">>=" => BinaryOp::Shr,
257 ">>>=" => BinaryOp::UShr,
258 _ => BinaryOp::Add,
259 };
260 self.eval_binary(&bop, cur, rhs)?
261 };
262 self.assign_to(target, final_val.clone(), scope, this)?;
263 Ok(final_val)
264 }
265
266 pub(crate) fn assign_to(
268 &mut self,
269 target: &Expression,
270 val: Value,
271 scope: &Rc<RefCell<Scope>>,
272 this: &Value,
273 ) -> Result<(), Value> {
274 match target {
275 Expression::Identifier(name) => {
276 if !scope_assign(scope, name, val.clone()) {
277 let mut s = scope.clone();
279 loop {
280 let p = s.borrow().parent.clone();
281 match p {
282 Some(pp) => s = pp,
283 None => break,
284 }
285 }
286 scope_declare(&s, name, val);
287 }
288 Ok(())
289 }
290 Expression::Member {
291 object, property, ..
292 } => {
293 let obj = self.eval(object, scope, this)?;
294 self.set_property(&obj, property, val);
295 Ok(())
296 }
297 Expression::Index { object, index, .. } => {
298 let obj = self.eval(object, scope, this)?;
299 let key = self.eval(index, scope, this)?;
300 self.set_property(&obj, &to_property_key(&key), val);
301 Ok(())
302 }
303 _ => Ok(()),
304 }
305 }
306
307 pub fn eval_binary(&mut self, op: &BinaryOp, l: Value, r: Value) -> EvalResult {
308 if matches!(l, Value::BigInt(_)) || matches!(r, Value::BigInt(_)) {
310 return self.eval_binary_bigint(op, l, r);
311 }
312 Ok(match op {
313 BinaryOp::Add => {
314 let l_str = matches!(l, Value::Str(_) | Value::Object(_));
316 let r_str = matches!(r, Value::Str(_) | Value::Object(_));
317 if l_str || r_str {
318 Value::str(format!("{}{}", l.to_js_string(), r.to_js_string()))
319 } else {
320 Value::Number(l.to_number() + r.to_number())
321 }
322 }
323 BinaryOp::Sub => Value::Number(l.to_number() - r.to_number()),
324 BinaryOp::Mul => Value::Number(l.to_number() * r.to_number()),
325 BinaryOp::Div => Value::Number(l.to_number() / r.to_number()),
326 BinaryOp::Mod => {
327 let a = l.to_number();
328 let b = r.to_number();
329 Value::Number(if b == 0.0 { f64::NAN } else { a % b })
330 }
331 BinaryOp::Pow => Value::Number(powf(l.to_number(), r.to_number())),
332 BinaryOp::Eq => Value::Bool(loose_eq(&l, &r)),
333 BinaryOp::NotEq => Value::Bool(!loose_eq(&l, &r)),
334 BinaryOp::StrictEq => Value::Bool(l.strict_eq(&r)),
335 BinaryOp::StrictNotEq => Value::Bool(!l.strict_eq(&r)),
336 BinaryOp::Lt => cmp(&l, &r, |o| o == core::cmp::Ordering::Less),
337 BinaryOp::Gt => cmp(&l, &r, |o| o == core::cmp::Ordering::Greater),
338 BinaryOp::LtEq => cmp(&l, &r, |o| o != core::cmp::Ordering::Greater),
339 BinaryOp::GtEq => cmp(&l, &r, |o| o != core::cmp::Ordering::Less),
340 BinaryOp::BitAnd => {
341 Value::Number((to_i32(l.to_number()) & to_i32(r.to_number())) as f64)
342 }
343 BinaryOp::BitOr => {
344 Value::Number((to_i32(l.to_number()) | to_i32(r.to_number())) as f64)
345 }
346 BinaryOp::BitXor => {
347 Value::Number((to_i32(l.to_number()) ^ to_i32(r.to_number())) as f64)
348 }
349 BinaryOp::Shl => Value::Number(
350 (to_i32(l.to_number()).wrapping_shl(to_u32(r.to_number()) & 31)) as f64,
351 ),
352 BinaryOp::Shr => Value::Number(
353 (to_i32(l.to_number()).wrapping_shr(to_u32(r.to_number()) & 31)) as f64,
354 ),
355 BinaryOp::UShr => Value::Number(
356 ((to_u32(l.to_number())).wrapping_shr(to_u32(r.to_number()) & 31)) as f64,
357 ),
358 BinaryOp::In => {
359 let key = l.to_js_string();
360 match &r {
361 Value::Object(o) => {
362 let proxy = match &o.borrow().kind {
364 ObjKind::Proxy { target, handler } => {
365 Some((target.clone(), handler.clone()))
366 }
367 _ => None,
368 };
369 if let Some((target, handler)) = proxy {
370 let trap = handler.borrow().props.get("has").cloned();
371 if let Some(trap) = trap.filter(
372 |t| matches!(t, Value::Object(f) if f.borrow().is_callable()),
373 ) {
374 let args = [Value::Object(target), Value::str(&key)];
375 let res = self.call_value(&trap, Value::Object(handler), &args)?;
376 return Ok(Value::Bool(res.truthy()));
377 }
378 return self.eval_binary(&BinaryOp::In, l, Value::Object(target));
379 }
380 Value::Bool(self.has_property_chain(o, &key))
381 }
382 _ => Value::Bool(false),
383 }
384 }
385 BinaryOp::InstanceOf => self.instance_of(&l, &r)?,
386 })
387 }
388
389 pub(crate) fn get_dom_node_proto(&self, node_idx: usize) -> Option<ObjRef> {
392 let tag_or_text = {
393 let dom = self.dom.borrow();
394 dom.nodes.get(node_idx).map(|n| (n.tag.to_lowercase(), n.is_text))
395 }?;
396
397 let (tag, is_text) = tag_or_text;
398 let cls_name = if is_text {
399 "Node"
400 } else {
401 match tag.as_str() {
402 "input" => "HTMLInputElement",
403 "form" => "HTMLFormElement",
404 "a" => "HTMLAnchorElement",
405 "img" => "HTMLImageElement",
406 "button" => "HTMLButtonElement",
407 "select" => "HTMLSelectElement",
408 "option" => "HTMLOptionElement",
409 "textarea" => "HTMLTextAreaElement",
410 "div" => "HTMLDivElement",
411 "span" => "HTMLSpanElement",
412 "script" => "HTMLScriptElement",
413 "style" => "HTMLStyleElement",
414 "iframe" => "HTMLIFrameElement",
415 "canvas" => "HTMLCanvasElement",
416 "p" => "HTMLParagraphElement",
417 "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => "HTMLHeadingElement",
418 "table" => "HTMLTableElement",
419 "tr" => "HTMLTableRowElement",
420 "td" | "th" => "HTMLTableCellElement",
421 "ul" | "ol" => "HTMLUListElement",
422 "li" => "HTMLLIElement",
423 "template" => "HTMLTemplateElement",
424 _ => "HTMLUnknownElement",
425 }
426 };
427
428 let ctor_val = super::scope_get(&self.global, cls_name)?;
429 if let Value::Object(ctor_obj) = ctor_val {
430 if let Some(Value::Object(proto)) = ctor_obj.borrow().props.get("prototype") {
431 return Some(proto.clone());
432 }
433 }
434 None
435 }
436
437 pub(crate) fn instance_of(&mut self, l: &Value, r: &Value) -> EvalResult {
440 let ctor = match r {
441 Value::Object(o) if o.borrow().is_callable() => o.clone(),
442 _ => return Err(self.throw("Right-hand side of 'instanceof' is not callable")),
443 };
444 let has_instance = ctor.borrow().props.get("Symbol(Symbol.hasInstance)").cloned();
449 if let Some(f) = has_instance {
450 if matches!(&f, Value::Object(fo) if fo.borrow().is_callable()) {
451 let result = self.call_value(&f, Value::Object(ctor), core::slice::from_ref(l))?;
452 return Ok(Value::Bool(result.truthy()));
453 }
454 }
455 let proto = match ctor.borrow().props.get("prototype") {
456 Some(Value::Object(p)) => p.clone(),
457 _ => return Ok(Value::Bool(false)),
458 };
459 let mut cur = match l {
460 Value::Object(o) => {
461 let p = o.borrow().proto.clone();
462 if p.is_none() {
463 if let ObjKind::DomElement(idx) = o.borrow().kind {
464 self.get_dom_node_proto(idx)
465 } else {
466 None
467 }
468 } else {
469 p
470 }
471 }
472 _ => None,
473 };
474 let mut guard = 0;
475 while let Some(p) = cur {
476 if Rc::ptr_eq(&p, &proto) {
477 return Ok(Value::Bool(true));
478 }
479 cur = p.borrow().proto.clone();
480 guard += 1;
481 if guard > 1000 {
482 break;
483 }
484 }
485 Ok(Value::Bool(false))
486 }
487
488 pub(crate) fn eval_binary_bigint(&mut self, op: &BinaryOp, l: Value, r: Value) -> EvalResult {
491 use super::super::bigint::BigInt;
492 if matches!(op, BinaryOp::Add) && (matches!(l, Value::Str(_)) || matches!(r, Value::Str(_)))
494 {
495 return Ok(Value::str(format!(
496 "{}{}",
497 l.to_js_string(),
498 r.to_js_string()
499 )));
500 }
501
502 match op {
504 BinaryOp::StrictEq => return Ok(Value::Bool(l.strict_eq(&r))),
505 BinaryOp::StrictNotEq => return Ok(Value::Bool(!l.strict_eq(&r))),
506 BinaryOp::Eq => return Ok(Value::Bool(self.bigint_loose_eq(&l, &r))),
507 BinaryOp::NotEq => return Ok(Value::Bool(!self.bigint_loose_eq(&l, &r))),
508 BinaryOp::Lt | BinaryOp::Gt | BinaryOp::LtEq | BinaryOp::GtEq => {
509 return Ok(self.bigint_compare(op, &l, &r));
510 }
511 BinaryOp::In | BinaryOp::InstanceOf => {
512 return Ok(Value::Bool(false));
513 }
514 _ => {}
515 }
516
517 let (a, b) = match (&l, &r) {
519 (Value::BigInt(a), Value::BigInt(b)) => (a.clone(), b.clone()),
520 _ => {
521 return Err(
522 self.throw("Cannot mix BigInt and other types, use explicit conversions")
523 );
524 }
525 };
526 let result: BigInt = match op {
527 BinaryOp::Add => a.add(&b),
528 BinaryOp::Sub => a.sub(&b),
529 BinaryOp::Mul => a.mul(&b),
530 BinaryOp::Div => match a.div(&b) {
531 Some(v) => v,
532 None => return Err(self.throw("Division by zero")),
533 },
534 BinaryOp::Mod => match a.rem(&b) {
535 Some(v) => v,
536 None => return Err(self.throw("Division by zero")),
537 },
538 BinaryOp::Pow => match a.pow(&b) {
539 Some(v) => v,
540 None => return Err(self.throw("Exponent must be non-negative")),
541 },
542 BinaryOp::BitAnd => a.bitand(&b),
543 BinaryOp::BitOr => a.bitor(&b),
544 BinaryOp::BitXor => a.bitxor(&b),
545 BinaryOp::Shl => {
546 match shift_amount(&b) {
548 Some(n) => a.shl(n),
549 None => return Err(self.throw("BigInt shift amount out of range")),
550 }
551 }
552 BinaryOp::Shr => match shift_amount(&b) {
553 Some(n) => a.shr(n),
554 None => return Err(self.throw("BigInt shift amount out of range")),
555 },
556 BinaryOp::UShr => {
557 return Err(self.throw("BigInts have no unsigned right shift, use >> instead"));
559 }
560 _ => return Err(self.throw("Unsupported BigInt operation")),
561 };
562 Ok(Value::bigint(result))
563 }
564
565 pub(crate) fn bigint_loose_eq(&self, l: &Value, r: &Value) -> bool {
567 use core::cmp::Ordering;
568 match (l, r) {
569 (Value::BigInt(a), Value::BigInt(b)) => a.cmp(b) == Ordering::Equal,
570 (Value::BigInt(a), Value::Number(n)) | (Value::Number(n), Value::BigInt(a)) => {
571 n.is_finite() && a.to_f64() == *n
572 }
573 (Value::BigInt(a), Value::Str(s)) | (Value::Str(s), Value::BigInt(a)) => {
574 match super::super::bigint::BigInt::parse_str(s) {
575 Some(bi) => a.cmp(&bi) == Ordering::Equal,
576 None => false,
577 }
578 }
579 (Value::BigInt(a), Value::Bool(bb)) | (Value::Bool(bb), Value::BigInt(a)) => {
580 a.to_f64() == if *bb { 1.0 } else { 0.0 }
581 }
582 _ => false,
583 }
584 }
585
586 pub(crate) fn bigint_compare(&self, op: &BinaryOp, l: &Value, r: &Value) -> Value {
588 use core::cmp::Ordering;
589 let ord: Option<Ordering> = match (l, r) {
590 (Value::BigInt(a), Value::BigInt(b)) => Some(a.cmp(b)),
591 (Value::BigInt(a), _) => {
592 let x = a.to_f64();
593 let y = r.to_number();
594 cmp_f64(x, y)
595 }
596 (_, Value::BigInt(b)) => {
597 let x = l.to_number();
598 let y = b.to_f64();
599 cmp_f64(x, y)
600 }
601 _ => None,
602 };
603 let ord = match ord {
604 Some(o) => o,
605 None => return Value::Bool(false),
606 };
607 let res = match op {
608 BinaryOp::Lt => ord == Ordering::Less,
609 BinaryOp::Gt => ord == Ordering::Greater,
610 BinaryOp::LtEq => ord != Ordering::Greater,
611 BinaryOp::GtEq => ord != Ordering::Less,
612 _ => false,
613 };
614 Value::Bool(res)
615 }
616
617}