From c39081ede56ae65c47d1a7ba1ff2e10879749fd5 Mon Sep 17 00:00:00 2001 From: tofpie Date: Sun, 20 Dec 2020 13:32:41 +0100 Subject: [PATCH 1/9] Implement property accessors --- boa/src/builtins/array/array_iterator.rs | 12 +- boa/src/builtins/array/mod.rs | 231 ++++++++++-------- boa/src/builtins/error/eval.rs | 2 +- boa/src/builtins/error/mod.rs | 6 +- boa/src/builtins/error/range.rs | 2 +- boa/src/builtins/error/reference.rs | 2 +- boa/src/builtins/error/syntax.rs | 2 +- boa/src/builtins/error/type.rs | 2 +- boa/src/builtins/error/uri.rs | 2 +- boa/src/builtins/function/mod.rs | 10 +- boa/src/builtins/iterable/mod.rs | 2 +- boa/src/builtins/json/mod.rs | 20 +- boa/src/builtins/json/tests.rs | 41 +++- boa/src/builtins/map/map_iterator.rs | 4 +- boa/src/builtins/map/mod.rs | 43 ++-- boa/src/builtins/object/mod.rs | 7 +- boa/src/builtins/object/tests.rs | 1 - boa/src/builtins/regexp/mod.rs | 12 +- boa/src/builtins/string/mod.rs | 2 +- boa/src/builtins/string/string_iterator.rs | 4 +- boa/src/context.rs | 110 +++++---- .../environment/object_environment_record.rs | 6 +- boa/src/exec/tests.rs | 2 +- boa/src/object/gcobject.rs | 28 ++- boa/src/object/internal_methods.rs | 29 ++- boa/src/property/mod.rs | 6 +- boa/src/realm.rs | 3 +- boa/src/syntax/ast/node/call/mod.rs | 12 +- .../declaration/arrow_function_decl/mod.rs | 2 +- .../ast/node/declaration/function_decl/mod.rs | 4 +- .../ast/node/declaration/function_expr/mod.rs | 4 +- .../ast/node/field/get_const_field/mod.rs | 2 +- .../syntax/ast/node/field/get_field/mod.rs | 2 +- boa/src/syntax/ast/node/object/mod.rs | 50 +++- .../syntax/ast/node/operator/assign/mod.rs | 4 +- .../syntax/ast/node/operator/bin_op/mod.rs | 4 +- boa/src/value/display.rs | 27 +- boa/src/value/mod.rs | 44 ++-- boa/src/value/tests.rs | 16 +- 39 files changed, 467 insertions(+), 295 deletions(-) diff --git a/boa/src/builtins/array/array_iterator.rs b/boa/src/builtins/array/array_iterator.rs index 8a7bf513544..d46f79da8b1 100644 --- a/boa/src/builtins/array/array_iterator.rs +++ b/boa/src/builtins/array/array_iterator.rs @@ -50,7 +50,7 @@ impl ArrayIterator { array: Value, kind: ArrayIterationKind, ) -> Result { - let array_iterator = Value::new_object(Some(context.global_object())); + let array_iterator = Value::new_object(Some(context.global_object()), context); array_iterator.set_data(ObjectData::ArrayIterator(Self::new(array, kind))); array_iterator .as_object() @@ -77,7 +77,7 @@ impl ArrayIterator { } let len = array_iterator .array - .get_field("length") + .get_field("length", context)? .as_number() .ok_or_else(|| context.construct_type_error("Not an array"))? as u32; @@ -91,13 +91,13 @@ impl ArrayIterator { Ok(create_iter_result_object(context, index.into(), false)) } ArrayIterationKind::Value => { - let element_value = array_iterator.array.get_field(index); + let element_value = array_iterator.array.get_field(index, context)?; Ok(create_iter_result_object(context, element_value, false)) } ArrayIterationKind::KeyAndValue => { - let element_value = array_iterator.array.get_field(index); + let element_value = array_iterator.array.get_field(index, context)?; let result = Array::constructor( - &Value::new_object(Some(context.global_object())), + &Value::new_object(Some(context.global_object()), context), &[index.into(), element_value], context, )?; @@ -123,7 +123,7 @@ impl ArrayIterator { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); // Create prototype - let array_iterator = Value::new_object(Some(global)); + let array_iterator = Value::new_object(Some(global), context); make_builtin_fn(Self::next, "next", &array_iterator, 0, context); array_iterator .as_object() diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 2cf29826309..ab3c090f912 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -17,7 +17,7 @@ use crate::{ builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator}, builtins::BuiltIn, gc::GcObject, - object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE}, + object::{ConstructorBuilder, FunctionBuilder, ObjectData}, property::{Attribute, DataDescriptor}, value::{same_value_zero, IntegerOrInfinity, Value}, BoaProfiler, Context, Result, @@ -144,13 +144,17 @@ impl Array { 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)); + array.set_field(0, Value::from(length), context)?; + array.set_field("length", Value::from(1), context)?; } else { if length.is_double() { return context.throw_range_error("Invalid array length"); } - array.set_field("length", Value::from(length.to_u32(context).unwrap())); + array.set_field( + "length", + Value::from(length.to_u32(context).unwrap()), + context, + )?; } Ok(array) @@ -172,7 +176,7 @@ impl Array { let array = Array::array_create(this, items_len, Some(prototype), context)?; for (k, item) in items.iter().enumerate() { - array.set_field(k, item.clone()); + array.set_field(k, item.clone(), context)?; } Ok(array) @@ -213,13 +217,16 @@ impl Array { /// Creates a new `Array` instance. pub(crate) fn new_array(context: &Context) -> Result { - let array = Value::new_object(Some( - &context - .realm() - .environment - .get_global_object() - .expect("Could not get global object"), - )); + let array = Value::new_object( + Some( + &context + .realm() + .environment + .get_global_object() + .expect("Could not get global object"), + ), + context, + ); array.set_data(ObjectData::Array); array .as_object() @@ -245,7 +252,7 @@ impl Array { let array_obj_ptr = array_obj.clone(); // Wipe existing contents of the array object - let orig_length = array_obj.get_field("length").to_length(context)?; + let orig_length = array_obj.get_field("length", context)?.to_length(context)?; for n in 0..orig_length { array_obj_ptr.remove_property(n); } @@ -258,7 +265,7 @@ impl Array { array_obj_ptr.set_property("length".to_string(), length); for (n, value) in array_contents.iter().enumerate() { - array_obj_ptr.set_field(n, value); + array_obj_ptr.set_field(n, value, context)?; } Ok(array_obj_ptr) } @@ -270,17 +277,18 @@ impl Array { add_values: &[Value], context: &mut Context, ) -> Result { - let orig_length = array_ptr.get_field("length").to_length(context)?; + let orig_length = array_ptr.get_field("length", context)?.to_length(context)?; for (n, value) in add_values.iter().enumerate() { let new_index = orig_length.wrapping_add(n); - array_ptr.set_field(new_index, value); + array_ptr.set_field(new_index, value, context)?; } array_ptr.set_field( "length", Value::from(orig_length.wrapping_add(add_values.len())), - ); + context, + )?; Ok(array_ptr.clone()) } @@ -325,15 +333,17 @@ impl Array { // one) let mut new_values: Vec = Vec::new(); - let this_length = this.get_field("length").to_length(context)?; + let this_length = this.get_field("length", context)?.to_length(context)?; for n in 0..this_length { - new_values.push(this.get_field(n)); + new_values.push(this.get_field(n, context)?); } for concat_array in args { - let concat_length = concat_array.get_field("length").to_length(context)?; + let concat_length = concat_array + .get_field("length", context)? + .to_length(context)?; for n in 0..concat_length { - new_values.push(concat_array.get_field(n)); + new_values.push(concat_array.get_field(n, context)?); } } @@ -354,7 +364,7 @@ impl Array { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push pub(crate) fn push(this: &Value, args: &[Value], context: &mut Context) -> Result { let new_array = Self::add_to_array_object(this, args, context)?; - Ok(new_array.get_field("length")) + Ok(new_array.get_field("length", context)?) } /// `Array.prototype.pop()` @@ -368,15 +378,15 @@ impl Array { /// [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(crate) fn pop(this: &Value, _: &[Value], context: &mut Context) -> Result { - let curr_length = this.get_field("length").to_length(context)?; + let curr_length = this.get_field("length", context)?.to_length(context)?; if curr_length < 1 { return Ok(Value::undefined()); } let pop_index = curr_length.wrapping_sub(1); - let pop_value: Value = this.get_field(pop_index.to_string()); + let pop_value: Value = this.get_field(pop_index.to_string(), context)?; this.remove_property(pop_index); - this.set_field("length", Value::from(pop_index)); + this.set_field("length", Value::from(pop_index), context)?; Ok(pop_value) } @@ -398,10 +408,10 @@ impl Array { let callback_arg = args.get(0).expect("Could not get `callbackFn` argument."); let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length = this.get_field("length").to_length(context)?; + let length = this.get_field("length", context)?.to_length(context)?; for i in 0..length { - let element = this.get_field(i); + let element = this.get_field(i, context)?; let arguments = [element, Value::from(i), this.clone()]; context.call(callback_arg, &this_arg, &arguments)?; @@ -433,9 +443,9 @@ impl Array { }; let mut elem_strs = Vec::new(); - let length = this.get_field("length").to_length(context)?; + let length = this.get_field("length", context)?.to_length(context)?; for n in 0..length { - let elem_str = this.get_field(n).to_string(context)?.to_string(); + let elem_str = this.get_field(n, context)?.to_string(context)?.to_string(); elem_strs.push(elem_str); } @@ -459,14 +469,16 @@ impl Array { let method_name = "join"; let mut arguments = vec![Value::from(",")]; // 2. - let mut method = this.get_field(method_name); + let mut method = this.get_field(method_name, context)?; // 3. if !method.is_function() { - method = context - .global_object() - .get_field("Object") - .get_field(PROTOTYPE) - .get_field("toString"); + let object_prototype: Value = context + .standard_objects() + .object_object() + .prototype() + .clone() + .into(); + method = object_prototype.get_field("toString", context)?; arguments = Vec::new(); } @@ -495,7 +507,7 @@ impl Array { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse #[allow(clippy::else_if_without_else)] pub(crate) fn reverse(this: &Value, _: &[Value], context: &mut Context) -> Result { - let len = this.get_field("length").to_length(context)?; + let len = this.get_field("length", context)?.to_length(context)?; let middle = len.wrapping_div(2); @@ -505,17 +517,17 @@ impl Array { let upper_exists = this.has_field(upper); let lower_exists = this.has_field(lower); - let upper_value = this.get_field(upper); - let lower_value = this.get_field(lower); + let upper_value = this.get_field(upper, context)?; + let lower_value = this.get_field(lower, context)?; if upper_exists && lower_exists { - this.set_field(upper, lower_value); - this.set_field(lower, upper_value); + this.set_field(upper, lower_value, context)?; + this.set_field(lower, upper_value, context)?; } else if upper_exists { - this.set_field(lower, upper_value); + this.set_field(lower, upper_value, context)?; this.remove_property(upper); } else if lower_exists { - this.set_field(upper, lower_value); + this.set_field(upper, lower_value, context)?; this.remove_property(lower); } } @@ -534,30 +546,30 @@ impl Array { /// [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(crate) fn shift(this: &Value, _: &[Value], context: &mut Context) -> Result { - let len = this.get_field("length").to_length(context)?; + let len = this.get_field("length", context)?.to_length(context)?; if len == 0 { - this.set_field("length", 0); + this.set_field("length", 0, context)?; return Ok(Value::undefined()); } - let first: Value = this.get_field(0); + let first: Value = this.get_field(0, context)?; for k in 1..len { let from = k; let to = k.wrapping_sub(1); - let from_value = this.get_field(from); + let from_value = this.get_field(from, context)?; if from_value.is_undefined() { this.remove_property(to); } else { - this.set_field(to, from_value); + this.set_field(to, from_value, context)?; } } let final_index = len.wrapping_sub(1); this.remove_property(final_index); - this.set_field("length", Value::from(final_index)); + this.set_field("length", Value::from(final_index), context)?; Ok(first) } @@ -575,7 +587,7 @@ impl Array { /// [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(crate) fn unshift(this: &Value, args: &[Value], context: &mut Context) -> Result { - let len = this.get_field("length").to_length(context)?; + let len = this.get_field("length", context)?.to_length(context)?; let arg_c = args.len(); @@ -584,20 +596,24 @@ impl Array { let from = k.wrapping_sub(1); let to = k.wrapping_add(arg_c).wrapping_sub(1); - let from_value = this.get_field(from); + let from_value = this.get_field(from, context)?; if from_value.is_undefined() { this.remove_property(to); } else { - this.set_field(to, from_value); + this.set_field(to, from_value, context)?; } } for j in 0..arg_c { - this.set_field(j, args.get(j).expect("Could not get argument").clone()); + this.set_field( + j, + args.get(j).expect("Could not get argument").clone(), + context, + )?; } } let temp = len.wrapping_add(arg_c); - this.set_field("length", Value::from(temp)); + this.set_field("length", Value::from(temp), context)?; Ok(Value::from(temp)) } @@ -627,16 +643,19 @@ impl Array { Value::undefined() }; let mut i = 0; - let max_len = this.get_field("length").to_length(context)?; + let max_len = this.get_field("length", context)?.to_length(context)?; let mut len = max_len; while i < len { - let element = this.get_field(i); + let element = this.get_field(i, context)?; let arguments = [element, Value::from(i), this.clone()]; let result = context.call(callback, &this_arg, &arguments)?; if !result.to_boolean() { return Ok(Value::from(false)); } - len = min(max_len, this.get_field("length").to_length(context)?); + len = min( + max_len, + this.get_field("length", context)?.to_length(context)?, + ); i += 1; } Ok(Value::from(true)) @@ -663,7 +682,7 @@ impl Array { let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); let this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length = this.get_field("length").to_length(context)?; + let length = this.get_field("length", context)?.to_length(context)?; if length > 2usize.pow(32) - 1 { return context.throw_range_error("Invalid array length"); @@ -671,16 +690,14 @@ impl Array { let new = Self::new_array(context)?; - let values: Vec = (0..length) + let values = (0..length) .map(|idx| { - let element = this.get_field(idx); + let element = this.get_field(idx, context)?; let args = [element, Value::from(idx), new.clone()]; - context - .call(&callback, &this_val, &args) - .unwrap_or_else(|_| Value::undefined()) + context.call(&callback, &this_val, &args) }) - .collect(); + .collect::>>()?; Self::construct_array(&new, &values, context) } @@ -711,7 +728,7 @@ impl Array { } let search_element = args[0].clone(); - let len = this.get_field("length").to_length(context)?; + let len = this.get_field("length", context)?.to_length(context)?; let mut idx = match args.get(1) { Some(from_idx_ptr) => { @@ -731,7 +748,7 @@ impl Array { }; while idx < len { - let check_element = this.get_field(idx).clone(); + let check_element = this.get_field(idx, context)?.clone(); if check_element.strict_equals(&search_element) { return Ok(Value::from(idx)); @@ -773,7 +790,7 @@ impl Array { let search_element = args[0].clone(); let len: isize = this - .get_field("length") + .get_field("length", context)? .to_length(context)? .try_into() .map_err(interror_to_value)?; @@ -794,7 +811,7 @@ impl Array { }; while idx >= 0 { - let check_element = this.get_field(idx).clone(); + let check_element = this.get_field(idx, context)?.clone(); if check_element.strict_equals(&search_element) { return Ok(Value::from(i32::try_from(idx).map_err(interror_to_value)?)); @@ -826,9 +843,9 @@ impl Array { } let callback = &args[0]; let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); - let len = this.get_field("length").to_length(context)?; + let len = this.get_field("length", context)?.to_length(context)?; for i in 0..len { - let element = this.get_field(i); + let element = this.get_field(i, context)?; let arguments = [element.clone(), Value::from(i), this.clone()]; let result = context.call(callback, &this_arg, &arguments)?; if result.to_boolean() { @@ -861,10 +878,10 @@ impl Array { let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length = this.get_field("length").to_length(context)?; + let length = this.get_field("length", context)?.to_length(context)?; for i in 0..length { - let element = this.get_field(i); + let element = this.get_field(i, context)?; let arguments = [element, Value::from(i), this.clone()]; let result = context.call(predicate_arg, &this_arg, &arguments)?; @@ -890,7 +907,7 @@ impl Array { /// [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(crate) fn fill(this: &Value, args: &[Value], context: &mut Context) -> Result { - let len = this.get_field("length").to_length(context)?; + let len = this.get_field("length", context)?.to_length(context)?; let default_value = Value::undefined(); let value = args.get(0).unwrap_or(&default_value); @@ -898,7 +915,7 @@ impl Array { let fin = Self::get_relative_end(context, args.get(2), len)?; for i in start..fin { - this.set_field(i, value.clone()); + this.set_field(i, value.clone(), context)?; } Ok(this.clone()) @@ -921,10 +938,10 @@ impl Array { ) -> Result { let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined); - let length = this.get_field("length").to_length(context)?; + let length = this.get_field("length", context)?.to_length(context)?; for idx in 0..length { - let check_element = this.get_field(idx).clone(); + let check_element = this.get_field(idx, context)?.clone(); if same_value_zero(&check_element, &search_element) { return Ok(Value::from(true)); @@ -951,17 +968,17 @@ impl Array { pub(crate) fn slice(this: &Value, args: &[Value], context: &mut Context) -> Result { let new_array = Self::new_array(context)?; - let len = this.get_field("length").to_length(context)?; + let len = this.get_field("length", context)?.to_length(context)?; let from = Self::get_relative_start(context, args.get(0), len)?; let to = Self::get_relative_end(context, args.get(1), len)?; let span = max(to.saturating_sub(from), 0); let mut new_array_len: i32 = 0; for i in from..from.saturating_add(span) { - new_array.set_field(new_array_len, this.get_field(i)); + new_array.set_field(new_array_len, this.get_field(i, context)?, context)?; new_array_len = new_array_len.saturating_add(1); } - new_array.set_field("length", Value::from(new_array_len)); + new_array.set_field("length", Value::from(new_array_len), context)?; Ok(new_array) } @@ -986,27 +1003,26 @@ impl Array { let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); let this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length = this.get_field("length").to_length(context)?; + let length = this.get_field("length", context)?.to_length(context)?; let new = Self::new_array(context)?; let values = (0..length) - .filter_map(|idx| { - let element = this.get_field(idx); + .map(|idx| { + let element = this.get_field(idx, context)?; let args = [element.clone(), Value::from(idx), new.clone()]; - let callback_result = context - .call(&callback, &this_val, &args) - .unwrap_or_else(|_| Value::undefined()); + let callback_result = context.call(&callback, &this_val, &args)?; if callback_result.to_boolean() { - Some(element) + Ok(Some(element)) } else { - None + Ok(None) } }) - .collect::>(); + .collect::>>>()?; + let values = values.into_iter().filter_map(|v| v).collect::>(); Self::construct_array(&new, &values, context) } @@ -1039,17 +1055,20 @@ impl Array { Value::undefined() }; let mut i = 0; - let max_len = this.get_field("length").to_length(context)?; + let max_len = this.get_field("length", context)?.to_length(context)?; let mut len = max_len; while i < len { - let element = this.get_field(i); + let element = this.get_field(i, context)?; let arguments = [element, Value::from(i), this.clone()]; let result = context.call(callback, &this_arg, &arguments)?; if result.to_boolean() { return Ok(Value::from(true)); } // the length of the array must be updated because the callback can mutate it. - len = min(max_len, this.get_field("length").to_length(context)?); + len = min( + max_len, + this.get_field("length", context)?.to_length(context)?, + ); i += 1; } Ok(Value::from(false)) @@ -1073,7 +1092,7 @@ impl Array { _ => return context.throw_type_error("Reduce was called without a callback"), }; let initial_value = args.get(1).cloned().unwrap_or_else(Value::undefined); - let mut length = this.get_field("length").to_length(context)?; + let mut length = this.get_field("length", context)?.to_length(context)?; if length == 0 && initial_value.is_undefined() { return context .throw_type_error("Reduce was called on an empty array and with no initial value"); @@ -1093,7 +1112,7 @@ impl Array { "Reduce was called on an empty array and with no initial value", ); } - let result = this.get_field(k); + let result = this.get_field(k, context)?; k += 1; result } else { @@ -1101,12 +1120,20 @@ impl Array { }; while k < length { if this.has_field(k) { - let arguments = [accumulator, this.get_field(k), Value::from(k), this.clone()]; + let arguments = [ + accumulator, + this.get_field(k, context)?, + Value::from(k), + this.clone(), + ]; accumulator = context.call(&callback, &Value::undefined(), &arguments)?; /* We keep track of possibly shortened length in order to prevent unnecessary iteration. It may also be necessary to do this since shortening the array length does not delete array elements. See: https://github.com/boa-dev/boa/issues/557 */ - length = min(length, this.get_field("length").to_length(context)?); + length = min( + length, + this.get_field("length", context)?.to_length(context)?, + ); } k += 1; } @@ -1135,7 +1162,7 @@ impl Array { _ => return context.throw_type_error("reduceRight was called without a callback"), }; let initial_value = args.get(1).cloned().unwrap_or_else(Value::undefined); - let mut length = this.get_field("length").to_length(context)?; + let mut length = this.get_field("length", context)?.to_length(context)?; if length == 0 { return if initial_value.is_undefined() { context.throw_type_error( @@ -1165,7 +1192,7 @@ impl Array { "reduceRight was called on an empty array and with no initial value", ); } - let result = this.get_field(k); + let result = this.get_field(k, context)?; k = k.overflowing_sub(1).0; result } else { @@ -1174,12 +1201,20 @@ impl Array { // usize::MAX is bigger than the maximum array size so we can use it check for integer undeflow while k != usize::MAX { if this.has_field(k) { - let arguments = [accumulator, this.get_field(k), Value::from(k), this.clone()]; + let arguments = [ + accumulator, + this.get_field(k, context)?, + Value::from(k), + this.clone(), + ]; accumulator = context.call(&callback, &Value::undefined(), &arguments)?; /* We keep track of possibly shortened length in order to prevent unnecessary iteration. It may also be necessary to do this since shortening the array length does not delete array elements. See: https://github.com/boa-dev/boa/issues/557 */ - length = min(length, this.get_field("length").to_length(context)?); + length = min( + length, + this.get_field("length", context)?.to_length(context)?, + ); // move k to the last defined element if necessary or return if the length was set to 0 if k >= length { diff --git a/boa/src/builtins/error/eval.rs b/boa/src/builtins/error/eval.rs index fb2d4a6b548..e1ca438670f 100644 --- a/boa/src/builtins/error/eval.rs +++ b/boa/src/builtins/error/eval.rs @@ -62,7 +62,7 @@ impl EvalError { context: &mut Context, ) -> Result { if let Some(message) = args.get(0) { - this.set_field("message", message.to_string(context)?); + this.set_field("message", message.to_string(context)?, context)?; } // This value is used by console.log and other routines to match Object type diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index a6e4a0af981..505d51c9d01 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -79,7 +79,7 @@ impl Error { context: &mut Context, ) -> Result { if let Some(message) = args.get(0) { - this.set_field("message", message.to_string(context)?); + this.set_field("message", message.to_string(context)?, context)?; } // This value is used by console.log and other routines to match Object type @@ -103,7 +103,7 @@ impl Error { if !this.is_object() { return context.throw_type_error("'this' is not an Object"); } - let name = this.get_field("name"); + let name = this.get_field("name", context)?; let name_to_string; let name = if name.is_undefined() { "Error" @@ -112,7 +112,7 @@ impl Error { name_to_string.as_str() }; - let message = this.get_field("message"); + let message = this.get_field("message", context)?; let message_to_string; let message = if message.is_undefined() { "" diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index 55e7776894f..0a9211cddf3 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -60,7 +60,7 @@ impl RangeError { context: &mut Context, ) -> Result { if let Some(message) = args.get(0) { - this.set_field("message", message.to_string(context)?); + this.set_field("message", message.to_string(context)?, context)?; } // This value is used by console.log and other routines to match Object type diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index d77a9775bdf..c606d71e696 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -59,7 +59,7 @@ impl ReferenceError { context: &mut Context, ) -> Result { if let Some(message) = args.get(0) { - this.set_field("message", message.to_string(context)?); + this.set_field("message", message.to_string(context)?, context)?; } // This value is used by console.log and other routines to match Object type diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 92ca95d1500..b61f9fde3a0 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/boa/src/builtins/error/syntax.rs @@ -62,7 +62,7 @@ impl SyntaxError { context: &mut Context, ) -> Result { if let Some(message) = args.get(0) { - this.set_field("message", message.to_string(context)?); + this.set_field("message", message.to_string(context)?, context)?; } // This value is used by console.log and other routines to match Object type diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index f450c607e1f..497dc89154a 100644 --- a/boa/src/builtins/error/type.rs +++ b/boa/src/builtins/error/type.rs @@ -65,7 +65,7 @@ impl TypeError { context: &mut Context, ) -> Result { if let Some(message) = args.get(0) { - this.set_field("message", message.to_string(context)?); + this.set_field("message", message.to_string(context)?, context)?; } // This value is used by console.log and other routines to match Object type diff --git a/boa/src/builtins/error/uri.rs b/boa/src/builtins/error/uri.rs index 0fdbc407b77..2d8a944b119 100644 --- a/boa/src/builtins/error/uri.rs +++ b/boa/src/builtins/error/uri.rs @@ -61,7 +61,7 @@ impl UriError { context: &mut Context, ) -> Result { if let Some(message) = args.get(0) { - this.set_field("message", message.to_string(context)?); + this.set_field("message", message.to_string(context)?, context)?; } // This value is used by console.log and other routines to match Object type diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 536a915834c..9463bd37251 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -228,9 +228,11 @@ pub fn make_builtin_fn( let mut function = Object::function( Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), interpreter - .global_object() - .get_field("Function") - .get_field("prototype"), + .standard_objects() + .function_object() + .prototype() + .clone() + .into(), ); function.insert_property("length", length, Attribute::all()); @@ -300,7 +302,7 @@ impl BuiltInFunctionObject { return context.call(this, &this_arg, &[]); } let arg_list = context - .extract_array_properties(&arg_array) + .extract_array_properties(&arg_array)? .map_err(|()| arg_array)?; // TODO?: 5. PrepareForTailCall context.call(this, &this_arg, &arg_list) diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index 0609d2b3569..a7ff2bc89c1 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/boa/src/builtins/iterable/mod.rs @@ -58,7 +58,7 @@ impl IteratorPrototypes { /// /// Generates an object supporting the IteratorResult interface. pub fn create_iter_result_object(context: &mut Context, value: Value, done: bool) -> Value { - let object = Value::new_object(Some(context.global_object())); + let object = Value::new_object(Some(context.global_object()), context); // TODO: Fix attributes of value and done let value_property = DataDescriptor::new(value, Attribute::all()); let done_property = DataDescriptor::new(done, Attribute::all()); diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 000b3378fa1..0511d9d82ed 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -13,6 +13,7 @@ //! [json]: https://www.json.org/json-en.html //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON +use crate::object::Object; use crate::{ builtins::BuiltIn, object::ObjectInitializer, @@ -74,8 +75,8 @@ impl Json { let j = Value::from_json(json, context); match args.get(1) { Some(reviver) if reviver.is_function() => { - let mut holder = Value::new_object(None); - holder.set_field("", j); + let mut holder = Value::new_object(None, context); + holder.set_field("", j, context)?; Self::walk(reviver, context, &mut holder, &PropertyKey::from("")) } _ => Ok(j), @@ -97,7 +98,7 @@ impl Json { holder: &mut Value, key: &PropertyKey, ) -> Result { - let value = holder.get_field(key.clone()); + let value = holder.get_field(key.clone(), context)?; if let Value::Object(ref object) = value { let keys: Vec<_> = object.borrow().keys().collect(); @@ -106,7 +107,7 @@ impl Json { let v = Self::walk(reviver, context, &mut value.clone(), &key); match v { Ok(v) if !v.is_undefined() => { - value.set_field(key, v); + value.set_field(key, v, context)?; } Ok(_) => { value.remove_property(key); @@ -191,7 +192,7 @@ impl Json { object .as_object() .map(|obj| { - let object_to_return = Value::new_object(None); + let object_to_return = Value::object(Object::default()); for (key, val) in obj .borrow() .iter() @@ -224,7 +225,14 @@ impl Json { if key == "length" { None } else { - Some(replacer.get_field(key)) + Some( + replacer + .get_property(key) + .as_ref() + .and_then(|p| p.as_data_descriptor()) + .map(|d| d.value()) + .unwrap_or(Value::undefined()), + ) } }); for field in fields { diff --git a/boa/src/builtins/json/tests.rs b/boa/src/builtins/json/tests.rs index c08c5bba84e..abf4f5a1144 100644 --- a/boa/src/builtins/json/tests.rs +++ b/boa/src/builtins/json/tests.rs @@ -1,4 +1,4 @@ -use crate::{forward, forward_val, object::PROTOTYPE, value::same_value, Context}; +use crate::{forward, forward_val, value::same_value, Context, Value}; #[test] fn json_sanity() { @@ -341,19 +341,35 @@ fn json_parse_array_with_reviver() { ) .unwrap(); assert_eq!( - result.get_field("0").to_number(&mut context).unwrap() as u8, + result + .get_field("0", &mut context) + .unwrap() + .to_number(&mut context) + .unwrap() as u8, 2u8 ); assert_eq!( - result.get_field("1").to_number(&mut context).unwrap() as u8, + result + .get_field("1", &mut context) + .unwrap() + .to_number(&mut context) + .unwrap() as u8, 4u8 ); assert_eq!( - result.get_field("2").to_number(&mut context).unwrap() as u8, + result + .get_field("2", &mut context) + .unwrap() + .to_number(&mut context) + .unwrap() as u8, 6u8 ); assert_eq!( - result.get_field("3").to_number(&mut context).unwrap() as u8, + result + .get_field("3", &mut context) + .unwrap() + .to_number(&mut context) + .unwrap() as u8, 8u8 ); } @@ -405,14 +421,13 @@ fn json_parse_sets_prototypes() { .as_object() .unwrap() .prototype_instance(); - let global_object_prototype = context - .global_object() - .get_field("Object") - .get_field(PROTOTYPE); - let global_array_prototype = context - .global_object() - .get_field("Array") - .get_field(PROTOTYPE); + let global_object_prototype: Value = context + .standard_objects() + .object_object() + .prototype() + .into(); + let global_array_prototype: Value = + context.standard_objects().array_object().prototype().into(); assert_eq!( same_value(&object_prototype, &global_object_prototype), true diff --git a/boa/src/builtins/map/map_iterator.rs b/boa/src/builtins/map/map_iterator.rs index 900484a2586..2d2a582d00a 100644 --- a/boa/src/builtins/map/map_iterator.rs +++ b/boa/src/builtins/map/map_iterator.rs @@ -50,7 +50,7 @@ impl MapIterator { map: Value, kind: MapIterationKind, ) -> Result { - let map_iterator = Value::new_object(Some(context.global_object())); + let map_iterator = Value::new_object(Some(context.global_object()), context); map_iterator.set_data(ObjectData::MapIterator(Self::new(map, kind))); map_iterator .as_object() @@ -143,7 +143,7 @@ impl MapIterator { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); // Create prototype - let map_iterator = Value::new_object(Some(global)); + let map_iterator = Value::new_object(Some(global), context); make_builtin_fn(Self::next, "next", &map_iterator, 0, context); map_iterator .as_object() diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 253d3647920..c743a0f1157 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -2,13 +2,14 @@ use crate::{ builtins::BuiltIn, - object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE}, + object::{ConstructorBuilder, FunctionBuilder, ObjectData}, property::{Attribute, DataDescriptor}, BoaProfiler, Context, Result, Value, }; use ordered_map::OrderedMap; pub mod map_iterator; +use crate::object::PROTOTYPE; use map_iterator::{MapIterationKind, MapIterator}; pub mod ordered_map; @@ -77,8 +78,9 @@ impl Map { // Set Prototype let prototype = context .global_object() - .get_field("Map") - .get_field(PROTOTYPE); + .clone() + .get_field("Map", context)? + .get_field(PROTOTYPE, context)?; this.as_object() .expect("this is map object") @@ -96,14 +98,15 @@ impl Map { map } else if object.is_array() { let mut map = OrderedMap::new(); - let len = args[0].get_field("length").to_integer(context)? as i32; + let len = args[0].get_field("length", context)?.to_integer(context)? as i32; for i in 0..len { - let val = &args[0].get_field(i.to_string()); - let (key, value) = Self::get_key_value(val).ok_or_else(|| { - context.construct_type_error( - "iterable for Map should have array-like objects", - ) - })?; + let val = &args[0].get_field(i.to_string(), context)?; + let (key, value) = + Self::get_key_value(val, context)?.ok_or_else(|| { + context.construct_type_error( + "iterable for Map should have array-like objects", + ) + })?; map.insert(key, value); } map @@ -354,17 +357,21 @@ impl Map { } /// Helper function to get a key-value pair from an array. - fn get_key_value(value: &Value) -> Option<(Value, Value)> { + fn get_key_value(value: &Value, context: &mut Context) -> Result> { if let Value::Object(object) = value { if object.is_array() { - let (key, value) = match value.get_field("length").as_number().unwrap() as i32 { - 0 => (Value::Undefined, Value::Undefined), - 1 => (value.get_field("0"), Value::Undefined), - _ => (value.get_field("0"), value.get_field("1")), - }; - return Some((key, value)); + let (key, value) = + match value.get_field("length", context)?.as_number().unwrap() as i32 { + 0 => (Value::Undefined, Value::Undefined), + 1 => (value.get_field("0", context)?, Value::Undefined), + _ => ( + value.get_field("0", context)?, + value.get_field("1", context)?, + ), + }; + return Ok(Some((key, value))); } } - None + Ok(None) } } diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 026973bfdf3..5cde8237229 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -84,7 +84,7 @@ impl Object { } let global = context.global_object(); - Ok(Value::new_object(Some(global))) + Ok(Value::new_object(Some(global), context)) } /// `Object.create( proto, [propertiesObject] )` @@ -330,7 +330,10 @@ impl Object { } }; - let tag = o.get(&context.well_known_symbols().to_string_tag_symbol().into()); + let tag = o.get( + &context.well_known_symbols().to_string_tag_symbol().into(), + context, + )?; let tag_str = tag.as_string().map(|s| s.as_str()).unwrap_or(builtin_tag); diff --git a/boa/src/builtins/object/tests.rs b/boa/src/builtins/object/tests.rs index 5ab9b313a9d..fcfd06fb5c3 100644 --- a/boa/src/builtins/object/tests.rs +++ b/boa/src/builtins/object/tests.rs @@ -92,7 +92,6 @@ fn object_is() { assert_eq!(forward(&mut context, "Object.is()"), "true"); assert_eq!(forward(&mut context, "Object.is(undefined)"), "true"); assert!(context.global_object().is_global()); - assert!(!context.global_object().get_field("Object").is_global()); } #[test] fn object_has_own_property() { diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 934251a8fe0..070695d20e3 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -314,7 +314,7 @@ impl RegExp { .get(0) .expect("could not get argument") .to_string(context)?; - let mut last_index = this.get_field("lastIndex").to_index(context)?; + let mut last_index = this.get_field("lastIndex", context)?.to_index(context)?; let result = if let Some(object) = this.as_object() { let object = object.borrow(); let regex = object.as_regexp().unwrap(); @@ -334,7 +334,7 @@ impl RegExp { } else { panic!("object is not a regexp") }; - this.set_field("lastIndex", Value::from(last_index)); + this.set_field("lastIndex", Value::from(last_index), context)?; result } @@ -355,7 +355,7 @@ impl RegExp { .get(0) .expect("could not get argument") .to_string(context)?; - let mut last_index = this.get_field("lastIndex").to_index(context)?; + let mut last_index = this.get_field("lastIndex", context)?.to_index(context)?; let result = if let Some(object) = this.as_object() { let object = object.borrow(); let regex = object.as_regexp().unwrap(); @@ -391,7 +391,7 @@ impl RegExp { } else { panic!("object is not a regexp") }; - this.set_field("lastIndex", Value::from(last_index)); + this.set_field("lastIndex", Value::from(last_index), context)?; result } @@ -463,7 +463,7 @@ impl RegExp { /// [spec]: https://tc39.es/ecma262/#sec-regexp-prototype-matchall /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@matchAll // TODO: it's returning an array, it should return an iterator - pub(crate) fn match_all(this: &Value, arg_str: String) -> Result { + pub(crate) fn match_all(this: &Value, arg_str: String, context: &mut Context) -> Result { let matches = if let Some(object) = this.as_object() { let object = object.borrow(); let regex = object.as_regexp().unwrap(); @@ -499,7 +499,7 @@ impl RegExp { let length = matches.len(); let result = Value::from(matches); - result.set_field("length", Value::from(length)); + result.set_field("length", Value::from(length), context)?; result.set_data(ObjectData::Array); Ok(result) diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index 8edf12ba184..a9bdc22f29b 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -1219,7 +1219,7 @@ impl String { ), }?; - RegExp::match_all(&re, this.to_string(context)?.to_string()) + RegExp::match_all(&re, this.to_string(context)?.to_string(), context) } pub(crate) fn iterator(this: &Value, _: &[Value], context: &mut Context) -> Result { diff --git a/boa/src/builtins/string/string_iterator.rs b/boa/src/builtins/string/string_iterator.rs index 12bb25f6629..73c8cebd61c 100644 --- a/boa/src/builtins/string/string_iterator.rs +++ b/boa/src/builtins/string/string_iterator.rs @@ -23,7 +23,7 @@ impl StringIterator { } pub fn create_string_iterator(context: &mut Context, string: Value) -> Result { - let string_iterator = Value::new_object(Some(context.global_object())); + let string_iterator = Value::new_object(Some(context.global_object()), context); string_iterator.set_data(ObjectData::StringIterator(Self::new(string))); string_iterator .as_object() @@ -74,7 +74,7 @@ impl StringIterator { let _timer = BoaProfiler::global().start_event("String Iterator", "init"); // Create prototype - let array_iterator = Value::new_object(Some(global)); + let array_iterator = Value::new_object(Some(global), context); make_builtin_fn(Self::next, "next", &array_iterator, 0, context); array_iterator .as_object() diff --git a/boa/src/context.rs b/boa/src/context.rs index 2a251db8744..1e8cc3b3a11 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -284,11 +284,13 @@ impl Context { /// Construct an empty object. #[inline] - pub fn construct_object(&self) -> GcObject { - let object_prototype = self - .global_object() - .get_field("Object") - .get_field(PROTOTYPE); + pub fn construct_object(&mut self) -> GcObject { + let object_prototype: Value = self + .standard_objects() + .object_object() + .prototype() + .clone() + .into(); GcObject::new(Object::create(object_prototype)) } @@ -449,18 +451,20 @@ impl Context { params: P, body: B, flags: FunctionFlags, - ) -> Value + ) -> Result where P: Into>, B: Into, { - let function_prototype = self - .global_object() - .get_field("Function") - .get_field(PROTOTYPE); + let function_prototype: Value = self + .standard_objects() + .function_object() + .prototype() + .clone() + .into(); // Every new function has a prototype property pre-made - let proto = Value::new_object(Some(self.global_object())); + let proto = Value::new_object(Some(self.global_object()), self); let params = params.into(); let params_len = params.len(); @@ -476,12 +480,12 @@ impl Context { let val = Value::from(new_func); // Set constructor field to the newly created Value (function object) - proto.set_field("constructor", val.clone()); + proto.set_field("constructor", val.clone(), self)?; - val.set_field(PROTOTYPE, proto); - val.set_field("length", Value::from(params_len)); + val.set_field(PROTOTYPE, proto, self)?; + val.set_field("length", Value::from(params_len), self)?; - val + Ok(val) } /// Create a new builin function. @@ -491,20 +495,22 @@ impl Context { length: usize, body: NativeFunction, ) -> Result { - let function_prototype = self - .global_object() - .get_field("Function") - .get_field(PROTOTYPE); + let function_prototype: Value = self + .standard_objects() + .object_object() + .prototype() + .clone() + .into(); // Every new function has a prototype property pre-made - let proto = Value::new_object(Some(self.global_object())); + let proto = Value::new_object(Some(self.global_object()), self); let mut function = GcObject::new(Object::function( Function::BuiltIn(body.into(), FunctionFlags::CALLABLE), function_prototype, )); - function.set(PROTOTYPE.into(), proto); - function.set("length".into(), length.into()); - function.set("name".into(), name.into()); + function.set(PROTOTYPE.into(), proto, self)?; + function.set("length".into(), length.into(), self)?; + function.set("name".into(), name.into(), self)?; Ok(function) } @@ -518,7 +524,11 @@ impl Context { body: NativeFunction, ) -> Result<()> { let function = self.create_builtin_function(name, length, body)?; - self.global_object().set_field(name, function); + let global = self.global_object(); + global + .as_object() + .unwrap() + .insert_property(name, function, Attribute::all()); Ok(()) } @@ -526,15 +536,18 @@ impl Context { /// /// This is useful for the spread operator, for any other object an `Err` is returned /// TODO: Not needed for spread of arrays. Check in the future for Map and remove if necessary - pub(crate) fn extract_array_properties(&mut self, value: &Value) -> StdResult, ()> { + pub(crate) fn extract_array_properties( + &mut self, + value: &Value, + ) -> Result, ()>> { if let Value::Object(ref x) = value { // Check if object is array if let ObjectData::Array = x.borrow().data { - let length = value.get_field("length").as_number().unwrap() as i32; + let length = value.get_field("length", self)?.as_number().unwrap() as i32; let values = (0..length) - .map(|idx| value.get_field(idx.to_string())) - .collect(); - return Ok(values); + .map(|idx| value.get_field(idx.to_string(), self)) + .collect::>>()?; + return Ok(Ok(values)); } // Check if object is a Map else if let ObjectData::Map(ref map) = x.borrow().data { @@ -542,34 +555,37 @@ impl Context { .iter() .map(|(key, value)| { // Construct a new array containing the key-value pair - let array = Value::new_object(Some( - &self - .realm() - .environment - .get_global_object() - .expect("Could not get global object"), - )); + let array = Value::new_object( + Some( + &self + .realm() + .environment + .get_global_object() + .expect("Could not get global object"), + ), + self, + ); array.set_data(ObjectData::Array); array.as_object().expect("object").set_prototype_instance( self.realm() .environment .get_binding_value("Array") .expect("Array was not initialized") - .get_field(PROTOTYPE), + .get_field(PROTOTYPE, self)?, ); - array.set_field("0", key); - array.set_field("1", value); - array.set_field("length", Value::from(2)); - array + array.set_field("0", key, self)?; + array.set_field("1", value, self)?; + array.set_field("length", Value::from(2), self)?; + Ok(array) }) - .collect(); - return Ok(values); + .collect::>>()?; + return Ok(Ok(values)); } - return Err(()); + return Ok(Err(())); } - Err(()) + Ok(Err(())) } /// @@ -594,11 +610,11 @@ impl Context { Node::GetConstField(ref get_const_field_node) => Ok(get_const_field_node .obj() .run(self)? - .set_field(get_const_field_node.field(), value)), + .set_field(get_const_field_node.field(), value, self)?), Node::GetField(ref get_field) => { let field = get_field.field().run(self)?; let key = field.to_property_key(self)?; - Ok(get_field.obj().run(self)?.set_field(key, value)) + Ok(get_field.obj().run(self)?.set_field(key, value, self)?) } _ => panic!("TypeError: invalid assignment to {}", node), } diff --git a/boa/src/environment/object_environment_record.rs b/boa/src/environment/object_environment_record.rs index cad73cfb930..9b7855fceb5 100644 --- a/boa/src/environment/object_environment_record.rs +++ b/boa/src/environment/object_environment_record.rs @@ -6,6 +6,7 @@ //! Property keys that are not strings in the form of an `IdentifierName` are not included in the set of bound identifiers. //! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records) +use crate::property::PropertyDescriptor; use crate::{ environment::{ environment_record_trait::EnvironmentRecordTrait, @@ -73,7 +74,10 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { fn get_binding_value(&self, name: &str, strict: bool) -> Value { if self.bindings.has_field(name) { - self.bindings.get_field(name) + match self.bindings.get_property(name) { + Some(PropertyDescriptor::Data(ref d)) => d.value(), + _ => Value::Undefined, + } } else { if strict { // TODO: throw error here diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 7cf7b0da2fd..e95bbd729fb 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -782,7 +782,7 @@ mod in_operator { let foo_val = forward_val(&mut context, "Foo").unwrap(); assert!(bar_obj .prototype_instance() - .strict_equals(&foo_val.get_field("prototype"))); + .strict_equals(&foo_val.get_field("prototype", &mut context).unwrap())); } } diff --git a/boa/src/object/gcobject.rs b/boa/src/object/gcobject.rs index 6405174fb30..3bdd159f8ad 100644 --- a/boa/src/object/gcobject.rs +++ b/boa/src/object/gcobject.rs @@ -198,7 +198,7 @@ impl GcObject { // #[track_caller] pub fn construct(&self, args: &[Value], context: &mut Context) -> Result { - let this: Value = Object::create(self.get(&PROTOTYPE.into())).into(); + let this: Value = Object::create(self.get(&PROTOTYPE.into(), context)?).into(); let this_function_object = self.clone(); let body = if let Some(function) = self.borrow().as_function() { @@ -254,7 +254,7 @@ impl GcObject { } } } else { - let name = this.get_field("name").display().to_string(); + let name = this.get_field("name", context)?.display().to_string(); return context.throw_type_error(format!("{} is not a constructor", name)); } } else { @@ -332,7 +332,7 @@ impl GcObject { let this = Value::from(self.clone()); for name in &method_names { // a. Let method be ? Get(O, name). - let method: Value = this.get_field(*name); + let method: Value = this.get_field(*name, context)?; // b. If IsCallable(method) is true, then if method.is_function() { // i. Let result be ? Call(method, O). @@ -359,7 +359,7 @@ impl GcObject { let mut arr: Vec = Vec::with_capacity(keys.len()); let this = Value::from(self.clone()); for key in keys { - let value = this.get_field(key); + let value = this.get_field(key, context)?; if value.is_undefined() || value.is_function() || value.is_symbol() { arr.push(JSONValue::Null); } else { @@ -372,7 +372,7 @@ impl GcObject { let this = Value::from(self.clone()); for k in self.borrow().keys() { let key = k.clone(); - let value = this.get_field(k.to_string()); + let value = this.get_field(k.to_string(), context)?; if !value.is_undefined() && !value.is_function() && !value.is_symbol() { new_obj.insert(key.to_string(), value.to_json(context)?); } @@ -390,26 +390,28 @@ impl GcObject { let mut attribute = Attribute::empty(); let enumerable_key = PropertyKey::from("enumerable"); - if self.has_property(&enumerable_key) && self.get(&enumerable_key).to_boolean() { + if self.has_property(&enumerable_key) && self.get(&enumerable_key, context)?.to_boolean() { attribute |= Attribute::ENUMERABLE; } let configurable_key = PropertyKey::from("configurable"); - if self.has_property(&configurable_key) && self.get(&configurable_key).to_boolean() { + if self.has_property(&configurable_key) + && self.get(&configurable_key, context)?.to_boolean() + { attribute |= Attribute::CONFIGURABLE; } let mut value = None; let value_key = PropertyKey::from("value"); if self.has_property(&value_key) { - value = Some(self.get(&value_key)); + value = Some(self.get(&value_key, context)?); } let mut has_writable = false; let writable_key = PropertyKey::from("writable"); if self.has_property(&writable_key) { has_writable = true; - if self.get(&writable_key).to_boolean() { + if self.get(&writable_key, context)?.to_boolean() { attribute |= Attribute::WRITABLE; } } @@ -417,7 +419,7 @@ impl GcObject { let mut get = None; let get_key = PropertyKey::from("get"); if self.has_property(&get_key) { - let getter = self.get(&get_key); + let getter = self.get(&get_key, context)?; match getter { Value::Object(ref object) if object.is_callable() => { get = Some(object.clone()); @@ -433,7 +435,7 @@ impl GcObject { let mut set = None; let set_key = PropertyKey::from("set"); if self.has_property(&set_key) { - let setter = self.get(&set_key); + let setter = self.get(&set_key, context)?; match setter { Value::Object(ref object) if object.is_callable() => { set = Some(object.clone()); @@ -689,7 +691,7 @@ impl GcObject { K: Into, { let key = key.into(); - let value = self.get(&key); + let value = self.get(&key, context)?; if value.is_null_or_undefined() { return Ok(None); @@ -719,7 +721,7 @@ impl GcObject { // Return ? InstanceofOperator(O, BC). if let Some(object) = value.as_object() { - if let Some(prototype) = self.get(&"prototype".into()).as_object() { + if let Some(prototype) = self.get(&"prototype".into(), context)?.as_object() { let mut object = object.get_prototype_of(); while let Some(object_prototype) = object.as_object() { if GcObject::equals(&prototype, &object_prototype) { diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index d8bf656d8f1..e49e850a28d 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -71,28 +71,30 @@ impl GcObject { /// `[[Get]]` /// - pub fn get(&self, key: &PropertyKey) -> Value { + pub fn get(&self, key: &PropertyKey, context: &mut Context) -> Result { match self.get_own_property(key) { None => { // parent will either be null or an Object let parent = self.get_prototype_of(); if parent.is_null() { - return Value::undefined(); + return Ok(Value::undefined()); } - parent.get_field(key.clone()) + Ok(parent.get_field(key.clone(), context)?) } Some(ref desc) => match desc { - PropertyDescriptor::Data(desc) => desc.value(), - // TODO: Add accessors - PropertyDescriptor::Accessor(_) => Value::undefined(), + PropertyDescriptor::Data(desc) => Ok(desc.value()), + PropertyDescriptor::Accessor(AccessorDescriptor { get: Some(get), .. }) => { + get.call(&Value::from(self.clone()), &[], context) + } + _ => Ok(Value::undefined()), }, } } /// `[[Set]]` /// - pub fn set(&mut self, key: PropertyKey, val: Value) -> bool { + pub fn set(&mut self, key: PropertyKey, val: Value, context: &mut Context) -> Result { let _timer = BoaProfiler::global().start_event("Object::set", "object"); // Fetch property key @@ -113,14 +115,17 @@ impl GcObject { match &own_desc { PropertyDescriptor::Data(desc) => { if !desc.writable() { - return false; + return Ok(false); } let desc = DataDescriptor::new(val, own_desc.attributes()).into(); - self.define_own_property(key, desc) + Ok(self.define_own_property(key, desc)) + } + PropertyDescriptor::Accessor(AccessorDescriptor { set: Some(set), .. }) => { + set.call(&Value::from(self.clone()), &[val], context)?; + Ok(true) } - // TODO: Add accessors - PropertyDescriptor::Accessor(_) => false, + _ => Ok(false), } } @@ -260,7 +265,7 @@ impl GcObject { for next_key in keys { if let Some(prop_desc) = props.get_own_property(&next_key) { if prop_desc.enumerable() { - let desc_obj = props.get(&next_key); + let desc_obj = props.get(&next_key, context)?; let desc = desc_obj.to_property_descriptor(context)?; descriptors.push((next_key, desc)); } diff --git a/boa/src/property/mod.rs b/boa/src/property/mod.rs index 7e5db7a3c8d..f3a94def7b7 100644 --- a/boa/src/property/mod.rs +++ b/boa/src/property/mod.rs @@ -118,11 +118,11 @@ impl From for PropertyDescriptor { #[derive(Debug, Clone, Trace, Finalize)] pub struct AccessorDescriptor { /// The function serving as getter. - get: Option, + pub get: Option, /// The function serving as setter. - set: Option, + pub set: Option, /// The attributes of the accessor descriptor. - attributes: Attribute, + pub attributes: Attribute, } impl AccessorDescriptor { diff --git a/boa/src/realm.rs b/boa/src/realm.rs index ef8e87f0c8c..cfa23c38277 100644 --- a/boa/src/realm.rs +++ b/boa/src/realm.rs @@ -4,6 +4,7 @@ //! //! A realm is represented in this implementation as a Realm struct with the fields specified from the spec. +use crate::object::Object; use crate::{ environment::{ declarative_environment_record::DeclarativeEnvironmentRecord, @@ -31,7 +32,7 @@ impl Realm { let _timer = BoaProfiler::global().start_event("Realm::create", "realm"); // Create brand new global object // Global has no prototype to pass None to new_obj - let global = Value::new_object(None); + let global = Value::from(Object::default()); // Allow identification of the global object easily global.set_data(crate::object::ObjectData::Global); diff --git a/boa/src/syntax/ast/node/call/mod.rs b/boa/src/syntax/ast/node/call/mod.rs index dcdd5323704..3640422058d 100644 --- a/boa/src/syntax/ast/node/call/mod.rs +++ b/boa/src/syntax/ast/node/call/mod.rs @@ -65,12 +65,18 @@ impl Executable for Call { if obj.get_type() != Type::Object { obj = Value::Object(obj.to_object(context)?); } - (obj.clone(), obj.get_field(get_const_field.field())) + ( + obj.clone(), + obj.get_field(get_const_field.field(), context)?, + ) } Node::GetField(ref get_field) => { let obj = get_field.obj().run(context)?; let field = get_field.field().run(context)?; - (obj.clone(), obj.get_field(field.to_property_key(context)?)) + ( + obj.clone(), + obj.get_field(field.to_property_key(context)?, context)?, + ) } _ => (context.global_object().clone(), self.expr().run(context)?), // 'this' binding should come from the function's self-contained environment }; @@ -78,7 +84,7 @@ impl Executable for Call { for arg in self.args() { if let Node::Spread(ref x) = arg { let val = x.run(context)?; - let mut vals = context.extract_array_properties(&val).unwrap(); + let mut vals = context.extract_array_properties(&val)?.unwrap(); v_args.append(&mut vals); break; // after spread we don't accept any new arguments } diff --git a/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs index a783234208b..679c173fc91 100644 --- a/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs @@ -74,7 +74,7 @@ impl Executable for ArrowFunctionDecl { FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE | FunctionFlags::LEXICAL_THIS_MODE, - )) + )?) } } diff --git a/boa/src/syntax/ast/node/declaration/function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/function_decl/mod.rs index b31252e7a79..18bb8e1b72d 100644 --- a/boa/src/syntax/ast/node/declaration/function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/function_decl/mod.rs @@ -90,10 +90,10 @@ impl Executable for FunctionDecl { self.parameters().to_vec(), self.body().to_vec(), FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE, - ); + )?; // Set the name and assign it in the current environment - val.set_field("name", self.name()); + val.set_field("name", self.name(), context)?; context.realm_mut().environment.create_mutable_binding( self.name().to_owned(), false, diff --git a/boa/src/syntax/ast/node/declaration/function_expr/mod.rs b/boa/src/syntax/ast/node/declaration/function_expr/mod.rs index 8496697aab3..d3a1a1d978a 100644 --- a/boa/src/syntax/ast/node/declaration/function_expr/mod.rs +++ b/boa/src/syntax/ast/node/declaration/function_expr/mod.rs @@ -90,10 +90,10 @@ impl Executable for FunctionExpr { self.parameters().to_vec(), self.body().to_vec(), FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE, - ); + )?; if let Some(name) = self.name() { - val.set_field("name", Value::from(name)); + val.set_field("name", Value::from(name), context)?; } Ok(val) diff --git a/boa/src/syntax/ast/node/field/get_const_field/mod.rs b/boa/src/syntax/ast/node/field/get_const_field/mod.rs index 43fc3a29259..80287d076e5 100644 --- a/boa/src/syntax/ast/node/field/get_const_field/mod.rs +++ b/boa/src/syntax/ast/node/field/get_const_field/mod.rs @@ -69,7 +69,7 @@ impl Executable for GetConstField { obj = Value::Object(obj.to_object(context)?); } - Ok(obj.get_field(self.field())) + Ok(obj.get_field(self.field(), context)?) } } diff --git a/boa/src/syntax/ast/node/field/get_field/mod.rs b/boa/src/syntax/ast/node/field/get_field/mod.rs index 564ece18b36..e85ac02333a 100644 --- a/boa/src/syntax/ast/node/field/get_field/mod.rs +++ b/boa/src/syntax/ast/node/field/get_field/mod.rs @@ -69,7 +69,7 @@ impl Executable for GetField { } let field = self.field().run(context)?; - Ok(obj.get_field(field.to_property_key(context)?)) + Ok(obj.get_field(field.to_property_key(context)?, context)?) } } diff --git a/boa/src/syntax/ast/node/object/mod.rs b/boa/src/syntax/ast/node/object/mod.rs index 0e72e36d4bf..ab455f660d5 100644 --- a/boa/src/syntax/ast/node/object/mod.rs +++ b/boa/src/syntax/ast/node/object/mod.rs @@ -8,6 +8,7 @@ use crate::{ }; use std::fmt; +use crate::property::{AccessorDescriptor, Attribute, PropertyDescriptor}; #[cfg(feature = "deser")] use serde::{Deserialize, Serialize}; @@ -77,22 +78,53 @@ impl Executable for Object { .environment .get_global_object() .expect("Could not get the global object"); - let obj = Value::new_object(Some(global_val)); + let obj = Value::new_object(Some(global_val), context); // TODO: Implement the rest of the property types. for property in self.properties().iter() { match property { PropertyDefinition::Property(key, value) => { - obj.set_field(key.clone(), value.run(context)?); + obj.set_field(key.clone(), value.run(context)?, context)?; } - PropertyDefinition::MethodDefinition(kind, name, func) => { - if let MethodDefinitionKind::Ordinary = kind { - obj.set_field(name.clone(), func.run(context)?); - } else { - // TODO: Implement other types of MethodDefinitionKinds. - //unimplemented!("other types of property method definitions."); + PropertyDefinition::MethodDefinition(kind, name, func) => match kind { + MethodDefinitionKind::Ordinary => { + obj.set_field(name.clone(), func.run(context)?, context)?; } - } + MethodDefinitionKind::Get => { + let set = obj + .get_property(name.clone()) + .as_ref() + .and_then(|p| p.as_accessor_descriptor()) + .and_then(|a| a.setter().cloned()); + obj.set_property( + name.clone(), + PropertyDescriptor::Accessor(AccessorDescriptor { + get: func.run(context)?.as_object(), + set, + attributes: Attribute::WRITABLE + | Attribute::ENUMERABLE + | Attribute::CONFIGURABLE, + }), + ) + } + MethodDefinitionKind::Set => { + let get = obj + .get_property(name.clone()) + .as_ref() + .and_then(|p| p.as_accessor_descriptor()) + .and_then(|a| a.getter().cloned()); + obj.set_property( + name.clone(), + PropertyDescriptor::Accessor(AccessorDescriptor { + get, + set: func.run(context)?.as_object(), + attributes: Attribute::WRITABLE + | Attribute::ENUMERABLE + | Attribute::CONFIGURABLE, + }), + ) + } + }, _ => {} //unimplemented!("{:?} type of property", i), } } diff --git a/boa/src/syntax/ast/node/operator/assign/mod.rs b/boa/src/syntax/ast/node/operator/assign/mod.rs index 0e4f870826c..980a305b5f5 100644 --- a/boa/src/syntax/ast/node/operator/assign/mod.rs +++ b/boa/src/syntax/ast/node/operator/assign/mod.rs @@ -74,13 +74,13 @@ impl Executable for Assign { } Node::GetConstField(ref get_const_field) => { let val_obj = get_const_field.obj().run(context)?; - val_obj.set_field(get_const_field.field(), val.clone()); + val_obj.set_field(get_const_field.field(), val.clone(), context)?; } Node::GetField(ref get_field) => { let object = get_field.obj().run(context)?; let field = get_field.field().run(context)?; let key = field.to_property_key(context)?; - object.set_field(key, val.clone()); + object.set_field(key, val.clone(), context)?; } _ => (), } diff --git a/boa/src/syntax/ast/node/operator/bin_op/mod.rs b/boa/src/syntax/ast/node/operator/bin_op/mod.rs index 9923d69f835..0681035fc5c 100644 --- a/boa/src/syntax/ast/node/operator/bin_op/mod.rs +++ b/boa/src/syntax/ast/node/operator/bin_op/mod.rs @@ -180,10 +180,10 @@ impl Executable for BinOp { } Node::GetConstField(ref get_const_field) => { let v_r_a = get_const_field.obj().run(context)?; - let v_a = v_r_a.get_field(get_const_field.field()); + let v_a = v_r_a.get_field(get_const_field.field(), context)?; let v_b = self.rhs().run(context)?; let value = Self::run_assign(op, v_a, v_b, context)?; - v_r_a.set_field(get_const_field.field(), value.clone()); + v_r_a.set_field(get_const_field.field(), value.clone(), context)?; Ok(value) } _ => Ok(Value::undefined()), diff --git a/boa/src/value/display.rs b/boa/src/value/display.rs index 93900311291..cfb9998d836 100644 --- a/boa/src/value/display.rs +++ b/boa/src/value/display.rs @@ -48,8 +48,9 @@ macro_rules! print_obj_value { }; (props of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr, $print_internals:expr) => { print_obj_value!(impl $obj, |(key, val)| { + if val.is_data_descriptor() { let v = &val - // FIXME: handle accessor descriptors + .as_data_descriptor() .unwrap() .value(); @@ -60,6 +61,16 @@ macro_rules! print_obj_value { $display_fn(v, $encounters, $indent.wrapping_add(4), $print_internals), width = $indent, ) + } else { + let accessor = val.as_accessor_descriptor().unwrap(); + let display = match (accessor.setter().is_some(), accessor.getter().is_some()) { + (true, true) => "Getter & Setter", + (true, false) => "Setter", + (false, true) => "Getter", + _ => "No Getter/Setter" + }; + format!("{:>width$}: {}", key, display, width = $indent) + } }) }; @@ -185,8 +196,18 @@ pub(crate) fn display_obj(v: &Value, print_internals: bool) -> String { if let Value::Object(object) = v { if object.borrow().is_error() { - let name = v.get_field("name"); - let message = v.get_field("message"); + let name = v + .get_property("name") + .as_ref() + .and_then(|p| p.as_data_descriptor()) + .map(|d| d.value()) + .unwrap_or(Value::undefined()); + let message = v + .get_property("message") + .as_ref() + .and_then(|p| p.as_data_descriptor()) + .map(|d| d.value()) + .unwrap_or(Value::undefined()); return format!("{}: {}", name.display(), message.display()); } } diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index dcc1a64384a..0dfd4003b76 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -10,7 +10,7 @@ use crate::{ number::{f64_to_int32, f64_to_uint32}, BigInt, Number, }, - object::{GcObject, Object, ObjectData, PROTOTYPE}, + object::{GcObject, Object, ObjectData}, property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, BoaProfiler, Context, Result, }; @@ -34,6 +34,7 @@ mod rcstring; mod rcsymbol; mod r#type; +use crate::property::AccessorDescriptor; pub use conversions::*; pub use display::ValueDisplay; pub use equality::*; @@ -158,13 +159,18 @@ impl Value { } /// Returns a new empty object - pub fn new_object(global: Option<&Value>) -> Self { + pub fn new_object(global: Option<&Value>, context: &Context) -> Self { let _timer = BoaProfiler::global().start_event("new_object", "value"); - if let Some(global) = global { - let object_prototype = global.get_field("Object").get_field(PROTOTYPE); - - let object = Object::create(object_prototype); + if global.is_some() { + let object = Object::create( + context + .standard_objects() + .object_object() + .prototype() + .clone() + .into(), + ); Self::object(object) } else { Self::object(Object::default()) @@ -205,7 +211,7 @@ impl Value { new_obj } JSONValue::Object(obj) => { - let new_obj = Value::new_object(Some(context.global_object())); + let new_obj = Value::new_object(Some(context.global_object()), context); for (key, json) in obj.into_iter() { let value = Self::from_json(json, context); new_obj.set_property( @@ -224,7 +230,7 @@ impl Value { /// Converts the `Value` to `JSON`. pub fn to_json(&self, context: &mut Context) -> Result { - let to_json = self.get_field("toJSON"); + let to_json = self.get_field("toJSON", context)?; if to_json.is_function() { let json_value = context.call(&to_json, self, &[])?; return json_value.to_json(context); @@ -456,7 +462,7 @@ impl Value { /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// get_field receives a Property from get_prop(). It should then return the `[[Get]]` result value if that's set, otherwise fall back to `[[Value]]` /// TODO: this function should use the get Value if its set - pub fn get_field(&self, key: K) -> Self + pub fn get_field(&self, key: K, context: &mut Context) -> Result where K: Into, { @@ -464,11 +470,13 @@ impl Value { let key = key.into(); match self.get_property(key) { Some(ref desc) => match desc { - // TODO: Add accessors - PropertyDescriptor::Accessor(_) => Value::undefined(), - PropertyDescriptor::Data(desc) => desc.value(), + PropertyDescriptor::Accessor(AccessorDescriptor { get: Some(get), .. }) => { + get.call(&Value::from(self.clone()), &[], context) + } + PropertyDescriptor::Data(desc) => Ok(desc.value()), + _ => Ok(Value::undefined()), }, - None => Value::undefined(), + None => Ok(Value::undefined()), } } @@ -486,7 +494,7 @@ impl Value { /// Set the field in the value #[inline] - pub fn set_field(&self, key: K, value: V) -> Value + pub fn set_field(&self, key: K, value: V, context: &mut Context) -> Result where K: Into, V: Into, @@ -497,15 +505,15 @@ impl Value { if let Self::Object(ref obj) = *self { if let PropertyKey::Index(index) = key { if obj.is_array() { - let len = self.get_field("length").as_number().unwrap() as u32; + let len = self.get_field("length", context)?.as_number().unwrap() as u32; if len < index + 1 { - self.set_field("length", index + 1); + self.set_field("length", index + 1, context)?; } } } - obj.clone().set(key, value.clone()); + obj.clone().set(key, value.clone(), context)?; } - value + Ok(value) } /// Set the kind of an object. diff --git a/boa/src/value/tests.rs b/boa/src/value/tests.rs index 8cafe5320fd..4eddb975d8f 100644 --- a/boa/src/value/tests.rs +++ b/boa/src/value/tests.rs @@ -8,7 +8,8 @@ use std::hash::{Hash, Hasher}; #[test] fn is_object() { - let val = Value::new_object(None); + let context = Context::new(); + let val = Value::new_object(None, &context); assert_eq!(val.is_object(), true); } @@ -29,11 +30,18 @@ fn undefined() { #[test] fn get_set_field() { - let obj = Value::new_object(None); + let mut context = Context::new(); + let obj = Value::new_object(None, &context); // Create string and convert it to a Value let s = Value::from("bar"); - obj.set_field("foo", s); - assert_eq!(obj.get_field("foo").display().to_string(), "\"bar\""); + obj.set_field("foo", s, &mut context).unwrap(); + assert_eq!( + obj.get_field("foo", &mut context) + .unwrap() + .display() + .to_string(), + "\"bar\"" + ); } #[test] From 51e153617ccf31d0c358dd2a807680b455f540b1 Mon Sep 17 00:00:00 2001 From: tofpie Date: Sun, 20 Dec 2020 16:31:32 +0100 Subject: [PATCH 2/9] Fix clippy issues --- boa/src/builtins/array/mod.rs | 1 - boa/src/builtins/function/mod.rs | 1 - boa/src/builtins/json/mod.rs | 2 +- boa/src/context.rs | 3 --- boa/src/value/display.rs | 4 ++-- boa/src/value/mod.rs | 3 +-- 6 files changed, 4 insertions(+), 10 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index ab3c090f912..b781ed62f47 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -476,7 +476,6 @@ impl Array { .standard_objects() .object_object() .prototype() - .clone() .into(); method = object_prototype.get_field("toString", context)?; diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 9463bd37251..fad4f7f89b3 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -231,7 +231,6 @@ pub fn make_builtin_fn( .standard_objects() .function_object() .prototype() - .clone() .into(), ); function.insert_property("length", length, Attribute::all()); diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 0511d9d82ed..6d53935c2f8 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -231,7 +231,7 @@ impl Json { .as_ref() .and_then(|p| p.as_data_descriptor()) .map(|d| d.value()) - .unwrap_or(Value::undefined()), + .unwrap_or_else(Value::undefined), ) } }); diff --git a/boa/src/context.rs b/boa/src/context.rs index 1e8cc3b3a11..c5966f4a425 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -289,7 +289,6 @@ impl Context { .standard_objects() .object_object() .prototype() - .clone() .into(); GcObject::new(Object::create(object_prototype)) } @@ -460,7 +459,6 @@ impl Context { .standard_objects() .function_object() .prototype() - .clone() .into(); // Every new function has a prototype property pre-made @@ -499,7 +497,6 @@ impl Context { .standard_objects() .object_object() .prototype() - .clone() .into(); // Every new function has a prototype property pre-made diff --git a/boa/src/value/display.rs b/boa/src/value/display.rs index cfb9998d836..aedd5459335 100644 --- a/boa/src/value/display.rs +++ b/boa/src/value/display.rs @@ -201,13 +201,13 @@ pub(crate) fn display_obj(v: &Value, print_internals: bool) -> String { .as_ref() .and_then(|p| p.as_data_descriptor()) .map(|d| d.value()) - .unwrap_or(Value::undefined()); + .unwrap_or_else(Value::undefined); let message = v .get_property("message") .as_ref() .and_then(|p| p.as_data_descriptor()) .map(|d| d.value()) - .unwrap_or(Value::undefined()); + .unwrap_or_else(Value::undefined); return format!("{}: {}", name.display(), message.display()); } } diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 0dfd4003b76..4638f22d839 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -168,7 +168,6 @@ impl Value { .standard_objects() .object_object() .prototype() - .clone() .into(), ); Self::object(object) @@ -471,7 +470,7 @@ impl Value { match self.get_property(key) { Some(ref desc) => match desc { PropertyDescriptor::Accessor(AccessorDescriptor { get: Some(get), .. }) => { - get.call(&Value::from(self.clone()), &[], context) + get.call(&self.clone(), &[], context) } PropertyDescriptor::Data(desc) => Ok(desc.value()), _ => Ok(Value::undefined()), From 87c1e7802bccb2c60da0ce4f31e9e35d94381ac9 Mon Sep 17 00:00:00 2001 From: tofpie Date: Sun, 20 Dec 2020 16:42:36 +0100 Subject: [PATCH 3/9] Fix formatting --- boa/src/context.rs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/boa/src/context.rs b/boa/src/context.rs index c5966f4a425..97229ece4ee 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -285,11 +285,7 @@ impl Context { /// Construct an empty object. #[inline] pub fn construct_object(&mut self) -> GcObject { - let object_prototype: Value = self - .standard_objects() - .object_object() - .prototype() - .into(); + let object_prototype: Value = self.standard_objects().object_object().prototype().into(); GcObject::new(Object::create(object_prototype)) } @@ -455,11 +451,8 @@ impl Context { P: Into>, B: Into, { - let function_prototype: Value = self - .standard_objects() - .function_object() - .prototype() - .into(); + let function_prototype: Value = + self.standard_objects().function_object().prototype().into(); // Every new function has a prototype property pre-made let proto = Value::new_object(Some(self.global_object()), self); @@ -493,11 +486,7 @@ impl Context { length: usize, body: NativeFunction, ) -> Result { - let function_prototype: Value = self - .standard_objects() - .object_object() - .prototype() - .into(); + let function_prototype: Value = self.standard_objects().object_object().prototype().into(); // Every new function has a prototype property pre-made let proto = Value::new_object(Some(self.global_object()), self); From 73d6128fef35140b1966b6f58854159b4762341a Mon Sep 17 00:00:00 2001 From: tofpie Date: Sun, 20 Dec 2020 20:43:39 +0100 Subject: [PATCH 4/9] Add suggestions from review --- boa/src/builtins/map/mod.rs | 2 +- boa/src/context.rs | 6 +++--- boa/src/property/mod.rs | 6 +++--- boa/src/value/mod.rs | 15 ++++----------- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index c743a0f1157..0a1da9b9f8e 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -100,7 +100,7 @@ impl Map { let mut map = OrderedMap::new(); let len = args[0].get_field("length", context)?.to_integer(context)? as i32; for i in 0..len { - let val = &args[0].get_field(i.to_string(), context)?; + let val = &args[0].get_field(i, context)?; let (key, value) = Self::get_key_value(val, context)?.ok_or_else(|| { context.construct_type_error( diff --git a/boa/src/context.rs b/boa/src/context.rs index 97229ece4ee..8b45f4cb305 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -531,7 +531,7 @@ impl Context { if let ObjectData::Array = x.borrow().data { let length = value.get_field("length", self)?.as_number().unwrap() as i32; let values = (0..length) - .map(|idx| value.get_field(idx.to_string(), self)) + .map(|idx| value.get_field(idx, self)) .collect::>>()?; return Ok(Ok(values)); } @@ -559,8 +559,8 @@ impl Context { .expect("Array was not initialized") .get_field(PROTOTYPE, self)?, ); - array.set_field("0", key, self)?; - array.set_field("1", value, self)?; + array.set_field(0, key, self)?; + array.set_field(1, value, self)?; array.set_field("length", Value::from(2), self)?; Ok(array) }) diff --git a/boa/src/property/mod.rs b/boa/src/property/mod.rs index f3a94def7b7..f74dd439928 100644 --- a/boa/src/property/mod.rs +++ b/boa/src/property/mod.rs @@ -118,11 +118,11 @@ impl From for PropertyDescriptor { #[derive(Debug, Clone, Trace, Finalize)] pub struct AccessorDescriptor { /// The function serving as getter. - pub get: Option, + pub(crate) get: Option, /// The function serving as setter. - pub set: Option, + pub(crate) set: Option, /// The attributes of the accessor descriptor. - pub attributes: Attribute, + pub(crate) attributes: Attribute, } impl AccessorDescriptor { diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 4638f22d839..87ef209b1eb 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -34,7 +34,6 @@ mod rcstring; mod rcsymbol; mod r#type; -use crate::property::AccessorDescriptor; pub use conversions::*; pub use display::ValueDisplay; pub use equality::*; @@ -466,16 +465,10 @@ impl Value { K: Into, { let _timer = BoaProfiler::global().start_event("Value::get_field", "value"); - let key = key.into(); - match self.get_property(key) { - Some(ref desc) => match desc { - PropertyDescriptor::Accessor(AccessorDescriptor { get: Some(get), .. }) => { - get.call(&self.clone(), &[], context) - } - PropertyDescriptor::Data(desc) => Ok(desc.value()), - _ => Ok(Value::undefined()), - }, - None => Ok(Value::undefined()), + if let Self::Object(ref obj) = *self { + obj.clone().get(&key.into(), context) + } else { + Ok(Value::undefined()) } } From 40066cc9cb50562d676a9235494b579e5c082ef2 Mon Sep 17 00:00:00 2001 From: tofpie Date: Sun, 20 Dec 2020 21:01:12 +0100 Subject: [PATCH 5/9] Add unit test --- boa/src/value/tests.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/boa/src/value/tests.rs b/boa/src/value/tests.rs index 4eddb975d8f..386229e9fe0 100644 --- a/boa/src/value/tests.rs +++ b/boa/src/value/tests.rs @@ -606,6 +606,19 @@ fn to_integer_or_infinity() { ); } +#[test] +fn test_accessors() { + let mut context = Context::new(); + let src = r#" + let arr = []; + let a = { get b() { return "c" }, set b(value) { arr = arr.concat([value]) }} ; + a.b = "a"; + "#; + context.eval(src).unwrap(); + assert_eq!(forward(&mut context, "a.b"), r#""c""#); + assert_eq!(forward(&mut context, "arr"), r#"[ "a" ]"#); +} + /// Test cyclic conversions that previously caused stack overflows /// Relevant mitigations for these are in `GcObject::ordinary_to_primitive` and /// `GcObject::to_json` From ff56375802c926cc322b1ca3708936681bca9e96 Mon Sep 17 00:00:00 2001 From: tofpie Date: Mon, 21 Dec 2020 14:13:27 +0100 Subject: [PATCH 6/9] Add suggestions from review --- boa/src/builtins/map/mod.rs | 3 +- boa/src/syntax/ast/node/object/mod.rs | 2 +- boa/src/value/display.rs | 42 +++++++++++++-------------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 0a1da9b9f8e..f8bf5542cb7 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -2,14 +2,13 @@ use crate::{ builtins::BuiltIn, - object::{ConstructorBuilder, FunctionBuilder, ObjectData}, + object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE}, property::{Attribute, DataDescriptor}, BoaProfiler, Context, Result, Value, }; use ordered_map::OrderedMap; pub mod map_iterator; -use crate::object::PROTOTYPE; use map_iterator::{MapIterationKind, MapIterator}; pub mod ordered_map; diff --git a/boa/src/syntax/ast/node/object/mod.rs b/boa/src/syntax/ast/node/object/mod.rs index ab455f660d5..a30336e851a 100644 --- a/boa/src/syntax/ast/node/object/mod.rs +++ b/boa/src/syntax/ast/node/object/mod.rs @@ -3,12 +3,12 @@ use crate::{ exec::Executable, gc::{Finalize, Trace}, + property::{AccessorDescriptor, Attribute, PropertyDescriptor}, syntax::ast::node::{MethodDefinitionKind, Node, PropertyDefinition}, Context, Result, Value, }; use std::fmt; -use crate::property::{AccessorDescriptor, Attribute, PropertyDescriptor}; #[cfg(feature = "deser")] use serde::{Deserialize, Serialize}; diff --git a/boa/src/value/display.rs b/boa/src/value/display.rs index aedd5459335..f0b397636d9 100644 --- a/boa/src/value/display.rs +++ b/boa/src/value/display.rs @@ -48,29 +48,29 @@ macro_rules! print_obj_value { }; (props of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr, $print_internals:expr) => { print_obj_value!(impl $obj, |(key, val)| { - if val.is_data_descriptor() { - let v = &val + if val.is_data_descriptor() { + let v = &val - .as_data_descriptor() - .unwrap() - .value(); + .as_data_descriptor() + .unwrap() + .value(); - format!( - "{:>width$}: {}", - key, - $display_fn(v, $encounters, $indent.wrapping_add(4), $print_internals), - width = $indent, - ) - } else { - let accessor = val.as_accessor_descriptor().unwrap(); - let display = match (accessor.setter().is_some(), accessor.getter().is_some()) { - (true, true) => "Getter & Setter", - (true, false) => "Setter", - (false, true) => "Getter", - _ => "No Getter/Setter" - }; - format!("{:>width$}: {}", key, display, width = $indent) - } + format!( + "{:>width$}: {}", + key, + $display_fn(v, $encounters, $indent.wrapping_add(4), $print_internals), + width = $indent, + ) + } else { + let accessor = val.as_accessor_descriptor().unwrap(); + let display = match (accessor.setter().is_some(), accessor.getter().is_some()) { + (true, true) => "Getter & Setter", + (true, false) => "Setter", + (false, true) => "Getter", + _ => "No Getter/Setter" + }; + format!("{:>width$}: {}", key, display, width = $indent) + } }) }; From d6ed0181e151e0b6af5cd76dc5f59e16a378e93f Mon Sep 17 00:00:00 2001 From: tofpie Date: Mon, 28 Dec 2020 15:37:19 +0100 Subject: [PATCH 7/9] Add accessor handling in iterable and JSON built-in --- boa/src/builtins/iterable/mod.rs | 33 ++++++++++++-------------------- boa/src/builtins/json/mod.rs | 17 +++++----------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index a7ff2bc89c1..54273788d75 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/boa/src/builtins/iterable/mod.rs @@ -69,16 +69,16 @@ pub fn create_iter_result_object(context: &mut Context, value: Value, done: bool /// Get an iterator record pub fn get_iterator(context: &mut Context, iterable: Value) -> Result { - // TODO: Fix the accessor handling - let iterator_function = iterable - .get_property(context.well_known_symbols().iterator_symbol()) - .map(|p| p.as_data_descriptor().unwrap().value()) - .ok_or_else(|| context.construct_type_error("Not an iterable"))?; + let iterator_function = + iterable.get_field(context.well_known_symbols().iterator_symbol(), context)?; + if iterator_function.is_null_or_undefined() { + return Err(context.construct_type_error("Not an iterable")); + } let iterator_object = context.call(&iterator_function, &iterable, &[])?; - let next_function = iterator_object - .get_property("next") - .map(|p| p.as_data_descriptor().unwrap().value()) - .ok_or_else(|| context.construct_type_error("Could not find property `next`"))?; + let next_function = iterator_object.get_field("next", context)?; + if next_function.is_null_or_undefined() { + return Err(context.construct_type_error("Could not find property `next`")); + } Ok(IteratorRecord::new(iterator_object, next_function)) } @@ -125,18 +125,9 @@ impl IteratorRecord { /// [spec]: https://tc39.es/ecma262/#sec-iteratornext pub(crate) fn next(&self, context: &mut Context) -> Result { let next = context.call(&self.next_function, &self.iterator_object, &[])?; - // FIXME: handle accessor descriptors - let done = next - .get_property("done") - .map(|p| p.as_data_descriptor().unwrap().value()) - .and_then(|v| v.as_boolean()) - .ok_or_else(|| context.construct_type_error("Could not find property `done`"))?; - - // FIXME: handle accessor descriptors - let next_result = next - .get_property("value") - .map(|p| p.as_data_descriptor().unwrap().value()) - .unwrap_or_default(); + let done = next.get_field("done", context)?.to_boolean(); + + let next_result = next.get_field("value", context)?; Ok(IteratorResult::new(next_result, done)) } } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 6d53935c2f8..84c00cd6152 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -193,12 +193,8 @@ impl Json { .as_object() .map(|obj| { let object_to_return = Value::object(Object::default()); - for (key, val) in obj - .borrow() - .iter() - // FIXME: handle accessor descriptors - .map(|(k, v)| (k, v.as_data_descriptor().unwrap().value())) - { + for key in obj.borrow().keys() { + let val = obj.get(&key, context)?; let this_arg = object.clone(); object_to_return.set_property( key.to_owned(), @@ -236,12 +232,9 @@ impl Json { } }); for field in fields { - if let Some(value) = object - .get_property(field.to_string(context)?) - // FIXME: handle accessor descriptors - .map(|prop| prop.as_data_descriptor().unwrap().value().to_json(context)) - .transpose()? - { + let v = object.get_field(field.to_string(context)?, context)?; + if !v.is_undefined() { + let value = v.to_json(context)?; obj_to_return.insert(field.to_string(context)?.to_string(), value); } } From 9e5379e6439c4a3ba2007debc88eea49abd7d75b Mon Sep 17 00:00:00 2001 From: tofpie Date: Mon, 28 Dec 2020 17:19:49 +0100 Subject: [PATCH 8/9] Fix return of Object.defineProperty --- boa/src/builtins/array/mod.rs | 3 +++ boa/src/builtins/object/mod.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index b781ed62f47..1d364fa743b 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -972,6 +972,9 @@ impl Array { let to = Self::get_relative_end(context, args.get(1), len)?; let span = max(to.saturating_sub(from), 0); + if span > 2usize.pow(32) - 1 { + return context.throw_range_error("Invalid array length"); + } let mut new_array_len: i32 = 0; for i in from..from.saturating_add(span) { new_array.set_field(new_array_len, this.get_field(i, context)?, context)?; diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 7e1799b7466..af7ec496f1c 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -301,7 +301,7 @@ impl Object { return context.throw_type_error("Property description must be an object"); }; obj.set_property(prop, desc); - Ok(Value::undefined()) + Ok(obj.clone()) } /// `Object.defineProperties( proto, [propertiesObject] )` From 92e4fc1e2914ef5a3224b1aa1c3852963fe5e9cf Mon Sep 17 00:00:00 2001 From: tofpie Date: Tue, 29 Dec 2020 22:02:18 +0100 Subject: [PATCH 9/9] Change Value::new_object so that it does not use global --- boa/src/builtins/array/array_iterator.rs | 7 +++---- boa/src/builtins/array/mod.rs | 11 +---------- boa/src/builtins/iterable/mod.rs | 2 +- boa/src/builtins/json/mod.rs | 2 +- boa/src/builtins/map/map_iterator.rs | 5 ++--- boa/src/builtins/object/mod.rs | 4 +--- boa/src/builtins/string/string_iterator.rs | 5 ++--- boa/src/context.rs | 17 ++++------------- boa/src/syntax/ast/node/object/mod.rs | 7 +------ boa/src/value/mod.rs | 18 +++--------------- boa/src/value/tests.rs | 4 ++-- 11 files changed, 21 insertions(+), 61 deletions(-) diff --git a/boa/src/builtins/array/array_iterator.rs b/boa/src/builtins/array/array_iterator.rs index d46f79da8b1..729f012b933 100644 --- a/boa/src/builtins/array/array_iterator.rs +++ b/boa/src/builtins/array/array_iterator.rs @@ -50,7 +50,7 @@ impl ArrayIterator { array: Value, kind: ArrayIterationKind, ) -> Result { - let array_iterator = Value::new_object(Some(context.global_object()), context); + let array_iterator = Value::new_object(context); array_iterator.set_data(ObjectData::ArrayIterator(Self::new(array, kind))); array_iterator .as_object() @@ -97,7 +97,7 @@ impl ArrayIterator { ArrayIterationKind::KeyAndValue => { let element_value = array_iterator.array.get_field(index, context)?; let result = Array::constructor( - &Value::new_object(Some(context.global_object()), context), + &Value::new_object(context), &[index.into(), element_value], context, )?; @@ -119,11 +119,10 @@ impl ArrayIterator { /// /// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: Value) -> Value { - let global = context.global_object(); let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); // Create prototype - let array_iterator = Value::new_object(Some(global), context); + let array_iterator = Value::new_object(context); make_builtin_fn(Self::next, "next", &array_iterator, 0, context); array_iterator .as_object() diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 1d364fa743b..dafb17ec0fb 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -217,16 +217,7 @@ impl Array { /// Creates a new `Array` instance. pub(crate) fn new_array(context: &Context) -> Result { - let array = Value::new_object( - Some( - &context - .realm() - .environment - .get_global_object() - .expect("Could not get global object"), - ), - context, - ); + let array = Value::new_object(context); array.set_data(ObjectData::Array); array .as_object() diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index 54273788d75..d99ee418cd6 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/boa/src/builtins/iterable/mod.rs @@ -58,7 +58,7 @@ impl IteratorPrototypes { /// /// Generates an object supporting the IteratorResult interface. pub fn create_iter_result_object(context: &mut Context, value: Value, done: bool) -> Value { - let object = Value::new_object(Some(context.global_object()), context); + let object = Value::new_object(context); // TODO: Fix attributes of value and done let value_property = DataDescriptor::new(value, Attribute::all()); let done_property = DataDescriptor::new(done, Attribute::all()); diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 84c00cd6152..85d766f30e2 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -75,7 +75,7 @@ impl Json { let j = Value::from_json(json, context); match args.get(1) { Some(reviver) if reviver.is_function() => { - let mut holder = Value::new_object(None, context); + let mut holder = Value::object(Object::default()); holder.set_field("", j, context)?; Self::walk(reviver, context, &mut holder, &PropertyKey::from("")) } diff --git a/boa/src/builtins/map/map_iterator.rs b/boa/src/builtins/map/map_iterator.rs index 2d2a582d00a..a02456d34d7 100644 --- a/boa/src/builtins/map/map_iterator.rs +++ b/boa/src/builtins/map/map_iterator.rs @@ -50,7 +50,7 @@ impl MapIterator { map: Value, kind: MapIterationKind, ) -> Result { - let map_iterator = Value::new_object(Some(context.global_object()), context); + let map_iterator = Value::new_object(context); map_iterator.set_data(ObjectData::MapIterator(Self::new(map, kind))); map_iterator .as_object() @@ -139,11 +139,10 @@ impl MapIterator { /// /// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%-object pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: Value) -> Value { - let global = context.global_object(); let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); // Create prototype - let map_iterator = Value::new_object(Some(global), context); + let map_iterator = Value::new_object(context); make_builtin_fn(Self::next, "next", &map_iterator, 0, context); map_iterator .as_object() diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index af7ec496f1c..c13eff4b173 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -83,9 +83,7 @@ impl Object { return Ok(arg.to_object(context)?.into()); } } - let global = context.global_object(); - - Ok(Value::new_object(Some(global), context)) + Ok(Value::new_object(context)) } /// `Object.create( proto, [propertiesObject] )` diff --git a/boa/src/builtins/string/string_iterator.rs b/boa/src/builtins/string/string_iterator.rs index 73c8cebd61c..e1a56748015 100644 --- a/boa/src/builtins/string/string_iterator.rs +++ b/boa/src/builtins/string/string_iterator.rs @@ -23,7 +23,7 @@ impl StringIterator { } pub fn create_string_iterator(context: &mut Context, string: Value) -> Result { - let string_iterator = Value::new_object(Some(context.global_object()), context); + let string_iterator = Value::new_object(context); string_iterator.set_data(ObjectData::StringIterator(Self::new(string))); string_iterator .as_object() @@ -70,11 +70,10 @@ impl StringIterator { /// /// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: Value) -> Value { - let global = context.global_object(); let _timer = BoaProfiler::global().start_event("String Iterator", "init"); // Create prototype - let array_iterator = Value::new_object(Some(global), context); + let array_iterator = Value::new_object(context); make_builtin_fn(Self::next, "next", &array_iterator, 0, context); array_iterator .as_object() diff --git a/boa/src/context.rs b/boa/src/context.rs index d27fb84608b..c56e7170d73 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -315,7 +315,7 @@ impl Context { /// Construct an empty object. #[inline] - pub fn construct_object(&mut self) -> GcObject { + pub fn construct_object(&self) -> GcObject { let object_prototype: Value = self.standard_objects().object_object().prototype().into(); GcObject::new(Object::create(object_prototype)) } @@ -486,7 +486,7 @@ impl Context { self.standard_objects().function_object().prototype().into(); // Every new function has a prototype property pre-made - let proto = Value::new_object(Some(self.global_object()), self); + let proto = Value::new_object(self); let params = params.into(); let params_len = params.len(); @@ -520,7 +520,7 @@ impl Context { let function_prototype: Value = self.standard_objects().object_object().prototype().into(); // Every new function has a prototype property pre-made - let proto = Value::new_object(Some(self.global_object()), self); + let proto = Value::new_object(self); let mut function = GcObject::new(Object::function( Function::BuiltIn(body.into(), FunctionFlags::CALLABLE), function_prototype, @@ -572,16 +572,7 @@ impl Context { .iter() .map(|(key, value)| { // Construct a new array containing the key-value pair - let array = Value::new_object( - Some( - &self - .realm() - .environment - .get_global_object() - .expect("Could not get global object"), - ), - self, - ); + let array = Value::new_object(self); array.set_data(ObjectData::Array); array.as_object().expect("object").set_prototype_instance( self.realm() diff --git a/boa/src/syntax/ast/node/object/mod.rs b/boa/src/syntax/ast/node/object/mod.rs index a30336e851a..cb522892d30 100644 --- a/boa/src/syntax/ast/node/object/mod.rs +++ b/boa/src/syntax/ast/node/object/mod.rs @@ -73,12 +73,7 @@ impl Object { impl Executable for Object { fn run(&self, context: &mut Context) -> Result { - let global_val = &context - .realm() - .environment - .get_global_object() - .expect("Could not get the global object"); - let obj = Value::new_object(Some(global_val), context); + let obj = Value::new_object(context); // TODO: Implement the rest of the property types. for property in self.properties().iter() { diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 87ef209b1eb..2e79a1e65b9 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -158,21 +158,9 @@ impl Value { } /// Returns a new empty object - pub fn new_object(global: Option<&Value>, context: &Context) -> Self { + pub fn new_object(context: &Context) -> Self { let _timer = BoaProfiler::global().start_event("new_object", "value"); - - if global.is_some() { - let object = Object::create( - context - .standard_objects() - .object_object() - .prototype() - .into(), - ); - Self::object(object) - } else { - Self::object(Object::default()) - } + context.construct_object().into() } /// Convert from a JSON value to a JS value @@ -209,7 +197,7 @@ impl Value { new_obj } JSONValue::Object(obj) => { - let new_obj = Value::new_object(Some(context.global_object()), context); + let new_obj = Value::new_object(context); for (key, json) in obj.into_iter() { let value = Self::from_json(json, context); new_obj.set_property( diff --git a/boa/src/value/tests.rs b/boa/src/value/tests.rs index 386229e9fe0..0fef0eb1f4c 100644 --- a/boa/src/value/tests.rs +++ b/boa/src/value/tests.rs @@ -9,7 +9,7 @@ use std::hash::{Hash, Hasher}; #[test] fn is_object() { let context = Context::new(); - let val = Value::new_object(None, &context); + let val = Value::new_object(&context); assert_eq!(val.is_object(), true); } @@ -31,7 +31,7 @@ fn undefined() { #[test] fn get_set_field() { let mut context = Context::new(); - let obj = Value::new_object(None, &context); + let obj = Value::new_object(&context); // Create string and convert it to a Value let s = Value::from("bar"); obj.set_field("foo", s, &mut context).unwrap();