1#![allow(clippy::collapsible_if, clippy::collapsible_match)]
2
3use crate::error::JSError;
4use crate::js_promise::{PromiseState, run_event_loop};
5use crate::raise_eval_error;
6use crate::unicode::utf8_to_utf16;
7use std::cell::RefCell;
8use std::collections::HashMap;
9use std::rc::Rc;
10
11mod value;
12pub use value::*;
13
14mod property_key;
15pub use property_key::*;
16
17mod statement;
18pub use statement::*;
19
20mod token;
21pub use token::*;
22
23mod number;
24
25mod eval;
26pub use eval::*;
27
28mod parser;
29pub use parser::*;
30
31thread_local! {
32 static WELL_KNOWN_SYMBOLS: RefCell<HashMap<String, Rc<RefCell<Value>>>> = RefCell::new(HashMap::new());
34}
35
36pub fn evaluate_script<T, P>(script: T, script_path: Option<P>) -> Result<Value, JSError>
37where
38 T: AsRef<str>,
39 P: AsRef<std::path::Path>,
40{
41 let script = script.as_ref();
42 log::debug!("evaluate_script async called with script len {}", script.len());
43 let filtered = filter_input_script(script);
44 log::trace!("filtered script:\n{}", filtered);
45 let mut tokens = match tokenize(&filtered) {
46 Ok(t) => t,
47 Err(e) => {
48 log::debug!("tokenize error: {e:?}");
49 return Err(e);
50 }
51 };
52 let statements = match parse_statements(&mut tokens) {
53 Ok(s) => s,
54 Err(e) => {
55 log::debug!("parse_statements error: {e:?}");
56 return Err(e);
57 }
58 };
59 log::debug!("parsed {} statements", statements.len());
60 for (i, stmt) in statements.iter().enumerate() {
61 log::trace!("stmt[{i}] = {stmt:?}");
62 }
63 let env: JSObjectDataPtr = new_js_object_data();
64 env.borrow_mut().is_function_scope = true;
65 let path = script_path.map_or("<script>".to_string(), |p| p.as_ref().to_string_lossy().to_string());
67 let _ = obj_set_key_value(&env, &"__script_name".into(), Value::String(utf8_to_utf16(&path)));
68
69 for line in script.lines() {
72 let l = line.trim();
73 if l.starts_with("import * as")
74 && l.contains("from")
75 && let (Some(as_idx), Some(from_idx)) = (l.find("as"), l.find("from"))
76 {
77 let name_part = &l[as_idx + 2..from_idx].trim();
78 let name = PropertyKey::String(name_part.trim().to_string());
79 if let Some(start_quote) = l[from_idx..].find(|c: char| ['"', '\''].contains(&c)) {
80 let quote_char = l[from_idx + start_quote..].chars().next().unwrap();
81 let rest = &l[from_idx + start_quote + 1..];
82 if let Some(end_quote) = rest.find(quote_char) {
83 let module = &rest[..end_quote];
84 if module == "std" {
85 obj_set_key_value(&env, &name, Value::Object(crate::js_std::make_std_object()?))?;
86 } else if module == "os" {
87 obj_set_key_value(&env, &name, Value::Object(crate::js_os::make_os_object()?))?;
88 }
89 }
90 }
91 }
92 }
93
94 initialize_global_constructors(&env)?;
96
97 let v = evaluate_statements(&env, &statements)?;
98 if let Value::Object(obj) = &v
100 && let Some(promise_val_rc) = obj_get_key_value(obj, &"__promise".into())?
101 && let Value::Promise(promise) = &*promise_val_rc.borrow()
102 {
103 loop {
105 run_event_loop()?;
106 let promise_borrow = promise.borrow();
107 match &promise_borrow.state {
108 PromiseState::Fulfilled(val) => return Ok(val.clone()),
109 PromiseState::Rejected(reason) => {
110 return Err(raise_eval_error!(format!("Promise rejected: {}", value_to_string(reason))));
111 }
112 PromiseState::Pending => {
113 }
115 }
116 }
117 }
118 run_event_loop()?;
120 Ok(v)
121}
122
123pub fn ensure_constructor_object(env: &JSObjectDataPtr, name: &str, marker_key: &str) -> Result<JSObjectDataPtr, JSError> {
129 if let Some(val_rc) = obj_get_key_value(env, &name.into())? {
131 if let Value::Object(obj) = &*val_rc.borrow() {
132 return Ok(obj.clone());
133 }
134 }
135
136 let ctor = new_js_object_data();
137 obj_set_key_value(&ctor, &marker_key.into(), Value::Boolean(true))?;
139
140 let proto = new_js_object_data();
142 if let Some(object_ctor_val) = obj_get_key_value(env, &"Object".into())?
144 && let Value::Object(object_ctor) = &*object_ctor_val.borrow()
145 && let Some(obj_proto_val) = obj_get_key_value(object_ctor, &"prototype".into())?
146 && let Value::Object(obj_proto_obj) = &*obj_proto_val.borrow()
147 {
148 proto.borrow_mut().prototype = Some(obj_proto_obj.clone());
149 }
150
151 obj_set_key_value(&ctor, &"prototype".into(), Value::Object(proto.clone()))?;
152 obj_set_key_value(&proto, &"constructor".into(), Value::Object(ctor.clone()))?;
154
155 obj_set_key_value(env, &name.into(), Value::Object(ctor.clone()))?;
156 Ok(ctor)
157}
158
159pub fn get_constructor_prototype(env: &JSObjectDataPtr, name: &str) -> Result<Option<JSObjectDataPtr>, JSError> {
161 if let Some(val_rc) = obj_get_key_value(env, &name.into())? {
163 if let Value::Object(ctor_obj) = &*val_rc.borrow() {
164 if let Some(proto_val_rc) = obj_get_key_value(ctor_obj, &"prototype".into())? {
165 if let Value::Object(proto_obj) = &*proto_val_rc.borrow() {
166 return Ok(Some(proto_obj.clone()));
167 }
168 }
169 }
170 }
171
172 match evaluate_expr(env, &Expr::Var(name.to_string())) {
174 Ok(Value::Object(ctor_obj)) => {
175 if let Some(proto_val_rc) = obj_get_key_value(&ctor_obj, &"prototype".into())? {
176 if let Value::Object(proto_obj) = &*proto_val_rc.borrow() {
177 return Ok(Some(proto_obj.clone()));
178 }
179 }
180 Ok(None)
181 }
182 _ => Ok(None),
183 }
184}
185
186pub fn set_internal_prototype_from_constructor(obj: &JSObjectDataPtr, env: &JSObjectDataPtr, ctor_name: &str) -> Result<(), JSError> {
191 if let Some(proto_obj) = get_constructor_prototype(env, ctor_name)? {
192 obj.borrow_mut().prototype = Some(proto_obj.clone());
194 }
195 Ok(())
196}
197
198pub fn initialize_collection_from_iterable<F>(
201 args: &[Expr],
202 env: &JSObjectDataPtr,
203 constructor_name: &str,
204 mut process_item: F,
205) -> Result<(), JSError>
206where
207 F: FnMut(Value) -> Result<(), JSError>,
208{
209 if args.is_empty() {
210 return Ok(());
211 }
212 if args.len() > 1 {
213 let msg = format!("{constructor_name} constructor takes at most one argument",);
214 return Err(raise_eval_error!(msg));
215 }
216 let iterable = evaluate_expr(env, &args[0])?;
217 match iterable {
218 Value::Object(obj) => {
219 let mut i = 0;
220 loop {
221 let key = format!("{i}");
222 if let Some(item_val) = obj_get_key_value(&obj, &key.into())? {
223 let item = item_val.borrow().clone();
224 process_item(item)?;
225 } else {
226 break;
227 }
228 i += 1;
229 }
230 Ok(())
231 }
232 _ => Err(raise_eval_error!(format!("{constructor_name} constructor requires an iterable"))),
233 }
234}
235
236#[derive(Debug, Clone)]
237pub enum Expr {
238 Number(f64),
239 BigInt(String),
241 StringLit(Vec<u16>),
242 Boolean(bool),
243 Var(String),
244 Binary(Box<Expr>, BinaryOp, Box<Expr>),
245 UnaryNeg(Box<Expr>),
246 LogicalNot(Box<Expr>),
247 TypeOf(Box<Expr>),
248 Delete(Box<Expr>),
249 Void(Box<Expr>),
250 Assign(Box<Expr>, Box<Expr>), LogicalAndAssign(Box<Expr>, Box<Expr>), LogicalOrAssign(Box<Expr>, Box<Expr>), NullishAssign(Box<Expr>, Box<Expr>), AddAssign(Box<Expr>, Box<Expr>), SubAssign(Box<Expr>, Box<Expr>), PowAssign(Box<Expr>, Box<Expr>), MulAssign(Box<Expr>, Box<Expr>), DivAssign(Box<Expr>, Box<Expr>), ModAssign(Box<Expr>, Box<Expr>), BitXorAssign(Box<Expr>, Box<Expr>), BitAndAssign(Box<Expr>, Box<Expr>), BitOrAssign(Box<Expr>, Box<Expr>), LeftShiftAssign(Box<Expr>, Box<Expr>), RightShiftAssign(Box<Expr>, Box<Expr>), UnsignedRightShiftAssign(Box<Expr>, Box<Expr>), Increment(Box<Expr>),
267 Decrement(Box<Expr>),
268 PostIncrement(Box<Expr>),
269 PostDecrement(Box<Expr>),
270 Index(Box<Expr>, Box<Expr>),
271 Property(Box<Expr>, String),
272 Call(Box<Expr>, Vec<Expr>),
273 Function(Vec<String>, Vec<Statement>), AsyncFunction(Vec<String>, Vec<Statement>), GeneratorFunction(Vec<String>, Vec<Statement>), ArrowFunction(Vec<String>, Vec<Statement>), AsyncArrowFunction(Vec<String>, Vec<Statement>), Object(Vec<(String, Expr)>), Array(Vec<Expr>), Getter(Box<Expr>), Setter(Box<Expr>), Spread(Box<Expr>), OptionalProperty(Box<Expr>, String), OptionalCall(Box<Expr>, Vec<Expr>), OptionalIndex(Box<Expr>, Box<Expr>), Await(Box<Expr>), Yield(Option<Box<Expr>>), YieldStar(Box<Expr>), This, New(Box<Expr>, Vec<Expr>), Super, SuperCall(Vec<Expr>), SuperProperty(String), SuperMethod(String, Vec<Expr>), ArrayDestructuring(Vec<DestructuringElement>), ObjectDestructuring(Vec<ObjectDestructuringElement>), Conditional(Box<Expr>, Box<Expr>, Box<Expr>), Regex(String, String),
300 LogicalAnd(Box<Expr>, Box<Expr>),
302 LogicalOr(Box<Expr>, Box<Expr>),
303 Value(Value), }
305
306#[derive(Debug, Clone)]
307pub enum BinaryOp {
308 Add,
309 Sub,
310 Mul,
311 Div,
312 Mod,
313 Equal,
314 StrictEqual,
315 NotEqual,
316 StrictNotEqual,
317 LessThan,
318 GreaterThan,
319 LessEqual,
320 GreaterEqual,
321 InstanceOf,
322 In,
323 NullishCoalescing,
324 Pow,
325 BitXor,
326 BitAnd,
327 BitOr,
328 LeftShift,
329 RightShift,
330 UnsignedRightShift,
331}
332
333#[derive(Debug, Clone)]
334pub enum DestructuringElement {
335 Variable(String, Option<Box<Expr>>), NestedArray(Vec<DestructuringElement>), NestedObject(Vec<ObjectDestructuringElement>), Rest(String), Empty, }
341
342#[derive(Debug, Clone)]
343pub enum ObjectDestructuringElement {
344 Property { key: String, value: DestructuringElement }, Rest(String), }
347
348pub(crate) fn filter_input_script(script: &str) -> String {
349 let mut filtered = String::new();
351 let chars: Vec<char> = script.trim().chars().collect();
352 let mut i = 0;
353 let mut in_single = false;
354 let mut in_double = false;
355 let mut in_backtick = false;
356 let mut escape = false;
357
358 while i < chars.len() {
359 let ch = chars[i];
360
361 if escape {
363 filtered.push(ch);
364 escape = false;
365 i += 1;
366 continue;
367 }
368 if ch == '\\' {
369 escape = true;
370 filtered.push(ch);
371 i += 1;
372 continue;
373 }
374
375 match ch {
377 '\'' if !in_double && !in_backtick => {
378 in_single = !in_single;
379 filtered.push(ch);
380 i += 1;
381 continue;
382 }
383 '"' if !in_single && !in_backtick => {
384 in_double = !in_double;
385 filtered.push(ch);
386 i += 1;
387 continue;
388 }
389 '`' if !in_single && !in_double => {
390 in_backtick = !in_backtick;
391 filtered.push(ch);
392 i += 1;
393 continue;
394 }
395 _ => {}
396 }
397
398 if !in_single && !in_double && !in_backtick {
400 if i + 1 < chars.len() && ch == '/' && chars[i + 1] == '/' {
402 while i < chars.len() && chars[i] != '\n' {
404 i += 1;
405 }
406 continue;
408 }
409
410 if i + 1 < chars.len() && ch == '/' && chars[i + 1] == '*' {
412 i += 2; while i + 1 < chars.len() {
414 if chars[i] == '*' && chars[i + 1] == '/' {
415 i += 2; break;
417 }
418 i += 1;
419 }
420 continue;
421 }
422 }
423
424 filtered.push(ch);
426 i += 1;
427 }
428
429 let mut final_filtered = String::new();
431 for (i, line) in filtered.lines().enumerate() {
432 let mut current = String::new();
434 let mut in_single = false;
435 let mut in_double = false;
436 let mut in_backtick = false;
437 let mut escape = false;
438 let mut parts: Vec<(String, bool)> = Vec::new();
440 for ch in line.chars() {
441 if escape {
442 current.push(ch);
443 escape = false;
444 continue;
445 }
446 if ch == '\\' {
447 escape = true;
448 current.push(ch);
449 continue;
450 }
451 match ch {
452 '\'' if !in_double && !in_backtick => {
453 in_single = !in_single;
454 current.push(ch);
455 continue;
456 }
457 '"' if !in_single && !in_backtick => {
458 in_double = !in_double;
459 current.push(ch);
460 continue;
461 }
462 '`' if !in_single && !in_double => {
463 in_backtick = !in_backtick;
464 current.push(ch);
465 continue;
466 }
467 _ => {}
468 }
469 if ch == ';' && !in_single && !in_double && !in_backtick {
470 parts.push((current.clone(), true));
471 current.clear();
472 continue;
473 }
474 current.push(ch);
475 }
476 if !current.is_empty() {
478 parts.push((current, false));
479 }
480
481 for (part, had_semicolon) in parts.iter() {
482 let p = part.trim();
483 if p.is_empty() {
484 continue;
485 }
486 log::trace!("script part[{i}]='{p}'");
487 if p.starts_with("import * as") && p.contains("from") {
488 log::debug!("skipping import part[{i}]: \"{p}\"");
489 continue;
490 }
491 final_filtered.push_str(p);
492 if *had_semicolon {
494 final_filtered.push(';');
495 }
496 }
497 final_filtered.push('\n');
498 }
499
500 final_filtered.trim().to_string()
503}
504
505pub fn initialize_global_constructors(env: &JSObjectDataPtr) -> Result<(), JSError> {
507 let error_ctor = ensure_constructor_object(env, "Error", "__is_error_constructor")?;
509
510 if let Some(proto_val) = obj_get_key_value(&error_ctor, &"prototype".into())? {
512 if let Value::Object(proto_obj) = &*proto_val.borrow() {
513 obj_set_key_value(
514 proto_obj,
515 &"toString".into(),
516 Value::Function("Error.prototype.toString".to_string()),
517 )?;
518 }
519 }
520
521 let error_types = ["TypeError", "SyntaxError", "ReferenceError", "RangeError", "EvalError", "URIError"];
523 for t in error_types.iter() {
524 let ctor = ensure_constructor_object(env, t, &format!("__is_{}_constructor", t.to_lowercase()))?;
525 if let Some(proto_val) = obj_get_key_value(&ctor, &"prototype".into())? {
526 if let Value::Object(proto_obj) = &*proto_val.borrow() {
527 obj_set_key_value(
528 proto_obj,
529 &"toString".into(),
530 Value::Function("Error.prototype.toString".to_string()),
531 )?;
532 }
533 }
534 }
535
536 let mut env_borrow = env.borrow_mut();
537
538 let object_obj = new_js_object_data();
540
541 obj_set_key_value(&object_obj, &"keys".into(), Value::Function("Object.keys".to_string()))?;
543 obj_set_key_value(&object_obj, &"values".into(), Value::Function("Object.values".to_string()))?;
544 obj_set_key_value(&object_obj, &"assign".into(), Value::Function("Object.assign".to_string()))?;
545 obj_set_key_value(&object_obj, &"create".into(), Value::Function("Object.create".to_string()))?;
546 obj_set_key_value(
547 &object_obj,
548 &"getOwnPropertySymbols".into(),
549 Value::Function("Object.getOwnPropertySymbols".to_string()),
550 )?;
551 obj_set_key_value(
552 &object_obj,
553 &"getOwnPropertyNames".into(),
554 Value::Function("Object.getOwnPropertyNames".to_string()),
555 )?;
556 obj_set_key_value(
557 &object_obj,
558 &"getOwnPropertyDescriptors".into(),
559 Value::Function("Object.getOwnPropertyDescriptors".to_string()),
560 )?;
561
562 let object_prototype = new_js_object_data();
564 obj_set_key_value(
565 &object_prototype,
566 &"hasOwnProperty".into(),
567 Value::Function("Object.prototype.hasOwnProperty".to_string()),
568 )?;
569 obj_set_key_value(
570 &object_prototype,
571 &"isPrototypeOf".into(),
572 Value::Function("Object.prototype.isPrototypeOf".to_string()),
573 )?;
574 obj_set_key_value(
575 &object_prototype,
576 &"propertyIsEnumerable".into(),
577 Value::Function("Object.prototype.propertyIsEnumerable".to_string()),
578 )?;
579 obj_set_key_value(
580 &object_prototype,
581 &"toString".into(),
582 Value::Function("Object.prototype.toString".to_string()),
583 )?;
584 obj_set_key_value(
585 &object_prototype,
586 &"valueOf".into(),
587 Value::Function("Object.prototype.valueOf".to_string()),
588 )?;
589 obj_set_key_value(
591 &object_prototype,
592 &"toLocaleString".into(),
593 Value::Function("Object.prototype.toLocaleString".to_string()),
594 )?;
595
596 obj_set_key_value(&object_obj, &"prototype".into(), Value::Object(object_prototype.clone()))?;
598
599 env_borrow.insert(
601 PropertyKey::String("Object".to_string()),
602 Rc::new(RefCell::new(Value::Object(object_obj))),
603 );
604
605 env_borrow.insert(
613 PropertyKey::String("Array".to_string()),
614 Rc::new(RefCell::new(Value::Function("Array".to_string()))),
615 );
616
617 env_borrow.insert(
619 PropertyKey::String("Date".to_string()),
620 Rc::new(RefCell::new(Value::Function("Date".to_string()))),
621 );
622
623 env_borrow.insert(
625 PropertyKey::String("RegExp".to_string()),
626 Rc::new(RefCell::new(Value::Function("RegExp".to_string()))),
627 );
628
629 env_borrow.insert(
631 PropertyKey::String("Symbol".to_string()),
632 Rc::new(RefCell::new(Value::Function("Symbol".to_string()))),
633 );
634
635 env_borrow.insert(
637 PropertyKey::String("Map".to_string()),
638 Rc::new(RefCell::new(Value::Function("Map".to_string()))),
639 );
640
641 env_borrow.insert(
643 PropertyKey::String("Set".to_string()),
644 Rc::new(RefCell::new(Value::Function("Set".to_string()))),
645 );
646
647 env_borrow.insert(
649 PropertyKey::String("Proxy".to_string()),
650 Rc::new(RefCell::new(Value::Function("Proxy".to_string()))),
651 );
652
653 env_borrow.insert(
655 PropertyKey::String("WeakMap".to_string()),
656 Rc::new(RefCell::new(Value::Function("WeakMap".to_string()))),
657 );
658
659 env_borrow.insert(
661 PropertyKey::String("WeakSet".to_string()),
662 Rc::new(RefCell::new(Value::Function("WeakSet".to_string()))),
663 );
664
665 WELL_KNOWN_SYMBOLS.with(|wk| {
667 let mut map = wk.borrow_mut();
668 let iter_sym_data = Rc::new(SymbolData {
670 description: Some("Symbol.iterator".to_string()),
671 });
672 map.insert("iterator".to_string(), Rc::new(RefCell::new(Value::Symbol(iter_sym_data.clone()))));
673
674 let tt_sym_data = Rc::new(SymbolData {
676 description: Some("Symbol.toStringTag".to_string()),
677 });
678 map.insert("toStringTag".to_string(), Rc::new(RefCell::new(Value::Symbol(tt_sym_data.clone()))));
679 let tp_sym_data = Rc::new(SymbolData {
681 description: Some("Symbol.toPrimitive".to_string()),
682 });
683 map.insert("toPrimitive".to_string(), Rc::new(RefCell::new(Value::Symbol(tp_sym_data.clone()))));
684 });
685
686 env_borrow.insert(
688 PropertyKey::String("__internal_resolve_promise".to_string()),
689 Rc::new(RefCell::new(Value::Function("__internal_resolve_promise".to_string()))),
690 );
691 env_borrow.insert(
692 PropertyKey::String("__internal_reject_promise".to_string()),
693 Rc::new(RefCell::new(Value::Function("__internal_reject_promise".to_string()))),
694 );
695 env_borrow.insert(
696 PropertyKey::String("__internal_allsettled_state_record_fulfilled".to_string()),
697 Rc::new(RefCell::new(Value::Function(
698 "__internal_allsettled_state_record_fulfilled".to_string(),
699 ))),
700 );
701 env_borrow.insert(
702 PropertyKey::String("__internal_allsettled_state_record_rejected".to_string()),
703 Rc::new(RefCell::new(Value::Function(
704 "__internal_allsettled_state_record_rejected".to_string(),
705 ))),
706 );
707
708 let arraybuffer_constructor = crate::js_typedarray::make_arraybuffer_constructor()?;
710 env_borrow.insert(
711 PropertyKey::String("ArrayBuffer".to_string()),
712 Rc::new(RefCell::new(Value::Object(arraybuffer_constructor))),
713 );
714
715 let shared_arraybuffer_constructor = crate::js_typedarray::make_sharedarraybuffer_constructor()?;
717 env_borrow.insert(
718 PropertyKey::String("SharedArrayBuffer".to_string()),
719 Rc::new(RefCell::new(Value::Object(shared_arraybuffer_constructor))),
720 );
721
722 let dataview_constructor = crate::js_typedarray::make_dataview_constructor()?;
723 env_borrow.insert(
724 PropertyKey::String("DataView".to_string()),
725 Rc::new(RefCell::new(Value::Object(dataview_constructor))),
726 );
727
728 let typedarray_constructors = crate::js_typedarray::make_typedarray_constructors()?;
729 for (name, constructor) in typedarray_constructors {
730 env_borrow.insert(PropertyKey::String(name), Rc::new(RefCell::new(Value::Object(constructor))));
731 }
732
733 let atomics_obj = crate::js_typedarray::make_atomics_object()?;
735 env_borrow.insert(
736 PropertyKey::String("Atomics".to_string()),
737 Rc::new(RefCell::new(Value::Object(atomics_obj))),
738 );
739
740 env_borrow.insert(
742 PropertyKey::String("setTimeout".to_string()),
743 Rc::new(RefCell::new(Value::Function("setTimeout".to_string()))),
744 );
745
746 env_borrow.insert(
748 PropertyKey::String("clearTimeout".to_string()),
749 Rc::new(RefCell::new(Value::Function("clearTimeout".to_string()))),
750 );
751
752 Ok(())
753}