boa_engine/builtins/eval/
mod.rs1use crate::{
13 Context, JsArgs, JsExpect, JsResult, JsString, JsValue, SpannedSourceText,
14 builtins::{BuiltInObject, function::OrdinaryFunction},
15 bytecompiler::{ByteCompiler, prepare_eval_declaration_instantiation},
16 context::intrinsics::Intrinsics,
17 environments::SavedEnvironments,
18 error::JsNativeError,
19 js_string,
20 object::JsObject,
21 realm::Realm,
22 spanned_source_text::SourceText,
23 string::StaticJsStrings,
24 vm::{CallFrame, CallFrameFlags, source_info::SourcePath},
25};
26use boa_ast::{
27 operations::{ContainsSymbol, contains, contains_arguments},
28 scope::Scope,
29};
30use boa_gc::Gc;
31use boa_parser::{Parser, Source};
32
33use super::{BuiltInBuilder, IntrinsicObject};
34
35#[derive(Debug, Clone, Copy)]
36pub(crate) struct Eval;
37
38impl IntrinsicObject for Eval {
39 fn init(realm: &Realm) {
40 BuiltInBuilder::callable_with_intrinsic::<Self>(realm, Self::eval)
41 .name(Self::NAME)
42 .length(1)
43 .build();
44 }
45
46 fn get(intrinsics: &Intrinsics) -> JsObject {
47 intrinsics.objects().eval().into()
48 }
49}
50
51impl BuiltInObject for Eval {
52 const NAME: JsString = StaticJsStrings::EVAL;
53}
54
55impl Eval {
56 fn eval(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
63 Self::perform_eval(args.get_or_undefined(0), false, None, false, context)
65 }
66
67 pub(crate) fn perform_eval(
74 x: &JsValue,
75 direct: bool,
76 lexical_scope: Option<Scope>,
77 mut strict: bool,
78 context: &mut Context,
79 ) -> JsResult<JsValue> {
80 bitflags::bitflags! {
81 #[derive(Default)]
83 struct Flags: u8 {
84 const IN_FUNCTION = 0b0001;
85 const IN_METHOD = 0b0010;
86 const IN_DERIVED_CONSTRUCTOR = 0b0100;
87 const IN_CLASS_FIELD_INITIALIZER = 0b1000;
88 }
89 }
90
91 enum EnvStackAction {
94 Truncate(usize),
95 Restore(SavedEnvironments),
96 }
97
98 debug_assert!(direct || !strict);
100
101 let Some(x) = x.as_string() else {
104 return Ok(x.clone());
105 };
106
107 let eval_realm = context.realm().clone();
113
114 context
116 .host_hooks()
117 .ensure_can_compile_strings(eval_realm, &[], &x, direct, context)?;
118
119 let x = x.to_vec();
125 let source = Source::from_utf16(&x);
126
127 let mut parser = Parser::new(source);
128 parser.set_identifier(context.next_parser_identifier());
129 if strict {
130 parser.set_strict();
131 }
132 let (mut body, source) = parser.parse_eval(direct, context.interner_mut())?;
133
134 let flags = match {
140 let frame = context.vm.frame();
141 frame
142 .environments
143 .get_this_environment(frame.realm.environment())
144 }
145 .as_function()
146 {
147 Some(function_env) if direct => {
150 let function_object = function_env
152 .slots()
153 .function_object()
154 .downcast_ref::<OrdinaryFunction>()
155 .js_expect("must be function object")?;
156
157 let mut flags = Flags::IN_FUNCTION;
159
160 if function_env.has_super_binding() {
162 flags |= Flags::IN_METHOD;
163 }
164
165 if function_object.is_derived_constructor() {
167 flags |= Flags::IN_DERIVED_CONSTRUCTOR;
168 }
169
170 if function_object.in_class_field_initializer() {
173 flags |= Flags::IN_CLASS_FIELD_INITIALIZER;
174 }
175
176 flags
177 }
178 _ => Flags::default(),
179 };
180
181 if !flags.contains(Flags::IN_FUNCTION) && contains(&body, ContainsSymbol::NewTarget) {
182 return Err(JsNativeError::syntax()
183 .with_message("invalid `new.target` expression inside eval")
184 .into());
185 }
186 if !flags.contains(Flags::IN_METHOD) && contains(&body, ContainsSymbol::SuperProperty) {
187 return Err(JsNativeError::syntax()
188 .with_message("invalid `super` reference inside eval")
189 .into());
190 }
191 if !flags.contains(Flags::IN_DERIVED_CONSTRUCTOR)
192 && contains(&body, ContainsSymbol::SuperCall)
193 {
194 return Err(JsNativeError::syntax()
195 .with_message("invalid `super` call inside eval")
196 .into());
197 }
198 if flags.contains(Flags::IN_CLASS_FIELD_INITIALIZER) && contains_arguments(&body) {
199 return Err(JsNativeError::syntax()
200 .with_message("invalid `arguments` reference inside eval")
201 .into());
202 }
203
204 strict |= body.strict();
205
206 let action = if direct {
210 if !strict {
214 {
215 let frame = context.vm.frame_mut();
216 let global = frame.realm.environment();
217 frame.environments.poison_until_last_function(global);
218 }
219 }
220
221 let environments_len = context.vm.frame().environments.len();
223
224 EnvStackAction::Truncate(environments_len)
226 } else {
227 let saved = context.vm.frame_mut().environments.pop_to_global();
231
232 EnvStackAction::Restore(saved)
234 };
235
236 let context = &mut context.guard(move |ctx| match action {
237 EnvStackAction::Truncate(len) => ctx.vm.frame_mut().environments.truncate(len),
238 EnvStackAction::Restore(saved) => {
239 ctx.vm.frame_mut().environments.truncate(0);
240 ctx.vm.frame_mut().environments.restore_from_saved(saved);
241 }
242 });
243
244 let (var_environment, mut variable_scope) =
245 if let Some(e) = context.vm.frame().environments.outer_function_environment() {
246 (e.0, e.1)
247 } else {
248 (
249 context.realm().environment().clone(),
250 context.realm().scope().clone(),
251 )
252 };
253
254 let lexical_scope = lexical_scope.unwrap_or(context.realm().scope().clone());
255 let lexical_scope = Scope::new(lexical_scope, strict);
256
257 let mut annex_b_function_names = Vec::new();
258
259 prepare_eval_declaration_instantiation(
260 &mut annex_b_function_names,
261 &body,
262 strict,
263 if strict {
264 &lexical_scope
265 } else {
266 &variable_scope
267 },
268 &lexical_scope,
269 context,
270 )?;
271
272 let in_with = context.vm.frame().environments.has_object_environment();
273
274 let source_text = SourceText::new(source);
275 let spanned_source_text = SpannedSourceText::new_source_only(source_text);
276
277 let mut compiler = ByteCompiler::new(
278 js_string!("<eval>"),
279 body.strict(),
280 false,
281 variable_scope.clone(),
282 lexical_scope.clone(),
283 false,
284 false,
285 context.interner_mut(),
286 in_with,
287 spanned_source_text,
288 SourcePath::Eval,
290 );
291
292 compiler.current_open_environments_count += 1;
296
297 if strict {
298 variable_scope = lexical_scope.clone();
299 compiler.variable_scope = lexical_scope.clone();
300 }
301
302 #[cfg(feature = "annex-b")]
303 {
304 compiler
305 .annex_b_function_names
306 .clone_from(&annex_b_function_names);
307 }
308
309 let bindings = body
310 .analyze_scope_eval(
311 strict,
312 &variable_scope,
313 &lexical_scope,
314 &annex_b_function_names,
315 compiler.interner(),
316 )
317 .map_err(|e| JsNativeError::syntax().with_message(e))?;
318
319 compiler.eval_declaration_instantiation(&body, strict, &variable_scope, bindings);
320
321 compiler.compile_statement_list(body.statements(), true, false);
322
323 let code_block = Gc::new(compiler.finish());
324
325 if !strict {
328 var_environment.extend_from_compile();
329 }
330
331 let env_fp = context.vm.frame().environments.len() as u32;
332 let environments = context.vm.frame().environments.clone();
333 let realm = context.realm().clone();
334 context.vm.push_frame_with_stack(
335 CallFrame::new(code_block.clone(), None, environments, realm)
336 .with_env_fp(env_fp)
337 .with_flags(CallFrameFlags::EXIT_EARLY),
338 JsValue::undefined(),
339 JsValue::null(),
340 );
341
342 context.realm().resize_global_env();
343
344 {
348 let frame = context.vm.frame_mut();
349 let global = frame.realm.environment();
350 frame
351 .environments
352 .push_lexical(lexical_scope.num_bindings_non_local(), global);
353 }
354
355 context
356 .eval_declaration_instantiation(&code_block)
357 .inspect_err(|_| {
358 context.vm.pop_frame();
359 })?;
360
361 let record = context.run();
362 context.vm.pop_frame();
363
364 record.consume()
365 }
366}