diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 3617fefc82..6654ec61fb 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -15,7 +15,7 @@ mod tests; use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ builtins::{ - object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, + object::{ObjectData, PROTOTYPE}, property::Property, value::{same_value_zero, ResultValue, Value}, }, @@ -48,8 +48,7 @@ impl Array { .expect("Could not get global object"), )); array.set_data(ObjectData::Array); - array.borrow().set_internal_slot( - INSTANCE_PROTOTYPE, + array.as_object_mut().expect("array object").set_prototype( interpreter .realm() .environment @@ -116,7 +115,9 @@ impl Array { // Set Prototype let prototype = ctx.realm.global_obj.get_field("Array").get_field(PROTOTYPE); - this.set_internal_slot(INSTANCE_PROTOTYPE, prototype); + this.as_object_mut() + .expect("this should be an array object") + .set_prototype(prototype); // 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); @@ -961,7 +962,7 @@ impl Array { /// /// The reduce method traverses left to right starting from the first defined value in the array, /// accumulating a value using a given callback function. It returns the accumulated value. - /// + /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] diff --git a/boa/src/builtins/boolean/tests.rs b/boa/src/builtins/boolean/tests.rs index bc8ffe230d..c0eed466e3 100644 --- a/boa/src/builtins/boolean/tests.rs +++ b/boa/src/builtins/boolean/tests.rs @@ -1,7 +1,7 @@ use crate::{builtins::value::same_value, exec::Interpreter, forward, forward_val, realm::Realm}; /// Test the correct type is returned from call and construct -#[allow(clippy::result_unwrap_used)] +#[allow(clippy::unwrap_used)] #[test] fn construct_and_call() { let realm = Realm::create(); @@ -62,7 +62,7 @@ fn instances_have_correct_proto_set() { let bool_prototype = forward_val(&mut engine, "boolProto").expect("value expected"); assert!(same_value( - &bool_instance.get_internal_slot("__proto__"), + &bool_instance.as_object().unwrap().prototype().clone(), &bool_prototype )); } diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 83e5de2965..f97e9c480b 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -14,7 +14,7 @@ use crate::{ builtins::{ array::Array, - object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, + object::{Object, ObjectData, PROTOTYPE}, property::Property, value::{RcString, ResultValue, Value}, }, @@ -464,14 +464,10 @@ pub fn make_constructor_fn( let mut function = Function::builtin(Vec::new(), body); function.flags = FunctionFlags::from_parameters(callable, constructable); - let mut constructor = Object::function(function); - // Get reference to Function.prototype // Create the function object and point its instance prototype to Function.prototype - constructor.set_internal_slot( - INSTANCE_PROTOTYPE, - global.get_field("Function").get_field(PROTOTYPE), - ); + let mut constructor = + Object::function(function, global.get_field("Function").get_field(PROTOTYPE)); let length = Property::new() .value(Value::from(length)) @@ -527,7 +523,8 @@ where let name = name.into(); let _timer = BoaProfiler::global().start_event(&format!("make_builtin_fn: {}", &name), "init"); - let mut function = Object::function(Function::builtin(Vec::new(), function)); + // FIXME: function needs the Function prototype set. + let mut function = Object::function(Function::builtin(Vec::new(), function), Value::null()); function.insert_field("length", Value::from(length)); parent diff --git a/boa/src/builtins/json/tests.rs b/boa/src/builtins/json/tests.rs index 87033013e6..30f5ce237a 100644 --- a/boa/src/builtins/json/tests.rs +++ b/boa/src/builtins/json/tests.rs @@ -1,8 +1,5 @@ use crate::{ - builtins::{ - object::{INSTANCE_PROTOTYPE, PROTOTYPE}, - value::same_value, - }, + builtins::{object::PROTOTYPE, value::same_value}, exec::Interpreter, forward, forward_val, realm::Realm, @@ -284,10 +281,16 @@ fn json_parse_sets_prototypes() { eprintln!("{}", forward(&mut engine, init)); let object_prototype = forward_val(&mut engine, r#"jsonObj.ob"#) .unwrap() - .get_internal_slot(INSTANCE_PROTOTYPE); + .as_object() + .unwrap() + .prototype() + .clone(); let array_prototype = forward_val(&mut engine, r#"jsonObj.arr"#) .unwrap() - .get_internal_slot(INSTANCE_PROTOTYPE); + .as_object() + .unwrap() + .prototype() + .clone(); let global_object_prototype = engine .realm .global_obj diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 3c04dae4c5..75fb8d8654 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -3,7 +3,7 @@ use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ builtins::{ - object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, + object::{ObjectData, PROTOTYPE}, property::Property, value::{ResultValue, Value}, }, @@ -235,7 +235,9 @@ impl Map { // Set Prototype let prototype = ctx.realm.global_obj.get_field("Map").get_field(PROTOTYPE); - this.set_internal_slot(INSTANCE_PROTOTYPE, prototype); + this.as_object_mut() + .expect("this is array object") + .set_prototype(prototype); // This value is used by console.log and other routines to match Object type // to its Javascript Identifier (global constructor method name) diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/builtins/object/internal_methods.rs index 7b79c0c054..bbd51bcf6c 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/builtins/object/internal_methods.rs @@ -6,7 +6,7 @@ //! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots use crate::builtins::{ - object::{Object, INSTANCE_PROTOTYPE, PROTOTYPE}, + object::{Object, PROTOTYPE}, property::Property, value::{same_value, RcString, Value}, }; @@ -363,7 +363,7 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof #[inline] pub fn get_prototype_of(&self) -> Value { - self.get_internal_slot(INSTANCE_PROTOTYPE) + self.prototype.clone() } /// Helper function to get an immutable internal slot or `Null`. diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 4d2b02b1f1..40a5cc98a8 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -44,8 +44,8 @@ mod tests; /// Static `prototype`, usually set on constructors as a key to point to their respective prototype object. pub static PROTOTYPE: &str = "prototype"; -/// Static `__proto__`, usually set on Object instances as a key to point to their respective prototype object. -pub static INSTANCE_PROTOTYPE: &str = "__proto__"; +// /// Static `__proto__`, usually set on Object instances as a key to point to their respective prototype object. +// pub static INSTANCE_PROTOTYPE: &str = "__proto__"; /// The internal representation of an JavaScript object. #[derive(Debug, Trace, Finalize, Clone)] @@ -58,6 +58,8 @@ pub struct Object { properties: FxHashMap, /// Symbol Properties symbol_properties: FxHashMap, + /// Instance prototype `__proto__`. + prototype: Value, /// Some rust object that stores internal state state: Option, /// Whether it can have new properties added to it. @@ -109,6 +111,7 @@ impl Default for Object { internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), + prototype: Value::null(), state: None, extensible: true, } @@ -122,7 +125,7 @@ impl Object { } /// Return a new ObjectData struct, with `kind` set to Ordinary - pub fn function(function: Function) -> Self { + pub fn function(function: Function, prototype: Value) -> Self { let _timer = BoaProfiler::global().start_event("Object::Function", "object"); Self { @@ -130,6 +133,7 @@ impl Object { internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), + prototype, state: None, extensible: true, } @@ -144,8 +148,7 @@ impl Object { // TODO: proto should be a &Value here pub fn create(proto: Value) -> Self { let mut obj = Self::default(); - obj.internal_slots - .insert(INSTANCE_PROTOTYPE.to_string(), proto); + obj.prototype = proto; obj } @@ -156,6 +159,7 @@ impl Object { internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), + prototype: Value::null(), state: None, extensible: true, } @@ -168,6 +172,7 @@ impl Object { internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), + prototype: Value::null(), state: None, extensible: true, } @@ -183,6 +188,7 @@ impl Object { internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), + prototype: Value::null(), state: None, extensible: true, } @@ -195,6 +201,7 @@ impl Object { internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), + prototype: Value::null(), state: None, extensible: true, } @@ -419,6 +426,15 @@ impl Object { pub fn state_mut(&mut self) -> &mut Option { &mut self.state } + + pub fn prototype(&self) -> &Value { + &self.prototype + } + + pub fn set_prototype(&mut self, prototype: Value) { + assert!(prototype.is_null() || prototype.is_object()); + self.prototype = prototype + } } /// Create a new object. @@ -476,14 +492,16 @@ pub fn is(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// Get the `prototype` of an object. pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("Cannot get object"); - Ok(obj.get_field(INSTANCE_PROTOTYPE)) + Ok(obj + .as_object() + .map_or_else(Value::undefined, |object| object.prototype.clone())) } /// Set the `prototype` of an object. pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("Cannot get object").clone(); let proto = args.get(1).expect("Cannot get object").clone(); - obj.set_internal_slot(INSTANCE_PROTOTYPE, proto); + obj.as_object_mut().unwrap().prototype = proto; Ok(obj) } diff --git a/boa/src/builtins/string/tests.rs b/boa/src/builtins/string/tests.rs index ce4673de07..ae11910846 100644 --- a/boa/src/builtins/string/tests.rs +++ b/boa/src/builtins/string/tests.rs @@ -86,7 +86,7 @@ fn generic_concat() { assert_eq!(a, "100 - 50 = 50"); } -#[allow(clippy::result_unwrap_used)] +#[allow(clippy::unwrap_used)] #[test] /// Test the correct type is returned from call and construct fn construct_and_call() { diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index f6c532d3df..dafe62fc74 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -174,6 +174,13 @@ impl From for Value { } } +impl From for Value { + fn from(object: GcObject) -> Self { + let _timer = BoaProfiler::global().start_event("From", "value"); + Value::Object(object) + } +} + #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct TryFromObjectError; diff --git a/boa/src/builtins/value/display.rs b/boa/src/builtins/value/display.rs index a169884df2..1d936e6d90 100644 --- a/boa/src/builtins/value/display.rs +++ b/boa/src/builtins/value/display.rs @@ -21,14 +21,24 @@ macro_rules! print_obj_value { } }; (internals of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => { - print_obj_value!(impl internal_slots, $obj, |(key, val)| { - format!( - "{:>width$}: {}", - key, - $display_fn(&val, $encounters, $indent.wrapping_add(4), true), - width = $indent, - ) - }) + { + let object = $obj.borrow(); + if object.prototype().is_object() { + vec![format!( + "{:>width$}: {}", + "__proto__", + $display_fn(object.prototype(), $encounters, $indent.wrapping_add(4), true), + width = $indent, + )] + } else { + vec![format!( + "{:>width$}: {}", + "__proto__", + object.prototype(), + width = $indent, + )] + } + } }; (props of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr, $print_internals:expr) => { print_obj_value!(impl properties, $obj, |(key, val)| { diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 8d5447dfe1..2e398ceffa 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -11,10 +11,7 @@ pub use crate::builtins::value::val_type::Type; use crate::builtins::{ function::Function, - object::{ - GcObject, InternalState, InternalStateCell, Object, ObjectData, INSTANCE_PROTOTYPE, - PROTOTYPE, - }, + object::{GcObject, InternalState, InternalStateCell, Object, ObjectData, PROTOTYPE}, property::Property, BigInt, Symbol, }; @@ -176,11 +173,7 @@ impl Value { pub fn new_object_from_prototype(proto: Value, data: ObjectData) -> Self { let mut object = Object::default(); object.data = data; - - object - .internal_slots_mut() - .insert(INSTANCE_PROTOTYPE.to_string(), proto); - + object.set_prototype(proto); Self::object(object) } @@ -493,10 +486,7 @@ impl Value { let object = object.borrow(); match object.properties().get(field) { Some(value) => Some(value.clone()), - None => object - .internal_slots() - .get(INSTANCE_PROTOTYPE) - .and_then(|value| value.get_property(field)), + None => object.prototype().get_property(field), } } _ => None, @@ -723,7 +713,8 @@ impl Value { // Get Length let length = function.params.len(); // Object with Kind set to function - let new_func = Object::function(function); + // TODO: FIXME: Add function prototype + let new_func = Object::function(function, Value::null()); // Wrap Object in GC'd Value let new_func_val = Value::from(new_func); // Set length to parameters diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 389a08ab0c..29fba39608 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -26,7 +26,7 @@ use crate::{ builtins::{ function::{Function as FunctionObject, FunctionBody, ThisMode}, number::{f64_to_int32, f64_to_uint32}, - object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, + object::{Object, ObjectData, PROTOTYPE}, property::Property, value::{RcBigInt, RcString, ResultValue, Type, Value}, BigInt, Console, Number, @@ -122,7 +122,7 @@ impl Interpreter { P: Into>, B: Into, { - let function_prototype = &self + let function_prototype = self .realm .environment .get_global_object() @@ -149,10 +149,9 @@ impl Interpreter { callable, ); - let new_func = Object::function(func); + let new_func = Object::function(func, function_prototype); let val = Value::from(new_func); - val.set_internal_slot(INSTANCE_PROTOTYPE, function_prototype.clone()); val.set_field(PROTOTYPE, proto); val.set_field("length", Value::from(params_len)); @@ -393,8 +392,7 @@ impl Interpreter { .expect("Could not get global object"), )); array.set_data(ObjectData::Array); - array.borrow().set_internal_slot( - INSTANCE_PROTOTYPE, + array.as_object_mut().expect("object").set_prototype( self.realm() .environment .get_binding_value("Array") @@ -402,9 +400,9 @@ impl Interpreter { .borrow() .get_field(PROTOTYPE), ); - array.borrow().set_field("0", key); - array.borrow().set_field("1", value); - array.borrow().set_field("length", Value::from(2)); + array.set_field("0", key); + array.set_field("1", value); + array.set_field("length", Value::from(2)); array }) .collect(); diff --git a/boa/src/exec/new/mod.rs b/boa/src/exec/new/mod.rs index b611b617aa..472b8817c3 100644 --- a/boa/src/exec/new/mod.rs +++ b/boa/src/exec/new/mod.rs @@ -1,7 +1,7 @@ use super::{Executable, Interpreter}; use crate::{ builtins::{ - object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, + object::{ObjectData, PROTOTYPE}, value::{ResultValue, Value}, }, syntax::ast::node::New, @@ -23,7 +23,9 @@ impl Executable for New { } let this = Value::new_object(None); // Create a blank object, then set its __proto__ property to the [Constructor].prototype - this.set_internal_slot(INSTANCE_PROTOTYPE, func_object.get_field(PROTOTYPE)); + this.as_object_mut() + .expect("this was not an object") + .set_prototype(func_object.get_field(PROTOTYPE)); match func_object { Value::Object(ref obj) => { diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 68cdaa39f6..801eb8555b 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -657,7 +657,7 @@ fn unary_delete() { #[cfg(test)] mod in_operator { use super::*; - use crate::{builtins::object::INSTANCE_PROTOTYPE, forward_val}; + use crate::forward_val; #[test] fn propery_in_object() { let p_in_o = r#" @@ -762,7 +762,7 @@ mod in_operator { "#; forward(&mut engine, scenario); let a = forward_val(&mut engine, "bar").unwrap(); - assert!(a.get_internal_slot(INSTANCE_PROTOTYPE).is_object(), true); + assert!(a.as_object().unwrap().prototype().is_object()); } } @@ -1122,7 +1122,7 @@ fn check_this_binding_in_object_literal() { a: 3, bar: function () { return this.a + 5 } }; - + foo.bar() "#; diff --git a/boa/src/syntax/parser/tests.rs b/boa/src/syntax/parser/tests.rs index cf41a996c1..2e238ec401 100644 --- a/boa/src/syntax/parser/tests.rs +++ b/boa/src/syntax/parser/tests.rs @@ -14,7 +14,7 @@ use crate::syntax::{ }; /// Checks that the given JavaScript string gives the expected expression. -#[allow(clippy::result_unwrap_used)] +#[allow(clippy::unwrap_used)] // TODO: #[track_caller]: https://github.com/rust-lang/rust/issues/47809 pub(super) fn check_parser(js: &str, expr: L) where