Browse Source

Extracted `__proto__` from internal slots (#580)

pull/582/head
HalidOdat 4 years ago committed by GitHub
parent
commit
d8eb7caefd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      boa/src/builtins/array/mod.rs
  2. 4
      boa/src/builtins/boolean/tests.rs
  3. 13
      boa/src/builtins/function/mod.rs
  4. 15
      boa/src/builtins/json/tests.rs
  5. 6
      boa/src/builtins/map/mod.rs
  6. 4
      boa/src/builtins/object/internal_methods.rs
  7. 32
      boa/src/builtins/object/mod.rs
  8. 2
      boa/src/builtins/string/tests.rs
  9. 7
      boa/src/builtins/value/conversions.rs
  10. 22
      boa/src/builtins/value/display.rs
  11. 19
      boa/src/builtins/value/mod.rs
  12. 16
      boa/src/exec/mod.rs
  13. 6
      boa/src/exec/new/mod.rs
  14. 4
      boa/src/exec/tests.rs
  15. 2
      boa/src/syntax/parser/tests.rs

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

@ -15,7 +15,7 @@ mod tests;
use super::function::{make_builtin_fn, make_constructor_fn}; use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
builtins::{ builtins::{
object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{ObjectData, PROTOTYPE},
property::Property, property::Property,
value::{same_value_zero, ResultValue, Value}, value::{same_value_zero, ResultValue, Value},
}, },
@ -48,8 +48,7 @@ impl Array {
.expect("Could not get global object"), .expect("Could not get global object"),
)); ));
array.set_data(ObjectData::Array); array.set_data(ObjectData::Array);
array.borrow().set_internal_slot( array.as_object_mut().expect("array object").set_prototype(
INSTANCE_PROTOTYPE,
interpreter interpreter
.realm() .realm()
.environment .environment
@ -116,7 +115,9 @@ impl Array {
// Set Prototype // Set Prototype
let prototype = ctx.realm.global_obj.get_field("Array").get_field(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 // This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name) // to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::Array); this.set_data(ObjectData::Array);

4
boa/src/builtins/boolean/tests.rs

@ -1,7 +1,7 @@
use crate::{builtins::value::same_value, exec::Interpreter, forward, forward_val, realm::Realm}; use crate::{builtins::value::same_value, exec::Interpreter, forward, forward_val, realm::Realm};
/// Test the correct type is returned from call and construct /// Test the correct type is returned from call and construct
#[allow(clippy::result_unwrap_used)] #[allow(clippy::unwrap_used)]
#[test] #[test]
fn construct_and_call() { fn construct_and_call() {
let realm = Realm::create(); 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"); let bool_prototype = forward_val(&mut engine, "boolProto").expect("value expected");
assert!(same_value( assert!(same_value(
&bool_instance.get_internal_slot("__proto__"), &bool_instance.as_object().unwrap().prototype().clone(),
&bool_prototype &bool_prototype
)); ));
} }

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

@ -14,7 +14,7 @@
use crate::{ use crate::{
builtins::{ builtins::{
array::Array, array::Array,
object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{Object, ObjectData, PROTOTYPE},
property::Property, property::Property,
value::{RcString, ResultValue, Value}, value::{RcString, ResultValue, Value},
}, },
@ -464,14 +464,10 @@ pub fn make_constructor_fn(
let mut function = Function::builtin(Vec::new(), body); let mut function = Function::builtin(Vec::new(), body);
function.flags = FunctionFlags::from_parameters(callable, constructable); function.flags = FunctionFlags::from_parameters(callable, constructable);
let mut constructor = Object::function(function);
// Get reference to Function.prototype // Get reference to Function.prototype
// Create the function object and point its instance prototype to Function.prototype // Create the function object and point its instance prototype to Function.prototype
constructor.set_internal_slot( let mut constructor =
INSTANCE_PROTOTYPE, Object::function(function, global.get_field("Function").get_field(PROTOTYPE));
global.get_field("Function").get_field(PROTOTYPE),
);
let length = Property::new() let length = Property::new()
.value(Value::from(length)) .value(Value::from(length))
@ -527,7 +523,8 @@ where
let name = name.into(); let name = name.into();
let _timer = BoaProfiler::global().start_event(&format!("make_builtin_fn: {}", &name), "init"); 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)); function.insert_field("length", Value::from(length));
parent parent

15
boa/src/builtins/json/tests.rs

@ -1,8 +1,5 @@
use crate::{ use crate::{
builtins::{ builtins::{object::PROTOTYPE, value::same_value},
object::{INSTANCE_PROTOTYPE, PROTOTYPE},
value::same_value,
},
exec::Interpreter, exec::Interpreter,
forward, forward_val, forward, forward_val,
realm::Realm, realm::Realm,
@ -284,10 +281,16 @@ fn json_parse_sets_prototypes() {
eprintln!("{}", forward(&mut engine, init)); eprintln!("{}", forward(&mut engine, init));
let object_prototype = forward_val(&mut engine, r#"jsonObj.ob"#) let object_prototype = forward_val(&mut engine, r#"jsonObj.ob"#)
.unwrap() .unwrap()
.get_internal_slot(INSTANCE_PROTOTYPE); .as_object()
.unwrap()
.prototype()
.clone();
let array_prototype = forward_val(&mut engine, r#"jsonObj.arr"#) let array_prototype = forward_val(&mut engine, r#"jsonObj.arr"#)
.unwrap() .unwrap()
.get_internal_slot(INSTANCE_PROTOTYPE); .as_object()
.unwrap()
.prototype()
.clone();
let global_object_prototype = engine let global_object_prototype = engine
.realm .realm
.global_obj .global_obj

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

@ -3,7 +3,7 @@
use super::function::{make_builtin_fn, make_constructor_fn}; use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
builtins::{ builtins::{
object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{ObjectData, PROTOTYPE},
property::Property, property::Property,
value::{ResultValue, Value}, value::{ResultValue, Value},
}, },
@ -235,7 +235,9 @@ impl Map {
// Set Prototype // Set Prototype
let prototype = ctx.realm.global_obj.get_field("Map").get_field(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 // This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name) // to its Javascript Identifier (global constructor method name)

4
boa/src/builtins/object/internal_methods.rs

@ -6,7 +6,7 @@
//! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots //! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
use crate::builtins::{ use crate::builtins::{
object::{Object, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{Object, PROTOTYPE},
property::Property, property::Property,
value::{same_value, RcString, Value}, 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 /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof
#[inline] #[inline]
pub fn get_prototype_of(&self) -> Value { 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`. /// Helper function to get an immutable internal slot or `Null`.

32
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. /// Static `prototype`, usually set on constructors as a key to point to their respective prototype object.
pub static PROTOTYPE: &str = "prototype"; pub static PROTOTYPE: &str = "prototype";
/// Static `__proto__`, usually set on Object instances as a key to point to their respective prototype object. // /// Static `__proto__`, usually set on Object instances as a key to point to their respective prototype object.
pub static INSTANCE_PROTOTYPE: &str = "__proto__"; // pub static INSTANCE_PROTOTYPE: &str = "__proto__";
/// The internal representation of an JavaScript object. /// The internal representation of an JavaScript object.
#[derive(Debug, Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
@ -58,6 +58,8 @@ pub struct Object {
properties: FxHashMap<RcString, Property>, properties: FxHashMap<RcString, Property>,
/// Symbol Properties /// Symbol Properties
symbol_properties: FxHashMap<u32, Property>, symbol_properties: FxHashMap<u32, Property>,
/// Instance prototype `__proto__`.
prototype: Value,
/// Some rust object that stores internal state /// Some rust object that stores internal state
state: Option<InternalStateCell>, state: Option<InternalStateCell>,
/// Whether it can have new properties added to it. /// Whether it can have new properties added to it.
@ -109,6 +111,7 @@ impl Default for Object {
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None, state: None,
extensible: true, extensible: true,
} }
@ -122,7 +125,7 @@ impl Object {
} }
/// Return a new ObjectData struct, with `kind` set to Ordinary /// 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"); let _timer = BoaProfiler::global().start_event("Object::Function", "object");
Self { Self {
@ -130,6 +133,7 @@ impl Object {
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
prototype,
state: None, state: None,
extensible: true, extensible: true,
} }
@ -144,8 +148,7 @@ impl Object {
// TODO: proto should be a &Value here // TODO: proto should be a &Value here
pub fn create(proto: Value) -> Self { pub fn create(proto: Value) -> Self {
let mut obj = Self::default(); let mut obj = Self::default();
obj.internal_slots obj.prototype = proto;
.insert(INSTANCE_PROTOTYPE.to_string(), proto);
obj obj
} }
@ -156,6 +159,7 @@ impl Object {
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None, state: None,
extensible: true, extensible: true,
} }
@ -168,6 +172,7 @@ impl Object {
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None, state: None,
extensible: true, extensible: true,
} }
@ -183,6 +188,7 @@ impl Object {
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None, state: None,
extensible: true, extensible: true,
} }
@ -195,6 +201,7 @@ impl Object {
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None, state: None,
extensible: true, extensible: true,
} }
@ -419,6 +426,15 @@ impl Object {
pub fn state_mut(&mut self) -> &mut Option<InternalStateCell> { pub fn state_mut(&mut self) -> &mut Option<InternalStateCell> {
&mut self.state &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. /// Create a new object.
@ -476,14 +492,16 @@ pub fn is(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// Get the `prototype` of an object. /// Get the `prototype` of an object.
pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object"); 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. /// Set the `prototype` of an object.
pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object").clone(); let obj = args.get(0).expect("Cannot get object").clone();
let proto = args.get(1).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) Ok(obj)
} }

2
boa/src/builtins/string/tests.rs

@ -86,7 +86,7 @@ fn generic_concat() {
assert_eq!(a, "100 - 50 = 50"); assert_eq!(a, "100 - 50 = 50");
} }
#[allow(clippy::result_unwrap_used)] #[allow(clippy::unwrap_used)]
#[test] #[test]
/// Test the correct type is returned from call and construct /// Test the correct type is returned from call and construct
fn construct_and_call() { fn construct_and_call() {

7
boa/src/builtins/value/conversions.rs

@ -174,6 +174,13 @@ impl From<Object> for Value {
} }
} }
impl From<GcObject> for Value {
fn from(object: GcObject) -> Self {
let _timer = BoaProfiler::global().start_event("From<GcObject>", "value");
Value::Object(object)
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct TryFromObjectError; pub struct TryFromObjectError;

22
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) => { (internals of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => {
print_obj_value!(impl internal_slots, $obj, |(key, val)| { {
format!( let object = $obj.borrow();
if object.prototype().is_object() {
vec![format!(
"{:>width$}: {}", "{:>width$}: {}",
key, "__proto__",
$display_fn(&val, $encounters, $indent.wrapping_add(4), true), $display_fn(object.prototype(), $encounters, $indent.wrapping_add(4), true),
width = $indent, 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) => { (props of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr, $print_internals:expr) => {
print_obj_value!(impl properties, $obj, |(key, val)| { print_obj_value!(impl properties, $obj, |(key, val)| {

19
boa/src/builtins/value/mod.rs

@ -11,10 +11,7 @@ pub use crate::builtins::value::val_type::Type;
use crate::builtins::{ use crate::builtins::{
function::Function, function::Function,
object::{ object::{GcObject, InternalState, InternalStateCell, Object, ObjectData, PROTOTYPE},
GcObject, InternalState, InternalStateCell, Object, ObjectData, INSTANCE_PROTOTYPE,
PROTOTYPE,
},
property::Property, property::Property,
BigInt, Symbol, BigInt, Symbol,
}; };
@ -176,11 +173,7 @@ impl Value {
pub fn new_object_from_prototype(proto: Value, data: ObjectData) -> Self { pub fn new_object_from_prototype(proto: Value, data: ObjectData) -> Self {
let mut object = Object::default(); let mut object = Object::default();
object.data = data; object.data = data;
object.set_prototype(proto);
object
.internal_slots_mut()
.insert(INSTANCE_PROTOTYPE.to_string(), proto);
Self::object(object) Self::object(object)
} }
@ -493,10 +486,7 @@ impl Value {
let object = object.borrow(); let object = object.borrow();
match object.properties().get(field) { match object.properties().get(field) {
Some(value) => Some(value.clone()), Some(value) => Some(value.clone()),
None => object None => object.prototype().get_property(field),
.internal_slots()
.get(INSTANCE_PROTOTYPE)
.and_then(|value| value.get_property(field)),
} }
} }
_ => None, _ => None,
@ -723,7 +713,8 @@ impl Value {
// Get Length // Get Length
let length = function.params.len(); let length = function.params.len();
// Object with Kind set to function // 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 // Wrap Object in GC'd Value
let new_func_val = Value::from(new_func); let new_func_val = Value::from(new_func);
// Set length to parameters // Set length to parameters

16
boa/src/exec/mod.rs

@ -26,7 +26,7 @@ use crate::{
builtins::{ builtins::{
function::{Function as FunctionObject, FunctionBody, ThisMode}, function::{Function as FunctionObject, FunctionBody, ThisMode},
number::{f64_to_int32, f64_to_uint32}, number::{f64_to_int32, f64_to_uint32},
object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{Object, ObjectData, PROTOTYPE},
property::Property, property::Property,
value::{RcBigInt, RcString, ResultValue, Type, Value}, value::{RcBigInt, RcString, ResultValue, Type, Value},
BigInt, Console, Number, BigInt, Console, Number,
@ -122,7 +122,7 @@ impl Interpreter {
P: Into<Box<[FormalParameter]>>, P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>, B: Into<StatementList>,
{ {
let function_prototype = &self let function_prototype = self
.realm .realm
.environment .environment
.get_global_object() .get_global_object()
@ -149,10 +149,9 @@ impl Interpreter {
callable, callable,
); );
let new_func = Object::function(func); let new_func = Object::function(func, function_prototype);
let val = Value::from(new_func); let val = Value::from(new_func);
val.set_internal_slot(INSTANCE_PROTOTYPE, function_prototype.clone());
val.set_field(PROTOTYPE, proto); val.set_field(PROTOTYPE, proto);
val.set_field("length", Value::from(params_len)); val.set_field("length", Value::from(params_len));
@ -393,8 +392,7 @@ impl Interpreter {
.expect("Could not get global object"), .expect("Could not get global object"),
)); ));
array.set_data(ObjectData::Array); array.set_data(ObjectData::Array);
array.borrow().set_internal_slot( array.as_object_mut().expect("object").set_prototype(
INSTANCE_PROTOTYPE,
self.realm() self.realm()
.environment .environment
.get_binding_value("Array") .get_binding_value("Array")
@ -402,9 +400,9 @@ impl Interpreter {
.borrow() .borrow()
.get_field(PROTOTYPE), .get_field(PROTOTYPE),
); );
array.borrow().set_field("0", key); array.set_field("0", key);
array.borrow().set_field("1", value); array.set_field("1", value);
array.borrow().set_field("length", Value::from(2)); array.set_field("length", Value::from(2));
array array
}) })
.collect(); .collect();

6
boa/src/exec/new/mod.rs

@ -1,7 +1,7 @@
use super::{Executable, Interpreter}; use super::{Executable, Interpreter};
use crate::{ use crate::{
builtins::{ builtins::{
object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{ObjectData, PROTOTYPE},
value::{ResultValue, Value}, value::{ResultValue, Value},
}, },
syntax::ast::node::New, syntax::ast::node::New,
@ -23,7 +23,9 @@ impl Executable for New {
} }
let this = Value::new_object(None); let this = Value::new_object(None);
// Create a blank object, then set its __proto__ property to the [Constructor].prototype // 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 { match func_object {
Value::Object(ref obj) => { Value::Object(ref obj) => {

4
boa/src/exec/tests.rs

@ -657,7 +657,7 @@ fn unary_delete() {
#[cfg(test)] #[cfg(test)]
mod in_operator { mod in_operator {
use super::*; use super::*;
use crate::{builtins::object::INSTANCE_PROTOTYPE, forward_val}; use crate::forward_val;
#[test] #[test]
fn propery_in_object() { fn propery_in_object() {
let p_in_o = r#" let p_in_o = r#"
@ -762,7 +762,7 @@ mod in_operator {
"#; "#;
forward(&mut engine, scenario); forward(&mut engine, scenario);
let a = forward_val(&mut engine, "bar").unwrap(); 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());
} }
} }

2
boa/src/syntax/parser/tests.rs

@ -14,7 +14,7 @@ use crate::syntax::{
}; };
/// Checks that the given JavaScript string gives the expected expression. /// 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 // TODO: #[track_caller]: https://github.com/rust-lang/rust/issues/47809
pub(super) fn check_parser<L>(js: &str, expr: L) pub(super) fn check_parser<L>(js: &str, expr: L)
where where

Loading…
Cancel
Save