mirror of https://github.com/boa-dev/boa.git
Browse Source
* Initial implementation of for...of loop * Initial implementation of for...of loop * Extend for...of to support var, let, and const lhs * Use cached well known symbols * Nest use statements * Nest use statements * Add tests. * Initial implementation of for...of loop * Extend for...of to support var, let, and const lhs * Use cached well known symbols * Nest use statements * Nest use statements * Add tests. * Add tests, cache iterator prototypes, pull common iterator functions out into their own file to allow for repeated use. * Initial implementation of for...of loop * Extend for...of to support var, let, and const lhs * Use cached well known symbols * Nest use statements * Nest use statements * Add tests. * Add tests, cache iterator prototypes, pull common iterator functions out into their own file to allow for repeated use. * Initial implementation of for...of loop * Extend for...of to support var, let, and const lhs * Use cached well known symbols * Initial implementation of for...of loop * Nest use statements * Add string iterator * Clean up merge * Use ctx.global_iterator() * Merge upstream * Use u32 as array next index * Use into * Use boa::Result * Tidy up use statement * Tidy up use statementpull/722/head
joshwd36
4 years ago
committed by
GitHub
24 changed files with 1291 additions and 73 deletions
@ -0,0 +1,139 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, Value}, |
||||||
|
object::ObjectData, |
||||||
|
property::{Attribute, Property}, |
||||||
|
BoaProfiler, Context, Result, |
||||||
|
}; |
||||||
|
use gc::{Finalize, Trace}; |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Finalize, Trace)] |
||||||
|
pub enum ArrayIterationKind { |
||||||
|
Key, |
||||||
|
Value, |
||||||
|
KeyAndValue, |
||||||
|
} |
||||||
|
|
||||||
|
/// The Array Iterator object represents an iteration over an array. It implements the iterator protocol.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects
|
||||||
|
#[derive(Debug, Clone, Finalize, Trace)] |
||||||
|
pub struct ArrayIterator { |
||||||
|
array: Value, |
||||||
|
next_index: u32, |
||||||
|
kind: ArrayIterationKind, |
||||||
|
} |
||||||
|
|
||||||
|
impl ArrayIterator { |
||||||
|
pub(crate) const NAME: &'static str = "ArrayIterator"; |
||||||
|
|
||||||
|
fn new(array: Value, kind: ArrayIterationKind) -> Self { |
||||||
|
ArrayIterator { |
||||||
|
array, |
||||||
|
kind, |
||||||
|
next_index: 0, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// CreateArrayIterator( array, kind )
|
||||||
|
///
|
||||||
|
/// Creates a new iterator over the given array.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMA reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-createarrayiterator
|
||||||
|
pub(crate) fn create_array_iterator( |
||||||
|
ctx: &Context, |
||||||
|
array: Value, |
||||||
|
kind: ArrayIterationKind, |
||||||
|
) -> Result<Value> { |
||||||
|
let array_iterator = Value::new_object(Some(ctx.global_object())); |
||||||
|
array_iterator.set_data(ObjectData::ArrayIterator(Self::new(array, kind))); |
||||||
|
array_iterator |
||||||
|
.as_object_mut() |
||||||
|
.expect("array iterator object") |
||||||
|
.set_prototype_instance(ctx.iterator_prototypes().array_iterator().into()); |
||||||
|
Ok(array_iterator) |
||||||
|
} |
||||||
|
|
||||||
|
/// %ArrayIteratorPrototype%.next( )
|
||||||
|
///
|
||||||
|
/// Gets the next result in the array.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMA reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next
|
||||||
|
pub(crate) fn next(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> { |
||||||
|
if let Value::Object(ref object) = this { |
||||||
|
let mut object = object.borrow_mut(); |
||||||
|
if let Some(array_iterator) = object.as_array_iterator_mut() { |
||||||
|
let index = array_iterator.next_index; |
||||||
|
if array_iterator.array.is_undefined() { |
||||||
|
return Ok(create_iter_result_object(ctx, Value::undefined(), true)); |
||||||
|
} |
||||||
|
let len = array_iterator |
||||||
|
.array |
||||||
|
.get_field("length") |
||||||
|
.as_number() |
||||||
|
.ok_or_else(|| ctx.construct_type_error("Not an array"))? |
||||||
|
as u32; |
||||||
|
if array_iterator.next_index >= len { |
||||||
|
array_iterator.array = Value::undefined(); |
||||||
|
return Ok(create_iter_result_object(ctx, Value::undefined(), true)); |
||||||
|
} |
||||||
|
array_iterator.next_index = index + 1; |
||||||
|
match array_iterator.kind { |
||||||
|
ArrayIterationKind::Key => { |
||||||
|
Ok(create_iter_result_object(ctx, index.into(), false)) |
||||||
|
} |
||||||
|
ArrayIterationKind::Value => { |
||||||
|
let element_value = array_iterator.array.get_field(index); |
||||||
|
Ok(create_iter_result_object(ctx, element_value, false)) |
||||||
|
} |
||||||
|
ArrayIterationKind::KeyAndValue => { |
||||||
|
let element_value = array_iterator.array.get_field(index); |
||||||
|
let result = Array::make_array( |
||||||
|
&Value::new_object(Some(ctx.global_object())), |
||||||
|
&[index.into(), element_value], |
||||||
|
ctx, |
||||||
|
)?; |
||||||
|
Ok(create_iter_result_object(ctx, result, false)) |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
ctx.throw_type_error("`this` is not an ArrayIterator") |
||||||
|
} |
||||||
|
} else { |
||||||
|
ctx.throw_type_error("`this` is not an ArrayIterator") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Create the %ArrayIteratorPrototype% object
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMA reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
|
||||||
|
pub(crate) fn create_prototype(ctx: &mut Context, iterator_prototype: Value) -> Value { |
||||||
|
let global = ctx.global_object(); |
||||||
|
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); |
||||||
|
|
||||||
|
// Create prototype
|
||||||
|
let array_iterator = Value::new_object(Some(global)); |
||||||
|
make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx); |
||||||
|
array_iterator |
||||||
|
.as_object_mut() |
||||||
|
.expect("array iterator prototype object") |
||||||
|
.set_prototype_instance(iterator_prototype); |
||||||
|
|
||||||
|
let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol(); |
||||||
|
let to_string_tag_property = |
||||||
|
Property::data_descriptor(Value::string("Array Iterator"), Attribute::CONFIGURABLE); |
||||||
|
array_iterator.set_property(to_string_tag, to_string_tag_property); |
||||||
|
array_iterator |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,154 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::string::string_iterator::StringIterator, |
||||||
|
builtins::{ |
||||||
|
function::{BuiltInFunction, Function, FunctionFlags}, |
||||||
|
ArrayIterator, |
||||||
|
}, |
||||||
|
object::GcObject, |
||||||
|
object::{Object, PROTOTYPE}, |
||||||
|
property::Property, |
||||||
|
BoaProfiler, Context, Result, Value, |
||||||
|
}; |
||||||
|
|
||||||
|
#[derive(Debug, Default)] |
||||||
|
pub struct IteratorPrototypes { |
||||||
|
iterator_prototype: GcObject, |
||||||
|
array_iterator: GcObject, |
||||||
|
string_iterator: GcObject, |
||||||
|
} |
||||||
|
|
||||||
|
impl IteratorPrototypes { |
||||||
|
pub fn init(ctx: &mut Context) -> Self { |
||||||
|
let iterator_prototype = create_iterator_prototype(ctx); |
||||||
|
Self { |
||||||
|
iterator_prototype: iterator_prototype |
||||||
|
.as_gc_object() |
||||||
|
.expect("Iterator prototype is not an object"), |
||||||
|
array_iterator: ArrayIterator::create_prototype(ctx, iterator_prototype.clone()) |
||||||
|
.as_gc_object() |
||||||
|
.expect("Array Iterator Prototype is not an object"), |
||||||
|
string_iterator: StringIterator::create_prototype(ctx, iterator_prototype) |
||||||
|
.as_gc_object() |
||||||
|
.expect("String Iterator Prototype is not an object"), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn array_iterator(&self) -> GcObject { |
||||||
|
self.array_iterator.clone() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn iterator_prototype(&self) -> GcObject { |
||||||
|
self.iterator_prototype.clone() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn string_iterator(&self) -> GcObject { |
||||||
|
self.string_iterator.clone() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// CreateIterResultObject( value, done )
|
||||||
|
///
|
||||||
|
/// Generates an object supporting the IteratorResult interface.
|
||||||
|
pub fn create_iter_result_object(ctx: &mut Context, value: Value, done: bool) -> Value { |
||||||
|
let object = Value::new_object(Some(ctx.global_object())); |
||||||
|
let value_property = Property::default().value(value); |
||||||
|
let done_property = Property::default().value(Value::boolean(done)); |
||||||
|
object.set_property("value", value_property); |
||||||
|
object.set_property("done", done_property); |
||||||
|
object |
||||||
|
} |
||||||
|
|
||||||
|
/// Get an iterator record
|
||||||
|
pub fn get_iterator(ctx: &mut Context, iterable: Value) -> Result<IteratorRecord> { |
||||||
|
let iterator_function = iterable |
||||||
|
.get_property(ctx.well_known_symbols().iterator_symbol()) |
||||||
|
.and_then(|mut p| p.value.take()) |
||||||
|
.ok_or_else(|| ctx.construct_type_error("Not an iterable"))?; |
||||||
|
let iterator_object = ctx.call(&iterator_function, &iterable, &[])?; |
||||||
|
let next_function = iterator_object |
||||||
|
.get_property("next") |
||||||
|
.and_then(|mut p| p.value.take()) |
||||||
|
.ok_or_else(|| ctx.construct_type_error("Could not find property `next`"))?; |
||||||
|
Ok(IteratorRecord::new(iterator_object, next_function)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Create the %IteratorPrototype% object
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMA reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-%iteratorprototype%-object
|
||||||
|
fn create_iterator_prototype(ctx: &mut Context) -> Value { |
||||||
|
let global = ctx.global_object(); |
||||||
|
let _timer = BoaProfiler::global().start_event("Iterator Prototype", "init"); |
||||||
|
|
||||||
|
let iterator_prototype = Value::new_object(Some(global)); |
||||||
|
let mut function = Object::function( |
||||||
|
Function::BuiltIn( |
||||||
|
BuiltInFunction(|v, _, _| Ok(v.clone())), |
||||||
|
FunctionFlags::CALLABLE, |
||||||
|
), |
||||||
|
global.get_field("Function").get_field(PROTOTYPE), |
||||||
|
); |
||||||
|
function.insert_field("length", Value::from(0)); |
||||||
|
function.insert_field("name", Value::string("[Symbol.iterator]")); |
||||||
|
|
||||||
|
let symbol_iterator = ctx.well_known_symbols().iterator_symbol(); |
||||||
|
iterator_prototype.set_field(symbol_iterator, Value::from(function)); |
||||||
|
iterator_prototype |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct IteratorRecord { |
||||||
|
iterator_object: Value, |
||||||
|
next_function: Value, |
||||||
|
} |
||||||
|
|
||||||
|
impl IteratorRecord { |
||||||
|
fn new(iterator_object: Value, next_function: Value) -> Self { |
||||||
|
Self { |
||||||
|
iterator_object, |
||||||
|
next_function, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Get the next value in the iterator
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMA reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-iteratornext
|
||||||
|
pub(crate) fn next(&self, ctx: &mut Context) -> Result<IteratorResult> { |
||||||
|
let next = ctx.call(&self.next_function, &self.iterator_object, &[])?; |
||||||
|
let done = next |
||||||
|
.get_property("done") |
||||||
|
.and_then(|mut p| p.value.take()) |
||||||
|
.and_then(|v| v.as_boolean()) |
||||||
|
.ok_or_else(|| ctx.construct_type_error("Could not find property `done`"))?; |
||||||
|
let next_result = next |
||||||
|
.get_property("value") |
||||||
|
.and_then(|mut p| p.value.take()) |
||||||
|
.unwrap_or_default(); |
||||||
|
Ok(IteratorResult::new(next_result, done)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct IteratorResult { |
||||||
|
value: Value, |
||||||
|
done: bool, |
||||||
|
} |
||||||
|
|
||||||
|
impl IteratorResult { |
||||||
|
fn new(value: Value, done: bool) -> Self { |
||||||
|
Self { value, done } |
||||||
|
} |
||||||
|
|
||||||
|
pub fn is_done(&self) -> bool { |
||||||
|
self.done |
||||||
|
} |
||||||
|
|
||||||
|
pub fn value(self) -> Value { |
||||||
|
self.value |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
use crate::builtins::string::code_point_at; |
||||||
|
use crate::{ |
||||||
|
builtins::{function::make_builtin_fn, iterable::create_iter_result_object}, |
||||||
|
object::ObjectData, |
||||||
|
property::{Attribute, Property}, |
||||||
|
BoaProfiler, Context, Result, Value, |
||||||
|
}; |
||||||
|
use gc::{Finalize, Trace}; |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Finalize, Trace)] |
||||||
|
pub struct StringIterator { |
||||||
|
string: Value, |
||||||
|
next_index: i32, |
||||||
|
} |
||||||
|
|
||||||
|
impl StringIterator { |
||||||
|
fn new(string: Value) -> Self { |
||||||
|
Self { |
||||||
|
string, |
||||||
|
next_index: 0, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn create_string_iterator(ctx: &mut Context, string: Value) -> Result<Value> { |
||||||
|
let string_iterator = Value::new_object(Some(ctx.global_object())); |
||||||
|
string_iterator.set_data(ObjectData::StringIterator(Self::new(string))); |
||||||
|
string_iterator |
||||||
|
.as_object_mut() |
||||||
|
.expect("array iterator object") |
||||||
|
.set_prototype_instance(ctx.iterator_prototypes().string_iterator().into()); |
||||||
|
Ok(string_iterator) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn next(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> { |
||||||
|
if let Value::Object(ref object) = this { |
||||||
|
let mut object = object.borrow_mut(); |
||||||
|
if let Some(string_iterator) = object.as_string_iterator_mut() { |
||||||
|
if string_iterator.string.is_undefined() { |
||||||
|
return Ok(create_iter_result_object(ctx, Value::undefined(), true)); |
||||||
|
} |
||||||
|
let native_string = string_iterator.string.to_string(ctx)?; |
||||||
|
let len = native_string.encode_utf16().count() as i32; |
||||||
|
let position = string_iterator.next_index; |
||||||
|
if position >= len { |
||||||
|
string_iterator.string = Value::undefined(); |
||||||
|
return Ok(create_iter_result_object(ctx, Value::undefined(), true)); |
||||||
|
} |
||||||
|
let (_, code_unit_count, _) = |
||||||
|
code_point_at(native_string, position).expect("Invalid code point position"); |
||||||
|
string_iterator.next_index += code_unit_count as i32; |
||||||
|
let result_string = crate::builtins::string::String::substring( |
||||||
|
&string_iterator.string, |
||||||
|
&[position.into(), string_iterator.next_index.into()], |
||||||
|
ctx, |
||||||
|
)?; |
||||||
|
Ok(create_iter_result_object(ctx, result_string, false)) |
||||||
|
} else { |
||||||
|
ctx.throw_type_error("`this` is not an ArrayIterator") |
||||||
|
} |
||||||
|
} else { |
||||||
|
ctx.throw_type_error("`this` is not an ArrayIterator") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Create the %ArrayIteratorPrototype% object
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMA reference][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
|
||||||
|
pub(crate) fn create_prototype(ctx: &mut Context, iterator_prototype: Value) -> Value { |
||||||
|
let global = ctx.global_object(); |
||||||
|
let _timer = BoaProfiler::global().start_event("String Iterator", "init"); |
||||||
|
|
||||||
|
// Create prototype
|
||||||
|
let array_iterator = Value::new_object(Some(global)); |
||||||
|
make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx); |
||||||
|
array_iterator |
||||||
|
.as_object_mut() |
||||||
|
.expect("array iterator prototype object") |
||||||
|
.set_prototype_instance(iterator_prototype); |
||||||
|
|
||||||
|
let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol(); |
||||||
|
let to_string_tag_property = |
||||||
|
Property::data_descriptor(Value::string("String Iterator"), Attribute::CONFIGURABLE); |
||||||
|
array_iterator.set_property(to_string_tag, to_string_tag_property); |
||||||
|
array_iterator |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,208 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::iterable::get_iterator, |
||||||
|
environment::lexical_environment::{new_declarative_environment, VariableScope}, |
||||||
|
exec::{Executable, InterpreterState}, |
||||||
|
syntax::ast::node::Node, |
||||||
|
BoaProfiler, Context, Result, Value, |
||||||
|
}; |
||||||
|
use gc::{Finalize, Trace}; |
||||||
|
use std::fmt; |
||||||
|
|
||||||
|
#[cfg(feature = "serde")] |
||||||
|
use serde::{Deserialize, Serialize}; |
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
||||||
|
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] |
||||||
|
pub struct ForOfLoop { |
||||||
|
variable: Box<Node>, |
||||||
|
iterable: Box<Node>, |
||||||
|
body: Box<Node>, |
||||||
|
} |
||||||
|
|
||||||
|
impl ForOfLoop { |
||||||
|
pub fn new<V, I, B>(variable: V, iterable: I, body: B) -> Self |
||||||
|
where |
||||||
|
V: Into<Node>, |
||||||
|
I: Into<Node>, |
||||||
|
B: Into<Node>, |
||||||
|
{ |
||||||
|
Self { |
||||||
|
variable: Box::new(variable.into()), |
||||||
|
iterable: Box::new(iterable.into()), |
||||||
|
body: Box::new(body.into()), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn variable(&self) -> &Node { |
||||||
|
&self.variable |
||||||
|
} |
||||||
|
|
||||||
|
pub fn iterable(&self) -> &Node { |
||||||
|
&self.iterable |
||||||
|
} |
||||||
|
|
||||||
|
pub fn body(&self) -> &Node { |
||||||
|
&self.body |
||||||
|
} |
||||||
|
|
||||||
|
pub fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result { |
||||||
|
write!(f, "for ({} of {}) {{", self.variable, self.iterable)?; |
||||||
|
self.body().display(f, indentation + 1)?; |
||||||
|
f.write_str("}") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for ForOfLoop { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||||
|
self.display(f, 0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<ForOfLoop> for Node { |
||||||
|
fn from(for_of: ForOfLoop) -> Node { |
||||||
|
Self::ForOfLoop(for_of) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Executable for ForOfLoop { |
||||||
|
fn run(&self, interpreter: &mut Context) -> Result<Value> { |
||||||
|
let _timer = BoaProfiler::global().start_event("ForOf", "exec"); |
||||||
|
let iterable = self.iterable().run(interpreter)?; |
||||||
|
let iterator = get_iterator(interpreter, iterable)?; |
||||||
|
let mut result = Value::undefined(); |
||||||
|
|
||||||
|
loop { |
||||||
|
{ |
||||||
|
let env = &mut interpreter.realm_mut().environment; |
||||||
|
env.push(new_declarative_environment(Some( |
||||||
|
env.get_current_environment_ref().clone(), |
||||||
|
))); |
||||||
|
} |
||||||
|
let iterator_result = iterator.next(interpreter)?; |
||||||
|
if iterator_result.is_done() { |
||||||
|
break; |
||||||
|
} |
||||||
|
let next_result = iterator_result.value(); |
||||||
|
|
||||||
|
match self.variable() { |
||||||
|
Node::Identifier(ref name) => { |
||||||
|
let environment = &mut interpreter.realm_mut().environment; |
||||||
|
|
||||||
|
if environment.has_binding(name.as_ref()) { |
||||||
|
// Binding already exists
|
||||||
|
environment.set_mutable_binding(name.as_ref(), next_result.clone(), true); |
||||||
|
} else { |
||||||
|
environment.create_mutable_binding( |
||||||
|
name.as_ref().to_owned(), |
||||||
|
true, |
||||||
|
VariableScope::Function, |
||||||
|
); |
||||||
|
environment.initialize_binding(name.as_ref(), next_result.clone()); |
||||||
|
} |
||||||
|
} |
||||||
|
Node::VarDeclList(ref list) => match list.as_ref() { |
||||||
|
[var] => { |
||||||
|
let environment = &mut interpreter.realm_mut().environment; |
||||||
|
|
||||||
|
if var.init().is_some() { |
||||||
|
return interpreter.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer"); |
||||||
|
} |
||||||
|
|
||||||
|
if environment.has_binding(var.name()) { |
||||||
|
environment.set_mutable_binding(var.name(), next_result, true); |
||||||
|
} else { |
||||||
|
environment.create_mutable_binding( |
||||||
|
var.name().to_owned(), |
||||||
|
false, |
||||||
|
VariableScope::Function, |
||||||
|
); |
||||||
|
environment.initialize_binding(var.name(), next_result); |
||||||
|
} |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return interpreter.throw_syntax_error( |
||||||
|
"only one variable can be declared in the head of a for-of loop", |
||||||
|
) |
||||||
|
} |
||||||
|
}, |
||||||
|
Node::LetDeclList(ref list) => match list.as_ref() { |
||||||
|
[var] => { |
||||||
|
let environment = &mut interpreter.realm_mut().environment; |
||||||
|
|
||||||
|
if var.init().is_some() { |
||||||
|
return interpreter.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer"); |
||||||
|
} |
||||||
|
|
||||||
|
environment.create_mutable_binding( |
||||||
|
var.name().to_owned(), |
||||||
|
false, |
||||||
|
VariableScope::Block, |
||||||
|
); |
||||||
|
environment.initialize_binding(var.name(), next_result); |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return interpreter.throw_syntax_error( |
||||||
|
"only one variable can be declared in the head of a for-of loop", |
||||||
|
) |
||||||
|
} |
||||||
|
}, |
||||||
|
Node::ConstDeclList(ref list) => match list.as_ref() { |
||||||
|
[var] => { |
||||||
|
let environment = &mut interpreter.realm_mut().environment; |
||||||
|
|
||||||
|
if var.init().is_some() { |
||||||
|
return interpreter.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer"); |
||||||
|
} |
||||||
|
|
||||||
|
environment.create_immutable_binding( |
||||||
|
var.name().to_owned(), |
||||||
|
false, |
||||||
|
VariableScope::Block, |
||||||
|
); |
||||||
|
environment.initialize_binding(var.name(), next_result); |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return interpreter.throw_syntax_error( |
||||||
|
"only one variable can be declared in the head of a for-of loop", |
||||||
|
) |
||||||
|
} |
||||||
|
}, |
||||||
|
Node::Assign(_) => { |
||||||
|
return interpreter.throw_syntax_error( |
||||||
|
"a declaration in the head of a for-of loop can't have an initializer", |
||||||
|
); |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return interpreter |
||||||
|
.throw_syntax_error("unknown left hand side in head of for-of loop") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
result = self.body().run(interpreter)?; |
||||||
|
match interpreter.executor().get_current_state() { |
||||||
|
InterpreterState::Break(_label) => { |
||||||
|
// TODO break to label.
|
||||||
|
|
||||||
|
// Loops 'consume' breaks.
|
||||||
|
interpreter |
||||||
|
.executor() |
||||||
|
.set_current_state(InterpreterState::Executing); |
||||||
|
break; |
||||||
|
} |
||||||
|
InterpreterState::Continue(_label) => { |
||||||
|
// TODO continue to label.
|
||||||
|
interpreter |
||||||
|
.executor() |
||||||
|
.set_current_state(InterpreterState::Executing); |
||||||
|
// after breaking out of the block, continue execution of the loop
|
||||||
|
} |
||||||
|
InterpreterState::Return => return Ok(result), |
||||||
|
InterpreterState::Executing => { |
||||||
|
// Continue execution.
|
||||||
|
} |
||||||
|
} |
||||||
|
let _ = interpreter.realm_mut().environment.pop(); |
||||||
|
} |
||||||
|
Ok(result) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue