Skip to content

Commit

Permalink
function objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason Williams authored and jasonwilliams committed Apr 19, 2020
1 parent eb020d7 commit f62b7b5
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 130 deletions.
157 changes: 157 additions & 0 deletions boa/src/builtins/function_object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use crate::builtins::object::{Object, ObjectInternalMethods, ObjectKind, PROTOTYPE};
use crate::builtins::property::Property;
use crate::builtins::value::{same_value, to_value, Value, ValueData};
use gc::Gc;
use gc_derive::{Finalize, Trace};
use std::collections::HashMap;
/// Sets the functionKind
#[derive(Trace, Finalize, Debug, Clone)]
pub enum FunctionKind {
Normal,
ClassConstructor,
Generator,
Async,
AsyncGenerator,
NonConstructor,
}
/// Sets the ConstructorKind
#[derive(Debug, Copy, Clone)]
pub enum ConstructorKind {
Base,
Derived,
}
/// Defines how this references are interpreted within the formal parameters and code body of the function.
#[derive(Debug, Copy, Clone)]
pub enum ThisMode {
Lexical,
Strict,
Global,
}
/// Boa representation of a Function Object.
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects>
#[derive(Trace, Finalize, Debug, Clone)]
pub struct Function {
/// Kind, this *may* not be needed but will keep for now
pub kind: ObjectKind,
/// Internal Slots
pub internal_slots: Box<HashMap<String, Value>>,
/// Properties
pub properties: Box<HashMap<String, Property>>,
// Function Kind
pub function_kind: FunctionKind,
// is constructor??
pub is_constructor: bool,
}

impl Function {
// https://tc39.es/ecma262/#sec-functionallocate
pub fn allocate(proto: Value, mut kind: FunctionKind) -> Function {
let needs_construct: bool;

match kind {
FunctionKind::Normal => needs_construct = true,
FunctionKind::NonConstructor => {
needs_construct = false;
kind = FunctionKind::Normal;
}
_ => needs_construct = false,
}

let mut func = Function {
kind: ObjectKind::Function,
internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
function_kind: kind,
is_constructor: needs_construct,
};

func.set_internal_slot("extensible", to_value(true));
func.set_internal_slot(PROTOTYPE, to_value(proto.clone()));
// TODO: set to current realm record
func
}
}

impl ObjectInternalMethods for Function {
/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v
fn set_prototype_of(&mut self, val: Value) -> bool {
debug_assert!(val.is_object() || val.is_null());
let current = self.get_internal_slot(PROTOTYPE);
if current == val {
return true;
}
let extensible = self.get_internal_slot("extensible");
if extensible.is_null() {
return false;
}
let mut p = val.clone();
let mut done = false;
while !done {
if p.is_null() {
done = true
} else if same_value(&to_value(self.clone()), &p, false) {
return false;
} else {
p = p.get_internal_slot(PROTOTYPE);
}
}
self.set_internal_slot(PROTOTYPE, val);
true
}

/// 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 {
debug_assert!(Property::is_property_key(prop));
match self.properties.get(&prop.to_string()) {
// If O does not have an own property with key P, return undefined.
// In this case we return a new empty Property
None => Property::default(),
Some(ref v) => {
let mut d = Property::default();
if v.is_data_descriptor() {
d.value = v.value.clone();
d.writable = v.writable;
} else {
debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone();
d.set = v.set.clone();
}
d.enumerable = v.enumerable;
d.configurable = v.configurable;
d
}
}
}

/// Insert property into properties hashmap
fn insert_property(&mut self, name: String, p: Property) {
self.properties.insert(name, p);
}

/// Remove property from properties hashmap
fn remove_property(&mut self, name: &str) {
self.properties.remove(&name.to_string());
}

/// 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),
}
}

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

/// Function Prototype
/// <https://tc39.es/ecma262/#sec-properties-of-the-function-prototype-object>
pub fn create_function_prototype() {
let mut function_prototype: Object = Object::default();
// Set Kind to function
function_prototype.kind = ObjectKind::Function;
}
2 changes: 2 additions & 0 deletions boa/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub mod console;
pub mod error;
/// The global `Function` object and function value representations
pub mod function;
/// The new global `Function` object and function value representations
pub mod function_object;
/// The global `JSON` object
pub mod json;
/// The global `Math` object
Expand Down
108 changes: 106 additions & 2 deletions boa/src/builtins/object/internal_methods_trait.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::builtins::value::same_value;
use crate::builtins::{
object::{Object, PROTOTYPE},
property::Property,
Expand Down Expand Up @@ -141,6 +142,111 @@ pub trait ObjectInternalMethods {
}
}

#[allow(clippy::option_unwrap_used)]
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 extensible = self.is_extensible();

// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
// There currently isn't a property, lets create a new one
if current.value.is_none() || current.value.as_ref().expect("failed").is_undefined() {
if !extensible {
return false;
}

self.insert_property(property_key, desc);
return true;
}
// If every field is absent we don't need to set anything
if desc.is_none() {
return true;
}

// 4
if !current.configurable.unwrap_or(false) {
if desc.configurable.is_some() && desc.configurable.unwrap() {
return false;
}

if desc.enumerable.is_some()
&& (desc.enumerable.as_ref().unwrap() != current.enumerable.as_ref().unwrap())
{
return false;
}
}

// 5
if desc.is_generic_descriptor() {
// 6
} else if current.is_data_descriptor() != desc.is_data_descriptor() {
// a
if !current.configurable.unwrap() {
return false;
}
// b
if current.is_data_descriptor() {
// Convert to accessor
current.value = None;
current.writable = None;
} else {
// c
// convert to data
current.get = None;
current.set = None;
}

self.insert_property(property_key.clone(), current.clone());
// 7
} else if current.is_data_descriptor() && desc.is_data_descriptor() {
// a
if !current.configurable.unwrap() && !current.writable.unwrap() {
if desc.writable.is_some() && desc.writable.unwrap() {
return false;
}

if desc.value.is_some()
&& !same_value(
&desc.value.clone().unwrap(),
&current.value.clone().unwrap(),
false,
)
{
return false;
}

return true;
}
// 8
} else {
if !current.configurable.unwrap() {
if desc.set.is_some()
&& !same_value(
&desc.set.clone().unwrap(),
&current.set.clone().unwrap(),
false,
)
{
return false;
}

if desc.get.is_some()
&& !same_value(
&desc.get.clone().unwrap(),
&current.get.clone().unwrap(),
false,
)
{
return false;
}
}

return true;
}
// 9
self.insert_property(property_key, desc);
true
}

/// 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;
Expand All @@ -154,8 +260,6 @@ pub trait ObjectInternalMethods {
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;

Expand Down
Loading

0 comments on commit f62b7b5

Please sign in to comment.