Browse Source

Add newTarget to construct (#1045)

* Add newTarget to construct

* Fix construct for self-mutating function

* Implement suggestions from review

Co-authored-by: tofpie <tofpie@users.noreply.github.com>
pull/1069/head
tofpie 3 years ago committed by GitHub
parent
commit
a7dd470cc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 50
      boa/src/builtins/array/mod.rs
  2. 30
      boa/src/builtins/boolean/mod.rs
  3. 30
      boa/src/builtins/date/mod.rs
  4. 21
      boa/src/builtins/error/eval.rs
  5. 22
      boa/src/builtins/error/mod.rs
  6. 24
      boa/src/builtins/error/range.rs
  7. 22
      boa/src/builtins/error/reference.rs
  8. 22
      boa/src/builtins/error/syntax.rs
  9. 22
      boa/src/builtins/error/type.rs
  10. 22
      boa/src/builtins/error/uri.rs
  11. 20
      boa/src/builtins/function/mod.rs
  12. 39
      boa/src/builtins/map/mod.rs
  13. 22
      boa/src/builtins/number/mod.rs
  14. 24
      boa/src/builtins/object/mod.rs
  15. 24
      boa/src/builtins/regexp/mod.rs
  16. 34
      boa/src/builtins/string/mod.rs
  17. 9
      boa/src/builtins/symbol/mod.rs
  18. 3
      boa/src/environment/lexical_environment.rs
  19. 74
      boa/src/object/gcobject.rs
  20. 8
      boa/src/object/mod.rs
  21. 2
      boa/src/syntax/ast/node/new/mod.rs

50
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},
object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE},
property::{Attribute, DataDescriptor},
value::{same_value_zero, IntegerOrInfinity, Value},
BoaProfiler, Context, Result,
@ -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),
}
}
@ -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`.
@ -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()));
@ -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()));
@ -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,
@ -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.
@ -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),

30
boa/src/builtins/boolean/mod.rs

@ -14,7 +14,7 @@ mod tests;
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
property::Attribute,
BoaProfiler, Context, Result, Value,
};
@ -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]]`.

30
boa/src/builtins/date/mod.rs

@ -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,
@ -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)
}
}
}

21
boa/src/builtins/error/eval.rs

@ -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},
@ -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
boa/src/builtins/error/mod.rs

