Browse Source

creating trait for object-internal-methods

pull/256/head
Jason Williams 4 years ago
parent
commit
686d17a002
  1. 2
      boa/src/builtins/boolean.rs
  2. 2
      boa/src/builtins/function.rs
  3. 2
      boa/src/builtins/number.rs
  4. 167
      boa/src/builtins/object/internal_methods_trait.rs
  5. 340
      boa/src/builtins/object/mod.rs
  6. 5
      boa/src/builtins/symbol.rs
  7. 5
      boa/src/builtins/value.rs
  8. 5
      boa/src/exec.rs

2
boa/src/builtins/boolean.rs

@ -1,7 +1,7 @@
use crate::{ use crate::{
builtins::{ builtins::{
function::NativeFunctionData, function::NativeFunctionData,
object::{Object, ObjectKind, PROTOTYPE}, object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData}, value::{to_value, ResultValue, Value, ValueData},
}, },
exec::Interpreter, exec::Interpreter,

2
boa/src/builtins/function.rs

@ -1,6 +1,6 @@
use crate::{ use crate::{
builtins::{ builtins::{
object::Object, object::{internal_methods_trait::ObjectInternalMethods, Object},
property::Property, property::Property,
value::{to_value, ResultValue, Value, ValueData}, value::{to_value, ResultValue, Value, ValueData},
}, },

2
boa/src/builtins/number.rs

@ -1,7 +1,7 @@
use crate::{ use crate::{
builtins::{ builtins::{
function::NativeFunctionData, function::NativeFunctionData,
object::{Object, ObjectKind, PROTOTYPE}, object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData}, value::{to_value, ResultValue, Value, ValueData},
}, },
exec::Interpreter, exec::Interpreter,

167
boa/src/builtins/object/internal_methods_trait.rs

@ -0,0 +1,167 @@
use crate::builtins::{
object::{Object, PROTOTYPE},
property::Property,
value::{to_value, Value, ValueData},
};
use gc::Gc;
use std::borrow::Borrow;
use std::ops::Deref;
/// Here lies the internal methods for ordinary objects.
/// Most objects make use of these methods, including exotic objects like functions.
/// So thats why this is a trait
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots>
pub trait ObjectInternalMethods {
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
fn has_property(&self, val: &Value) -> bool {
debug_assert!(Property::is_property_key(val));
let prop = self.get_own_property(val);
if prop.value.is_none() {
let parent: Value = self.get_prototype_of();
if !parent.is_null() {
// the parent value variant should be an object
// In the unlikely event it isn't return false
return match *parent {
ValueData::Object(ref obj) => obj.borrow().has_property(val),
_ => false,
};
}
return false;
}
true
}
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible
fn is_extensible(&self) -> bool {
let val = self.get_internal_slot("extensible");
match *val.deref().borrow() {
ValueData::Boolean(b) => b,
_ => false,
}
}
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
fn prevent_extensions(&mut self) -> bool {
self.set_internal_slot("extensible", to_value(false));
true
}
// [[Delete]]
fn delete(&mut self, prop_key: &Value) -> bool {
debug_assert!(Property::is_property_key(prop_key));
let desc = self.get_own_property(prop_key);
if desc
.value
.clone()
.expect("unable to get value")
.is_undefined()
{
return true;
}
if desc.configurable.expect("unable to get value") {
self.remove_property(&prop_key.to_string());
return true;
}
false
}
// [[Get]]
fn get(&self, val: &Value) -> Value {
debug_assert!(Property::is_property_key(val));
let desc = self.get_own_property(val);
if desc.value.clone().is_none()
|| desc
.value
.clone()
.expect("Failed to get object")
.is_undefined()
{
// parent will either be null or an Object
let parent = self.get_prototype_of();
if parent.is_null() {
return Gc::new(ValueData::Undefined);
}
let parent_obj = Object::from(&parent).expect("Failed to get object");
return parent_obj.get(val);
}
if desc.is_data_descriptor() {
return desc.value.clone().expect("failed to extract value");
}
let getter = desc.get.clone();
if getter.is_none() || getter.expect("Failed to get object").is_undefined() {
return Gc::new(ValueData::Undefined);
}
// TODO!!!!! Call getter from here
Gc::new(ValueData::Undefined)
}
/// [[Set]]
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver>
fn set(&mut self, field: Value, val: Value) -> bool {
// [1]
debug_assert!(Property::is_property_key(&field));
// Fetch property key
let mut own_desc = self.get_own_property(&field);
// [2]
if own_desc.is_none() {
let parent = self.get_prototype_of();
if !parent.is_null() {
// TODO: come back to this
}
own_desc = Property::new()
.writable(true)
.enumerable(true)
.configurable(true);
}
// [3]
if own_desc.is_data_descriptor() {
if !own_desc.writable.unwrap() {
return false;
}
// Change value on the current descriptor
own_desc = own_desc.value(val);
return self.define_own_property(field.to_string(), own_desc);
}
// [4]
debug_assert!(own_desc.is_accessor_descriptor());
match own_desc.set {
None => false,
Some(_) => {
unimplemented!();
}
}
}
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
/// The specification returns a Property Descriptor or Undefined. These are 2 separate types and we can't do that here.
fn get_own_property(&self, prop: &Value) -> Property;
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v
fn set_prototype_of(&mut self, val: Value) -> bool;
/// Returns either the prototype or null
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof
fn get_prototype_of(&self) -> Value {
self.get_internal_slot(PROTOTYPE)
}
fn define_own_property(&mut self, property_key: String, desc: Property) -> bool;
/// Utility function to get an immutable internal slot or Null
fn get_internal_slot(&self, name: &str) -> Value;
fn set_internal_slot(&mut self, name: &str, val: Value);
fn insert_property(&mut self, name: String, p: Property);
fn remove_property(&mut self, name: &str);
}

340
boa/src/builtins/object.rs → boa/src/builtins/object/mod.rs

@ -10,8 +10,10 @@ use gc::Gc;
use gc_derive::{Finalize, Trace}; use gc_derive::{Finalize, Trace};
use std::{borrow::Borrow, collections::HashMap, ops::Deref}; use std::{borrow::Borrow, collections::HashMap, ops::Deref};
pub use internal_methods_trait::ObjectInternalMethods;
pub use internal_state::{InternalState, InternalStateCell}; pub use internal_state::{InternalState, InternalStateCell};
pub mod internal_methods_trait;
mod internal_state; mod internal_state;
/// 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.
@ -35,126 +37,9 @@ pub struct Object {
pub state: Option<Box<InternalStateCell>>, pub state: Option<Box<InternalStateCell>>,
} }
impl Object { impl ObjectInternalMethods for Object {
/// Return a new ObjectData struct, with `kind` set to Ordinary
pub fn default() -> Self {
let mut object = Object {
kind: ObjectKind::Ordinary,
internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
};
object.set_internal_slot("extensible", to_value(true));
object
}
/// ObjectCreate is used to specify the runtime creation of new ordinary objects
///
/// https://tc39.es/ecma262/#sec-objectcreate
// TODO: proto should be a &Value here
pub fn create(proto: Value) -> Object {
let mut obj = Object::default();
obj.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), proto);
obj.internal_slots
.insert("extensible".to_string(), to_value(true));
obj
}
/// Utility function to get an immutable internal slot or Null
pub fn get_internal_slot(&self, name: &str) -> Value {
match self.internal_slots.get(name) {
Some(v) => v.clone(),
None => Gc::new(ValueData::Null),
}
}
/// Utility function to set an internal slot
pub fn set_internal_slot(&mut self, name: &str, val: Value) {
self.internal_slots.insert(name.to_string(), val);
}
/// Utility function to set an internal slot which is a function
pub fn set_internal_method(&mut self, name: &str, val: NativeFunctionData) {
self.internal_slots.insert(name.to_string(), to_value(val));
}
/// Utility function to set a method on this object
/// The native function will live in the `properties` field of the Object
pub fn set_method(&mut self, name: &str, val: NativeFunctionData) {
self.properties
.insert(name.to_string(), Property::default().value(to_value(val)));
}
/// Return a new Boolean object whose [[BooleanData]] internal slot is set to argument.
fn from_boolean(argument: &Value) -> Self {
let mut obj = Object {
kind: ObjectKind::Boolean,
internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
};
obj.internal_slots
.insert("BooleanData".to_string(), argument.clone());
obj
}
/// Return a new Number object whose [[NumberData]] internal slot is set to argument.
fn from_number(argument: &Value) -> Self {
let mut obj = Object {
kind: ObjectKind::Number,
internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
};
obj.internal_slots
.insert("NumberData".to_string(), argument.clone());
obj
}
/// Return a new String object whose [[StringData]] internal slot is set to argument.
fn from_string(argument: &Value) -> Self {
let mut obj = Object {
kind: ObjectKind::String,
internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
};
obj.internal_slots
.insert("StringData".to_string(), argument.clone());
obj
}
// https://tc39.es/ecma262/#sec-toobject
pub fn from(value: &Value) -> Result<Self, ()> {
match *value.deref().borrow() {
ValueData::Boolean(_) => Ok(Self::from_boolean(value)),
ValueData::Number(_) => Ok(Self::from_number(value)),
ValueData::String(_) => Ok(Self::from_string(value)),
ValueData::Object(ref obj) => Ok(obj.borrow().clone()),
_ => Err(()),
}
}
/// Returns either the prototype or null
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof
pub fn get_prototype_of(&self) -> Value {
match self.internal_slots.get(PROTOTYPE) {
Some(v) => v.clone(),
None => Gc::new(ValueData::Null),
}
}
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v
pub fn set_prototype_of(&mut self, val: Value) -> bool { fn set_prototype_of(&mut self, val: Value) -> bool {
debug_assert!(val.is_object() || val.is_null()); debug_assert!(val.is_object() || val.is_null());
let current = self.get_internal_slot(PROTOTYPE); let current = self.get_internal_slot(PROTOTYPE);
if current == val { if current == val {
@ -179,26 +64,30 @@ impl Object {
true true
} }
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible fn insert_property(&mut self, name: String, p: Property) {
pub fn is_extensible(&self) -> bool { self.properties.insert(name, p);
match self.internal_slots.get("extensible") {
Some(ref v) => {
// try dereferencing it: `&(*v).clone()`
from_value((*v).clone()).expect("boolean expected")
}
None => false,
}
} }
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions fn remove_property(&mut self, name: &str) {
pub fn prevent_extensions(&mut self) -> bool { self.properties.remove(name);
self.set_internal_slot("extensible", to_value(false)); }
true
/// Utility function to set an internal slot
fn set_internal_slot(&mut self, name: &str, val: Value) {
self.internal_slots.insert(name.to_string(), val);
}
/// Utility function to get an immutable internal slot or Null
fn get_internal_slot(&self, name: &str) -> Value {
match self.internal_slots.get(name) {
Some(v) => v.clone(),
None => Gc::new(ValueData::Null),
}
} }
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
/// The specification returns a Property Descriptor or Undefined. These are 2 separate types and we can't do that here. /// The specification returns a Property Descriptor or Undefined. These are 2 separate types and we can't do that here.
pub fn get_own_property(&self, prop: &Value) -> Property { fn get_own_property(&self, prop: &Value) -> Property {
debug_assert!(Property::is_property_key(prop)); debug_assert!(Property::is_property_key(prop));
// Prop could either be a String or Symbol // Prop could either be a String or Symbol
match *(*prop) { match *(*prop) {
@ -254,28 +143,8 @@ impl Object {
} }
} }
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
pub fn has_property(&self, val: &Value) -> bool {
debug_assert!(Property::is_property_key(val));
let prop = self.get_own_property(val);
if prop.value.is_none() {
let parent: Value = self.get_prototype_of();
if !parent.is_null() {
// the parent value variant should be an object
// In the unlikely event it isn't return false
return match *parent {
ValueData::Object(ref obj) => obj.borrow().has_property(val),
_ => false,
};
}
return false;
}
true
}
#[allow(clippy::option_unwrap_used)] #[allow(clippy::option_unwrap_used)]
pub fn define_own_property(&mut self, property_key: String, desc: Property) -> bool { fn define_own_property(&mut self, property_key: String, desc: Property) -> bool {
let mut current = self.get_own_property(&to_value(property_key.to_string())); let mut current = self.get_own_property(&to_value(property_key.to_string()));
let extensible = self.is_extensible(); let extensible = self.is_extensible();
@ -399,98 +268,101 @@ impl Object {
self.properties.insert(property_key, desc); self.properties.insert(property_key, desc);
true true
} }
}
// [[Delete]] impl Object {
pub fn delete(&mut self, prop_key: &Value) -> bool { /// Return a new ObjectData struct, with `kind` set to Ordinary
debug_assert!(Property::is_property_key(prop_key)); pub fn default() -> Self {
let desc = self.get_own_property(prop_key); let mut object = Object {
if desc kind: ObjectKind::Ordinary,
.value internal_slots: Box::new(HashMap::new()),
.clone() properties: Box::new(HashMap::new()),
.expect("unable to get value") sym_properties: Box::new(HashMap::new()),
.is_undefined() state: None,
{ };
return true;
}
if desc.configurable.expect("unable to get value") {
self.properties.remove(&prop_key.to_string());
return true;
}
false object.set_internal_slot("extensible", to_value(true));
object
} }
// [[Get]] /// ObjectCreate is used to specify the runtime creation of new ordinary objects
pub fn get(&self, val: &Value) -> Value { ///
debug_assert!(Property::is_property_key(val)); /// https://tc39.es/ecma262/#sec-objectcreate
let desc = self.get_own_property(val); // TODO: proto should be a &Value here
if desc.value.clone().is_none() pub fn create(proto: Value) -> Object {
|| desc let mut obj = Object::default();
.value obj.internal_slots
.clone() .insert(INSTANCE_PROTOTYPE.to_string(), proto);
.expect("Failed to get object") obj.internal_slots
.is_undefined() .insert("extensible".to_string(), to_value(true));
{ obj
// parent will either be null or an Object }
let parent = self.get_prototype_of();
if parent.is_null() {
return Gc::new(ValueData::Undefined);
}
let parent_obj = Object::from(&parent).expect("Failed to get object"); /// Utility function to set an internal slot which is a function
pub fn set_internal_method(&mut self, name: &str, val: NativeFunctionData) {
self.internal_slots.insert(name.to_string(), to_value(val));
}
return parent_obj.get(val); /// Utility function to set a method on this object
} /// The native function will live in the `properties` field of the Object
pub fn set_method(&mut self, name: &str, val: NativeFunctionData) {
self.properties
.insert(name.to_string(), Property::default().value(to_value(val)));
}
if desc.is_data_descriptor() { /// Return a new Boolean object whose [[BooleanData]] internal slot is set to argument.
return desc.value.clone().expect("failed to extract value"); fn from_boolean(argument: &Value) -> Self {
} let mut obj = Object {
kind: ObjectKind::Boolean,
internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
};
let getter = desc.get.clone(); obj.internal_slots
if getter.is_none() || getter.expect("Failed to get object").is_undefined() { .insert("BooleanData".to_string(), argument.clone());
return Gc::new(ValueData::Undefined); obj
} }
/// Return a new Number object whose [[NumberData]] internal slot is set to argument.
fn from_number(argument: &Value) -> Self {
let mut obj = Object {
kind: ObjectKind::Number,
internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
state: None,
};
// TODO!!!!! Call getter from here obj.internal_slots
Gc::new(ValueData::Undefined) .insert("NumberData".to_string(), argument.clone());
obj
} }
/// [[Set]] /// Return a new String object whose [[StringData]] internal slot is set to argument.
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver> fn from_string(argument: &Value) -> Self {
pub fn set(&mut self, field: Value, val: Value) -> bool { let mut obj = Object {
// [1] kind: ObjectKind::String,
debug_assert!(Property::is_property_key(&field)); internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
// Fetch property key sym_properties: Box::new(HashMap::new()),
let mut own_desc = self.get_own_property(&field); state: None,
// [2] };
if own_desc.is_none() {
let parent = self.get_prototype_of();
if !parent.is_null() {
// TODO: come back to this
}
own_desc = Property::new()
.writable(true)
.enumerable(true)
.configurable(true);
}
// [3]
if own_desc.is_data_descriptor() {
if !own_desc.writable.unwrap() {
return false;
}
// Change value on the current descriptor obj.internal_slots
own_desc = own_desc.value(val); .insert("StringData".to_string(), argument.clone());
return self.define_own_property(field.to_string(), own_desc); obj
} }
// [4]
debug_assert!(own_desc.is_accessor_descriptor()); // https://tc39.es/ecma262/#sec-toobject
match own_desc.set { pub fn from(value: &Value) -> Result<Self, ()> {
None => false, match *value.deref().borrow() {
Some(_) => { ValueData::Boolean(_) => Ok(Self::from_boolean(value)),
unimplemented!(); ValueData::Number(_) => Ok(Self::from_number(value)),
} ValueData::String(_) => Ok(Self::from_string(value)),
ValueData::Object(ref obj) => Ok(obj.borrow().clone()),
_ => Err(()),
} }
} }
} }

5
boa/src/builtins/symbol.rs

@ -1,6 +1,9 @@
use crate::{ use crate::{
builtins::{ builtins::{
object::{Object, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{
internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, INSTANCE_PROTOTYPE,
PROTOTYPE,
},
value::{to_value, ResultValue, Value, ValueData}, value::{to_value, ResultValue, Value, ValueData},
}, },
exec::Interpreter, exec::Interpreter,

5
boa/src/builtins/value.rs

@ -1,6 +1,9 @@
use crate::builtins::{ use crate::builtins::{
function::{Function, NativeFunction, NativeFunctionData}, function::{Function, NativeFunction, NativeFunctionData},
object::{InternalState, InternalStateCell, Object, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{
internal_methods_trait::ObjectInternalMethods, InternalState, InternalStateCell, Object,
ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE,
},
property::Property, property::Property,
}; };
use gc::{Gc, GcCell}; use gc::{Gc, GcCell};

5
boa/src/exec.rs

@ -2,7 +2,10 @@ use crate::{
builtins::{ builtins::{
array, array,
function::{create_unmapped_arguments_object, Function, RegularFunction}, function::{create_unmapped_arguments_object, Function, RegularFunction},
object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{
internal_methods_trait::ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE,
PROTOTYPE,
},
value::{from_value, to_value, ResultValue, Value, ValueData}, value::{from_value, to_value, ResultValue, Value, ValueData},
}, },
environment::lexical_environment::{ environment::lexical_environment::{

Loading…
Cancel
Save