Skip to content

Commit

Permalink
Add newTarget to construct (#1045)
Browse files Browse the repository at this point in the history
* Add newTarget to construct

* Fix construct for self-mutating function

* Implement suggestions from review

Co-authored-by: tofpie <tofpie@users.noreply.github.com>
  • Loading branch information
tofpie and tofpie committed Jan 12, 2021
1 parent 7dffd44 commit a7dd470
Show file tree
Hide file tree
Showing 21 changed files with 400 additions and 124 deletions.
50 changes: 28 additions & 22 deletions boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator},
builtins::BuiltIn,
gc::GcObject,
object::{ConstructorBuilder, FunctionBuilder, ObjectData},
object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE},
property::{Attribute, DataDescriptor},
value::{same_value_zero, IntegerOrInfinity, Value},
BoaProfiler, Context, Result,
Expand Down Expand Up @@ -108,12 +108,21 @@ impl BuiltIn for Array {
impl Array {
const LENGTH: usize = 1;

fn constructor(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
fn constructor(new_target: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().array_object().prototype());
// Delegate to the appropriate constructor based on the number of arguments
match args.len() {
0 => Array::construct_array_empty(this, context),
1 => Array::construct_array_length(this, &args[0], context),
_ => Array::construct_array_values(this, args, context),
0 => Array::construct_array_empty(prototype, context),
1 => Array::construct_array_length(prototype, &args[0], context),
_ => Array::construct_array_values(prototype, args, context),
}
}

Expand All @@ -123,10 +132,8 @@ impl Array {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-array-constructor-array
fn construct_array_empty(this: &Value, context: &mut Context) -> Result<Value> {
let prototype = context.standard_objects().array_object().prototype();

Array::array_create(this, 0, Some(prototype), context)
fn construct_array_empty(proto: GcObject, context: &mut Context) -> Result<Value> {
Array::array_create(0, Some(proto), context)
}

/// By length constructor for `Array`.
Expand All @@ -136,12 +143,11 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array-len
fn construct_array_length(
this: &Value,
prototype: GcObject,
length: &Value,
context: &mut Context,
) -> Result<Value> {
let prototype = context.standard_objects().array_object().prototype();
let array = Array::array_create(this, 0, Some(prototype), context)?;
let array = Array::array_create(0, Some(prototype), context)?;

if !length.is_number() {
array.set_property(0, DataDescriptor::new(length, Attribute::all()));
Expand All @@ -163,13 +169,12 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array-items
fn construct_array_values(
this: &Value,
prototype: GcObject,
items: &[Value],
context: &mut Context,
) -> Result<Value> {
let prototype = context.standard_objects().array_object().prototype();
let items_len = items.len().try_into().map_err(interror_to_value)?;
let array = Array::array_create(this, items_len, Some(prototype), context)?;
let array = Array::array_create(items_len, Some(prototype), context)?;

for (k, item) in items.iter().enumerate() {
array.set_property(k, DataDescriptor::new(item.clone(), Attribute::all()));
Expand All @@ -185,7 +190,6 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-arraycreate
fn array_create(
this: &Value,
length: u32,
prototype: Option<GcObject>,
context: &mut Context,
Expand All @@ -194,21 +198,23 @@ impl Array {
Some(prototype) => prototype,
None => context.standard_objects().array_object().prototype(),
};
let array = Value::new_object(context);

this.as_object()
.expect("this should be an array object")
array
.as_object()
.expect("'array' should be an object")
.set_prototype_instance(prototype.into());
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::Array);
array.set_data(ObjectData::Array);

let length = DataDescriptor::new(
length,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
this.set_property("length", length);
array.set_property("length", length);

Ok(this.clone())
Ok(array)
}

/// Creates a new `Array` instance.
Expand All @@ -217,7 +223,7 @@ impl Array {
array.set_data(ObjectData::Array);
array
.as_object()
.expect("array object")
.expect("'array' should be an object")
.set_prototype_instance(context.standard_objects().array_object().prototype().into());
let length = DataDescriptor::new(
Value::from(0),
Expand Down
30 changes: 26 additions & 4 deletions boa/src/builtins/boolean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mod tests;

use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
property::Attribute,
BoaProfiler, Context, Result, Value,
};
Expand Down Expand Up @@ -56,12 +56,34 @@ impl Boolean {
/// `[[Construct]]` Create a new boolean object
///
/// `[[Call]]` Creates a new boolean primitive
pub(crate) fn constructor(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
// Get the argument, if any
let data = args.get(0).map(|x| x.to_boolean()).unwrap_or(false);
this.set_data(ObjectData::Boolean(data));
if new_target.is_undefined() {
return Ok(Value::from(data));
}
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().object_object().prototype());
let boolean = Value::new_object(context);

boolean
.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
boolean.set_data(ObjectData::Boolean(data));

Ok(Value::from(data))
Ok(boolean)
}

/// An Utility function used to get the internal `[[BooleanData]]`.
Expand Down
30 changes: 22 additions & 8 deletions boa/src/builtins/date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod tests;
use crate::{
builtins::BuiltIn,
gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
property::Attribute,
value::{PreferredType, Value},
BoaProfiler, Context, Result,
Expand Down Expand Up @@ -324,18 +324,32 @@ impl Date {
/// [spec]: https://tc39.es/ecma262/#sec-date-constructor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
pub(crate) fn constructor(
this: &Value,
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if this.is_global() {
if new_target.is_undefined() {
Self::make_date_string()
} else if args.is_empty() {
Self::make_date_now(this)
} else if args.len() == 1 {
Self::make_date_single(this, args, context)
} else {
Self::make_date_multiple(this, args, context)
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().object_object().prototype());
let mut obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = obj.into();
if args.is_empty() {
Self::make_date_now(&this)
} else if args.len() == 1 {
Self::make_date_single(&this, args, context)
} else {
Self::make_date_multiple(&this, args, context)
}
}
}

Expand Down
21 changes: 18 additions & 3 deletions boa/src/builtins/error/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError

use crate::object::PROTOTYPE;
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
Expand Down Expand Up @@ -57,17 +58,31 @@ impl EvalError {

/// Create a new error object.
pub(crate) fn constructor(
this: &Value,
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(context)?, context)?;
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, context)?;
}
}

// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::Error);
Ok(this.clone())
Ok(this)
}
}
22 changes: 18 additions & 4 deletions boa/src/builtins/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
Expand Down Expand Up @@ -74,18 +74,32 @@ impl Error {
///
/// Create a new error object.
pub(crate) fn constructor(
this: &Value,
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(context)?, context)?;
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, context)?;
}
}

// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::Error);
Ok(this.clone())
Ok(this)
}

/// `Error.prototype.toString()`
Expand Down
24 changes: 19 additions & 5 deletions boa/src/builtins/error/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
};

/// JavaScript `RangeError` impleentation.
/// JavaScript `RangeError` implementation.
#[derive(Debug, Clone, Copy)]
pub(crate) struct RangeError;

Expand Down Expand Up @@ -55,17 +55,31 @@ impl RangeError {

/// Create a new error object.
pub(crate) fn constructor(
this: &Value,
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), context)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| context.standard_objects().error_object().prototype());
let mut obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(context)?, context)?;
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, context)?;
}
}

// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::Error);
Ok(this.clone())
Ok(this)
}
}

0 comments on commit a7dd470

Please sign in to comment.