@ -12,7 +12,7 @@
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
@ -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()`

24
boa/src/builtins/error/range.rs

@ -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;
@ -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)
}
}

22
boa/src/builtins/error/reference.rs

@ -11,7 +11,7 @@
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
@ -54,17 +54,31 @@ 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 = 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
boa/src/builtins/error/syntax.rs

@ -13,7 +13,7 @@
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
@ -57,17 +57,31 @@ impl SyntaxError {
/// 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
boa/src/builtins/error/type.rs

@ -17,7 +17,7 @@
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
property::Attribute,
BoaProfiler, Context, Result, Value,
};
@ -60,17 +60,31 @@ impl TypeError {
/// 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
boa/src/builtins/error/uri.rs

@ -12,7 +12,7 @@
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
@ -56,17 +56,31 @@ impl UriError {
/// 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)
}
}

20
boa/src/builtins/function/mod.rs

@ -11,6 +11,7 @@
//! [spec]: https://tc39.es/ecma262/#sec-function-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
use crate::object::PROTOTYPE;
use crate::{
builtins::{Array, BuiltIn},
environment::lexical_environment::Environment,
@ -255,12 +256,27 @@ pub struct BuiltInFunctionObject;
impl BuiltInFunctionObject {
pub const LENGTH: usize = 1;
fn constructor(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
fn constructor(new_target: &Value, _: &[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().object_object().prototype());
let this = Value::new_object(context);
this.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
this.set_data(ObjectData::Function(Function::BuiltIn(
BuiltInFunction(|_, _, _| Ok(Value::undefined())),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,
)));
Ok(this.clone())
Ok(this)
}
fn prototype(_: &Value, _: &[Value], _: &mut Context) -> Result<Value> {

39
boa/src/builtins/map/mod.rs

@ -70,22 +70,33 @@ impl Map {
/// Create a new map
pub(crate) fn constructor(
this: &Value,
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
// Set Prototype
let prototype = context
if new_target.is_undefined() {
return context.throw_type_error("Map requires new");
}
let map_prototype = context
.global_object()
.clone()
.get_field("Map", context)?
.get_field(PROTOTYPE, context)?;
this.as_object()
.expect("this is map object")
.set_prototype_instance(prototype);
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
.get_field(PROTOTYPE, context)?
.as_object()
.expect("'Map' global property should be an object");
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(map_prototype);
let mut obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = Value::from(obj);
// add our arguments in
let data = match args.len() {
@ -122,12 +133,14 @@ impl Map {
},
};
// finally create length property
Self::set_size(this, data.len());
// finally create size property
Self::set_size(&this, data.len());
// 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::Map(data));
Ok(this.clone())
Ok(this)
}
/// `Map.prototype.entries()`

22
boa/src/builtins/number/mod.rs

@ -16,7 +16,7 @@
use super::function::make_builtin_fn;
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
property::Attribute,
value::{AbstractRelation, IntegerOrInfinity, Value},
BoaProfiler, Context, Result,
@ -154,7 +154,7 @@ impl Number {
/// `Number( value )`
pub(crate) fn constructor(
this: &Value,
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
@ -162,9 +162,25 @@ impl Number {
Some(ref value) => value.to_numeric_number(context)?,
None => 0.0,
};
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 this = Value::new_object(context);
this.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
this.set_data(ObjectData::Number(data));
Ok(Value::from(data))
Ok(this)
}
/// This function returns a `Result` of the number `Value`.

24
boa/src/builtins/object/mod.rs

@ -15,7 +15,9 @@
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, Object as BuiltinObject, ObjectData, ObjectInitializer},
object::{
ConstructorBuilder, Object as BuiltinObject, ObjectData, ObjectInitializer, PROTOTYPE,
},
property::Attribute,
property::DataDescriptor,
property::PropertyDescriptor,
@ -78,7 +80,25 @@ impl BuiltIn for Object {
impl Object {
const LENGTH: usize = 1;
fn constructor(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
fn constructor(new_target: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if !new_target.is_undefined() {
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 object = Value::new_object(context);
object
.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
return Ok(object);
}
if let Some(arg) = args.get(0) {
if !arg.is_null_or_undefined() {
return Ok(arg.to_object(context)?.into());

24
boa/src/builtins/regexp/mod.rs

@ -12,7 +12,7 @@
use crate::{
builtins::BuiltIn,
gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, ObjectData},
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
property::{Attribute, DataDescriptor},
value::{RcString, Value},
BoaProfiler, Context, Result,
@ -98,7 +98,25 @@ impl RegExp {
pub(crate) const LENGTH: usize = 2;
/// Create a new `RegExp`
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
new_target: &Value,
args: &[Value],
ctx: &mut Context,
) -> Result<Value> {
let prototype = new_target
.as_object()
.and_then(|obj| {
obj.get(&PROTOTYPE.into(), obj.clone().into(), ctx)
.map(|o| o.as_object())
.transpose()
})
.transpose()?
.unwrap_or_else(|| ctx.standard_objects().regexp_object().prototype());
let this = Value::new_object(ctx);
this.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
let arg = args.get(0).ok_or_else(Value::undefined)?;
let (regex_body, mut regex_flags) = match arg {
@ -186,7 +204,7 @@ impl RegExp {
this.set_data(ObjectData::RegExp(Box::new(regexp)));
Ok(this.clone())
Ok(this)
}
// /// `RegExp.prototype.dotAll`

34
boa/src/builtins/string/mod.rs

@ -13,6 +13,8 @@ pub mod string_iterator;
#[cfg(test)]
mod tests;
use crate::builtins::Symbol;
use crate::object::PROTOTYPE;
use crate::property::DataDescriptor;
use crate::{
builtins::{string::string_iterator::StringIterator, Array, BuiltIn, RegExp},
@ -131,26 +133,50 @@ impl String {
///
/// <https://tc39.es/ecma262/#sec-string-constructor-string-value>
pub(crate) fn constructor(
this: &Value,
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
// This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
let string = match args.get(0) {
Some(value) if value.is_symbol() && new_target.is_undefined() => {
Symbol::to_string(value, &[], context)?
.as_string()
.expect("'Symbol::to_string' returns 'Value::String'")
.clone()
}
Some(ref value) => value.to_string(context)?,
None => RcString::default(),
};
if new_target.is_undefined() {
return Ok(string.into());
}
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 this = Value::new_object(context);
this.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
let length = DataDescriptor::new(
Value::from(string.encode_utf16().count()),
Attribute::NON_ENUMERABLE,
);
this.set_property("length", length);
this.set_data(ObjectData::String(string.clone()));
this.set_data(ObjectData::String(string));
Ok(Value::from(string))
Ok(this)
}
fn this_string_value(this: &Value, context: &mut Context) -> Result<RcString> {

9
boa/src/builtins/symbol/mod.rs

@ -315,7 +315,14 @@ impl Symbol {
///
/// [spec]: https://tc39.es/ecma262/#sec-symbol-description
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol
pub(crate) fn constructor(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
pub(crate) fn constructor(
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if new_target.is_undefined() {
return context.throw_type_error("Symbol is not a constructor");
}
let description = match args.get(0) {
Some(ref value) if !value.is_undefined() => Some(value.to_string(context)?),
_ => None,

3
boa/src/environment/lexical_environment.rs

@ -266,13 +266,14 @@ pub fn new_function_environment(
this: Option<Value>,
outer: Option<Environment>,
binding_status: BindingStatus,
new_target: Value,
) -> Environment {
let mut func_env = FunctionEnvironmentRecord {
env_rec: FxHashMap::default(),
function: f,
this_binding_status: binding_status,
home_object: Value::undefined(),
new_target: Value::undefined(),
new_target,
outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.es/ecma262/#sec-ecmascript-function-objects
this_value: Value::undefined(),
};

74
boa/src/object/gcobject.rs

@ -40,7 +40,8 @@ pub struct GcObject(Gc<GcCell<Object>>);
/// This is needed for the call method since we cannot mutate the function itself since we
/// already borrow it so we get the function body clone it then drop the borrow and run the body
enum FunctionBody {
BuiltIn(NativeFunction),
BuiltInFunction(NativeFunction),
BuiltInConstructor(NativeFunction),
Ordinary(RcStatementList),
}
@ -119,8 +120,12 @@ impl GcObject {
let f_body = if let Some(function) = self.borrow().as_function() {
if function.is_callable() {
match function {
Function::BuiltIn(BuiltInFunction(function), _) => {
FunctionBody::BuiltIn(*function)
Function::BuiltIn(BuiltInFunction(function), flags) => {
if flags.is_constructable() {
FunctionBody::BuiltInConstructor(*function)
} else {
FunctionBody::BuiltInFunction(*function)
}
}
Function::Ordinary {
body,
@ -144,6 +149,7 @@ impl GcObject {
} else {
BindingStatus::Uninitialized
},
Value::undefined(),
);
// Add argument bindings to the function environment
@ -182,7 +188,8 @@ impl GcObject {
};
match f_body {
FunctionBody::BuiltIn(func) => func(this, args, context),
FunctionBody::BuiltInFunction(func) => func(this, args, context),
FunctionBody::BuiltInConstructor(func) => func(&Value::undefined(), args, context),
FunctionBody::Ordinary(body) => {
let result = body.run(context);
context.realm_mut().environment.pop();
@ -199,29 +206,18 @@ impl GcObject {
/// Panics if the object is currently mutably borrowed.
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
#[track_caller]
pub fn construct(&self, args: &[Value], context: &mut Context) -> Result<Value> {
// If the prototype of the constructor is not an object, then use the default object
// prototype as prototype for the new object
// see <https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor>
// see <https://tc39.es/ecma262/#sec-getprototypefromconstructor>
let proto = self.get(&PROTOTYPE.into(), self.clone().into(), context)?;
let proto = if proto.is_object() {
proto
} else {
context
.standard_objects()
.object_object()
.prototype()
.into()
};
let this: Value = Object::create(proto).into();
pub fn construct(
&self,
args: &[Value],
new_target: Value,
context: &mut Context,
) -> Result<Value> {
let this_function_object = self.clone();
let body = if let Some(function) = self.borrow().as_function() {
if function.is_constructable() {
match function {
Function::BuiltIn(BuiltInFunction(function), _) => {
FunctionBody::BuiltIn(*function)
FunctionBody::BuiltInConstructor(*function)
}
Function::Ordinary {
body,
@ -229,11 +225,31 @@ impl GcObject {
environment,
flags,
} => {
// If the prototype of the constructor is not an object, then use the default object
// prototype as prototype for the new object
// see <https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor>
// see <https://tc39.es/ecma262/#sec-getprototypefromconstructor>
let proto = new_target.as_object().unwrap().get(
&PROTOTYPE.into(),
new_target.clone(),
context,
)?;
let proto = if proto.is_object() {
proto
} else {
context
.standard_objects()
.object_object()
.prototype()
.into()
};
let this = Value::from(Object::create(proto));
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
this_function_object,
Some(this.clone()),
Some(this),
Some(environment.clone()),
// Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records
if flags.is_lexical_this_mode() {
@ -241,6 +257,7 @@ impl GcObject {
} else {
BindingStatus::Uninitialized
},
new_target.clone(),
);
// Add argument bindings to the function environment
@ -272,7 +289,10 @@ impl GcObject {
}
}
} else {
let name = this.get_field("name", context)?.display().to_string();
let name = self
.get(&"name".into(), self.clone().into(), context)?
.display()
.to_string();
return context.throw_type_error(format!("{} is not a constructor", name));
}
} else {
@ -280,10 +300,7 @@ impl GcObject {
};
match body {
FunctionBody::BuiltIn(function) => {
function(&this, args, context)?;
Ok(this)
}
FunctionBody::BuiltInConstructor(function) => function(&new_target, args, context),
FunctionBody::Ordinary(body) => {
let _ = body.run(context);
@ -291,6 +308,7 @@ impl GcObject {
let binding = context.realm_mut().environment.get_this_binding();
binding.map_err(|e| e.to_error(context))
}
FunctionBody::BuiltInFunction(_) => unreachable!("Cannot have a function in construct"),
}
}

8
boa/src/object/mod.rs

@ -828,7 +828,7 @@ impl<'context> ObjectInitializer<'context> {
/// Builder for creating constructors objects, like `Array`.
pub struct ConstructorBuilder<'context> {
context: &'context mut Context,
constrcutor_function: NativeFunction,
constructor_function: NativeFunction,
constructor_object: GcObject,
prototype: GcObject,
name: Option<String>,
@ -858,7 +858,7 @@ impl<'context> ConstructorBuilder<'context> {
pub fn new(context: &'context mut Context, constructor: NativeFunction) -> Self {
Self {
context,
constrcutor_function: constructor,
constructor_function: constructor,
constructor_object: GcObject::new(Object::default()),
prototype: GcObject::new(Object::default()),
length: 0,
@ -877,7 +877,7 @@ impl<'context> ConstructorBuilder<'context> {
) -> Self {
Self {
context,
constrcutor_function: constructor,
constructor_function: constructor,
constructor_object: object.constructor,
prototype: object.prototype,
length: 0,
@ -1020,7 +1020,7 @@ impl<'context> ConstructorBuilder<'context> {
pub fn build(&mut self) -> GcObject {
// Create the native function
let function = Function::BuiltIn(
self.constrcutor_function.into(),
self.constructor_function.into(),
FunctionFlags::from_parameters(self.callable, self.constructable),
);

2
boa/src/syntax/ast/node/new/mod.rs

@ -69,7 +69,7 @@ impl Executable for New {
}
match func_object {
Value::Object(ref object) => object.construct(&v_args, context),
Value::Object(ref object) => object.construct(&v_args, object.clone().into(), context),
_ => context
.throw_type_error(format!("{} is not a constructor", self.expr().to_string(),)),
}

Loading…
Cancel
Save