1use crate::runner::ds::error::JErrorType;
4use crate::runner::ds::lex_env::JsLexEnvironmentType;
5use crate::runner::ds::object_property::PropertyKey;
6use crate::runner::ds::operations::type_conversion::to_string;
7use crate::runner::ds::value::{JsNumberType, JsValue, JsValueOrSelf};
8use crate::runner::plugin::registry::BuiltInRegistry;
9use crate::runner::plugin::types::EvalContext;
10
11use super::reg_bytecode::{RegChunk, RegOpCode};
12
13pub enum RegVmResult {
15 Ok(JsValue),
16 Error(JErrorType),
17}
18
19#[derive(Clone)]
20struct EnvCacheEntry {
21 version: u64,
22 env: JsLexEnvironmentType,
23}
24
25#[derive(Clone)]
26struct PropCacheEntry {
27 obj: crate::runner::ds::object::JsObjectType,
28 prop: String,
29}
30
31pub struct RegVm<'a> {
32 chunk: &'a RegChunk,
33 ip: usize,
34 registers: Vec<JsValue>,
35 get_var_cache: Vec<Option<EnvCacheEntry>>,
36 set_var_cache: Vec<Option<EnvCacheEntry>>,
37 get_prop_cache: Vec<Option<PropCacheEntry>>,
38 ctx: EvalContext,
39 registry: &'a BuiltInRegistry,
40}
41
42impl<'a> RegVm<'a> {
43 pub fn new(chunk: &'a RegChunk, ctx: EvalContext, registry: &'a BuiltInRegistry) -> Self {
44 RegVm {
45 chunk,
46 ip: 0,
47 registers: vec![JsValue::Undefined; chunk.register_count as usize],
48 get_var_cache: vec![None; chunk.code.len()],
49 set_var_cache: vec![None; chunk.code.len()],
50 get_prop_cache: vec![None; chunk.code.len()],
51 ctx,
52 registry,
53 }
54 }
55
56 pub fn into_ctx(mut self) -> EvalContext {
57 self.sync_locals_into_ctx();
58 self.ctx
59 }
60
61 pub fn run(&mut self) -> RegVmResult {
62 loop {
63 if self.ip >= self.chunk.code.len() {
64 return RegVmResult::Ok(JsValue::Undefined);
65 }
66 let instr_index = self.ip;
67 let instr = &self.chunk.code[instr_index];
68 self.ip += 1;
69 match instr.op {
70 RegOpCode::LoadConst => {
71 let val = self.chunk.constants[instr.imm as usize].clone();
72 self.write_reg(instr.dst, val);
73 }
74 RegOpCode::LoadUndefined => self.write_reg(instr.dst, JsValue::Undefined),
75 RegOpCode::LoadNull => self.write_reg(instr.dst, JsValue::Null),
76 RegOpCode::LoadTrue => self.write_reg(instr.dst, JsValue::Boolean(true)),
77 RegOpCode::LoadFalse => self.write_reg(instr.dst, JsValue::Boolean(false)),
78 RegOpCode::Move => {
79 let val = self.read_reg(instr.src1).clone();
80 self.write_reg(instr.dst, val);
81 }
82 RegOpCode::Add => {
83 let a = self.read_reg(instr.src1).clone();
84 let b = self.read_reg(instr.src2).clone();
85 self.write_reg(instr.dst, self.js_add(a, b));
86 }
87 RegOpCode::Sub => {
88 let a = self.read_reg(instr.src1).clone();
89 let b = self.read_reg(instr.src2).clone();
90 self.write_reg(instr.dst, self.js_sub(a, b));
91 }
92 RegOpCode::Mul => {
93 let a = self.read_reg(instr.src1).clone();
94 let b = self.read_reg(instr.src2).clone();
95 self.write_reg(instr.dst, self.js_mul(a, b));
96 }
97 RegOpCode::Div => {
98 let a = self.read_reg(instr.src1).clone();
99 let b = self.read_reg(instr.src2).clone();
100 self.write_reg(instr.dst, self.js_div(a, b));
101 }
102 RegOpCode::Mod => {
103 let a = self.read_reg(instr.src1).clone();
104 let b = self.read_reg(instr.src2).clone();
105 self.write_reg(instr.dst, self.js_mod(a, b));
106 }
107 RegOpCode::Negate => {
108 let val = self.read_reg(instr.src1).clone();
109 self.write_reg(instr.dst, self.js_negate(val));
110 }
111 RegOpCode::BitAnd => {
112 let a = self.read_reg(instr.src1).clone();
113 let b = self.read_reg(instr.src2).clone();
114 self.write_reg(instr.dst, self.js_bit_and(a, b));
115 }
116 RegOpCode::BitOr => {
117 let a = self.read_reg(instr.src1).clone();
118 let b = self.read_reg(instr.src2).clone();
119 self.write_reg(instr.dst, self.js_bit_or(a, b));
120 }
121 RegOpCode::BitXor => {
122 let a = self.read_reg(instr.src1).clone();
123 let b = self.read_reg(instr.src2).clone();
124 self.write_reg(instr.dst, self.js_bit_xor(a, b));
125 }
126 RegOpCode::BitNot => {
127 let val = self.read_reg(instr.src1).clone();
128 let a = self.to_i32(val);
129 self.write_reg(instr.dst, JsValue::Number(JsNumberType::Integer((!a) as i64)));
130 }
131 RegOpCode::ShiftLeft => {
132 let a = self.read_reg(instr.src1).clone();
133 let b = self.read_reg(instr.src2).clone();
134 self.write_reg(instr.dst, self.js_shift_left(a, b));
135 }
136 RegOpCode::ShiftRight => {
137 let a = self.read_reg(instr.src1).clone();
138 let b = self.read_reg(instr.src2).clone();
139 self.write_reg(instr.dst, self.js_shift_right(a, b));
140 }
141 RegOpCode::UShiftRight => {
142 let a = self.read_reg(instr.src1).clone();
143 let b = self.read_reg(instr.src2).clone();
144 self.write_reg(instr.dst, self.js_ushift_right(a, b));
145 }
146 RegOpCode::StrictEqual => {
147 let res = self.js_strict_equal(self.read_reg(instr.src1), self.read_reg(instr.src2));
148 self.write_reg(instr.dst, JsValue::Boolean(res));
149 }
150 RegOpCode::StrictNotEqual => {
151 let res = !self.js_strict_equal(self.read_reg(instr.src1), self.read_reg(instr.src2));
152 self.write_reg(instr.dst, JsValue::Boolean(res));
153 }
154 RegOpCode::Equal => {
155 let res = self.js_abstract_equal(self.read_reg(instr.src1), self.read_reg(instr.src2));
156 self.write_reg(instr.dst, JsValue::Boolean(res));
157 }
158 RegOpCode::NotEqual => {
159 let res = !self.js_abstract_equal(self.read_reg(instr.src1), self.read_reg(instr.src2));
160 self.write_reg(instr.dst, JsValue::Boolean(res));
161 }
162 RegOpCode::LessThan => {
163 let res = self.js_less_than(self.read_reg(instr.src1), self.read_reg(instr.src2));
164 self.write_reg(instr.dst, JsValue::Boolean(res));
165 }
166 RegOpCode::LessEqual => {
167 let res = !self.js_less_than(self.read_reg(instr.src2), self.read_reg(instr.src1));
168 self.write_reg(instr.dst, JsValue::Boolean(res));
169 }
170 RegOpCode::GreaterThan => {
171 let res = self.js_less_than(self.read_reg(instr.src2), self.read_reg(instr.src1));
172 self.write_reg(instr.dst, JsValue::Boolean(res));
173 }
174 RegOpCode::GreaterEqual => {
175 let res = !self.js_less_than(self.read_reg(instr.src1), self.read_reg(instr.src2));
176 self.write_reg(instr.dst, JsValue::Boolean(res));
177 }
178 RegOpCode::Not => {
179 let val = self.read_reg(instr.src1).clone();
180 self.write_reg(instr.dst, JsValue::Boolean(!self.is_truthy(&val)));
181 }
182 RegOpCode::TypeOf => {
183 let val = self.read_reg(instr.src1).clone();
184 let type_str = match &val {
185 JsValue::Undefined => "undefined",
186 JsValue::Null => "object",
187 JsValue::Boolean(_) => "boolean",
188 JsValue::Number(_) => "number",
189 JsValue::String(_) => "string",
190 JsValue::Symbol(_) => "symbol",
191 JsValue::Object(_) => "object",
192 };
193 self.write_reg(instr.dst, JsValue::String(type_str.to_string()));
194 }
195 RegOpCode::UnaryPlus => {
196 let val = self.read_reg(instr.src1).clone();
197 self.write_reg(instr.dst, self.to_number_value(val));
198 }
199 RegOpCode::GetVar => {
200 let name = self.chunk.get_name(instr.imm);
201 match self.get_var_cached(instr_index, name) {
202 Ok(val) => self.write_reg(instr.dst, val),
203 Err(e) => return RegVmResult::Error(e),
204 }
205 }
206 RegOpCode::SetVar => {
207 let name = self.chunk.get_name(instr.imm);
208 let val = self.read_reg(instr.src1).clone();
209 if let Err(e) = self.set_var_cached(instr_index, name, val) {
210 return RegVmResult::Error(e);
211 }
212 }
213 RegOpCode::DeclareVar => {
214 let name = self.chunk.get_name(instr.imm);
215 if !self.ctx.has_var_binding(name) {
216 if let Err(e) = self.ctx.create_var_binding(name) {
217 return RegVmResult::Error(e);
218 }
219 }
220 }
221 RegOpCode::DeclareLet => {
222 let name = self.chunk.get_name(instr.imm);
223 if let Err(e) = self.ctx.create_binding(name, false) {
224 return RegVmResult::Error(e);
225 }
226 }
227 RegOpCode::DeclareConst => {
228 let name = self.chunk.get_name(instr.imm);
229 if let Err(e) = self.ctx.create_binding(name, true) {
230 return RegVmResult::Error(e);
231 }
232 }
233 RegOpCode::InitVar => {
234 let name = self.chunk.get_name(instr.imm);
235 let val = self.read_reg(instr.src1).clone();
236 if let Err(e) = self.ctx.initialize_var_binding(name, val.clone()) {
237 return RegVmResult::Error(e);
238 }
239 let _ = self.ctx.set_var_binding(name, val);
240 }
241 RegOpCode::InitBinding => {
242 let name = self.chunk.get_name(instr.imm);
243 let val = self.read_reg(instr.src1).clone();
244 if let Err(e) = self.ctx.initialize_binding(name, val) {
245 return RegVmResult::Error(e);
246 }
247 }
248 RegOpCode::GetProp => {
249 let obj_val = self.read_reg(instr.src1).clone();
250 let prop_name = self.chunk.get_name(instr.imm);
251 match self.get_prop_cached(instr_index, &obj_val, prop_name) {
252 Ok(val) => self.write_reg(instr.dst, val),
253 Err(e) => return RegVmResult::Error(e),
254 }
255 }
256 RegOpCode::SetProp => {
257 let obj_val = self.read_reg(instr.src1).clone();
258 let val = self.read_reg(instr.src2).clone();
259 let prop_name = self.chunk.get_name(instr.imm);
260 if let Err(e) = self.set_prop_value(&obj_val, prop_name, val.clone()) {
261 return RegVmResult::Error(e);
262 }
263 self.write_reg(instr.dst, val);
264 }
265 RegOpCode::GetElem => {
266 let obj_val = self.read_reg(instr.src1).clone();
267 let key_val = self.read_reg(instr.src2).clone();
268 let prop_name = match self.to_property_key(&key_val) {
269 Ok(key) => key,
270 Err(e) => return RegVmResult::Error(e),
271 };
272 match self.get_prop_cached(instr_index, &obj_val, &prop_name) {
273 Ok(val) => self.write_reg(instr.dst, val),
274 Err(e) => return RegVmResult::Error(e),
275 }
276 }
277 RegOpCode::SetElem => {
278 let obj_val = self.read_reg(instr.src1).clone();
279 let key_val = self.read_reg(instr.src2).clone();
280 let val = self.read_reg(instr.dst).clone();
281 let prop_name = match self.to_property_key(&key_val) {
282 Ok(key) => key,
283 Err(e) => return RegVmResult::Error(e),
284 };
285 if let Err(e) = self.set_prop_value(&obj_val, &prop_name, val.clone()) {
286 return RegVmResult::Error(e);
287 }
288 }
289 RegOpCode::Jump => {
290 self.ip = instr.imm as usize;
291 }
292 RegOpCode::JumpIfFalse => {
293 let val = self.read_reg(instr.src1).clone();
294 if !self.is_truthy(&val) {
295 self.ip = instr.imm as usize;
296 }
297 }
298 RegOpCode::JumpIfTrue => {
299 let val = self.read_reg(instr.src1).clone();
300 if self.is_truthy(&val) {
301 self.ip = instr.imm as usize;
302 }
303 }
304 RegOpCode::Call => {
305 let _callee = self.read_reg(instr.src1).clone();
306 self.write_reg(instr.dst, JsValue::Undefined);
307 }
308 RegOpCode::CallMethod => {
309 let object = self.read_reg(instr.src1).clone();
310 let method_name = self.chunk.get_name(instr.src2);
311 let obj_name = self.resolve_object_name(&object);
312 if let Some(ref name) = obj_name {
313 if let Some(builtin_fn) = self.registry.get_method(name, method_name) {
314 match builtin_fn.call(&mut self.ctx, object, Vec::new()) {
315 Ok(result) => {
316 self.write_reg(instr.dst, result);
317 continue;
318 }
319 Err(e) => return RegVmResult::Error(e),
320 }
321 }
322 }
323 self.write_reg(instr.dst, JsValue::Undefined);
324 }
325 RegOpCode::Return => {
326 let val = self.read_reg(instr.src1).clone();
327 return RegVmResult::Ok(val);
328 }
329 RegOpCode::Halt => {
330 return RegVmResult::Ok(JsValue::Undefined);
331 }
332 }
333 }
334 }
335
336 fn read_reg(&self, reg: u32) -> &JsValue {
337 self.registers.get(reg as usize).unwrap_or(&JsValue::Undefined)
338 }
339
340 fn write_reg(&mut self, reg: u32, value: JsValue) {
341 if let Some(slot) = self.registers.get_mut(reg as usize) {
342 *slot = value;
343 }
344 }
345
346 fn get_var_cached(&mut self, instr_index: usize, name: &str) -> Result<JsValue, JErrorType> {
347 if let Some(Some(entry)) = self.get_var_cache.get(instr_index) {
348 if entry.version == self.ctx.current_lex_env_version() {
349 if let Ok(val) = self.ctx.get_binding_in_env(&entry.env, name) {
350 return Ok(val);
351 }
352 }
353 }
354 let (val, env) = self.ctx.get_binding_with_env(name)?;
355 let entry = EnvCacheEntry {
356 version: self.ctx.current_lex_env_version(),
357 env,
358 };
359 if let Some(slot) = self.get_var_cache.get_mut(instr_index) {
360 *slot = Some(entry);
361 }
362 Ok(val)
363 }
364
365 fn set_var_cached(
366 &mut self,
367 instr_index: usize,
368 name: &str,
369 value: JsValue,
370 ) -> Result<(), JErrorType> {
371 if let Some(Some(entry)) = self.set_var_cache.get(instr_index) {
372 if entry.version == self.ctx.current_lex_env_version() {
373 if self
374 .ctx
375 .set_binding_in_env_cached(&entry.env, name, value.clone())
376 .is_ok()
377 {
378 return Ok(());
379 }
380 }
381 }
382 let env = self.ctx.set_binding_with_env(name, value)?;
383 let entry = EnvCacheEntry {
384 version: self.ctx.current_lex_env_version(),
385 env,
386 };
387 if let Some(slot) = self.set_var_cache.get_mut(instr_index) {
388 *slot = Some(entry);
389 }
390 Ok(())
391 }
392
393 fn get_prop_cached(
394 &mut self,
395 instr_index: usize,
396 obj_val: &JsValue,
397 prop_name: &str,
398 ) -> Result<JsValue, JErrorType> {
399 if let JsValue::Object(obj) = obj_val {
400 if let Some(Some(entry)) = self.get_prop_cache.get(instr_index) {
401 if std::rc::Rc::ptr_eq(obj, &entry.obj) && entry.prop == prop_name {
402 let prop_key = PropertyKey::Str(prop_name.to_string());
403 return obj
404 .borrow()
405 .as_js_object()
406 .get(&mut self.ctx.ctx_stack, &prop_key, JsValueOrSelf::SelfValue);
407 }
408 }
409
410 let prop_key = PropertyKey::Str(prop_name.to_string());
411 let val = obj
412 .borrow()
413 .as_js_object()
414 .get(&mut self.ctx.ctx_stack, &prop_key, JsValueOrSelf::SelfValue)?;
415 let entry = PropCacheEntry {
416 obj: obj.clone(),
417 prop: prop_name.to_string(),
418 };
419 if let Some(slot) = self.get_prop_cache.get_mut(instr_index) {
420 *slot = Some(entry);
421 }
422 Ok(val)
423 } else if matches!(obj_val, JsValue::Undefined | JsValue::Null) {
424 Err(JErrorType::TypeError("Cannot read property of null/undefined".to_string()))
425 } else {
426 Ok(JsValue::Undefined)
427 }
428 }
429
430 fn set_prop_value(
431 &mut self,
432 obj_val: &JsValue,
433 prop_name: &str,
434 value: JsValue,
435 ) -> Result<(), JErrorType> {
436 match obj_val {
437 JsValue::Object(obj) => {
438 let prop_key = PropertyKey::Str(prop_name.to_string());
439 let mut obj_ref = obj.borrow_mut();
440 let ok = obj_ref
441 .as_js_object_mut()
442 .set(&mut self.ctx.ctx_stack, prop_key, value, JsValueOrSelf::SelfValue)?;
443 if ok {
444 Ok(())
445 } else {
446 Err(JErrorType::TypeError("Failed to set property".to_string()))
447 }
448 }
449 _ => Err(JErrorType::TypeError("Cannot set property on non-object".to_string())),
450 }
451 }
452
453 fn to_property_key(&mut self, value: &JsValue) -> Result<String, JErrorType> {
454 to_string(&mut self.ctx.ctx_stack, value)
455 }
456
457 fn sync_locals_into_ctx(&mut self) {
458 for local in &self.chunk.locals {
459 let name = self.chunk.get_name(local.name_idx);
460 let val = self.read_reg(local.reg).clone();
461 if !self.ctx.has_var_binding(name) {
462 let _ = self.ctx.create_var_binding(name);
463 let _ = self.ctx.initialize_var_binding(name, val.clone());
464 } else {
465 let _ = self.ctx.set_var_binding(name, val.clone());
466 }
467 }
468 }
469
470 fn resolve_object_name(&self, value: &JsValue) -> Option<String> {
471 match value {
472 JsValue::String(_) => Some("String".to_string()),
473 JsValue::Number(_) => Some("Number".to_string()),
474 _ => None,
475 }
476 }
477
478 fn is_truthy(&self, val: &JsValue) -> bool {
479 match val {
480 JsValue::Undefined | JsValue::Null => false,
481 JsValue::Boolean(b) => *b,
482 JsValue::Number(n) => match n {
483 JsNumberType::NaN => false,
484 JsNumberType::Integer(i) => *i != 0,
485 JsNumberType::Float(f) => *f != 0.0,
486 JsNumberType::PositiveInfinity => true,
487 JsNumberType::NegativeInfinity => true,
488 },
489 JsValue::String(s) => !s.is_empty(),
490 JsValue::Symbol(_) => true,
491 JsValue::Object(_) => true,
492 }
493 }
494
495 fn to_f64(&self, val: &JsValue) -> f64 {
496 match val {
497 JsValue::Undefined => f64::NAN,
498 JsValue::Null => 0.0,
499 JsValue::Boolean(b) => if *b { 1.0 } else { 0.0 },
500 JsValue::Number(n) => self.num_to_f64(n),
501 JsValue::String(s) => s.parse::<f64>().unwrap_or(f64::NAN),
502 JsValue::Symbol(_) => f64::NAN,
503 JsValue::Object(_) => f64::NAN,
504 }
505 }
506
507 fn num_to_f64(&self, n: &JsNumberType) -> f64 {
508 match n {
509 JsNumberType::Integer(i) => *i as f64,
510 JsNumberType::Float(f) => *f,
511 JsNumberType::NaN => f64::NAN,
512 JsNumberType::PositiveInfinity => f64::INFINITY,
513 JsNumberType::NegativeInfinity => f64::NEG_INFINITY,
514 }
515 }
516
517 fn f64_to_jsvalue(&self, n: f64) -> JsValue {
518 if n.is_nan() {
519 JsValue::Number(JsNumberType::NaN)
520 } else if n == f64::INFINITY {
521 JsValue::Number(JsNumberType::PositiveInfinity)
522 } else if n == f64::NEG_INFINITY {
523 JsValue::Number(JsNumberType::NegativeInfinity)
524 } else if n.fract() == 0.0 && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
525 JsValue::Number(JsNumberType::Integer(n as i64))
526 } else {
527 JsValue::Number(JsNumberType::Float(n))
528 }
529 }
530
531 fn to_number_value(&self, val: JsValue) -> JsValue {
532 match val {
533 JsValue::Number(_) => val,
534 _ => self.f64_to_jsvalue(self.to_f64(&val)),
535 }
536 }
537
538 fn js_add(&self, a: JsValue, b: JsValue) -> JsValue {
539 match (&a, &b) {
540 (JsValue::String(sa), JsValue::String(sb)) => JsValue::String(format!("{}{}", sa, sb)),
541 (JsValue::String(sa), _) => JsValue::String(format!("{}{}", sa, self.to_display_string(&b))),
542 (_, JsValue::String(sb)) => JsValue::String(format!("{}{}", self.to_display_string(&a), sb)),
543 _ => {
544 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x + y) {
545 return val;
546 }
547 self.f64_to_jsvalue(self.to_f64(&a) + self.to_f64(&b))
548 }
549 }
550 }
551
552 fn js_sub(&self, a: JsValue, b: JsValue) -> JsValue {
553 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x - y) {
554 return val;
555 }
556 self.f64_to_jsvalue(self.to_f64(&a) - self.to_f64(&b))
557 }
558
559 fn js_mul(&self, a: JsValue, b: JsValue) -> JsValue {
560 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x * y) {
561 return val;
562 }
563 self.f64_to_jsvalue(self.to_f64(&a) * self.to_f64(&b))
564 }
565
566 fn js_div(&self, a: JsValue, b: JsValue) -> JsValue {
567 if let Some(nb) = self.as_number(&b) {
568 let nb = self.num_to_f64(nb);
569 if nb == 0.0 {
570 let na = self.to_f64(&a);
571 if na == 0.0 || na.is_nan() {
572 return JsValue::Number(JsNumberType::NaN);
573 } else if na > 0.0 {
574 return JsValue::Number(JsNumberType::PositiveInfinity);
575 } else {
576 return JsValue::Number(JsNumberType::NegativeInfinity);
577 }
578 }
579 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x / y) {
580 return val;
581 }
582 }
583
584 let nb = self.to_f64(&b);
585 if nb == 0.0 {
586 let na = self.to_f64(&a);
587 if na == 0.0 || na.is_nan() {
588 JsValue::Number(JsNumberType::NaN)
589 } else if na > 0.0 {
590 JsValue::Number(JsNumberType::PositiveInfinity)
591 } else {
592 JsValue::Number(JsNumberType::NegativeInfinity)
593 }
594 } else {
595 self.f64_to_jsvalue(self.to_f64(&a) / nb)
596 }
597 }
598
599 fn js_mod(&self, a: JsValue, b: JsValue) -> JsValue {
600 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x % y) {
601 return val;
602 }
603 let na = self.to_f64(&a);
604 let nb = self.to_f64(&b);
605 if nb == 0.0 {
606 JsValue::Number(JsNumberType::NaN)
607 } else {
608 self.f64_to_jsvalue(na % nb)
609 }
610 }
611
612 fn js_negate(&self, a: JsValue) -> JsValue {
613 if let JsValue::Number(n) = &a {
614 return self.f64_to_jsvalue(-self.num_to_f64(n));
615 }
616 self.f64_to_jsvalue(-self.to_f64(&a))
617 }
618
619 fn js_bit_and(&self, a: JsValue, b: JsValue) -> JsValue {
620 let av = self.to_i32(a);
621 let bv = self.to_i32(b);
622 JsValue::Number(JsNumberType::Integer((av & bv) as i64))
623 }
624
625 fn js_bit_or(&self, a: JsValue, b: JsValue) -> JsValue {
626 let av = self.to_i32(a);
627 let bv = self.to_i32(b);
628 JsValue::Number(JsNumberType::Integer((av | bv) as i64))
629 }
630
631 fn js_bit_xor(&self, a: JsValue, b: JsValue) -> JsValue {
632 let av = self.to_i32(a);
633 let bv = self.to_i32(b);
634 JsValue::Number(JsNumberType::Integer((av ^ bv) as i64))
635 }
636
637 fn js_shift_left(&self, a: JsValue, b: JsValue) -> JsValue {
638 let av = self.to_i32(a);
639 let bv = self.to_u32(b);
640 JsValue::Number(JsNumberType::Integer((av << (bv & 0x1f)) as i64))
641 }
642
643 fn js_shift_right(&self, a: JsValue, b: JsValue) -> JsValue {
644 let av = self.to_i32(a);
645 let bv = self.to_u32(b);
646 JsValue::Number(JsNumberType::Integer((av >> (bv & 0x1f)) as i64))
647 }
648
649 fn js_ushift_right(&self, a: JsValue, b: JsValue) -> JsValue {
650 let av = self.to_u32(a);
651 let bv = self.to_u32(b);
652 JsValue::Number(JsNumberType::Integer((av >> (bv & 0x1f)) as i64))
653 }
654
655 fn to_i32(&self, val: JsValue) -> i32 {
656 self.to_f64(&val) as i32
657 }
658
659 fn to_u32(&self, val: JsValue) -> u32 {
660 self.to_f64(&val) as u32
661 }
662
663 fn js_strict_equal(&self, a: &JsValue, b: &JsValue) -> bool {
664 match (a, b) {
665 (JsValue::Undefined, JsValue::Undefined) => true,
666 (JsValue::Null, JsValue::Null) => true,
667 (JsValue::Boolean(a), JsValue::Boolean(b)) => a == b,
668 (JsValue::Number(a), JsValue::Number(b)) => self.num_to_f64(a) == self.num_to_f64(b),
669 (JsValue::String(a), JsValue::String(b)) => a == b,
670 (JsValue::Symbol(a), JsValue::Symbol(b)) => a == b,
671 (JsValue::Object(a), JsValue::Object(b)) => std::rc::Rc::ptr_eq(a, b),
672 _ => false,
673 }
674 }
675
676 fn js_abstract_equal(&self, a: &JsValue, b: &JsValue) -> bool {
677 self.js_strict_equal(a, b)
678 }
679
680 fn js_less_than(&self, a: &JsValue, b: &JsValue) -> bool {
681 match (a, b) {
682 (JsValue::String(sa), JsValue::String(sb)) => sa < sb,
683 _ => self.to_f64(a) < self.to_f64(b),
684 }
685 }
686
687 fn as_number<'b>(&self, v: &'b JsValue) -> Option<&'b JsNumberType> {
688 match v {
689 JsValue::Number(n) => Some(n),
690 _ => None,
691 }
692 }
693
694 fn fast_number_binop(
695 &self,
696 a: &JsValue,
697 b: &JsValue,
698 op: fn(f64, f64) -> f64,
699 ) -> Option<JsValue> {
700 match (self.as_number(a), self.as_number(b)) {
701 (Some(na), Some(nb)) => {
702 let fa = self.num_to_f64(na);
703 let fb = self.num_to_f64(nb);
704 Some(self.f64_to_jsvalue(op(fa, fb)))
705 }
706 _ => None,
707 }
708 }
709
710 fn to_display_string(&self, val: &JsValue) -> String {
711 match val {
712 JsValue::Undefined => "undefined".to_string(),
713 JsValue::Null => "null".to_string(),
714 JsValue::Boolean(b) => b.to_string(),
715 JsValue::Number(n) => match n {
716 JsNumberType::Integer(i) => i.to_string(),
717 JsNumberType::Float(f) => f.to_string(),
718 JsNumberType::NaN => "NaN".to_string(),
719 JsNumberType::PositiveInfinity => "Infinity".to_string(),
720 JsNumberType::NegativeInfinity => "-Infinity".to_string(),
721 },
722 JsValue::String(s) => s.clone(),
723 JsValue::Symbol(_) => "symbol".to_string(),
724 JsValue::Object(_) => "[object]".to_string(),
725 }
726 }
727}