#![allow(clippy::collapsible_if, clippy::collapsible_match)]
use crate::error::JSError;
use crate::js_promise::{PollResult, PromiseState, run_event_loop};
use crate::raise_eval_error;
use crate::unicode::utf8_to_utf16;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
mod value;
pub use value::*;
mod property_key;
pub use property_key::*;
mod statement;
pub use statement::*;
mod token;
pub use token::*;
mod number;
mod eval;
pub use eval::*;
mod parser;
pub use parser::*;
thread_local! {
static WELL_KNOWN_SYMBOLS: RefCell<HashMap<String, Rc<RefCell<Value>>>> = RefCell::new(HashMap::new());
}
pub(crate) fn drain_event_loop() -> Result<(), JSError> {
loop {
match run_event_loop()? {
PollResult::Executed => continue,
PollResult::Wait(duration) => std::thread::sleep(duration),
PollResult::Empty => break,
}
}
Ok(())
}
fn run_promise_resolution_loop(promise: &Rc<RefCell<crate::js_promise::JSPromise>>) -> Result<Value, JSError> {
loop {
drain_event_loop()?;
let promise_borrow = promise.borrow();
match &promise_borrow.state {
PromiseState::Fulfilled(val) => return Ok(val.clone()),
PromiseState::Rejected(_reason) => {
log::trace!("evaluate_script: top-level promise is Rejected, running EXTRA_ITERATIONS");
const EXTRA_ITERATIONS: usize = 5;
for _ in 0..EXTRA_ITERATIONS {
drain_event_loop()?;
if let PromiseState::Pending | PromiseState::Fulfilled(_) = &promise.borrow().state {
break;
}
if !promise.borrow().on_rejected.is_empty() {
drain_event_loop()?;
break;
}
}
let promise_borrow = promise.borrow();
if let PromiseState::Rejected(_reason) = &promise_borrow.state {
const EXTRA_UNHANDLED_ITER: usize = 5;
for _ in 0..EXTRA_UNHANDLED_ITER {
if crate::js_promise::pending_unhandled_count() == 0 {
break;
}
drain_event_loop()?;
}
const FINAL_DRAIN_ITER: usize = 5;
if crate::js_promise::peek_unhandled_rejection().is_some() {
log::trace!("evaluate_script: peek_unhandled_rejection -> Some; running final drain");
for _ in 0..FINAL_DRAIN_ITER {
drain_event_loop()?;
if crate::js_promise::pending_unhandled_count() == 0 && crate::js_promise::task_queue_len() == 0 {
break;
}
}
} else {
log::trace!("evaluate_script: peek_unhandled_rejection -> None");
}
drain_event_loop()?;
if let Some(unhandled_reason) = crate::js_promise::take_unhandled_rejection() {
log::trace!("evaluate_script: consuming recorded unhandled rejection (surfacing as error)");
return Err(crate::raise_throw_error!(unhandled_reason));
}
log::debug!("Not surfacing top-level rejection: no recorded unhandled rejection");
return Ok(Value::Undefined);
}
}
PromiseState::Pending => {
}
}
}
}
pub fn evaluate_script<T, P>(script: T, script_path: Option<P>) -> Result<Value, JSError>
where
T: AsRef<str>,
P: AsRef<std::path::Path>,
{
let script = script.as_ref();
log::debug!("evaluate_script async called with script len {}", script.len());
log::trace!("evaluate_script: entry");
let mut tokens = match tokenize(script) {
Ok(t) => t,
Err(e) => {
log::debug!("tokenize error: {e:?}");
return Err(e);
}
};
let statements = match parse_statements(&mut tokens) {
Ok(s) => s,
Err(e) => {
log::debug!("parse_statements error: {e:?}");
return Err(e);
}
};
log::debug!("parsed {} statements", statements.len());
for (i, stmt) in statements.iter().enumerate() {
log::trace!("stmt[{i}] = {stmt:?}");
}
let env: JSObjectDataPtr = new_js_object_data();
env.borrow_mut().is_function_scope = true;
let path = script_path.map_or("<script>".to_string(), |p| p.as_ref().to_string_lossy().to_string());
let _ = obj_set_key_value(&env, &"__script_name".into(), Value::String(utf8_to_utf16(&path)));
initialize_global_constructors(&env)?;
obj_set_key_value(&env, &"globalThis".into(), Value::Object(env.clone()))?;
obj_set_key_value(&env, &"undefined".into(), Value::Undefined)?;
let v = evaluate_statements(&env, &statements)?;
if let Value::Object(obj) = &v
&& let Some(promise_val_rc) = obj_get_key_value(obj, &"__promise".into())?
&& let Value::Promise(promise) = &*promise_val_rc.borrow()
{
return run_promise_resolution_loop(promise);
}
drain_event_loop()?;
const EXTRA_UNHANDLED_ITER: usize = 3;
for _ in 0..EXTRA_UNHANDLED_ITER {
if crate::js_promise::pending_unhandled_count() == 0 {
break;
}
drain_event_loop()?;
}
const FINAL_DRAIN_ITER: usize = 5;
if crate::js_promise::peek_unhandled_rejection().is_some() {
for _ in 0..FINAL_DRAIN_ITER {
drain_event_loop()?;
if crate::js_promise::pending_unhandled_count() == 0 && crate::js_promise::task_queue_len() == 0 {
break;
}
}
if crate::js_promise::take_unhandled_rejection().is_some() {
log::debug!("Recorded unhandled rejection present after final chance; deferring surfacing");
return Ok(Value::Undefined);
}
}
Ok(v)
}
pub fn read_script_file<P: AsRef<std::path::Path>>(path: P) -> Result<String, JSError> {
let path = path.as_ref();
let bytes = std::fs::read(path).map_err(|e| raise_eval_error!(format!("Failed to read script file '{}': {e}", path.display())))?;
if bytes.len() >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF {
let s = std::str::from_utf8(&bytes[3..]).map_err(|e| raise_eval_error!(format!("Script file contains invalid UTF-8: {e}")))?;
return Ok(s.to_string());
}
if bytes.len() >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE {
if (bytes.len() - 2) % 2 != 0 {
return Err(raise_eval_error!("Invalid UTF-16LE script file length"));
}
let mut u16s = Vec::with_capacity((bytes.len() - 2) / 2);
for chunk in bytes[2..].chunks(2) {
let lo = chunk[0] as u16;
let hi = chunk[1] as u16;
u16s.push((hi << 8) | lo);
}
return String::from_utf16(&u16s).map_err(|e| raise_eval_error!(format!("Invalid UTF-16LE script file contents: {e}")));
}
if bytes.len() >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF {
if (bytes.len() - 2) % 2 != 0 {
return Err(raise_eval_error!("Invalid UTF-16BE script file length"));
}
let mut u16s = Vec::with_capacity((bytes.len() - 2) / 2);
for chunk in bytes[2..].chunks(2) {
let hi = chunk[0] as u16;
let lo = chunk[1] as u16;
u16s.push((hi << 8) | lo);
}
return String::from_utf16(&u16s).map_err(|e| raise_eval_error!(format!("Invalid UTF-16BE script file contents: {e}")));
}
std::str::from_utf8(&bytes)
.map(|s| s.to_string())
.map_err(|e| raise_eval_error!(format!("Script file contains invalid UTF-8: {e}")))
}
pub fn ensure_constructor_object(env: &JSObjectDataPtr, name: &str, marker_key: &str) -> Result<JSObjectDataPtr, JSError> {
if let Some(val_rc) = obj_get_key_value(env, &name.into())? {
if let Value::Object(obj) = &*val_rc.borrow() {
return Ok(obj.clone());
}
}
let ctor = new_js_object_data();
obj_set_key_value(&ctor, &marker_key.into(), Value::Boolean(true))?;
obj_set_key_value(&ctor, &"__is_constructor".into(), Value::Boolean(true))?;
let proto = new_js_object_data();
if let Some(object_ctor_val) = obj_get_key_value(env, &"Object".into())?
&& let Value::Object(object_ctor) = &*object_ctor_val.borrow()
&& let Some(obj_proto_val) = obj_get_key_value(object_ctor, &"prototype".into())?
&& let Value::Object(obj_proto_obj) = &*obj_proto_val.borrow()
{
proto.borrow_mut().prototype = Some(obj_proto_obj.clone());
}
obj_set_key_value(&ctor, &"prototype".into(), Value::Object(proto.clone()))?;
obj_set_key_value(&proto, &"constructor".into(), Value::Object(ctor.clone()))?;
obj_set_key_value(env, &name.into(), Value::Object(ctor.clone()))?;
Ok(ctor)
}
pub fn get_constructor_prototype(env: &JSObjectDataPtr, name: &str) -> Result<Option<JSObjectDataPtr>, JSError> {
if let Some(val_rc) = obj_get_key_value(env, &name.into())? {
if let Value::Object(ctor_obj) = &*val_rc.borrow() {
if let Some(proto_val_rc) = obj_get_key_value(ctor_obj, &"prototype".into())? {
if let Value::Object(proto_obj) = &*proto_val_rc.borrow() {
return Ok(Some(proto_obj.clone()));
}
}
}
}
match evaluate_expr(env, &Expr::Var(name.to_string(), None, None)) {
Ok(Value::Object(ctor_obj)) => {
if let Some(proto_val_rc) = obj_get_key_value(&ctor_obj, &"prototype".into())? {
if let Value::Object(proto_obj) = &*proto_val_rc.borrow() {
return Ok(Some(proto_obj.clone()));
}
}
Ok(None)
}
_ => Ok(None),
}
}
pub fn set_internal_prototype_from_constructor(obj: &JSObjectDataPtr, env: &JSObjectDataPtr, ctor_name: &str) -> Result<(), JSError> {
if let Some(proto_obj) = get_constructor_prototype(env, ctor_name)? {
obj.borrow_mut().prototype = Some(proto_obj.clone());
}
Ok(())
}
pub fn initialize_collection_from_iterable<F>(
args: &[Expr],
env: &JSObjectDataPtr,
constructor_name: &str,
mut process_item: F,
) -> Result<(), JSError>
where
F: FnMut(Value) -> Result<(), JSError>,
{
if args.is_empty() {
return Ok(());
}
if args.len() > 1 {
let msg = format!("{constructor_name} constructor takes at most one argument",);
return Err(raise_eval_error!(msg));
}
let iterable = evaluate_expr(env, &args[0])?;
match iterable {
Value::Object(obj) => {
let mut i = 0;
loop {
let key = format!("{i}");
if let Some(item_val) = obj_get_key_value(&obj, &key.into())? {
let item = item_val.borrow().clone();
process_item(item)?;
} else {
break;
}
i += 1;
}
Ok(())
}
_ => Err(raise_eval_error!(format!("{constructor_name} constructor requires an iterable"))),
}
}
#[derive(Debug, Clone)]
pub enum Expr {
Number(f64),
BigInt(String), StringLit(Vec<u16>),
Boolean(bool),
Var(String, Option<usize>, Option<usize>), Binary(Box<Expr>, BinaryOp, Box<Expr>),
UnaryNeg(Box<Expr>),
UnaryPlus(Box<Expr>),
BitNot(Box<Expr>),
LogicalNot(Box<Expr>),
TypeOf(Box<Expr>),
Delete(Box<Expr>),
Void(Box<Expr>),
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>),
Decrement(Box<Expr>),
PostIncrement(Box<Expr>),
PostDecrement(Box<Expr>),
Index(Box<Expr>, Box<Expr>),
Property(Box<Expr>, String),
Call(Box<Expr>, Vec<Expr>),
Function(Option<String>, Vec<DestructuringElement>, Vec<Statement>), AsyncFunction(Option<String>, Vec<DestructuringElement>, Vec<Statement>), GeneratorFunction(Option<String>, Vec<DestructuringElement>, Vec<Statement>), ArrowFunction(Vec<DestructuringElement>, Vec<Statement>), AsyncArrowFunction(Vec<DestructuringElement>, Vec<Statement>), Object(Vec<(Expr, Expr, bool)>), Array(Vec<Option<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),
LogicalAnd(Box<Expr>, Box<Expr>),
LogicalOr(Box<Expr>, Box<Expr>),
Comma(Box<Expr>, Box<Expr>), TaggedTemplate(Box<Expr>, Vec<Vec<u16>>, Vec<Expr>), Value(Value), Class(Rc<crate::js_class::ClassDefinition>), }
#[derive(Debug, Clone)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Mod,
Equal,
StrictEqual,
NotEqual,
StrictNotEqual,
LessThan,
GreaterThan,
LessEqual,
GreaterEqual,
InstanceOf,
In,
NullishCoalescing,
Pow,
BitXor,
BitAnd,
BitOr,
LeftShift,
RightShift,
UnsignedRightShift,
}
#[derive(Debug, Clone)]
pub enum DestructuringElement {
Variable(String, Option<Box<Expr>>), NestedArray(Vec<DestructuringElement>), NestedObject(Vec<ObjectDestructuringElement>), Rest(String), Empty, }
#[derive(Debug, Clone)]
pub enum ObjectDestructuringElement {
Property { key: String, value: DestructuringElement }, Rest(String), }
pub fn initialize_global_constructors(env: &JSObjectDataPtr) -> Result<(), JSError> {
let _function_ctor = ensure_constructor_object(env, "Function", "__is_function_constructor")?;
if let Some(func_proto) = get_constructor_prototype(env, "Function")? {
obj_set_key_value(&func_proto, &"call".into(), Value::Function("Function.prototype.call".to_string()))?;
obj_set_key_value(
&func_proto,
&"apply".into(),
Value::Function("Function.prototype.apply".to_string()),
)?;
}
let error_ctor = ensure_constructor_object(env, "Error", "__is_error_constructor")?;
if let Some(proto_val) = obj_get_key_value(&error_ctor, &"prototype".into())? {
if let Value::Object(proto_obj) = &*proto_val.borrow() {
obj_set_key_value(
proto_obj,
&"toString".into(),
Value::Function("Error.prototype.toString".to_string()),
)?;
}
}
let error_types = ["TypeError", "SyntaxError", "ReferenceError", "RangeError", "EvalError", "URIError"];
for t in error_types.iter() {
let ctor = ensure_constructor_object(env, t, &format!("__is_{}_constructor", t.to_lowercase()))?;
obj_set_key_value(&ctor, &"__is_error_constructor".into(), Value::Boolean(true))?;
if let Some(proto_val) = obj_get_key_value(&ctor, &"prototype".into())? {
if let Value::Object(proto_obj) = &*proto_val.borrow() {
obj_set_key_value(
proto_obj,
&"toString".into(),
Value::Function("Error.prototype.toString".to_string()),
)?;
}
}
}
let mut env_borrow = env.borrow_mut();
let object_obj = new_js_object_data();
obj_set_key_value(&object_obj, &"__is_constructor".into(), Value::Boolean(true))?;
obj_set_key_value(&object_obj, &"keys".into(), Value::Function("Object.keys".to_string()))?;
obj_set_key_value(&object_obj, &"values".into(), Value::Function("Object.values".to_string()))?;
obj_set_key_value(&object_obj, &"assign".into(), Value::Function("Object.assign".to_string()))?;
obj_set_key_value(&object_obj, &"create".into(), Value::Function("Object.create".to_string()))?;
obj_set_key_value(
&object_obj,
&"getOwnPropertySymbols".into(),
Value::Function("Object.getOwnPropertySymbols".to_string()),
)?;
obj_set_key_value(
&object_obj,
&"getOwnPropertyNames".into(),
Value::Function("Object.getOwnPropertyNames".to_string()),
)?;
obj_set_key_value(
&object_obj,
&"getOwnPropertyDescriptors".into(),
Value::Function("Object.getOwnPropertyDescriptors".to_string()),
)?;
obj_set_key_value(
&object_obj,
&"getPrototypeOf".into(),
Value::Function("Object.getPrototypeOf".to_string()),
)?;
obj_set_key_value(
&object_obj,
&"defineProperty".into(),
Value::Function("Object.defineProperty".to_string()),
)?;
obj_set_key_value(
&object_obj,
&"defineProperties".into(),
Value::Function("Object.defineProperties".to_string()),
)?;
let object_prototype = new_js_object_data();
obj_set_key_value(
&object_prototype,
&"hasOwnProperty".into(),
Value::Function("Object.prototype.hasOwnProperty".to_string()),
)?;
obj_set_key_value(
&object_prototype,
&"isPrototypeOf".into(),
Value::Function("Object.prototype.isPrototypeOf".to_string()),
)?;
obj_set_key_value(
&object_prototype,
&"propertyIsEnumerable".into(),
Value::Function("Object.prototype.propertyIsEnumerable".to_string()),
)?;
obj_set_key_value(
&object_prototype,
&"toString".into(),
Value::Function("Object.prototype.toString".to_string()),
)?;
obj_set_key_value(
&object_prototype,
&"valueOf".into(),
Value::Function("Object.prototype.valueOf".to_string()),
)?;
obj_set_key_value(
&object_prototype,
&"toLocaleString".into(),
Value::Function("Object.prototype.toLocaleString".to_string()),
)?;
obj_set_key_value(&object_obj, &"prototype".into(), Value::Object(object_prototype.clone()))?;
env_borrow.insert(
PropertyKey::String("Object".to_string()),
Rc::new(RefCell::new(Value::Object(object_obj))),
);
env_borrow.insert(
PropertyKey::String("RegExp".to_string()),
Rc::new(RefCell::new(Value::Function("RegExp".to_string()))),
);
env_borrow.insert(
PropertyKey::String("Symbol".to_string()),
Rc::new(RefCell::new(Value::Function("Symbol".to_string()))),
);
env_borrow.insert(
PropertyKey::String("Map".to_string()),
Rc::new(RefCell::new(Value::Function("Map".to_string()))),
);
env_borrow.insert(
PropertyKey::String("Set".to_string()),
Rc::new(RefCell::new(Value::Function("Set".to_string()))),
);
env_borrow.insert(
PropertyKey::String("Proxy".to_string()),
Rc::new(RefCell::new(Value::Function("Proxy".to_string()))),
);
env_borrow.insert(
PropertyKey::String("WeakMap".to_string()),
Rc::new(RefCell::new(Value::Function("WeakMap".to_string()))),
);
env_borrow.insert(
PropertyKey::String("WeakSet".to_string()),
Rc::new(RefCell::new(Value::Function("WeakSet".to_string()))),
);
WELL_KNOWN_SYMBOLS.with(|wk| {
let mut map = wk.borrow_mut();
let iter_sym_data = Rc::new(SymbolData {
description: Some("Symbol.iterator".to_string()),
});
map.insert("iterator".to_string(), Rc::new(RefCell::new(Value::Symbol(iter_sym_data.clone()))));
let tt_sym_data = Rc::new(SymbolData {
description: Some("Symbol.toStringTag".to_string()),
});
map.insert("toStringTag".to_string(), Rc::new(RefCell::new(Value::Symbol(tt_sym_data.clone()))));
let tp_sym_data = Rc::new(SymbolData {
description: Some("Symbol.toPrimitive".to_string()),
});
map.insert("toPrimitive".to_string(), Rc::new(RefCell::new(Value::Symbol(tp_sym_data.clone()))));
});
env_borrow.insert(
PropertyKey::String("__internal_resolve_promise".to_string()),
Rc::new(RefCell::new(Value::Function("__internal_resolve_promise".to_string()))),
);
env_borrow.insert(
PropertyKey::String("__internal_reject_promise".to_string()),
Rc::new(RefCell::new(Value::Function("__internal_reject_promise".to_string()))),
);
env_borrow.insert(
PropertyKey::String("__internal_allsettled_state_record_fulfilled".to_string()),
Rc::new(RefCell::new(Value::Function(
"__internal_allsettled_state_record_fulfilled".to_string(),
))),
);
env_borrow.insert(
PropertyKey::String("__internal_allsettled_state_record_rejected".to_string()),
Rc::new(RefCell::new(Value::Function(
"__internal_allsettled_state_record_rejected".to_string(),
))),
);
let arraybuffer_constructor = crate::js_typedarray::make_arraybuffer_constructor()?;
env_borrow.insert(
PropertyKey::String("ArrayBuffer".to_string()),
Rc::new(RefCell::new(Value::Object(arraybuffer_constructor))),
);
let shared_arraybuffer_constructor = crate::js_typedarray::make_sharedarraybuffer_constructor()?;
env_borrow.insert(
PropertyKey::String("SharedArrayBuffer".to_string()),
Rc::new(RefCell::new(Value::Object(shared_arraybuffer_constructor))),
);
let dataview_constructor = crate::js_typedarray::make_dataview_constructor()?;
env_borrow.insert(
PropertyKey::String("DataView".to_string()),
Rc::new(RefCell::new(Value::Object(dataview_constructor))),
);
let typedarray_constructors = crate::js_typedarray::make_typedarray_constructors()?;
for (name, constructor) in typedarray_constructors {
env_borrow.insert(PropertyKey::String(name), Rc::new(RefCell::new(Value::Object(constructor))));
}
let atomics_obj = crate::js_typedarray::make_atomics_object()?;
env_borrow.insert(
PropertyKey::String("Atomics".to_string()),
Rc::new(RefCell::new(Value::Object(atomics_obj))),
);
env_borrow.insert(
PropertyKey::String("setTimeout".to_string()),
Rc::new(RefCell::new(Value::Function("setTimeout".to_string()))),
);
env_borrow.insert(
PropertyKey::String("clearTimeout".to_string()),
Rc::new(RefCell::new(Value::Function("clearTimeout".to_string()))),
);
env_borrow.insert(
PropertyKey::String("setInterval".to_string()),
Rc::new(RefCell::new(Value::Function("setInterval".to_string()))),
);
env_borrow.insert(
PropertyKey::String("clearInterval".to_string()),
Rc::new(RefCell::new(Value::Function("clearInterval".to_string()))),
);
env_borrow.insert(
PropertyKey::String("NaN".to_string()),
Rc::new(RefCell::new(Value::Number(f64::NAN))),
);
env_borrow.insert(
PropertyKey::String("Infinity".to_string()),
Rc::new(RefCell::new(Value::Number(f64::INFINITY))),
);
let global_funcs = [
"eval",
"isNaN",
"isFinite",
"parseInt",
"parseFloat",
"encodeURI",
"encodeURIComponent",
"decodeURI",
"decodeURIComponent",
];
for func_name in global_funcs.iter() {
env_borrow.insert(
PropertyKey::String(func_name.to_string()),
Rc::new(RefCell::new(Value::Function(func_name.to_string()))),
);
}
let math_obj = crate::js_math::make_math_object()?;
env_borrow.insert(
PropertyKey::String("Math".to_string()),
Rc::new(RefCell::new(Value::Object(math_obj))),
);
drop(env_borrow);
if let Some(func_ctor_val) = obj_get_key_value(env, &"Function".into())? {
if let Value::Object(func_ctor) = &*func_ctor_val.borrow() {
if let Some(func_proto_val) = obj_get_key_value(func_ctor, &"prototype".into())? {
if let Value::Object(func_proto) = &*func_proto_val.borrow() {
let set_proto = |target: &JSObjectDataPtr| {
target.borrow_mut().prototype = Some(func_proto.clone());
let _ = obj_set_key_value(target, &"__proto__".into(), Value::Object(func_proto.clone()));
};
set_proto(func_ctor);
if let Some(obj_ctor_val) = obj_get_key_value(env, &"Object".into())? {
if let Value::Object(obj_ctor) = &*obj_ctor_val.borrow() {
set_proto(obj_ctor);
if let Some(obj_proto_val) = obj_get_key_value(obj_ctor, &"prototype".into())? {
if let Value::Object(obj_proto) = &*obj_proto_val.borrow() {
func_proto.borrow_mut().prototype = Some(obj_proto.clone());
let _ = obj_set_key_value(func_proto, &"__proto__".into(), Value::Object(obj_proto.clone()));
if let Some(err_ctor_val) = obj_get_key_value(env, &"Error".into())? {
if let Value::Object(err_ctor) = &*err_ctor_val.borrow() {
set_proto(err_ctor);
if let Some(err_proto_val) = obj_get_key_value(err_ctor, &"prototype".into())? {
if let Value::Object(err_proto) = &*err_proto_val.borrow() {
err_proto.borrow_mut().prototype = Some(obj_proto.clone());
let _ =
obj_set_key_value(err_proto, &"__proto__".into(), Value::Object(obj_proto.clone()));
}
}
}
}
let error_types = ["TypeError", "SyntaxError", "ReferenceError", "RangeError", "EvalError", "URIError"];
for t in error_types.iter() {
if let Some(ctor_val) = obj_get_key_value(env, &t.to_string().into())? {
if let Value::Object(ctor) = &*ctor_val.borrow() {
set_proto(ctor);
}
}
}
}
}
}
}
}
}
}
}
Ok(())
}