1use crate::parser::ast::{
6 ClassData, ExpressionPatternType, FunctionBodyData, FunctionData, LiteralType, NumberLiteralType,
7 PatternType, StatementType, DeclarationType, VariableDeclarationData, VariableDeclarationKind,
8 BlockStatementData, ExpressionType, SwitchCaseData, CatchClauseData, ForIteratorData,
9 VariableDeclarationOrPattern,
10};
11use crate::runner::ds::error::JErrorType;
12use crate::runner::ds::value::JsValue;
13use crate::runner::plugin::types::EvalContext;
14
15use super::types::{Completion, CompletionType, EvalResult};
16use super::expression::{evaluate_expression, to_boolean, create_function_object, evaluate_class};
17
18pub fn execute_statement(
20 stmt: &StatementType,
21 ctx: &mut EvalContext,
22) -> EvalResult {
23 match stmt {
24 StatementType::EmptyStatement { .. } => {
25 Ok(Completion::normal())
26 }
27
28 StatementType::ExpressionStatement { expression, .. } => {
29 let value = evaluate_expression(expression, ctx)?;
30 Ok(Completion::normal_with_value(value))
31 }
32
33 StatementType::BlockStatement(block) => {
34 execute_block_statement(block, ctx)
35 }
36
37 StatementType::DeclarationStatement(decl) => {
38 execute_declaration(decl, ctx)
39 }
40
41 StatementType::IfStatement { test, consequent, alternate, .. } => {
42 execute_if_statement(test, consequent, alternate.as_ref().map(|a| a.as_ref()), ctx)
43 }
44
45 StatementType::WhileStatement { test, body, .. } => {
46 execute_while_statement(test, body, ctx)
47 }
48
49 StatementType::DoWhileStatement { test, body, .. } => {
50 execute_do_while_statement(body, test, ctx)
51 }
52
53 StatementType::ForStatement { init, test, update, body, .. } => {
54 execute_for_statement(init.as_ref(), test.as_ref().map(|t| t.as_ref()), update.as_ref().map(|u| u.as_ref()), body, ctx)
55 }
56
57 StatementType::ForInStatement(data) => {
58 execute_for_in_statement(data, ctx)
59 }
60
61 StatementType::ForOfStatement(data) => {
62 execute_for_of_statement(data, ctx)
63 }
64
65 StatementType::SwitchStatement { discriminant, cases, .. } => {
66 execute_switch_statement(discriminant, cases, ctx)
67 }
68
69 StatementType::BreakStatement { .. } => {
70 Ok(Completion::break_completion(None))
71 }
72
73 StatementType::ContinueStatement { .. } => {
74 Ok(Completion::continue_completion(None))
75 }
76
77 StatementType::ReturnStatement { argument, .. } => {
78 let value = if let Some(arg) = argument {
79 evaluate_expression(arg, ctx)?
80 } else {
81 JsValue::Undefined
82 };
83 Ok(Completion::return_value(value))
84 }
85
86 StatementType::ThrowStatement { argument, .. } => {
87 let value = evaluate_expression(argument, ctx)?;
88 Ok(Completion {
89 completion_type: CompletionType::Throw,
90 value: Some(value),
91 target: None,
92 })
93 }
94
95 StatementType::TryStatement { block, handler, finalizer, .. } => {
96 execute_try_statement(block, handler.as_ref(), finalizer.as_ref(), ctx)
97 }
98
99 StatementType::DebuggerStatement { .. } => {
100 Ok(Completion::normal())
101 }
102
103 StatementType::FunctionBody(body) => {
104 execute_function_body(body, ctx)
106 }
107 }
108}
109
110fn execute_block_statement(
112 block: &BlockStatementData,
113 ctx: &mut EvalContext,
114) -> EvalResult {
115 ctx.push_block_scope();
117
118 let mut completion = Completion::normal();
119
120 for stmt in block.body.iter() {
121 completion = execute_statement(stmt, ctx)?;
122
123 if completion.is_abrupt() {
124 ctx.pop_block_scope();
125 return Ok(completion);
126 }
127 }
128
129 ctx.pop_block_scope();
131
132 Ok(completion)
133}
134
135fn execute_declaration(
137 decl: &DeclarationType,
138 ctx: &mut EvalContext,
139) -> EvalResult {
140 match decl {
141 DeclarationType::VariableDeclaration(var_decl) => {
142 execute_variable_declaration(var_decl, ctx)
143 }
144
145 DeclarationType::FunctionOrGeneratorDeclaration(func_data) => {
146 execute_function_declaration(func_data, ctx)
147 }
148
149 DeclarationType::ClassDeclaration(class_data) => {
150 execute_class_declaration(class_data, ctx)
151 }
152 }
153}
154
155fn execute_variable_declaration(
157 var_decl: &VariableDeclarationData,
158 ctx: &mut EvalContext,
159) -> EvalResult {
160 let is_const = matches!(var_decl.kind, VariableDeclarationKind::Const);
161 let is_var = matches!(var_decl.kind, VariableDeclarationKind::Var);
162
163 for declarator in &var_decl.declarations {
164 let value = if let Some(init) = &declarator.init {
166 evaluate_expression(init, ctx)?
167 } else {
168 JsValue::Undefined
170 };
171
172 bind_pattern(&declarator.id, value, ctx, is_const, is_var)?;
174 }
175
176 Ok(Completion::normal())
177}
178
179fn bind_pattern(
181 pattern: &PatternType,
182 value: JsValue,
183 ctx: &mut EvalContext,
184 is_const: bool,
185 is_var: bool,
186) -> Result<(), JErrorType> {
187 match pattern {
188 PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(id)) => {
189 let name = &id.name;
191 if is_var {
192 if ctx.has_var_binding(name) {
193 ctx.set_var_binding(name, value)?;
194 } else {
195 ctx.create_var_binding(name)?;
196 ctx.initialize_var_binding(name, value)?;
197 }
198 } else {
199 ctx.create_binding(name, is_const)?;
200 ctx.initialize_binding(name, value)?;
201 }
202 Ok(())
203 }
204
205 PatternType::ObjectPattern { properties, .. } => {
206 for prop in properties {
208 let prop_data = &prop.0;
209
210 let key_name = get_property_key_name(&prop_data.key)?;
212
213 let prop_value = get_property_value(&value, &key_name)?;
215
216 bind_pattern(&prop_data.value, prop_value, ctx, is_const, is_var)?;
220 }
221 Ok(())
222 }
223
224 PatternType::ArrayPattern { elements, .. } => {
225 for (index, element) in elements.iter().enumerate() {
227 if let Some(elem_pattern) = element {
228 if let PatternType::RestElement { argument, .. } = elem_pattern.as_ref() {
230 let rest_value = get_rest_elements(&value, index)?;
232 bind_pattern(argument, rest_value, ctx, is_const, is_var)?;
233 } else {
234 let elem_value = get_array_element(&value, index)?;
236 bind_pattern(elem_pattern, elem_value, ctx, is_const, is_var)?;
237 }
238 }
239 }
241 Ok(())
242 }
243
244 PatternType::AssignmentPattern { left, right, .. } => {
245 let actual_value = if matches!(value, JsValue::Undefined) {
248 evaluate_expression(right, ctx)?
249 } else {
250 value
251 };
252 bind_pattern(left, actual_value, ctx, is_const, is_var)
253 }
254
255 PatternType::RestElement { argument, .. } => {
256 bind_pattern(argument, value, ctx, is_const, is_var)
259 }
260 }
261}
262
263fn get_property_key_name(key_expr: &ExpressionType) -> Result<String, JErrorType> {
265 match key_expr {
266 ExpressionType::ExpressionWhichCanBePattern(ExpressionPatternType::Identifier(id)) => {
267 Ok(id.name.clone())
268 }
269 ExpressionType::Literal(lit_data) => {
270 match &lit_data.value {
271 LiteralType::StringLiteral(s) => Ok(s.clone()),
272 LiteralType::NumberLiteral(num) => {
273 match num {
274 NumberLiteralType::IntegerLiteral(n) => Ok(n.to_string()),
275 NumberLiteralType::FloatLiteral(n) => Ok(n.to_string()),
276 }
277 }
278 _ => Err(JErrorType::TypeError("Invalid property key in destructuring".to_string())),
279 }
280 }
281 _ => Err(JErrorType::TypeError("Computed property keys not yet supported in destructuring".to_string())),
282 }
283}
284
285fn get_property_value(obj: &JsValue, key: &str) -> Result<JsValue, JErrorType> {
287 use crate::runner::ds::object_property::{PropertyDescriptor, PropertyKey};
288
289 match obj {
290 JsValue::Object(obj_ref) => {
291 let borrowed = obj_ref.borrow();
292 let base = borrowed.as_js_object().get_object_base();
293 let prop_key = PropertyKey::Str(key.to_string());
294
295 if let Some(PropertyDescriptor::Data(data)) = base.properties.get(&prop_key) {
296 Ok(data.value.clone())
297 } else {
298 Ok(JsValue::Undefined)
299 }
300 }
301 _ => Err(JErrorType::TypeError("Cannot destructure non-object".to_string())),
302 }
303}
304
305fn get_array_element(arr: &JsValue, index: usize) -> Result<JsValue, JErrorType> {
307 use crate::runner::ds::object_property::{PropertyDescriptor, PropertyKey};
308
309 match arr {
310 JsValue::Object(obj_ref) => {
311 let borrowed = obj_ref.borrow();
312 let base = borrowed.as_js_object().get_object_base();
313 let prop_key = PropertyKey::Str(index.to_string());
314
315 if let Some(PropertyDescriptor::Data(data)) = base.properties.get(&prop_key) {
316 Ok(data.value.clone())
317 } else {
318 Ok(JsValue::Undefined)
319 }
320 }
321 _ => Err(JErrorType::TypeError("Cannot destructure non-array".to_string())),
322 }
323}
324
325fn get_rest_elements(arr: &JsValue, start_index: usize) -> Result<JsValue, JErrorType> {
327 use crate::runner::ds::object::{JsObject, JsObjectType, ObjectType};
328 use crate::runner::ds::object_property::{PropertyDescriptor, PropertyDescriptorData, PropertyKey};
329 use crate::runner::ds::value::JsNumberType;
330 use crate::runner::plugin::types::SimpleObject;
331 use std::cell::RefCell;
332 use std::rc::Rc;
333
334 match arr {
335 JsValue::Object(obj_ref) => {
336 let borrowed = obj_ref.borrow();
337 let base = borrowed.as_js_object().get_object_base();
338
339 let length = if let Some(PropertyDescriptor::Data(data)) =
341 base.properties.get(&PropertyKey::Str("length".to_string()))
342 {
343 match &data.value {
344 JsValue::Number(JsNumberType::Integer(n)) => *n as usize,
345 JsValue::Number(JsNumberType::Float(n)) => *n as usize,
346 _ => 0,
347 }
348 } else {
349 0
350 };
351
352 let mut rest_obj = SimpleObject::new();
354 let mut rest_index = 0;
355
356 for i in start_index..length {
357 let prop_key = PropertyKey::Str(i.to_string());
358 if let Some(PropertyDescriptor::Data(data)) = base.properties.get(&prop_key) {
359 rest_obj.get_object_base_mut().properties.insert(
360 PropertyKey::Str(rest_index.to_string()),
361 PropertyDescriptor::Data(PropertyDescriptorData {
362 value: data.value.clone(),
363 writable: true,
364 enumerable: true,
365 configurable: true,
366 }),
367 );
368 rest_index += 1;
369 }
370 }
371
372 rest_obj.get_object_base_mut().properties.insert(
374 PropertyKey::Str("length".to_string()),
375 PropertyDescriptor::Data(PropertyDescriptorData {
376 value: JsValue::Number(JsNumberType::Integer(rest_index as i64)),
377 writable: true,
378 enumerable: false,
379 configurable: false,
380 }),
381 );
382
383 let obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(rest_obj))));
384 Ok(JsValue::Object(obj))
385 }
386 _ => Err(JErrorType::TypeError("Cannot use rest with non-array".to_string())),
387 }
388}
389
390fn execute_function_declaration(
392 func_data: &FunctionData,
393 ctx: &mut EvalContext,
394) -> EvalResult {
395 let name = func_data.id.as_ref()
397 .ok_or_else(|| JErrorType::TypeError("Function declaration must have a name".to_string()))?
398 .name.clone();
399
400 let func_value = create_function_object(func_data, ctx)?;
402
403 if ctx.has_var_binding(&name) {
405 ctx.set_var_binding(&name, func_value)?;
407 } else {
408 ctx.create_var_binding(&name)?;
410 ctx.initialize_var_binding(&name, func_value)?;
411 }
412
413 Ok(Completion::normal())
414}
415
416fn execute_class_declaration(
418 class_data: &ClassData,
419 ctx: &mut EvalContext,
420) -> EvalResult {
421 let name = class_data.id.as_ref()
423 .ok_or_else(|| JErrorType::TypeError("Class declaration must have a name".to_string()))?
424 .name.clone();
425
426 let class_value = evaluate_class(class_data, ctx)?;
428
429 ctx.create_binding(&name, false)?;
431 ctx.initialize_binding(&name, class_value)?;
432
433 Ok(Completion::normal())
434}
435
436fn get_binding_name(pattern: &PatternType) -> Result<String, JErrorType> {
438 match pattern {
439 PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(id)) => {
440 Ok(id.name.clone())
441 }
442 PatternType::ObjectPattern { .. } => {
443 Err(JErrorType::TypeError("Object destructuring not yet supported".to_string()))
444 }
445 PatternType::ArrayPattern { .. } => {
446 Err(JErrorType::TypeError("Array destructuring not yet supported".to_string()))
447 }
448 PatternType::RestElement { .. } => {
449 Err(JErrorType::TypeError("Rest element not yet supported".to_string()))
450 }
451 PatternType::AssignmentPattern { .. } => {
452 Err(JErrorType::TypeError("Default value patterns not yet supported".to_string()))
453 }
454 }
455}
456
457fn execute_if_statement(
459 test: &ExpressionType,
460 consequent: &StatementType,
461 alternate: Option<&StatementType>,
462 ctx: &mut EvalContext,
463) -> EvalResult {
464 let test_value = evaluate_expression(test, ctx)?;
465
466 if to_boolean(&test_value) {
467 execute_statement(consequent, ctx)
468 } else if let Some(alt) = alternate {
469 execute_statement(alt, ctx)
470 } else {
471 Ok(Completion::normal())
472 }
473}
474
475fn execute_while_statement(
477 test: &ExpressionType,
478 body: &StatementType,
479 ctx: &mut EvalContext,
480) -> EvalResult {
481 let mut completion = Completion::normal();
482
483 loop {
484 let test_value = evaluate_expression(test, ctx)?;
485
486 if !to_boolean(&test_value) {
487 break;
488 }
489
490 completion = execute_statement(body, ctx)?;
491
492 match completion.completion_type {
493 CompletionType::Break => {
494 return Ok(Completion::normal_with_value(completion.get_value()));
495 }
496 CompletionType::Continue => {
497 continue;
498 }
499 CompletionType::Return | CompletionType::Throw | CompletionType::Yield => {
500 return Ok(completion);
501 }
502 CompletionType::Normal => {}
503 }
504 }
505
506 Ok(completion)
507}
508
509fn execute_do_while_statement(
511 body: &StatementType,
512 test: &ExpressionType,
513 ctx: &mut EvalContext,
514) -> EvalResult {
515 loop {
516 let completion = execute_statement(body, ctx)?;
517
518 match completion.completion_type {
519 CompletionType::Break => {
520 return Ok(Completion::normal_with_value(completion.get_value()));
521 }
522 CompletionType::Continue => {}
523 CompletionType::Return | CompletionType::Throw | CompletionType::Yield => {
524 return Ok(completion);
525 }
526 CompletionType::Normal => {}
527 }
528
529 let test_value = evaluate_expression(test, ctx)?;
530 if !to_boolean(&test_value) {
531 break;
532 }
533 }
534
535 Ok(Completion::normal())
536}
537
538fn execute_for_statement(
540 init: Option<&crate::parser::ast::VariableDeclarationOrExpression>,
541 test: Option<&ExpressionType>,
542 update: Option<&ExpressionType>,
543 body: &StatementType,
544 ctx: &mut EvalContext,
545) -> EvalResult {
546 use crate::parser::ast::VariableDeclarationOrExpression;
547
548 if let Some(init) = init {
549 match init {
550 VariableDeclarationOrExpression::VariableDeclaration(decl) => {
551 execute_variable_declaration(decl, ctx)?;
552 }
553 VariableDeclarationOrExpression::Expression(expr) => {
554 evaluate_expression(expr, ctx)?;
555 }
556 }
557 }
558
559 let mut completion = Completion::normal();
560
561 loop {
562 if let Some(test) = test {
563 let test_value = evaluate_expression(test, ctx)?;
564 if !to_boolean(&test_value) {
565 break;
566 }
567 }
568
569 completion = execute_statement(body, ctx)?;
570
571 match completion.completion_type {
572 CompletionType::Break => {
573 return Ok(Completion::normal_with_value(completion.get_value()));
574 }
575 CompletionType::Continue => {}
576 CompletionType::Return | CompletionType::Throw | CompletionType::Yield => {
577 return Ok(completion);
578 }
579 CompletionType::Normal => {}
580 }
581
582 if let Some(update) = update {
583 evaluate_expression(update, ctx)?;
584 }
585 }
586
587 Ok(completion)
588}
589
590fn execute_function_body(
592 body: &FunctionBodyData,
593 ctx: &mut EvalContext,
594) -> EvalResult {
595 let mut completion = Completion::normal();
596
597 for stmt in body.body.iter() {
598 completion = execute_statement(stmt, ctx)?;
599
600 match completion.completion_type {
602 CompletionType::Return | CompletionType::Throw | CompletionType::Yield => {
603 return Ok(completion);
604 }
605 CompletionType::Break | CompletionType::Continue => {
606 return Ok(completion);
609 }
610 CompletionType::Normal => {}
611 }
612 }
613
614 Ok(completion)
615}
616
617fn execute_switch_statement(
619 discriminant: &ExpressionType,
620 cases: &[SwitchCaseData],
621 ctx: &mut EvalContext,
622) -> EvalResult {
623 use super::expression::strict_equality;
624
625 let switch_value = evaluate_expression(discriminant, ctx)?;
627
628 let mut found_match = false;
630 let mut default_index: Option<usize> = None;
631 let mut start_index: Option<usize> = None;
632
633 for (i, case) in cases.iter().enumerate() {
635 if let Some(test) = &case.test {
636 let case_value = evaluate_expression(test, ctx)?;
638 if strict_equality(&switch_value, &case_value) {
639 start_index = Some(i);
640 found_match = true;
641 break;
642 }
643 } else {
644 default_index = Some(i);
646 }
647 }
648
649 if !found_match {
651 start_index = default_index;
652 }
653
654 let mut completion = Completion::normal();
656
657 if let Some(start) = start_index {
658 for case in cases.iter().skip(start) {
659 for stmt in &case.consequent {
660 completion = execute_statement(stmt, ctx)?;
661
662 match completion.completion_type {
663 CompletionType::Break => {
664 return Ok(Completion::normal_with_value(completion.get_value()));
666 }
667 CompletionType::Return | CompletionType::Throw | CompletionType::Continue | CompletionType::Yield => {
668 return Ok(completion);
670 }
671 CompletionType::Normal => {}
672 }
673 }
674 }
675 }
676
677 Ok(completion)
678}
679
680fn execute_try_statement(
682 block: &BlockStatementData,
683 handler: Option<&CatchClauseData>,
684 finalizer: Option<&BlockStatementData>,
685 ctx: &mut EvalContext,
686) -> EvalResult {
687 let try_result = execute_block_statement(block, ctx);
689
690 let mut completion = match try_result {
691 Ok(comp) => {
692 if comp.completion_type == CompletionType::Throw {
694 if let Some(catch_clause) = handler {
696 handle_catch(comp, catch_clause, ctx)?
697 } else {
698 comp
699 }
700 } else {
701 comp
702 }
703 }
704 Err(err) => {
705 if let Some(catch_clause) = handler {
707 let throw_completion = Completion {
708 completion_type: CompletionType::Throw,
709 value: Some(error_to_js_value(&err)),
710 target: None,
711 };
712 handle_catch(throw_completion, catch_clause, ctx)?
713 } else {
714 return Err(err);
715 }
716 }
717 };
718
719 if let Some(finally_block) = finalizer {
721 let finally_result = execute_block_statement(finally_block, ctx)?;
722
723 if finally_result.is_abrupt() {
725 completion = finally_result;
726 }
727 }
728
729 Ok(completion)
730}
731
732fn handle_catch(
734 thrown: Completion,
735 catch_clause: &CatchClauseData,
736 ctx: &mut EvalContext,
737) -> EvalResult {
738 ctx.push_block_scope();
740
741 let param_name = get_binding_name(&catch_clause.param)?;
743 let error_value = thrown.value.unwrap_or(JsValue::Undefined);
744
745 ctx.create_binding(¶m_name, false)?;
746 ctx.initialize_binding(¶m_name, error_value)?;
747
748 let result = execute_block_statement(&catch_clause.body, ctx);
750
751 ctx.pop_block_scope();
753
754 result
755}
756
757fn error_to_js_value(err: &JErrorType) -> JsValue {
759 match err {
760 JErrorType::TypeError(msg) => JsValue::String(format!("TypeError: {}", msg)),
761 JErrorType::ReferenceError(msg) => JsValue::String(format!("ReferenceError: {}", msg)),
762 JErrorType::SyntaxError(msg) => JsValue::String(format!("SyntaxError: {}", msg)),
763 JErrorType::RangeError(msg) => JsValue::String(format!("RangeError: {}", msg)),
764 JErrorType::YieldValue(v) => v.clone(),
765 }
766}
767
768fn execute_for_in_statement(
770 data: &ForIteratorData,
771 ctx: &mut EvalContext,
772) -> EvalResult {
773 let obj_value = evaluate_expression(&data.right, ctx)?;
775
776 let keys: Vec<String> = match &obj_value {
778 JsValue::Object(obj) => {
779 let obj_ref = obj.borrow();
780 obj_ref.as_js_object().get_object_base().properties
781 .keys()
782 .filter_map(|k| match k {
783 crate::runner::ds::object_property::PropertyKey::Str(s) => Some(s.clone()),
784 _ => None,
785 })
786 .collect()
787 }
788 JsValue::String(s) => {
789 (0..s.len()).map(|i| i.to_string()).collect()
791 }
792 JsValue::Null | JsValue::Undefined => {
793 return Ok(Completion::normal());
795 }
796 _ => Vec::new(),
797 };
798
799 let mut completion = Completion::normal();
801
802 for key in keys {
803 bind_for_iterator_variable(&data.left, JsValue::String(key), ctx)?;
805
806 completion = execute_statement(&data.body, ctx)?;
808
809 match completion.completion_type {
810 CompletionType::Break => {
811 return Ok(Completion::normal_with_value(completion.get_value()));
812 }
813 CompletionType::Continue => {
814 continue;
815 }
816 CompletionType::Return | CompletionType::Throw | CompletionType::Yield => {
817 return Ok(completion);
818 }
819 CompletionType::Normal => {}
820 }
821 }
822
823 Ok(completion)
824}
825
826fn execute_for_of_statement(
828 data: &ForIteratorData,
829 ctx: &mut EvalContext,
830) -> EvalResult {
831 use crate::runner::ds::value::JsNumberType;
832 use crate::runner::ds::object_property::PropertyKey;
833
834 let iterable_value = evaluate_expression(&data.right, ctx)?;
836
837 let values: Vec<JsValue> = match &iterable_value {
839 JsValue::Object(obj) => {
840 let obj_ref = obj.borrow();
841 let base = obj_ref.as_js_object().get_object_base();
842
843 if let Some(prop) = base.properties.get(&PropertyKey::Str("length".to_string())) {
845 if let crate::runner::ds::object_property::PropertyDescriptor::Data(length_data) = prop {
846 if let JsValue::Number(JsNumberType::Integer(len)) = &length_data.value {
847 let len = *len as usize;
848 let mut vals = Vec::with_capacity(len);
849 for i in 0..len {
850 let key = PropertyKey::Str(i.to_string());
851 if let Some(prop) = base.properties.get(&key) {
852 if let crate::runner::ds::object_property::PropertyDescriptor::Data(d) = prop {
853 vals.push(d.value.clone());
854 } else {
855 vals.push(JsValue::Undefined);
856 }
857 } else {
858 vals.push(JsValue::Undefined);
859 }
860 }
861 drop(obj_ref);
862 return execute_for_of_with_values(data, vals, ctx);
863 }
864 }
865 }
866
867 base.properties
869 .values()
870 .filter_map(|prop| {
871 if let crate::runner::ds::object_property::PropertyDescriptor::Data(d) = prop {
872 if d.enumerable {
873 Some(d.value.clone())
874 } else {
875 None
876 }
877 } else {
878 None
879 }
880 })
881 .collect()
882 }
883 JsValue::String(s) => {
884 s.chars().map(|c| JsValue::String(c.to_string())).collect()
886 }
887 JsValue::Null | JsValue::Undefined => {
888 return Err(JErrorType::TypeError(
889 "Cannot iterate over null or undefined".to_string(),
890 ));
891 }
892 _ => {
893 return Err(JErrorType::TypeError(
894 "Object is not iterable".to_string(),
895 ));
896 }
897 };
898
899 execute_for_of_with_values(data, values, ctx)
900}
901
902fn execute_for_of_with_values(
904 data: &ForIteratorData,
905 values: Vec<JsValue>,
906 ctx: &mut EvalContext,
907) -> EvalResult {
908 let mut completion = Completion::normal();
909
910 for value in values {
911 bind_for_iterator_variable(&data.left, value, ctx)?;
913
914 completion = execute_statement(&data.body, ctx)?;
916
917 match completion.completion_type {
918 CompletionType::Break => {
919 return Ok(Completion::normal_with_value(completion.get_value()));
920 }
921 CompletionType::Continue => {
922 continue;
923 }
924 CompletionType::Return | CompletionType::Throw | CompletionType::Yield => {
925 return Ok(completion);
926 }
927 CompletionType::Normal => {}
928 }
929 }
930
931 Ok(completion)
932}
933
934fn bind_for_iterator_variable(
936 left: &VariableDeclarationOrPattern,
937 value: JsValue,
938 ctx: &mut EvalContext,
939) -> Result<(), JErrorType> {
940 match left {
941 VariableDeclarationOrPattern::VariableDeclaration(var_decl) => {
942 for declarator in &var_decl.declarations {
944 let name = get_binding_name(&declarator.id)?;
945 let is_var = matches!(var_decl.kind, VariableDeclarationKind::Var);
946
947 if is_var {
948 if !ctx.has_binding(&name) {
950 ctx.create_var_binding(&name)?;
951 }
952 ctx.initialize_var_binding(&name, value.clone())?;
953 } else {
954 if !ctx.has_binding(&name) {
956 ctx.create_binding(&name, matches!(var_decl.kind, VariableDeclarationKind::Const))?;
957 ctx.initialize_binding(&name, value.clone())?;
958 } else {
959 ctx.set_binding(&name, value.clone())?;
960 }
961 }
962 }
963 }
964 VariableDeclarationOrPattern::Pattern(pattern) => {
965 let name = get_binding_name(pattern)?;
967 ctx.set_binding(&name, value)?;
968 }
969 }
970 Ok(())
971}