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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
}
}