1use crate::runner::ds::error::JErrorType;
8use crate::runner::ds::value::{JsNumberType, JsValue};
9use crate::runner::plugin::types::EvalContext;
10use crate::runner::ds::lex_env::JsLexEnvironmentType;
11use crate::runner::ds::object::{JsObject, ObjectType};
12use crate::runner::ds::object_property::{PropertyDescriptor, PropertyDescriptorData, PropertyKey};
13use crate::runner::ds::value::JsValueOrSelf;
14use crate::runner::ds::operations::type_conversion::to_string;
15use crate::runner::eval::expression::{SimpleFunctionObject, call_function_object};
16
17use std::cell::RefCell;
18use std::rc::Rc;
19
20use super::bytecode::{Chunk, OpCode};
21
22pub enum VmResult {
24 Ok(JsValue),
26 Error(JErrorType),
28}
29
30#[derive(Clone)]
31struct EnvCacheEntry {
32 version: u64,
33 env: JsLexEnvironmentType,
34}
35
36#[derive(Clone)]
37struct PropCacheEntry {
38 obj: crate::runner::ds::object::JsObjectType,
39 prop: String,
40}
41
42pub struct Vm<'a> {
44 chunk: &'a Chunk,
45 ip: usize,
47 stack: Vec<JsValue>,
49 locals: Vec<JsValue>,
51 get_var_cache: Vec<Option<EnvCacheEntry>>,
53 set_var_cache: Vec<Option<EnvCacheEntry>>,
55 get_prop_cache: Vec<Option<PropCacheEntry>>,
57 ctx: EvalContext,
59}
60
61impl<'a> Vm<'a> {
62 pub fn new(chunk: &'a Chunk, ctx: EvalContext) -> Self {
63 Vm {
64 chunk,
65 ip: 0,
66 stack: Vec::with_capacity(256),
67 locals: vec![JsValue::Undefined; chunk.locals.len()],
68 get_var_cache: vec![None; chunk.code.len()],
69 set_var_cache: vec![None; chunk.code.len()],
70 get_prop_cache: vec![None; chunk.code.len()],
71 ctx,
72 }
73 }
74
75 pub fn into_ctx(mut self) -> EvalContext {
77 self.sync_locals_into_ctx();
78 self.ctx
79 }
80
81 pub fn run(&mut self) -> VmResult {
83 loop {
84 if self.ip >= self.chunk.code.len() {
85 return VmResult::Ok(self.stack.pop().unwrap_or(JsValue::Undefined));
86 }
87
88 let instr_index = self.ip;
89 let instr = &self.chunk.code[instr_index];
90 let op = instr.op;
91 let operand = instr.operand;
92 let operand2 = instr.operand2;
93 let operand3 = instr.operand3;
94 self.ip += 1;
95
96 match op {
97 OpCode::Constant => {
99 let val = self.chunk.constants[operand as usize].clone();
100 self.stack.push(val);
101 }
102 OpCode::Undefined => self.stack.push(JsValue::Undefined),
103 OpCode::Null => self.stack.push(JsValue::Null),
104 OpCode::True => self.stack.push(JsValue::Boolean(true)),
105 OpCode::False => self.stack.push(JsValue::Boolean(false)),
106
107 OpCode::Add => {
109 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
110 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
111 self.stack.push(self.js_add(a, b));
112 }
113 OpCode::Sub => {
114 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
115 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
116 self.stack.push(self.js_sub(a, b));
117 }
118 OpCode::Mul => {
119 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
120 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
121 self.stack.push(self.js_mul(a, b));
122 }
123 OpCode::Div => {
124 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
125 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
126 self.stack.push(self.js_div(a, b));
127 }
128 OpCode::Mod => {
129 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
130 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
131 self.stack.push(self.js_mod(a, b));
132 }
133 OpCode::Negate => {
134 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
135 self.stack.push(self.js_negate(a));
136 }
137
138 OpCode::BitAnd => {
140 let bv = self.stack.pop().unwrap_or(JsValue::Undefined);
141 let av = self.stack.pop().unwrap_or(JsValue::Undefined);
142 let b = self.to_i32(bv);
143 let a = self.to_i32(av);
144 self.stack.push(JsValue::Number(JsNumberType::Integer((a & b) as i64)));
145 }
146 OpCode::BitOr => {
147 let bv = self.stack.pop().unwrap_or(JsValue::Undefined);
148 let av = self.stack.pop().unwrap_or(JsValue::Undefined);
149 let b = self.to_i32(bv);
150 let a = self.to_i32(av);
151 self.stack.push(JsValue::Number(JsNumberType::Integer((a | b) as i64)));
152 }
153 OpCode::BitXor => {
154 let bv = self.stack.pop().unwrap_or(JsValue::Undefined);
155 let av = self.stack.pop().unwrap_or(JsValue::Undefined);
156 let b = self.to_i32(bv);
157 let a = self.to_i32(av);
158 self.stack.push(JsValue::Number(JsNumberType::Integer((a ^ b) as i64)));
159 }
160 OpCode::BitNot => {
161 let av = self.stack.pop().unwrap_or(JsValue::Undefined);
162 let a = self.to_i32(av);
163 self.stack.push(JsValue::Number(JsNumberType::Integer((!a) as i64)));
164 }
165 OpCode::ShiftLeft => {
166 let bv = self.stack.pop().unwrap_or(JsValue::Undefined);
167 let av = self.stack.pop().unwrap_or(JsValue::Undefined);
168 let b = self.to_u32(bv);
169 let a = self.to_i32(av);
170 self.stack.push(JsValue::Number(JsNumberType::Integer(
171 (a << (b & 0x1f)) as i64,
172 )));
173 }
174 OpCode::ShiftRight => {
175 let bv = self.stack.pop().unwrap_or(JsValue::Undefined);
176 let av = self.stack.pop().unwrap_or(JsValue::Undefined);
177 let b = self.to_u32(bv);
178 let a = self.to_i32(av);
179 self.stack.push(JsValue::Number(JsNumberType::Integer(
180 (a >> (b & 0x1f)) as i64,
181 )));
182 }
183 OpCode::UShiftRight => {
184 let bv = self.stack.pop().unwrap_or(JsValue::Undefined);
185 let av = self.stack.pop().unwrap_or(JsValue::Undefined);
186 let b = self.to_u32(bv);
187 let a = self.to_u32(av);
188 self.stack.push(JsValue::Number(JsNumberType::Integer(
189 (a >> (b & 0x1f)) as i64,
190 )));
191 }
192
193 OpCode::StrictEqual => {
195 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
196 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
197 self.stack.push(JsValue::Boolean(self.js_strict_equal(&a, &b)));
198 }
199 OpCode::StrictNotEqual => {
200 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
201 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
202 self.stack.push(JsValue::Boolean(!self.js_strict_equal(&a, &b)));
203 }
204 OpCode::Equal => {
205 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
206 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
207 self.stack.push(JsValue::Boolean(self.js_abstract_equal(&a, &b)));
208 }
209 OpCode::NotEqual => {
210 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
211 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
212 self.stack.push(JsValue::Boolean(!self.js_abstract_equal(&a, &b)));
213 }
214 OpCode::LessThan => {
215 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
216 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
217 self.stack.push(JsValue::Boolean(self.js_less_than(&a, &b)));
218 }
219 OpCode::LessEqual => {
220 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
221 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
222 self.stack.push(JsValue::Boolean(!self.js_less_than(&b, &a)));
224 }
225 OpCode::GreaterThan => {
226 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
227 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
228 self.stack.push(JsValue::Boolean(self.js_less_than(&b, &a)));
229 }
230 OpCode::GreaterEqual => {
231 let b = self.stack.pop().unwrap_or(JsValue::Undefined);
232 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
233 self.stack.push(JsValue::Boolean(!self.js_less_than(&a, &b)));
234 }
235
236 OpCode::Not => {
238 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
239 self.stack.push(JsValue::Boolean(!self.is_truthy(&a)));
240 }
241 OpCode::TypeOf => {
242 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
243 let type_str = match &a {
244 JsValue::Undefined => "undefined",
245 JsValue::Null => "object",
246 JsValue::Boolean(_) => "boolean",
247 JsValue::Number(_) => "number",
248 JsValue::String(_) => "string",
249 JsValue::Symbol(_) => "symbol",
250 JsValue::Object(_) => "object",
251 };
252 self.stack.push(JsValue::String(type_str.to_string()));
253 }
254 OpCode::Void => {
255 self.stack.pop();
256 self.stack.push(JsValue::Undefined);
257 }
258 OpCode::UnaryPlus => {
259 let a = self.stack.pop().unwrap_or(JsValue::Undefined);
260 self.stack.push(self.to_number_value(a));
261 }
262
263 OpCode::GetVar => {
265 let name = self.chunk.get_name(operand);
266 match self.get_var_cached(instr_index, name) {
267 Ok(val) => self.stack.push(val),
268 Err(e) => return VmResult::Error(e),
269 }
270 }
271 OpCode::SetVar => {
272 let name = self.chunk.get_name(operand);
273 let val = self.stack.pop().unwrap_or(JsValue::Undefined);
274 if let Err(e) = self.set_var_cached(instr_index, name, val) {
275 return VmResult::Error(e);
276 }
277 }
278 OpCode::DeclareVar => {
279 let name = self.chunk.get_name(operand);
280 if !self.ctx.has_var_binding(name) {
281 if let Err(e) = self.ctx.create_var_binding(name) {
282 return VmResult::Error(e);
283 }
284 }
285 }
286 OpCode::DeclareLet => {
287 let name = self.chunk.get_name(operand);
288 if let Err(e) = self.ctx.create_binding(name, false) {
289 return VmResult::Error(e);
290 }
291 }
292 OpCode::DeclareConst => {
293 let name = self.chunk.get_name(operand);
294 if let Err(e) = self.ctx.create_binding(name, true) {
295 return VmResult::Error(e);
296 }
297 }
298 OpCode::InitVar => {
299 let name = self.chunk.get_name(operand);
300 let val = self.stack.pop().unwrap_or(JsValue::Undefined);
301 if let Err(e) = self.ctx.initialize_var_binding(name, val.clone()) {
305 return VmResult::Error(e);
306 }
307 let _ = self.ctx.set_var_binding(name, val);
310 }
311 OpCode::InitBinding => {
312 let name = self.chunk.get_name(operand);
313 let val = self.stack.pop().unwrap_or(JsValue::Undefined);
314 if let Err(e) = self.ctx.initialize_binding(name, val) {
315 return VmResult::Error(e);
316 }
317 }
318
319 OpCode::GetLocal => {
320 let slot = operand as usize;
321 let val = self.locals.get(slot).cloned().unwrap_or(JsValue::Undefined);
322 self.stack.push(val);
323 }
324 OpCode::SetLocal | OpCode::InitLocal => {
325 let slot = operand as usize;
326 let val = self.stack.pop().unwrap_or(JsValue::Undefined);
327 if let Some(local) = self.locals.get_mut(slot) {
328 *local = val;
329 }
330 }
331
332 OpCode::Jump => {
334 self.ip = operand as usize;
335 }
336 OpCode::JumpIfFalse => {
337 let val = self.stack.pop().unwrap_or(JsValue::Undefined);
338 if !self.is_truthy(&val) {
339 self.ip = operand as usize;
340 }
341 }
342 OpCode::JumpIfTrue => {
343 let val = self.stack.pop().unwrap_or(JsValue::Undefined);
344 if self.is_truthy(&val) {
345 self.ip = operand as usize;
346 }
347 }
348
349 OpCode::LoopStart => {
350 }
352
353 OpCode::Pop => {
355 self.stack.pop();
356 }
357 OpCode::Dup => {
358 if let Some(top) = self.stack.last() {
359 self.stack.push(top.clone());
360 }
361 }
362 OpCode::Dup2 => {
363 if self.stack.len() >= 2 {
364 let len = self.stack.len();
365 let first = self.stack[len - 2].clone();
366 let second = self.stack[len - 1].clone();
367 self.stack.push(first);
368 self.stack.push(second);
369 }
370 }
371
372 OpCode::PushScope => {
374 self.ctx.push_block_scope();
375 }
376 OpCode::PopScope => {
377 self.ctx.pop_block_scope();
378 }
379
380 OpCode::GetProp => {
382 let obj_val = self.stack.pop().unwrap_or(JsValue::Undefined);
383 let prop_name = self.chunk.get_name(operand);
384 match self.get_prop_cached(instr_index, &obj_val, prop_name) {
385 Ok(val) => self.stack.push(val),
386 Err(e) => return VmResult::Error(e),
387 }
388 }
389 OpCode::SetProp => {
390 let val = self.stack.pop().unwrap_or(JsValue::Undefined);
391 let obj_val = self.stack.pop().unwrap_or(JsValue::Undefined);
392 let prop_name = self.chunk.get_name(operand);
393 if let Err(e) = self.set_prop_value(&obj_val, prop_name, val.clone()) {
394 return VmResult::Error(e);
395 }
396 self.stack.push(val);
397 }
398 OpCode::GetElem => {
399 let key_val = self.stack.pop().unwrap_or(JsValue::Undefined);
400 let obj_val = self.stack.pop().unwrap_or(JsValue::Undefined);
401 let prop_name = match self.to_property_key(&key_val) {
402 Ok(key) => key,
403 Err(e) => return VmResult::Error(e),
404 };
405 match self.get_prop_cached(instr_index, &obj_val, &prop_name) {
406 Ok(val) => self.stack.push(val),
407 Err(e) => return VmResult::Error(e),
408 }
409 }
410 OpCode::SetElem => {
411 let val = self.stack.pop().unwrap_or(JsValue::Undefined);
412 let key_val = self.stack.pop().unwrap_or(JsValue::Undefined);
413 let obj_val = self.stack.pop().unwrap_or(JsValue::Undefined);
414 let prop_name = match self.to_property_key(&key_val) {
415 Ok(key) => key,
416 Err(e) => return VmResult::Error(e),
417 };
418 if let Err(e) = self.set_prop_value(&obj_val, &prop_name, val.clone()) {
419 return VmResult::Error(e);
420 }
421 self.stack.push(val);
422 }
423
424 OpCode::Call => {
426 let argc = operand as usize;
427 let mut args = Vec::with_capacity(argc);
428 for _ in 0..argc {
429 args.push(self.stack.pop().unwrap_or(JsValue::Undefined));
430 }
431 args.reverse();
432 let callee = self.stack.pop().unwrap_or(JsValue::Undefined);
433 match &callee {
434 JsValue::Object(obj) => {
435 let this_val = JsValue::Undefined;
436 match call_function_object(obj, this_val, args, &mut self.ctx) {
437 Ok(result) => self.stack.push(result),
438 Err(e) => return VmResult::Error(e),
439 }
440 }
441 _ => return VmResult::Error(JErrorType::TypeError(
442 format!("{} is not a function", callee),
443 )),
444 }
445 }
446 OpCode::CallMethod => {
447 let argc = operand as usize;
448 let method_name = self.chunk.get_name(operand2);
449 let obj_var_name = self.chunk.get_name(operand3);
450 let mut args = Vec::with_capacity(argc);
451 for _ in 0..argc {
452 args.push(self.stack.pop().unwrap_or(JsValue::Undefined));
453 }
454 args.reverse();
455 let object = self.stack.pop().unwrap_or(JsValue::Undefined);
456
457 if !obj_var_name.is_empty() {
460 let sg = self.ctx.super_global.clone();
461 let result = sg.borrow().call_method(
462 obj_var_name, method_name,
463 &mut self.ctx, object.clone(), args.clone(),
464 );
465 if let Some(res) = result {
466 match res {
467 Ok(val) => { self.stack.push(val); continue; }
468 Err(e) => return VmResult::Error(e),
469 }
470 }
471 }
472
473 let type_name = match &object {
475 JsValue::String(_) => Some("String"),
476 JsValue::Number(_) => Some("Number"),
477 _ => None,
478 };
479 if let Some(type_name) = type_name {
480 let sg = self.ctx.super_global.clone();
481 let result = sg.borrow().call_method(
482 type_name, method_name,
483 &mut self.ctx, object.clone(), args.clone(),
484 );
485 if let Some(res) = result {
486 match res {
487 Ok(val) => { self.stack.push(val); continue; }
488 Err(e) => return VmResult::Error(e),
489 }
490 }
491 }
492
493 if let JsValue::Object(ref obj) = object {
495 let prop_key = PropertyKey::Str(method_name.to_string());
496 let method_val = {
497 let obj_ref = obj.borrow();
498 obj_ref
499 .as_js_object()
500 .get_own_property(&prop_key)
501 .ok()
502 .flatten()
503 .and_then(|desc| match desc {
504 PropertyDescriptor::Data(data) => Some(data.value.clone()),
505 _ => None,
506 })
507 };
508 let method_val = method_val.or_else(|| {
509 let obj_ref = obj.borrow();
510 let proto = obj_ref.as_js_object().get_prototype_of();
511 if let Some(proto) = proto {
512 let proto_ref = proto.borrow();
513 proto_ref
514 .as_js_object()
515 .get_own_property(&prop_key)
516 .ok()
517 .flatten()
518 .and_then(|desc| match desc {
519 PropertyDescriptor::Data(data) => Some(data.value.clone()),
520 _ => None,
521 })
522 } else {
523 None
524 }
525 });
526
527 if let Some(JsValue::Object(method_obj)) = method_val {
528 match call_function_object(&method_obj, object.clone(), args, &mut self.ctx) {
529 Ok(result) => {
530 self.stack.push(result);
531 continue;
532 }
533 Err(e) => return VmResult::Error(e),
534 }
535 }
536 }
537
538 self.stack.push(JsValue::Undefined);
540 }
541
542 OpCode::Return => {
544 let val = self.stack.pop().unwrap_or(JsValue::Undefined);
545 return VmResult::Ok(val);
546 }
547 OpCode::Halt => {
548 return VmResult::Ok(self.stack.pop().unwrap_or(JsValue::Undefined));
549 }
550
551 OpCode::PreIncVar => {
553 let name = self.chunk.get_name(operand);
554 match self.get_var_cached(instr_index, name) {
555 Ok(val) => {
556 let num = self.to_f64(&val) + 1.0;
557 let new_val = self.f64_to_jsvalue(num);
558 if let Err(e) = self.set_var_cached(instr_index, name, new_val.clone()) {
559 return VmResult::Error(e);
560 }
561 self.stack.push(new_val);
562 }
563 Err(e) => return VmResult::Error(e),
564 }
565 }
566 OpCode::PreDecVar => {
567 let name = self.chunk.get_name(operand);
568 match self.get_var_cached(instr_index, name) {
569 Ok(val) => {
570 let num = self.to_f64(&val) - 1.0;
571 let new_val = self.f64_to_jsvalue(num);
572 if let Err(e) = self.set_var_cached(instr_index, name, new_val.clone()) {
573 return VmResult::Error(e);
574 }
575 self.stack.push(new_val);
576 }
577 Err(e) => return VmResult::Error(e),
578 }
579 }
580 OpCode::PostIncVar => {
581 let name = self.chunk.get_name(operand);
582 match self.get_var_cached(instr_index, name) {
583 Ok(val) => {
584 let old_val = val.clone();
585 let num = self.to_f64(&val) + 1.0;
586 let new_val = self.f64_to_jsvalue(num);
587 if let Err(e) = self.set_var_cached(instr_index, name, new_val) {
588 return VmResult::Error(e);
589 }
590 self.stack.push(old_val);
591 }
592 Err(e) => return VmResult::Error(e),
593 }
594 }
595 OpCode::PostDecVar => {
596 let name = self.chunk.get_name(operand);
597 match self.get_var_cached(instr_index, name) {
598 Ok(val) => {
599 let old_val = val.clone();
600 let num = self.to_f64(&val) - 1.0;
601 let new_val = self.f64_to_jsvalue(num);
602 if let Err(e) = self.set_var_cached(instr_index, name, new_val) {
603 return VmResult::Error(e);
604 }
605 self.stack.push(old_val);
606 }
607 Err(e) => return VmResult::Error(e),
608 }
609 }
610
611 OpCode::GetVarForUpdate => {
612 let name = self.chunk.get_name(operand);
613 match self.get_var_cached(instr_index, name) {
614 Ok(val) => self.stack.push(val),
615 Err(e) => return VmResult::Error(e),
616 }
617 }
618
619 OpCode::MakeClosure => {
621 let func_template = &self.chunk.functions[operand as usize];
622 let body_ptr = func_template.body_ptr;
623 let params_ptr = func_template.params_ptr;
624 let environment = self.ctx.lex_env.clone();
625
626 let mut func_obj = SimpleFunctionObject::new(body_ptr, params_ptr, environment);
627
628 func_obj.get_object_base_mut().properties.insert(
630 PropertyKey::Str("__simple_function__".to_string()),
631 PropertyDescriptor::Data(PropertyDescriptorData {
632 value: JsValue::Boolean(true),
633 writable: false,
634 enumerable: false,
635 configurable: false,
636 }),
637 );
638
639 let obj_ref = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(func_obj))));
640 self.stack.push(JsValue::Object(obj_ref));
641 }
642 }
643 }
644 }
645
646 fn get_var_cached(&mut self, instr_index: usize, name: &str) -> Result<JsValue, JErrorType> {
650 if let Some(Some(entry)) = self.get_var_cache.get(instr_index) {
651 if entry.version == self.ctx.current_lex_env_version() {
652 if let Ok(val) = self.ctx.get_binding_in_env(&entry.env, name) {
653 return Ok(val);
654 }
655 }
656 }
657
658 let (val, env) = self.ctx.get_binding_with_env(name)?;
659 let entry = EnvCacheEntry {
660 version: self.ctx.current_lex_env_version(),
661 env,
662 };
663 if let Some(slot) = self.get_var_cache.get_mut(instr_index) {
664 *slot = Some(entry);
665 }
666 Ok(val)
667 }
668
669 fn set_var_cached(
670 &mut self,
671 instr_index: usize,
672 name: &str,
673 value: JsValue,
674 ) -> Result<(), JErrorType> {
675 if let Some(Some(entry)) = self.set_var_cache.get(instr_index) {
676 if entry.version == self.ctx.current_lex_env_version() {
677 if self
678 .ctx
679 .set_binding_in_env_cached(&entry.env, name, value.clone())
680 .is_ok()
681 {
682 return Ok(());
683 }
684 }
685 }
686
687 let env = self.ctx.set_binding_with_env(name, value)?;
688 let entry = EnvCacheEntry {
689 version: self.ctx.current_lex_env_version(),
690 env,
691 };
692 if let Some(slot) = self.set_var_cache.get_mut(instr_index) {
693 *slot = Some(entry);
694 }
695 Ok(())
696 }
697
698 fn get_prop_cached(
699 &mut self,
700 instr_index: usize,
701 obj_val: &JsValue,
702 prop_name: &str,
703 ) -> Result<JsValue, JErrorType> {
704 if let JsValue::Object(obj) = obj_val {
705 if let Some(Some(entry)) = self.get_prop_cache.get(instr_index) {
706 if std::rc::Rc::ptr_eq(obj, &entry.obj) && entry.prop == prop_name {
707 let prop_key = PropertyKey::Str(prop_name.to_string());
708 return obj
709 .borrow()
710 .as_js_object()
711 .get(&mut self.ctx.ctx_stack, &prop_key, JsValueOrSelf::SelfValue);
712 }
713 }
714
715 let prop_key = PropertyKey::Str(prop_name.to_string());
716 let val = obj
717 .borrow()
718 .as_js_object()
719 .get(&mut self.ctx.ctx_stack, &prop_key, JsValueOrSelf::SelfValue)?;
720 let entry = PropCacheEntry {
721 obj: obj.clone(),
722 prop: prop_name.to_string(),
723 };
724 if let Some(slot) = self.get_prop_cache.get_mut(instr_index) {
725 *slot = Some(entry);
726 }
727 Ok(val)
728 } else if matches!(obj_val, JsValue::Undefined | JsValue::Null) {
729 Err(JErrorType::TypeError("Cannot read property of null/undefined".to_string()))
730 } else {
731 Ok(JsValue::Undefined)
732 }
733 }
734
735 fn set_prop_value(
736 &mut self,
737 obj_val: &JsValue,
738 prop_name: &str,
739 value: JsValue,
740 ) -> Result<(), JErrorType> {
741 match obj_val {
742 JsValue::Object(obj) => {
743 let prop_key = PropertyKey::Str(prop_name.to_string());
744 let mut obj_ref = obj.borrow_mut();
745 let ok = obj_ref
746 .as_js_object_mut()
747 .set(&mut self.ctx.ctx_stack, prop_key, value, JsValueOrSelf::SelfValue)?;
748 if ok {
749 Ok(())
750 } else {
751 Err(JErrorType::TypeError("Failed to set property".to_string()))
752 }
753 }
754 _ => Err(JErrorType::TypeError("Cannot set property on non-object".to_string())),
755 }
756 }
757
758 fn to_property_key(&mut self, value: &JsValue) -> Result<String, JErrorType> {
759 to_string(&mut self.ctx.ctx_stack, value)
760 }
761
762 fn sync_locals_into_ctx(&mut self) {
763 for (slot, val) in self.locals.iter().enumerate() {
764 let name = self.chunk.get_local_name(slot as u32);
765 if !self.ctx.has_var_binding(name) {
766 let _ = self.ctx.create_var_binding(name);
767 let _ = self.ctx.initialize_var_binding(name, val.clone());
768 } else {
769 let _ = self.ctx.set_var_binding(name, val.clone());
770 }
771 }
772 }
773
774 fn is_truthy(&self, val: &JsValue) -> bool {
777 match val {
778 JsValue::Undefined | JsValue::Null => false,
779 JsValue::Boolean(b) => *b,
780 JsValue::Number(n) => match n {
781 JsNumberType::Integer(i) => *i != 0,
782 JsNumberType::Float(f) => *f != 0.0 && !f.is_nan(),
783 JsNumberType::NaN => false,
784 JsNumberType::PositiveInfinity | JsNumberType::NegativeInfinity => true,
785 },
786 JsValue::String(s) => !s.is_empty(),
787 _ => true,
788 }
789 }
790
791 fn to_f64(&self, val: &JsValue) -> f64 {
792 match val {
793 JsValue::Number(JsNumberType::Integer(i)) => *i as f64,
794 JsValue::Number(JsNumberType::Float(f)) => *f,
795 JsValue::Number(JsNumberType::NaN) => f64::NAN,
796 JsValue::Number(JsNumberType::PositiveInfinity) => f64::INFINITY,
797 JsValue::Number(JsNumberType::NegativeInfinity) => f64::NEG_INFINITY,
798 JsValue::Boolean(true) => 1.0,
799 JsValue::Boolean(false) => 0.0,
800 JsValue::Null => 0.0,
801 JsValue::Undefined => f64::NAN,
802 JsValue::String(s) => s.parse::<f64>().unwrap_or(f64::NAN),
803 _ => f64::NAN,
804 }
805 }
806
807 fn to_i32(&self, val: JsValue) -> i32 {
808 let n = self.to_f64(&val);
809 if n.is_nan() || n.is_infinite() || n == 0.0 {
810 0
811 } else {
812 (n as i64 & 0xFFFFFFFF) as i32
813 }
814 }
815
816 fn to_u32(&self, val: JsValue) -> u32 {
817 let n = self.to_f64(&val);
818 if n.is_nan() || n.is_infinite() || n == 0.0 {
819 0
820 } else {
821 (n as i64 & 0xFFFFFFFF) as u32
822 }
823 }
824
825 fn f64_to_jsvalue(&self, n: f64) -> JsValue {
826 if n.is_nan() {
827 JsValue::Number(JsNumberType::NaN)
828 } else if n.is_infinite() {
829 if n > 0.0 {
830 JsValue::Number(JsNumberType::PositiveInfinity)
831 } else {
832 JsValue::Number(JsNumberType::NegativeInfinity)
833 }
834 } else if n.fract() == 0.0 && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
835 JsValue::Number(JsNumberType::Integer(n as i64))
836 } else {
837 JsValue::Number(JsNumberType::Float(n))
838 }
839 }
840
841 fn to_number_value(&self, val: JsValue) -> JsValue {
842 match val {
843 JsValue::Number(_) => val,
844 _ => self.f64_to_jsvalue(self.to_f64(&val)),
845 }
846 }
847
848 fn js_add(&self, a: JsValue, b: JsValue) -> JsValue {
851 match (&a, &b) {
853 (JsValue::String(sa), JsValue::String(sb)) => {
854 JsValue::String(format!("{}{}", sa, sb))
855 }
856 (JsValue::String(sa), _) => {
857 JsValue::String(format!("{}{}", sa, self.to_display_string(&b)))
858 }
859 (_, JsValue::String(sb)) => {
860 JsValue::String(format!("{}{}", self.to_display_string(&a), sb))
861 }
862 _ => {
863 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x + y) {
864 return val;
865 }
866 self.f64_to_jsvalue(self.to_f64(&a) + self.to_f64(&b))
867 }
868 }
869 }
870
871 fn js_sub(&self, a: JsValue, b: JsValue) -> JsValue {
872 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x - y) {
873 return val;
874 }
875 self.f64_to_jsvalue(self.to_f64(&a) - self.to_f64(&b))
876 }
877
878 fn js_mul(&self, a: JsValue, b: JsValue) -> JsValue {
879 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x * y) {
880 return val;
881 }
882 self.f64_to_jsvalue(self.to_f64(&a) * self.to_f64(&b))
883 }
884
885 fn js_div(&self, a: JsValue, b: JsValue) -> JsValue {
886 if let Some(nb) = self.as_number(&b) {
887 let nb = self.num_to_f64(nb);
888 if nb == 0.0 {
889 let na = self.to_f64(&a);
890 if na == 0.0 || na.is_nan() {
891 return JsValue::Number(JsNumberType::NaN);
892 } else if na > 0.0 {
893 return JsValue::Number(JsNumberType::PositiveInfinity);
894 } else {
895 return JsValue::Number(JsNumberType::NegativeInfinity);
896 }
897 }
898 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x / y) {
899 return val;
900 }
901 }
902
903 let nb = self.to_f64(&b);
904 if nb == 0.0 {
905 let na = self.to_f64(&a);
906 if na == 0.0 || na.is_nan() {
907 JsValue::Number(JsNumberType::NaN)
908 } else if na > 0.0 {
909 JsValue::Number(JsNumberType::PositiveInfinity)
910 } else {
911 JsValue::Number(JsNumberType::NegativeInfinity)
912 }
913 } else {
914 self.f64_to_jsvalue(self.to_f64(&a) / nb)
915 }
916 }
917
918 fn js_mod(&self, a: JsValue, b: JsValue) -> JsValue {
919 if let Some(val) = self.fast_number_binop(&a, &b, |x, y| x % y) {
920 return val;
921 }
922 let na = self.to_f64(&a);
923 let nb = self.to_f64(&b);
924 if nb == 0.0 {
925 JsValue::Number(JsNumberType::NaN)
926 } else {
927 self.f64_to_jsvalue(na % nb)
928 }
929 }
930
931 fn js_negate(&self, a: JsValue) -> JsValue {
932 if let JsValue::Number(n) = &a {
933 return self.f64_to_jsvalue(-self.num_to_f64(n));
934 }
935 self.f64_to_jsvalue(-self.to_f64(&a))
936 }
937
938 fn as_number<'b>(&self, v: &'b JsValue) -> Option<&'b JsNumberType> {
939 match v {
940 JsValue::Number(n) => Some(n),
941 _ => None,
942 }
943 }
944
945 fn fast_number_binop(
946 &self,
947 a: &JsValue,
948 b: &JsValue,
949 op: fn(f64, f64) -> f64,
950 ) -> Option<JsValue> {
951 match (self.as_number(a), self.as_number(b)) {
952 (Some(na), Some(nb)) => {
953 let fa = self.num_to_f64(na);
954 let fb = self.num_to_f64(nb);
955 Some(self.f64_to_jsvalue(op(fa, fb)))
956 }
957 _ => None,
958 }
959 }
960
961 fn js_strict_equal(&self, a: &JsValue, b: &JsValue) -> bool {
964 match (a, b) {
965 (JsValue::Undefined, JsValue::Undefined) => true,
966 (JsValue::Null, JsValue::Null) => true,
967 (JsValue::Boolean(a), JsValue::Boolean(b)) => a == b,
968 (JsValue::Number(a), JsValue::Number(b)) => {
969 let fa = self.num_to_f64(a);
970 let fb = self.num_to_f64(b);
971 if fa.is_nan() || fb.is_nan() {
972 false
973 } else {
974 fa == fb
975 }
976 }
977 (JsValue::String(a), JsValue::String(b)) => a == b,
978 _ => false,
979 }
980 }
981
982 fn js_abstract_equal(&self, a: &JsValue, b: &JsValue) -> bool {
983 match (a, b) {
985 (JsValue::Undefined, JsValue::Null) | (JsValue::Null, JsValue::Undefined) => true,
986 _ => {
987 if std::mem::discriminant(a) == std::mem::discriminant(b) {
989 self.js_strict_equal(a, b)
990 } else {
991 let na = self.to_f64(a);
993 let nb = self.to_f64(b);
994 if na.is_nan() || nb.is_nan() {
995 false
996 } else {
997 na == nb
998 }
999 }
1000 }
1001 }
1002 }
1003
1004 fn js_less_than(&self, a: &JsValue, b: &JsValue) -> bool {
1005 match (a, b) {
1006 (JsValue::String(sa), JsValue::String(sb)) => sa < sb,
1007 _ => {
1008 let na = self.to_f64(a);
1009 let nb = self.to_f64(b);
1010 if na.is_nan() || nb.is_nan() {
1011 false
1012 } else {
1013 na < nb
1014 }
1015 }
1016 }
1017 }
1018
1019 fn num_to_f64(&self, n: &JsNumberType) -> f64 {
1020 match n {
1021 JsNumberType::Integer(i) => *i as f64,
1022 JsNumberType::Float(f) => *f,
1023 JsNumberType::NaN => f64::NAN,
1024 JsNumberType::PositiveInfinity => f64::INFINITY,
1025 JsNumberType::NegativeInfinity => f64::NEG_INFINITY,
1026 }
1027 }
1028
1029 fn to_display_string(&self, val: &JsValue) -> String {
1030 match val {
1031 JsValue::Undefined => "undefined".to_string(),
1032 JsValue::Null => "null".to_string(),
1033 JsValue::Boolean(b) => b.to_string(),
1034 JsValue::Number(n) => match n {
1035 JsNumberType::Integer(i) => i.to_string(),
1036 JsNumberType::Float(f) => f.to_string(),
1037 JsNumberType::NaN => "NaN".to_string(),
1038 JsNumberType::PositiveInfinity => "Infinity".to_string(),
1039 JsNumberType::NegativeInfinity => "-Infinity".to_string(),
1040 },
1041 JsValue::String(s) => s.clone(),
1042 _ => "[object Object]".to_string(),
1043 }
1044 }
1045}