|
|
|
@ -16,18 +16,17 @@ use crate::{
|
|
|
|
|
builtins::{ |
|
|
|
|
object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE}, |
|
|
|
|
property::Property, |
|
|
|
|
value::{from_value, to_value, undefined, ResultValue, Value, ValueData}, |
|
|
|
|
value::{ResultValue, Value, ValueData}, |
|
|
|
|
}, |
|
|
|
|
exec::Interpreter, |
|
|
|
|
}; |
|
|
|
|
use gc::Gc; |
|
|
|
|
use std::borrow::Borrow; |
|
|
|
|
use std::cmp::{max, min}; |
|
|
|
|
use std::ops::Deref; |
|
|
|
|
|
|
|
|
|
/// Creates a new `Array` instance.
|
|
|
|
|
pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue { |
|
|
|
|
let array = ValueData::new_obj(Some( |
|
|
|
|
let array = Value::new_object(Some( |
|
|
|
|
&interpreter |
|
|
|
|
.get_realm() |
|
|
|
|
.environment |
|
|
|
@ -44,7 +43,7 @@ pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
|
|
|
|
|
.borrow() |
|
|
|
|
.get_field_slice(PROTOTYPE), |
|
|
|
|
); |
|
|
|
|
array.borrow().set_field_slice("length", to_value(0)); |
|
|
|
|
array.borrow().set_field_slice("length", Value::from(0)); |
|
|
|
|
Ok(array) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -56,20 +55,19 @@ pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultVal
|
|
|
|
|
let array_obj_ptr = array_obj.clone(); |
|
|
|
|
|
|
|
|
|
// Wipe existing contents of the array object
|
|
|
|
|
let orig_length: i32 = |
|
|
|
|
from_value(array_obj.get_field_slice("length")).expect("failed to convert length to i32"); |
|
|
|
|
let orig_length = i32::from(&array_obj.get_field_slice("length")); |
|
|
|
|
for n in 0..orig_length { |
|
|
|
|
array_obj_ptr.remove_prop(&n.to_string()); |
|
|
|
|
array_obj_ptr.remove_property(&n.to_string()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Create length
|
|
|
|
|
let length = Property::new() |
|
|
|
|
.value(to_value(array_contents.len() as i32)) |
|
|
|
|
.value(Value::from(array_contents.len() as i32)) |
|
|
|
|
.writable(true) |
|
|
|
|
.configurable(false) |
|
|
|
|
.enumerable(false); |
|
|
|
|
|
|
|
|
|
array_obj_ptr.set_prop("length".to_string(), length); |
|
|
|
|
array_obj_ptr.set_property("length".to_string(), length); |
|
|
|
|
|
|
|
|
|
for (n, value) in array_contents.iter().enumerate() { |
|
|
|
|
array_obj_ptr.set_field_slice(&n.to_string(), value.clone()); |
|
|
|
@ -80,8 +78,7 @@ pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultVal
|
|
|
|
|
/// Utility function which takes an existing array object and puts additional
|
|
|
|
|
/// values on the end, correctly rewriting the length
|
|
|
|
|
pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> ResultValue { |
|
|
|
|
let orig_length: i32 = |
|
|
|
|
from_value(array_ptr.get_field_slice("length")).expect("failed to conveert lenth to i32"); |
|
|
|
|
let orig_length = i32::from(&array_ptr.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
for (n, value) in add_values.iter().enumerate() { |
|
|
|
|
let new_index = orig_length.wrapping_add(n as i32); |
|
|
|
@ -90,7 +87,7 @@ pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> Re
|
|
|
|
|
|
|
|
|
|
array_ptr.set_field_slice( |
|
|
|
|
"length", |
|
|
|
|
to_value(orig_length.wrapping_add(add_values.len() as i32)), |
|
|
|
|
Value::from(orig_length.wrapping_add(add_values.len() as i32)), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
Ok(array_ptr.clone()) |
|
|
|
@ -117,10 +114,10 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re
|
|
|
|
|
let mut length = args.len() as i32; |
|
|
|
|
match args.len() { |
|
|
|
|
1 if args[0].is_integer() => { |
|
|
|
|
length = from_value::<i32>(args[0].clone()).expect("Could not convert argument to i32"); |
|
|
|
|
length = i32::from(&args[0]); |
|
|
|
|
// 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_slice(&n.to_string(), Gc::new(ValueData::Undefined)); |
|
|
|
|
this.set_field_slice(&n.to_string(), Value::undefined()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
1 if args[0].is_double() => { |
|
|
|
@ -136,12 +133,12 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re
|
|
|
|
|
|
|
|
|
|
// finally create length property
|
|
|
|
|
let length = Property::new() |
|
|
|
|
.value(to_value(length)) |
|
|
|
|
.value(Value::from(length)) |
|
|
|
|
.writable(true) |
|
|
|
|
.configurable(false) |
|
|
|
|
.enumerable(false); |
|
|
|
|
|
|
|
|
|
this.set_prop("length".to_string(), length); |
|
|
|
|
this.set_property("length".to_string(), length); |
|
|
|
|
|
|
|
|
|
Ok(this.clone()) |
|
|
|
|
} |
|
|
|
@ -158,12 +155,12 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-array.isarray
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
|
|
|
|
|
pub fn is_array(_this: &mut Value, args: &[Value], _interpreter: &mut Interpreter) -> ResultValue { |
|
|
|
|
let value_true = Gc::new(ValueData::Boolean(true)); |
|
|
|
|
let value_false = Gc::new(ValueData::Boolean(false)); |
|
|
|
|
let value_true = Value::boolean(true); |
|
|
|
|
let value_false = Value::boolean(false); |
|
|
|
|
|
|
|
|
|
match args.get(0) { |
|
|
|
|
Some(arg) => { |
|
|
|
|
match *(*arg).clone() { |
|
|
|
|
match arg.data() { |
|
|
|
|
// 1.
|
|
|
|
|
ValueData::Object(ref obj) => { |
|
|
|
|
// 2.
|
|
|
|
@ -202,15 +199,13 @@ pub fn concat(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa
|
|
|
|
|
// one)
|
|
|
|
|
let mut new_values: Vec<Value> = Vec::new(); |
|
|
|
|
|
|
|
|
|
let this_length: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); |
|
|
|
|
let this_length = i32::from(&this.get_field_slice("length")); |
|
|
|
|
for n in 0..this_length { |
|
|
|
|
new_values.push(this.get_field_slice(&n.to_string())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for concat_array in args { |
|
|
|
|
let concat_length: i32 = from_value(concat_array.get_field_slice("length")) |
|
|
|
|
.expect("Could not convert argument to i32"); |
|
|
|
|
let concat_length = i32::from(&concat_array.get_field_slice("length")); |
|
|
|
|
for n in 0..concat_length { |
|
|
|
|
new_values.push(concat_array.get_field_slice(&n.to_string())); |
|
|
|
|
} |
|
|
|
@ -247,15 +242,14 @@ pub fn push(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop
|
|
|
|
|
pub fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
|
let curr_length: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); |
|
|
|
|
let curr_length = i32::from(&this.get_field_slice("length")); |
|
|
|
|
if curr_length < 1 { |
|
|
|
|
return Ok(Gc::new(ValueData::Undefined)); |
|
|
|
|
return Ok(Value::undefined()); |
|
|
|
|
} |
|
|
|
|
let pop_index = curr_length.wrapping_sub(1); |
|
|
|
|
let pop_value: Value = this.get_field_slice(&pop_index.to_string()); |
|
|
|
|
this.remove_prop(&pop_index.to_string()); |
|
|
|
|
this.set_field_slice("length", to_value(pop_index)); |
|
|
|
|
this.remove_property(&pop_index.to_string()); |
|
|
|
|
this.set_field_slice("length", Value::from(pop_index)); |
|
|
|
|
Ok(pop_value) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -271,25 +265,22 @@ pub fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
|
|
|
|
|
pub fn for_each(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
return Err(to_value( |
|
|
|
|
"Missing argument for Array.prototype.forEach".to_string(), |
|
|
|
|
)); |
|
|
|
|
return Err(Value::from("Missing argument for Array.prototype.forEach")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let callback_arg = args.get(0).expect("Could not get `callbackFn` argument."); |
|
|
|
|
let mut this_arg = args.get(1).cloned().unwrap_or_else(undefined); |
|
|
|
|
let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); |
|
|
|
|
|
|
|
|
|
let length: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not get `length` property."); |
|
|
|
|
let length = i32::from(&this.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
for i in 0..length { |
|
|
|
|
let element = this.get_field_slice(&i.to_string()); |
|
|
|
|
let arguments = [element, to_value(i), this.clone()]; |
|
|
|
|
let arguments = [element, Value::from(i), this.clone()]; |
|
|
|
|
|
|
|
|
|
interpreter.call(callback_arg, &mut this_arg, &arguments)?; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(Gc::new(ValueData::Undefined)) |
|
|
|
|
Ok(Value::undefined()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.join( separator )`
|
|
|
|
@ -312,14 +303,13 @@ pub fn join(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let mut elem_strs: Vec<String> = Vec::new(); |
|
|
|
|
let length: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); |
|
|
|
|
let length = i32::from(&this.get_field_slice("length")); |
|
|
|
|
for n in 0..length { |
|
|
|
|
let elem_str: String = this.get_field_slice(&n.to_string()).to_string(); |
|
|
|
|
elem_strs.push(elem_str); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(to_value(elem_strs.join(&separator))) |
|
|
|
|
Ok(Value::from(elem_strs.join(&separator))) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.toString( separator )`
|
|
|
|
@ -336,10 +326,9 @@ pub fn join(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString
|
|
|
|
|
pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { |
|
|
|
|
let method_name = "join"; |
|
|
|
|
let mut arguments = vec![to_value(",")]; |
|
|
|
|
let mut arguments = vec![Value::from(",")]; |
|
|
|
|
// 2.
|
|
|
|
|
let mut method: Value = |
|
|
|
|
from_value(this.get_field_slice(method_name)).expect("failed to get Array.prototype.join"); |
|
|
|
|
let mut method = this.get_field_slice(method_name); |
|
|
|
|
// 3.
|
|
|
|
|
if !method.is_function() { |
|
|
|
|
method = _ctx |
|
|
|
@ -349,7 +338,6 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R
|
|
|
|
|
.get_field_slice(PROTOTYPE) |
|
|
|
|
.get_field_slice("toString"); |
|
|
|
|
|
|
|
|
|
method = from_value(method).expect("failed to get Object.prototype.toString"); |
|
|
|
|
arguments = Vec::new(); |
|
|
|
|
} |
|
|
|
|
// 4.
|
|
|
|
@ -361,7 +349,7 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R
|
|
|
|
|
}, |
|
|
|
|
Err(v) => format!("error: {}", v), |
|
|
|
|
}; |
|
|
|
|
Ok(to_value(match_string)) |
|
|
|
|
Ok(Value::from(match_string)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.reverse()`
|
|
|
|
@ -377,8 +365,7 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
|
|
|
|
|
#[allow(clippy::else_if_without_else)] |
|
|
|
|
pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
|
let len: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); |
|
|
|
|
let len = i32::from(&this.get_field_slice("length")); |
|
|
|
|
let middle: i32 = len.wrapping_div(2); |
|
|
|
|
|
|
|
|
|
for lower in 0..middle { |
|
|
|
@ -395,10 +382,10 @@ pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu
|
|
|
|
|
this.set_field_slice(&lower.to_string(), upper_value); |
|
|
|
|
} else if upper_exists { |
|
|
|
|
this.set_field_slice(&lower.to_string(), upper_value); |
|
|
|
|
this.remove_prop(&upper.to_string()); |
|
|
|
|
this.remove_property(&upper.to_string()); |
|
|
|
|
} else if lower_exists { |
|
|
|
|
this.set_field_slice(&upper.to_string(), lower_value); |
|
|
|
|
this.remove_prop(&lower.to_string()); |
|
|
|
|
this.remove_property(&lower.to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -416,11 +403,10 @@ pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
|
|
|
|
|
pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
|
let len: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); |
|
|
|
|
let len = i32::from(&this.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
if len == 0 { |
|
|
|
|
this.set_field_slice("length", to_value(0_i32)); |
|
|
|
|
this.set_field_slice("length", Value::from(0)); |
|
|
|
|
// Since length is 0, this will be an Undefined value
|
|
|
|
|
return Ok(this.get_field_slice(&0.to_string())); |
|
|
|
|
} |
|
|
|
@ -432,16 +418,16 @@ pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
|
|
|
|
|
let to = (k.wrapping_sub(1)).to_string(); |
|
|
|
|
|
|
|
|
|
let from_value = this.get_field_slice(&from); |
|
|
|
|
if from_value == Gc::new(ValueData::Undefined) { |
|
|
|
|
this.remove_prop(&to); |
|
|
|
|
if from_value == Value::undefined() { |
|
|
|
|
this.remove_property(&to); |
|
|
|
|
} else { |
|
|
|
|
this.set_field_slice(&to, from_value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let final_index = len.wrapping_sub(1); |
|
|
|
|
this.remove_prop(&(final_index).to_string()); |
|
|
|
|
this.set_field_slice("length", to_value(final_index)); |
|
|
|
|
this.remove_property(&(final_index).to_string()); |
|
|
|
|
this.set_field_slice("length", Value::from(final_index)); |
|
|
|
|
|
|
|
|
|
Ok(first) |
|
|
|
|
} |
|
|
|
@ -459,8 +445,7 @@ pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
|
|
|
|
|
pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
|
let len: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); |
|
|
|
|
let len = i32::from(&this.get_field_slice("length")); |
|
|
|
|
let arg_c: i32 = args.len() as i32; |
|
|
|
|
|
|
|
|
|
if arg_c > 0 { |
|
|
|
@ -469,8 +454,8 @@ pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultV
|
|
|
|
|
let to = (k.wrapping_add(arg_c).wrapping_sub(1)).to_string(); |
|
|
|
|
|
|
|
|
|
let from_value = this.get_field_slice(&from); |
|
|
|
|
if from_value == Gc::new(ValueData::Undefined) { |
|
|
|
|
this.remove_prop(&to); |
|
|
|
|
if from_value == Value::undefined() { |
|
|
|
|
this.remove_property(&to); |
|
|
|
|
} else { |
|
|
|
|
this.set_field_slice(&to, from_value); |
|
|
|
|
} |
|
|
|
@ -486,8 +471,8 @@ pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultV
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let temp = len.wrapping_add(arg_c); |
|
|
|
|
this.set_field_slice("length", to_value(temp)); |
|
|
|
|
Ok(to_value(temp)) |
|
|
|
|
this.set_field_slice("length", Value::from(temp)); |
|
|
|
|
Ok(Value::from(temp)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.every( callback, [ thisArg ] )`
|
|
|
|
@ -505,32 +490,32 @@ pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultV
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
|
|
|
|
|
pub fn every(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
return Err(to_value( |
|
|
|
|
"missing callback when calling function Array.prototype.every".to_string(), |
|
|
|
|
return Err(Value::from( |
|
|
|
|
"missing callback when calling function Array.prototype.every", |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
let callback = &args[0]; |
|
|
|
|
let mut this_arg = if args.len() > 1 { |
|
|
|
|
args[1].clone() |
|
|
|
|
} else { |
|
|
|
|
Gc::new(ValueData::Undefined) |
|
|
|
|
Value::undefined() |
|
|
|
|
}; |
|
|
|
|
let mut i = 0; |
|
|
|
|
let max_len: i32 = from_value(this.get_field_slice("length")).unwrap(); |
|
|
|
|
let max_len = i32::from(&this.get_field_slice("length")); |
|
|
|
|
let mut len = max_len; |
|
|
|
|
while i < len { |
|
|
|
|
let element = this.get_field_slice(&i.to_string()); |
|
|
|
|
let arguments = [element, to_value(i), this.clone()]; |
|
|
|
|
let arguments = [element, Value::from(i), this.clone()]; |
|
|
|
|
let result = interpreter |
|
|
|
|
.call(callback, &mut this_arg, &arguments)? |
|
|
|
|
.is_true(); |
|
|
|
|
if !result { |
|
|
|
|
return Ok(to_value(false)); |
|
|
|
|
return Ok(Value::from(false)); |
|
|
|
|
} |
|
|
|
|
len = min(max_len, from_value(this.get_field_slice("length")).unwrap()); |
|
|
|
|
len = min(max_len, i32::from(&this.get_field_slice("length"))); |
|
|
|
|
i += 1; |
|
|
|
|
} |
|
|
|
|
Ok(to_value(true)) |
|
|
|
|
Ok(Value::from(true)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.map( callback, [ thisArg ] )`
|
|
|
|
@ -546,29 +531,28 @@ pub fn every(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) ->
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
|
|
|
|
|
pub fn map(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
return Err(to_value( |
|
|
|
|
return Err(Value::from( |
|
|
|
|
"missing argument 0 when calling function Array.prototype.map", |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let callback = args.get(0).cloned().unwrap_or_else(undefined); |
|
|
|
|
let mut this_val = args.get(1).cloned().unwrap_or_else(undefined); |
|
|
|
|
let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); |
|
|
|
|
let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); |
|
|
|
|
|
|
|
|
|
let length: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not get `length` property."); |
|
|
|
|
let length = i32::from(&this.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
let new = new_array(&interpreter)?; |
|
|
|
|
|
|
|
|
|
let values = (0..length) |
|
|
|
|
let values: Vec<Value> = (0..length) |
|
|
|
|
.map(|idx| { |
|
|
|
|
let element = this.get_field_slice(&idx.to_string()); |
|
|
|
|
let args = [element, to_value(idx), new.clone()]; |
|
|
|
|
let args = [element, Value::from(idx), new.clone()]; |
|
|
|
|
|
|
|
|
|
interpreter |
|
|
|
|
.call(&callback, &mut this_val, &args) |
|
|
|
|
.unwrap_or_else(|_| undefined()) |
|
|
|
|
.unwrap_or_else(|_| Value::undefined()) |
|
|
|
|
}) |
|
|
|
|
.collect::<Vec<Value>>(); |
|
|
|
|
.collect(); |
|
|
|
|
|
|
|
|
|
construct_array(&new, &values) |
|
|
|
|
} |
|
|
|
@ -595,17 +579,15 @@ pub fn map(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> R
|
|
|
|
|
pub fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
|
// If no arguments, return -1. Not described in spec, but is what chrome does.
|
|
|
|
|
if args.is_empty() { |
|
|
|
|
return Ok(to_value(-1)); |
|
|
|
|
return Ok(Value::from(-1)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let search_element = args[0].clone(); |
|
|
|
|
let len: i32 = from_value(this.get_field_slice("length")) |
|
|
|
|
.expect("Expected array property \"length\" is not set."); |
|
|
|
|
let len = i32::from(&this.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
let mut idx = match args.get(1) { |
|
|
|
|
Some(from_idx_ptr) => { |
|
|
|
|
let from_idx = from_value(from_idx_ptr.clone()) |
|
|
|
|
.expect("Error parsing \"Array.prototype.indexOf - fromIndex\" argument"); |
|
|
|
|
let from_idx = i32::from(from_idx_ptr); |
|
|
|
|
|
|
|
|
|
if from_idx < 0 { |
|
|
|
|
len + from_idx |
|
|
|
@ -620,13 +602,13 @@ pub fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result
|
|
|
|
|
let check_element = this.get_field_slice(&idx.to_string()).clone(); |
|
|
|
|
|
|
|
|
|
if check_element == search_element { |
|
|
|
|
return Ok(to_value(idx)); |
|
|
|
|
return Ok(Value::from(idx)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
idx += 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(to_value(-1)) |
|
|
|
|
Ok(Value::from(-1)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.lastIndexOf( searchElement[, fromIndex ] )`
|
|
|
|
@ -650,17 +632,15 @@ pub fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result
|
|
|
|
|
pub fn last_index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
|
// If no arguments, return -1. Not described in spec, but is what chrome does.
|
|
|
|
|
if args.is_empty() { |
|
|
|
|
return Ok(to_value(-1)); |
|
|
|
|
return Ok(Value::from(-1)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let search_element = args[0].clone(); |
|
|
|
|
let len: i32 = from_value(this.get_field_slice("length")) |
|
|
|
|
.expect("Expected array property \"length\" is not set."); |
|
|
|
|
let len = i32::from(&this.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
let mut idx = match args.get(1) { |
|
|
|
|
Some(from_idx_ptr) => { |
|
|
|
|
let from_idx = from_value(from_idx_ptr.clone()) |
|
|
|
|
.expect("Error parsing \"Array.prototype.indexOf - fromIndex\" argument"); |
|
|
|
|
let from_idx = i32::from(from_idx_ptr); |
|
|
|
|
|
|
|
|
|
if from_idx >= 0 { |
|
|
|
|
min(from_idx, len - 1) |
|
|
|
@ -675,13 +655,13 @@ pub fn last_index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> R
|
|
|
|
|
let check_element = this.get_field_slice(&idx.to_string()).clone(); |
|
|
|
|
|
|
|
|
|
if check_element == search_element { |
|
|
|
|
return Ok(to_value(idx)); |
|
|
|
|
return Ok(Value::from(idx)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
idx -= 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(to_value(-1)) |
|
|
|
|
Ok(Value::from(-1)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.find( callback, [thisArg] )`
|
|
|
|
@ -698,26 +678,26 @@ pub fn last_index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> R
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
|
|
|
|
|
pub fn find(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
return Err(to_value( |
|
|
|
|
"missing callback when calling function Array.prototype.find".to_string(), |
|
|
|
|
return Err(Value::from( |
|
|
|
|
"missing callback when calling function Array.prototype.find", |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
let callback = &args[0]; |
|
|
|
|
let mut this_arg = if args.len() > 1 { |
|
|
|
|
args[1].clone() |
|
|
|
|
} else { |
|
|
|
|
Gc::new(ValueData::Undefined) |
|
|
|
|
Value::undefined() |
|
|
|
|
}; |
|
|
|
|
let len: i32 = from_value(this.get_field_slice("length")).unwrap(); |
|
|
|
|
let len = i32::from(&this.get_field_slice("length")); |
|
|
|
|
for i in 0..len { |
|
|
|
|
let element = this.get_field_slice(&i.to_string()); |
|
|
|
|
let arguments = [element.clone(), to_value(i), this.clone()]; |
|
|
|
|
let arguments = [element.clone(), Value::from(i), this.clone()]; |
|
|
|
|
let result = interpreter.call(callback, &mut this_arg, &arguments)?; |
|
|
|
|
if result.is_true() { |
|
|
|
|
return Ok(element); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Ok(Gc::new(ValueData::Undefined)) |
|
|
|
|
Ok(Value::undefined()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.findIndex( predicate [ , thisArg ] )`
|
|
|
|
@ -734,33 +714,29 @@ pub fn find(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) ->
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
|
|
|
|
|
pub fn find_index(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
return Err(to_value( |
|
|
|
|
"Missing argument for Array.prototype.findIndex".to_string(), |
|
|
|
|
return Err(Value::from( |
|
|
|
|
"Missing argument for Array.prototype.findIndex", |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let predicate_arg = args.get(0).expect("Could not get `predicate` argument."); |
|
|
|
|
|
|
|
|
|
let mut this_arg = args |
|
|
|
|
.get(1) |
|
|
|
|
.cloned() |
|
|
|
|
.unwrap_or_else(|| Gc::new(ValueData::Undefined)); |
|
|
|
|
let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); |
|
|
|
|
|
|
|
|
|
let length: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not get `length` property."); |
|
|
|
|
let length = i32::from(&this.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
for i in 0..length { |
|
|
|
|
let element = this.get_field_slice(&i.to_string()); |
|
|
|
|
let arguments = [element, to_value(i), this.clone()]; |
|
|
|
|
let arguments = [element, Value::from(i), this.clone()]; |
|
|
|
|
|
|
|
|
|
let result = interpreter.call(predicate_arg, &mut this_arg, &arguments)?; |
|
|
|
|
|
|
|
|
|
if result.is_true() { |
|
|
|
|
return Ok(Gc::new(ValueData::Rational(f64::from(i)))); |
|
|
|
|
return Ok(Value::rational(f64::from(i))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(Gc::new(ValueData::Rational(f64::from(-1)))) |
|
|
|
|
Ok(Value::rational(-1_f64)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.fill( value[, start[, end]] )`
|
|
|
|
@ -775,8 +751,8 @@ pub fn find_index(this: &mut Value, args: &[Value], interpreter: &mut Interprete
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
|
|
|
|
|
pub fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
|
let len: i32 = from_value(this.get_field_slice("length")).expect("Could not get argument"); |
|
|
|
|
let default_value = undefined(); |
|
|
|
|
let len: i32 = i32::from(&this.get_field_slice("length")); |
|
|
|
|
let default_value = Value::undefined(); |
|
|
|
|
let value = args.get(0).unwrap_or(&default_value); |
|
|
|
|
let relative_start = args.get(1).unwrap_or(&default_value).to_number() as i32; |
|
|
|
|
let relative_end_val = args.get(2).unwrap_or(&default_value); |
|
|
|
@ -814,23 +790,19 @@ pub fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
|
|
|
|
|
pub fn includes_value(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { |
|
|
|
|
let search_element = args |
|
|
|
|
.get(0) |
|
|
|
|
.cloned() |
|
|
|
|
.unwrap_or_else(|| Gc::new(ValueData::Undefined)); |
|
|
|
|
let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined); |
|
|
|
|
|
|
|
|
|
let length: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not get `length` property."); |
|
|
|
|
let length = i32::from(&this.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
for idx in 0..length { |
|
|
|
|
let check_element = this.get_field_slice(&idx.to_string()).clone(); |
|
|
|
|
|
|
|
|
|
if check_element == search_element { |
|
|
|
|
return Ok(to_value(true)); |
|
|
|
|
return Ok(Value::from(true)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(to_value(false)) |
|
|
|
|
Ok(Value::from(false)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Array.prototype.slice( [begin[, end]] )`
|
|
|
|
@ -849,15 +821,14 @@ pub fn includes_value(this: &mut Value, args: &[Value], _: &mut Interpreter) ->
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
|
|
|
|
|
pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { |
|
|
|
|
let new_array = new_array(interpreter)?; |
|
|
|
|
let len: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); |
|
|
|
|
let len = i32::from(&this.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
let start = match args.get(0) { |
|
|
|
|
Some(v) => from_value(v.clone()).expect("failed to parse argument for Array method"), |
|
|
|
|
Some(v) => i32::from(v), |
|
|
|
|
None => 0, |
|
|
|
|
}; |
|
|
|
|
let end = match args.get(1) { |
|
|
|
|
Some(v) => from_value(v.clone()).expect("failed to parse argument for Array method"), |
|
|
|
|
Some(v) => i32::from(v), |
|
|
|
|
None => len, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -881,7 +852,7 @@ pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) ->
|
|
|
|
|
); |
|
|
|
|
new_array_len = new_array_len.wrapping_add(1); |
|
|
|
|
} |
|
|
|
|
new_array.set_field_slice("length", to_value(new_array_len)); |
|
|
|
|
new_array.set_field_slice("length", Value::from(new_array_len)); |
|
|
|
|
Ok(new_array) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -898,16 +869,15 @@ pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) ->
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
|
|
|
|
|
pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
return Err(to_value( |
|
|
|
|
return Err(Value::from( |
|
|
|
|
"missing argument 0 when calling function Array.prototype.filter", |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let callback = args.get(0).cloned().unwrap_or_else(undefined); |
|
|
|
|
let mut this_val = args.get(1).cloned().unwrap_or_else(undefined); |
|
|
|
|
let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); |
|
|
|
|
let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); |
|
|
|
|
|
|
|
|
|
let length: i32 = |
|
|
|
|
from_value(this.get_field_slice("length")).expect("Could not get `length` property."); |
|
|
|
|
let length = i32::from(&this.get_field_slice("length")); |
|
|
|
|
|
|
|
|
|
let new = new_array(&interpreter)?; |
|
|
|
|
|
|
|
|
@ -915,11 +885,11 @@ pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -
|
|
|
|
|
.filter_map(|idx| { |
|
|
|
|
let element = this.get_field_slice(&idx.to_string()); |
|
|
|
|
|
|
|
|
|
let args = [element.clone(), to_value(idx), new.clone()]; |
|
|
|
|
let args = [element.clone(), Value::from(idx), new.clone()]; |
|
|
|
|
|
|
|
|
|
let callback_result = interpreter |
|
|
|
|
.call(&callback, &mut this_val, &args) |
|
|
|
|
.unwrap_or_else(|_| undefined()); |
|
|
|
|
.unwrap_or_else(|_| Value::undefined()); |
|
|
|
|
|
|
|
|
|
if callback_result.is_true() { |
|
|
|
|
Some(element) |
|
|
|
@ -949,42 +919,42 @@ pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
|
|
|
|
|
pub fn some(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
return Err(to_value( |
|
|
|
|
"missing callback when calling function Array.prototype.some".to_string(), |
|
|
|
|
return Err(Value::from( |
|
|
|
|
"missing callback when calling function Array.prototype.some", |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
let callback = &args[0]; |
|
|
|
|
let mut this_arg = if args.len() > 1 { |
|
|
|
|
args[1].clone() |
|
|
|
|
} else { |
|
|
|
|
Gc::new(ValueData::Undefined) |
|
|
|
|
Value::undefined() |
|
|
|
|
}; |
|
|
|
|
let mut i = 0; |
|
|
|
|
let max_len: i32 = from_value(this.get_field_slice("length")).unwrap(); |
|
|
|
|
let max_len = i32::from(&this.get_field_slice("length")); |
|
|
|
|
let mut len = max_len; |
|
|
|
|
while i < len { |
|
|
|
|
let element = this.get_field_slice(&i.to_string()); |
|
|
|
|
let arguments = [element, to_value(i), this.clone()]; |
|
|
|
|
let arguments = [element, Value::from(i), this.clone()]; |
|
|
|
|
let result = interpreter |
|
|
|
|
.call(callback, &mut this_arg, &arguments)? |
|
|
|
|
.is_true(); |
|
|
|
|
if result { |
|
|
|
|
return Ok(to_value(true)); |
|
|
|
|
return Ok(Value::from(true)); |
|
|
|
|
} |
|
|
|
|
// the length of the array must be updated because the callback can mutate it.
|
|
|
|
|
len = min(max_len, from_value(this.get_field_slice("length")).unwrap()); |
|
|
|
|
len = min(max_len, i32::from(&this.get_field_slice("length"))); |
|
|
|
|
i += 1; |
|
|
|
|
} |
|
|
|
|
Ok(to_value(false)) |
|
|
|
|
Ok(Value::from(false)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Create a new `Array` object.
|
|
|
|
|
pub fn create(global: &Value) -> Value { |
|
|
|
|
// Create prototype
|
|
|
|
|
let prototype = ValueData::new_obj(None); |
|
|
|
|
let length = Property::default().value(to_value(0_i32)); |
|
|
|
|
let prototype = Value::new_object(None); |
|
|
|
|
let length = Property::default().value(Value::from(0)); |
|
|
|
|
|
|
|
|
|
prototype.set_prop_slice("length", length); |
|
|
|
|
prototype.set_property_slice("length", length); |
|
|
|
|
|
|
|
|
|
make_builtin_fn!(concat, named "concat", with length 1, of prototype); |
|
|
|
|
make_builtin_fn!(push, named "push", with length 1, of prototype); |
|
|
|
|