Browse Source

Implement spec compliant Array constructor (#859)

* Implement spec compliant Array constructor

* Address review comments
pull/878/head
George Roman 4 years ago committed by GitHub
parent
commit
e6a28e6b14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 109
      boa/src/builtins/array/mod.rs

109
boa/src/builtins/array/mod.rs

@ -16,6 +16,7 @@ mod tests;
use crate::{ use crate::{
builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator}, builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator},
builtins::BuiltIn, builtins::BuiltIn,
gc::GcObject,
object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE}, object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE},
property::{Attribute, DataDescriptor}, property::{Attribute, DataDescriptor},
value::{same_value_zero, Value}, value::{same_value_zero, Value},
@ -100,43 +101,103 @@ impl Array {
const LENGTH: usize = 1; const LENGTH: usize = 1;
fn constructor(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> { fn constructor(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// Set Prototype // Delegate to the appropriate constructor based on the number of arguments
let prototype = context.standard_objects().array_object().prototype(); match args.len() {
0 => Array::construct_array_empty(this, context),
1 => Array::construct_array_length(this, &args[0], context),
_ => Array::construct_array_values(this, args, context),
}
}
this.as_object_mut() /// No argument constructor for `Array`.
.expect("this should be an array object") ///
.set_prototype_instance(prototype.into()); /// More information:
// This value is used by console.log and other routines to match Object type /// - [ECMAScript reference][spec]
// to its Javascript Identifier (global constructor method name) ///
this.set_data(ObjectData::Array); /// [spec]: https://tc39.es/ecma262/#sec-array-constructor-array
fn construct_array_empty(this: &Value, context: &mut Context) -> Result<Value> {
let prototype = context.standard_objects().array_object().prototype();
// add our arguments in Array::array_create(this, 0, Some(prototype), context)
let mut length = args.len() as i32;
match args.len() {
1 if args[0].is_integer() => {
length = args[0].as_number().unwrap() as i32;
// TODO: It should not create an array of undefineds, but an empty array ("holy" array in V8) with length `n`.
for n in 0..length {
this.set_field(n, Value::undefined());
} }
/// By length constructor for `Array`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-array-len
fn construct_array_length(
this: &Value,
length: &Value,
context: &mut Context,
) -> Result<Value> {
let prototype = context.standard_objects().array_object().prototype();
let array = Array::array_create(this, 0, Some(prototype), context)?;
if !length.is_number() {
array.set_field(0, Value::from(length));
array.set_field("length", Value::from(1));
} else {
if length.is_double() {
return context.throw_range_error("Invalid array length");
} }
1 if args[0].is_double() => { array.set_field("length", Value::from(length.to_u32(context).unwrap()));
return context.throw_range_error("invalid array length");
} }
_ => {
for (n, value) in args.iter().enumerate() { Ok(array)
this.set_field(n, value.clone());
} }
/// From items constructor for `Array`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-array-items
fn construct_array_values(
this: &Value,
items: &[Value],
context: &mut Context,
) -> Result<Value> {
let prototype = context.standard_objects().array_object().prototype();
let array = Array::array_create(this, items.len() as u32, Some(prototype), context)?;
for (k, item) in items.iter().enumerate() {
array.set_field(k, item.clone());
} }
Ok(array)
} }
// finally create length property /// Utility for constructing `Array` objects.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-arraycreate
fn array_create(
this: &Value,
length: u32,
prototype: Option<GcObject>,
context: &mut Context,
) -> Result<Value> {
let prototype = match prototype {
Some(prototype) => prototype,
None => context.standard_objects().array_object().prototype(),
};
this.as_object_mut()
.expect("this should be an array object")
.set_prototype_instance(prototype.into());
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::Array);
let length = DataDescriptor::new( let length = DataDescriptor::new(
length, length,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
); );
this.set_property("length", length);
this.set_property("length".to_string(), length);
Ok(this.clone()) Ok(this.clone())
} }

Loading…
Cancel
Save