Browse Source

passing primitives to methods (#86)

* passing primitives to methods

* finishing unboxing_primitives, tests added, using internal_slots for __proto__
pull/87/head
Jason Williams 5 years ago committed by GitHub
parent
commit
c5b75c55ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 54
      src/lib/exec.rs
  2. 12
      src/lib/js/function.rs
  3. 64
      src/lib/js/object.rs
  4. 16
      src/lib/js/string.rs
  5. 45
      src/lib/js/value.rs
  6. 2
      tests/js/test.js

54
src/lib/exec.rs

@ -86,7 +86,10 @@ impl Executor for Interpreter {
ExprDef::Call(ref callee, ref args) => {
let (this, func) = match callee.def {
ExprDef::GetConstField(ref obj, ref field) => {
let obj = self.run(obj)?;
let mut obj = self.run(obj)?;
if obj.get_type() != "object" {
obj = self.to_object(&obj).expect("failed to convert to object");
}
(obj.clone(), obj.borrow().get_field(field))
}
ExprDef::GetField(ref obj, ref field) => {
@ -171,7 +174,7 @@ impl Executor for Interpreter {
arr_map.borrow().set_field(index.to_string(), val);
index += 1;
}
arr_map.borrow().set_field_slice(
arr_map.borrow().set_internal_slot(
INSTANCE_PROTOTYPE,
self.environment
.get_binding_value("Array")
@ -272,8 +275,10 @@ impl Executor for Interpreter {
}
let this = ValueData::new_obj(None);
// Create a blank object, then set its __proto__ property to the [Constructor].prototype
this.borrow()
.set_field_slice(INSTANCE_PROTOTYPE, func.borrow().get_field_slice(PROTOTYPE));
this.borrow().set_internal_slot(
INSTANCE_PROTOTYPE,
func.borrow().get_field_slice(PROTOTYPE),
);
match *func {
ValueData::Function(ref inner_func) => match inner_func.clone().into_inner() {
Function::NativeFunc(ref ntv) => {
@ -480,6 +485,47 @@ impl Interpreter {
}
}
/// The abstract operation ToObject converts argument to a value of type Object
/// https://tc39.es/ecma262/#sec-toobject
#[allow(clippy::wrong_self_convention)]
pub fn to_object(&mut self, value: &Value) -> ResultValue {
match *value.deref().borrow() {
ValueData::Undefined
| ValueData::Function(_)
| ValueData::Integer(_)
| ValueData::Null => Err(Gc::new(ValueData::Undefined)),
ValueData::Boolean(_) => {
let proto = self
.environment
.get_binding_value("Boolean")
.get_field_slice(PROTOTYPE);
let bool_obj = ValueData::new_obj_from_prototype(proto, ObjectKind::Boolean);
bool_obj.set_internal_slot("BooleanData", value.clone());
Ok(bool_obj)
}
ValueData::Number(_) => {
let proto = self
.environment
.get_binding_value("Number")
.get_field_slice(PROTOTYPE);
let number_obj = ValueData::new_obj_from_prototype(proto, ObjectKind::Number);
number_obj.set_internal_slot("NumberData", value.clone());
Ok(number_obj)
}
ValueData::String(_) => {
let proto = self
.environment
.get_binding_value("String")
.get_field_slice(PROTOTYPE);
let string_obj = ValueData::new_obj_from_prototype(proto, ObjectKind::String);
string_obj.set_internal_slot("StringData", value.clone());
Ok(string_obj)
}
ValueData::Object(_) => Ok(value.clone()),
}
}
/// value_to_rust_string() converts a value into a rust heap allocated string
pub fn value_to_rust_string(&mut self, value: &Value) -> String {
match *value.deref().borrow() {

12
src/lib/js/function.rs

@ -1,7 +1,7 @@
use crate::{
exec::Interpreter,
js::{
object::{ObjectData, Property},
object::{Object, Property},
value::{to_value, ResultValue, Value, ValueData},
},
syntax::ast::expr::Expr,
@ -31,7 +31,7 @@ pub enum Function {
#[derive(Trace, Finalize, Debug, Clone)]
pub struct RegularFunction {
/// The fields associated with the function
pub object: ObjectData,
pub object: Object,
/// This function's expression
pub expr: Expr,
/// The argument names of the function
@ -42,7 +42,7 @@ impl RegularFunction {
/// Make a new regular function
#[allow(clippy::cast_possible_wrap)]
pub fn new(expr: Expr, args: Vec<String>) -> Self {
let mut object = ObjectData::default();
let mut object = Object::default();
object.properties.insert(
"arguments".to_string(),
Property::new(Gc::new(ValueData::Integer(args.len() as i32))),
@ -55,7 +55,7 @@ impl RegularFunction {
/// Represents a native javascript function in memory
pub struct NativeFunction {
/// The fields associated with the function
pub object: ObjectData,
pub object: Object,
/// The callable function data
pub data: NativeFunctionData,
}
@ -63,7 +63,7 @@ pub struct NativeFunction {
impl NativeFunction {
/// Make a new native function with the given function data
pub fn new(data: NativeFunctionData) -> Self {
let object = ObjectData::default();
let object = Object::default();
Self { object, data }
}
}
@ -84,7 +84,7 @@ unsafe impl gc::Trace for NativeFunction {
/// Create a new `Function` object
pub fn _create() -> Value {
let function: ObjectData = ObjectData::default();
let function: Object = Object::default();
to_value(function)
}
/// Initialise the global object with the `Function` object

64
src/lib/js/object.rs

@ -7,7 +7,7 @@ use crate::{
};
use gc::Gc;
use gc_derive::{Finalize, Trace};
use std::collections::HashMap;
use std::{borrow::Borrow, collections::HashMap, ops::Deref};
/// Static `prototype`, usually set on constructors as a key to point to their respective prototype object.
/// As this string will be used a lot throughout the program, its best being a static global string which will be referenced
@ -19,7 +19,7 @@ pub static INSTANCE_PROTOTYPE: &str = "__proto__";
/// `ObjectData` is the representation of an object in JavaScript
#[derive(Trace, Finalize, Debug, Clone)]
pub struct ObjectData {
pub struct Object {
/// Kind
pub kind: ObjectKind,
/// Internal Slots
@ -30,16 +30,68 @@ pub struct ObjectData {
pub sym_properties: Box<HashMap<usize, Property>>,
}
impl ObjectData {
impl Object {
/// Return a new ObjectData struct, with `kind` set to Ordinary
pub fn default() -> Self {
Self {
Object {
kind: ObjectKind::Ordinary,
internal_slots: Box::new(HashMap::new()),
properties: Box::new(HashMap::new()),
sym_properties: Box::new(HashMap::new()),
}
}
/// 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()),
};
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()),
};
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()),
};
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)),
_ => Err(()),
}
}
}
#[derive(Trace, Finalize, Clone, Debug)]
pub enum ObjectKind {
@ -49,6 +101,8 @@ pub enum ObjectKind {
Symbol,
Error,
Ordinary,
Boolean,
Number,
}
/// A Javascript Property AKA The Property Descriptor
@ -130,7 +184,7 @@ pub fn get_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultVal
pub fn set_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).unwrap().clone();
let proto = args.get(1).unwrap().clone();
obj.set_field_slice(INSTANCE_PROTOTYPE, proto);
obj.set_internal_slot(INSTANCE_PROTOTYPE, proto);
Ok(obj)
}

16
src/lib/js/string.rs

@ -645,12 +645,20 @@ mod tests {
const empty = new String('');
const en = new String('english');
const zh = new String('');
const emptyLiteral = '';
const enLiteral = 'english';
const zhLiteral = '';
"#;
forward(&mut engine, init);
let pass = String::from("true");
assert_eq!(forward(&mut engine, "empty.startsWith('')"), pass);
assert_eq!(forward(&mut engine, "en.startsWith('e')"), pass);
assert_eq!(forward(&mut engine, "zh.startsWith('中')"), pass);
assert_eq!(forward(&mut engine, "emptyLiteral.startsWith('')"), pass);
assert_eq!(forward(&mut engine, "enLiteral.startsWith('e')"), pass);
assert_eq!(forward(&mut engine, "zhLiteral.startsWith('中')"), pass);
}
#[test]
@ -660,11 +668,19 @@ mod tests {
const empty = new String('');
const en = new String('english');
const zh = new String('');
const emptyLiteral = '';
const enLiteral = 'english';
const zhLiteral = '';
"#;
forward(&mut engine, init);
let pass = String::from("true");
assert_eq!(forward(&mut engine, "empty.endsWith('')"), pass);
assert_eq!(forward(&mut engine, "en.endsWith('h')"), pass);
assert_eq!(forward(&mut engine, "zh.endsWith('文')"), pass);
assert_eq!(forward(&mut engine, "emptyLiteral.endsWith('')"), pass);
assert_eq!(forward(&mut engine, "enLiteral.endsWith('h')"), pass);
assert_eq!(forward(&mut engine, "zhLiteral.endsWith('文')"), pass);
}
}

45
src/lib/js/value.rs

@ -1,6 +1,6 @@
use crate::js::{
function::{Function, NativeFunction, NativeFunctionData},
object::{ObjectData, ObjectKind, Property, INSTANCE_PROTOTYPE, PROTOTYPE},
object::{Object, ObjectKind, Property, INSTANCE_PROTOTYPE, PROTOTYPE},
};
use gc::{Gc, GcCell};
use gc_derive::{Finalize, Trace};
@ -34,7 +34,7 @@ pub enum ValueData {
/// `Number` - A 32-bit integer, such as `42`
Integer(i32),
/// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values
Object(GcCell<ObjectData>),
Object(GcCell<Object>),
/// `Function` - A runnable block of code, such as `Math.sqrt`, which can take some variables and return a useful value or act upon an object
Function(Box<GcCell<Function>>),
}
@ -42,25 +42,28 @@ pub enum ValueData {
impl ValueData {
/// Returns a new empty object
pub fn new_obj(global: Option<&Value>) -> Value {
let mut obj = ObjectData::default();
let mut obj = Object::default();
if global.is_some() {
let obj_proto = global
.expect("Expected global object in making-new-object")
.get_field_slice("Object")
.get_field_slice(PROTOTYPE);
obj.properties
.insert(INSTANCE_PROTOTYPE.to_string(), Property::new(obj_proto));
obj.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), obj_proto);
}
Gc::new(ValueData::Object(GcCell::new(obj)))
}
/// Similar to `new_obj`, but you can pass a prototype to create from
pub fn new_obj_from_prototype(proto: Value) -> Value {
let mut obj = ObjectData::default();
/// Similar to `new_obj`, but you can pass a prototype to create from,
/// plus a kind
pub fn new_obj_from_prototype(proto: Value, kind: ObjectKind) -> Value {
let mut obj = Object::default();
obj.kind = kind;
obj.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), proto);
Gc::new(ValueData::Object(GcCell::new(obj)))
}
@ -199,7 +202,7 @@ impl ValueData {
}
}
let obj: ObjectData = match *self {
let obj: Object = match *self {
ValueData::Object(ref obj) => {
let hash = obj.clone();
// TODO: This will break, we should return a GcCellRefMut instead
@ -216,8 +219,8 @@ impl ValueData {
match obj.properties.get(field) {
Some(val) => Some(val.clone()),
None => match obj.properties.get(&INSTANCE_PROTOTYPE.to_string()) {
Some(prop) => prop.value.get_prop(field),
None => match obj.internal_slots.get(&INSTANCE_PROTOTYPE.to_string()) {
Some(value) => value.get_prop(field),
None => None,
},
}
@ -234,7 +237,7 @@ impl ValueData {
writable: Option<bool>,
configurable: Option<bool>,
) {
let obj: Option<ObjectData> = match self {
let obj: Option<Object> = match self {
ValueData::Object(ref obj) => Some(obj.borrow_mut().deref_mut().clone()),
// Accesing .object on borrow() seems to automatically dereference it, so we don't need the *
ValueData::Function(ref func) => match func.borrow_mut().deref_mut() {
@ -258,7 +261,7 @@ impl ValueData {
/// Resolve the property in the object
/// Returns a copy of the Property
pub fn get_internal_slot(&self, field: &str) -> Value {
let obj: ObjectData = match *self {
let obj: Object = match *self {
ValueData::Object(ref obj) => {
let hash = obj.clone();
hash.into_inner()
@ -389,7 +392,7 @@ impl ValueData {
JSONValue::String(v) => ValueData::String(v),
JSONValue::Bool(v) => ValueData::Boolean(v),
JSONValue::Array(vs) => {
let mut new_obj = ObjectData::default();
let mut new_obj = Object::default();
for (idx, json) in vs.iter().enumerate() {
new_obj
.properties
@ -402,7 +405,7 @@ impl ValueData {
ValueData::Object(GcCell::new(new_obj))
}
JSONValue::Object(obj) => {
let mut new_obj = ObjectData::default();
let mut new_obj = Object::default();
for (key, json) in obj.iter() {
new_obj
.properties
@ -421,9 +424,9 @@ impl ValueData {
ValueData::Boolean(b) => JSONValue::Bool(b),
ValueData::Object(ref obj) => {
let mut new_obj = Map::new();
for (k, v) in obj.borrow().properties.iter() {
for (k, v) in obj.borrow().internal_slots.iter() {
if k != INSTANCE_PROTOTYPE {
new_obj.insert(k.clone(), v.value.to_json());
new_obj.insert(k.clone(), v.to_json());
}
}
JSONValue::Object(new_obj)
@ -680,7 +683,7 @@ impl FromValue for bool {
impl<'s, T: ToValue> ToValue for &'s [T] {
fn to_value(&self) -> Value {
let mut arr = ObjectData::default();
let mut arr = Object::default();
for (i, item) in self.iter().enumerate() {
arr.properties
.insert(i.to_string(), Property::new(item.to_value()));
@ -690,7 +693,7 @@ impl<'s, T: ToValue> ToValue for &'s [T] {
}
impl<T: ToValue> ToValue for Vec<T> {
fn to_value(&self) -> Value {
let mut arr = ObjectData::default();
let mut arr = Object::default();
for (i, item) in self.iter().enumerate() {
arr.properties
.insert(i.to_string(), Property::new(item.to_value()));
@ -710,13 +713,13 @@ impl<T: FromValue> FromValue for Vec<T> {
}
}
impl ToValue for ObjectData {
impl ToValue for Object {
fn to_value(&self) -> Value {
Gc::new(ValueData::Object(GcCell::new(self.clone())))
}
}
impl FromValue for ObjectData {
impl FromValue for Object {
fn from_value(v: Value) -> Result<Self, &'static str> {
match *v {
ValueData::Object(ref obj) => Ok(obj.clone().into_inner()),

2
tests/js/test.js

@ -1 +1 @@
"12345".charAt(2);
("Jason").charAt(2);

Loading…
Cancel
Save