Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add newTarget to construct #1045

Merged
merged 8 commits into from
Jan 12, 2021
Merged
44 changes: 24 additions & 20 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,19 @@ 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 = match new_target {
Value::Object(obj) => match obj.get(&PROTOTYPE.into(), context)? {
Value::Object(ref o) => o.clone(),
_ => context.standard_objects().array_object().prototype(),
},
_ => 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 +130,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 +141,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_field(0, Value::from(length), context)?;
Expand All @@ -167,13 +171,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_field(k, item.clone(), context)?;
Expand All @@ -189,7 +192,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 @@ -198,21 +200,23 @@ impl Array {
Some(prototype) => prototype,
None => context.standard_objects().array_object().prototype(),
};
let array = Value::new_object(context);

this.as_object()
array
.as_object()
.expect("this should be an array object")
RageKnify marked this conversation as resolved.
Show resolved Hide resolved
.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 Down
28 changes: 24 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,32 @@ 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 = match new_target {
Value::Object(obj) => match obj.get(&PROTOTYPE.into(), context)? {
Value::Object(ref o) => o.clone(),
_ => context.standard_objects().object_object().prototype(),
},
_ => 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
28 changes: 20 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,30 @@ 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 = match new_target {
Value::Object(obj) => match obj.get(&PROTOTYPE.into(), context)? {
Value::Object(ref o) => o.clone(),
_ => context.standard_objects().object_object().prototype(),
},
_ => 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
15 changes: 13 additions & 2 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,27 @@ 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 = match new_target {
Value::Object(obj) => match obj.get(&PROTOTYPE.into(), context)? {
Value::Object(ref o) => o.clone(),
_ => context.standard_objects().error_object().prototype(),
},
tofpie marked this conversation as resolved.
Show resolved Hide resolved
_ => 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)?;
}
RageKnify marked this conversation as resolved.
Show resolved Hide resolved

// 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)
}
}
16 changes: 13 additions & 3 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,28 @@ 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 = match new_target {
Value::Object(obj) => match obj.get(&PROTOTYPE.into(), context)? {
Value::Object(ref o) => o.clone(),
_ => context.standard_objects().error_object().prototype(),
},
_ => 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)?;
}
RageKnify marked this conversation as resolved.
Show resolved Hide resolved

// 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
18 changes: 14 additions & 4 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,27 @@ 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 = match new_target {
Value::Object(obj) => match obj.get(&PROTOTYPE.into(), context)? {
Value::Object(ref o) => o.clone(),
_ => context.standard_objects().error_object().prototype(),
},
_ => 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)?;
}
RageKnify marked this conversation as resolved.
Show resolved Hide resolved

// 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)
}
}
16 changes: 13 additions & 3 deletions boa/src/builtins/error/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
Expand Down Expand Up @@ -54,17 +54,27 @@ impl ReferenceError {

/// Create a new error object.
pub(crate) fn constructor(
this: &Value,
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
let prototype = match new_target {
Value::Object(obj) => match obj.get(&PROTOTYPE.into(), context)? {
Value::Object(ref o) => o.clone(),
_ => context.standard_objects().error_object().prototype(),
},
_ => 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)?;
}
RageKnify marked this conversation as resolved.
Show resolved Hide resolved

// 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)
}
}