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. 11
      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. 26
      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. 6
      boa/src/exec/tests.rs
  15. 2
      boa/src/syntax/parser/tests.rs

11
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]

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

13
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

15
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

6
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)

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
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`.

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.
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<RcString, Property>,
/// Symbol Properties
symbol_properties: FxHashMap<u32, Property>,
/// Instance prototype `__proto__`.
prototype: Value,
/// Some rust object that stores internal state
state: Option<InternalStateCell>,
/// 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<InternalStateCell> {
&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)
}

2
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() {

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)]
pub struct TryFromObjectError;

26
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)| {

19
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

16
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<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
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();

6
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) => {

6
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()
"#;

2
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<L>(js: &str, expr: L)
where

Loading…
Cancel
Save