use crate::{
builtins::{BuiltIn, JsArgs},
object::FunctionBuilder,
property::Attribute,
Context, JsValue,
};
use boa_profiler::Profiler;
use rustc_hash::FxHashSet;
#[derive(Debug, Clone, Copy)]
pub(crate) struct Eval;
impl BuiltIn for Eval {
const NAME: &'static str = "eval";
const ATTRIBUTE: Attribute = Attribute::CONFIGURABLE
.union(Attribute::NON_ENUMERABLE)
.union(Attribute::WRITABLE);
fn init(context: &mut Context) -> Option<JsValue> {
let _timer = Profiler::global().start_event(Self::NAME, "init");
let object = FunctionBuilder::native(context, Self::eval)
.name("eval")
.length(1)
.constructor(false)
.build();
Some(object.into())
}
}
impl Eval {
fn eval(_: &JsValue, args: &[JsValue], context: &mut Context) -> Result<JsValue, JsValue> {
Self::perform_eval(args.get_or_undefined(0), false, false, context)
}
pub(crate) fn perform_eval(
x: &JsValue,
direct: bool,
strict: bool,
context: &mut Context,
) -> Result<JsValue, JsValue> {
if !direct {
debug_assert!(!strict);
}
let x = if let Some(x) = x.as_string() {
x.clone()
} else {
return Ok(x.clone());
};
let body = match context.parse_eval(x.as_bytes(), direct, strict) {
Ok(body) => body,
Err(e) => return context.throw_syntax_error(e.to_string()),
};
if direct {
context.realm.environments.poison_current();
context.realm.compile_env = context.realm.environments.current_compile_environment();
let environments_len = context.realm.environments.len();
let mut vars = FxHashSet::default();
body.var_declared_names_new(&mut vars);
if let Some(name) = context
.realm
.environments
.has_lex_binding_until_function_environment(&vars)
{
let name = context.interner().resolve_expect(name);
let msg = format!("variable declaration {name} in eval function already exists as lexically declaration");
return context.throw_syntax_error(msg);
}
let code_block = context.compile_with_new_declarative(&body, strict)?;
context
.realm
.environments
.extend_outer_function_environment();
let result = context.execute(code_block);
context.realm.environments.truncate(environments_len);
result
} else {
context.realm.environments.poison_all();
let environments = context.realm.environments.pop_to_global();
let environments_len = context.realm.environments.len();
context.realm.compile_env = context.realm.environments.current_compile_environment();
let code_block = context.compile_with_new_declarative(&body, false)?;
let result = context.execute(code_block);
context.realm.environments.truncate(environments_len);
context.realm.environments.extend(environments);
result
}
}
}