Skip to content

Commit

Permalink
Extracted __proto__ from internal slots
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed Jul 20, 2020
1 parent 08a608a commit 875b291
Show file tree
Hide file tree
Showing 15 changed files with 99 additions and 70 deletions.
11 changes: 6 additions & 5 deletions boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
},
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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]
Expand Down
4 changes: 2 additions & 2 deletions boa/src/builtins/boolean/tests.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -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 changes: 5 additions & 8 deletions boa/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
},
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
15 changes: 9 additions & 6 deletions boa/src/builtins/json/tests.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions boa/src/builtins/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
},
Expand Down Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions boa/src/builtins/object/internal_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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`.
Expand Down
32 changes: 25 additions & 7 deletions boa/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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.
Expand Down Expand Up @@ -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,
}
Expand All @@ -122,14 +125,15 @@ 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 {
data: ObjectData::Function(function),
internal_slots: FxHashMap::default(),
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype,
state: None,
extensible: true,
}
Expand All @@ -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
}

Expand All @@ -156,6 +159,7 @@ impl Object {
internal_slots: FxHashMap::default(),
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None,
extensible: true,
}
Expand All @@ -168,6 +172,7 @@ impl Object {
internal_slots: FxHashMap::default(),
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None,
extensible: true,
}
Expand All @@ -183,6 +188,7 @@ impl Object {
internal_slots: FxHashMap::default(),
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None,
extensible: true,
}
Expand All @@ -195,6 +201,7 @@ impl Object {
internal_slots: FxHashMap::default(),
properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(),
prototype: Value::null(),
state: None,
extensible: true,
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}

Expand Down
2 changes: 1 addition & 1 deletion boa/src/builtins/string/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
7 changes: 7 additions & 0 deletions boa/src/builtins/value/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
26 changes: 18 additions & 8 deletions boa/src/builtins/value/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)| {
Expand Down
19 changes: 5 additions & 14 deletions boa/src/builtins/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 875b291

Please sign in to comment.