Browse Source

Adding object internal methods (#96)

* adding internal methods

* adding more methods

* Property refactored for optional fields, Docker container now working for debugging

* working through object internal methods
pull/97/head
Jason Williams 5 years ago committed by GitHub
parent
commit
2df9a24011
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 40
      .devcontainer/devcontainer.json
  2. 5
      Dockerfile
  3. 4
      src/lib/environment/global_environment_record.rs
  4. 11
      src/lib/environment/object_environment_record.rs
  5. 11
      src/lib/js/array.rs
  6. 6
      src/lib/js/console.rs
  7. 4
      src/lib/js/function.rs
  8. 228
      src/lib/js/object.rs
  9. 144
      src/lib/js/property.rs
  10. 14
      src/lib/js/regexp.rs
  11. 11
      src/lib/js/string.rs
  12. 112
      src/lib/js/value.rs
  13. 3
      src/lib/lib.rs

40
.devcontainer/devcontainer.json

@ -0,0 +1,40 @@
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or the definition README at
// https://github.com/microsoft/vscode-dev-containers/tree/master/containers/docker-existing-dockerfile
{
"name": "Existing Dockerfile",
// Sets the run context to one level up instead of the .devcontainer folder.
"context": "..",
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
"dockerFile": "..\\Dockerfile",
// The optional 'runArgs' property can be used to specify additional runtime arguments.
"runArgs": [
// Uncomment the next line to use Docker from inside the container. See https://aka.ms/vscode-remote/samples/docker-in-docker for details.
// "-v","/var/run/docker.sock:/var/run/docker.sock",
// Uncomment the next line if you will be using a ptrace-based debugger like C++, Go, and Rust.
"--cap-add=SYS_PTRACE",
"--security-opt",
"seccomp=unconfined"
// You may want to add a non-root user to your Dockerfile. On Linux, this will prevent
// new files getting created as root. See https://aka.ms/vscode-remote/containers/non-root-user
// for the needed Dockerfile updates and then uncomment the next line.
// "-u", "vscode"
],
// Use 'settings' to set *default* container specific settings.json values on container create.
// You can edit these settings after create using File > Preferences > Settings > Remote.
"settings": {
// This will ignore your local shell user setting for Linux since shells like zsh are typically
// not in base container images. You can also update this to an specific shell to ensure VS Code
// uses the right one for terminals and tasks. For example, /bin/bash (or /bin/ash for Alpine).
"terminal.integrated.shell.linux": null
},
// Uncomment the next line if you want to publish any ports.
// "appPort": [],
// Uncomment the next line to run commands after the container is created - for example installing git.
// "postCreateCommand": "apt-get update && apt-get install -y git",
// Add the IDs of extensions you want installed when the container is created in the array below.
"extensions": [
"vadimcn.vscode-lldb",
"rust-lang.rust"
],
"postCreateCommand": "rustup toolchain install stable && rustup component add rls rust-src rust-analysis --toolchain stable"
}

5
Dockerfile

@ -8,7 +8,10 @@ EXPOSE 9228
RUN apt-get -y update && \
apt-get -y upgrade && \
apt-get install -y sudo software-properties-common
apt-get install -y sudo software-properties-common libpython2.7
# codelldb depends on libpython2.7
# https://stackoverflow.com/questions/20842732/libpython2-7-so-1-0-cannot-open-shared-object-file-no-such-file-or-directory
# https://askubuntu.com/questions/787383/how-to-install-llvm-3-9
# http://apt.llvm.org/

4
src/lib/environment/global_environment_record.rs

@ -46,7 +46,7 @@ impl GlobalEnvironmentRecord {
let existing_prop = global_object.get_prop(name);
match existing_prop {
Some(prop) => {
if prop.value.is_undefined() || prop.configurable {
if prop.value.is_none() || prop.configurable.unwrap_or(false) {
return false;
}
true
@ -75,7 +75,7 @@ impl GlobalEnvironmentRecord {
let global_object = &mut self.object_record.bindings;
let existing_prop = global_object.get_prop(&name);
if let Some(prop) = existing_prop {
if prop.value.is_undefined() || prop.configurable {
if prop.value.is_none() || prop.configurable.unwrap_or(false) {
global_object.update_prop(
name,
Some(value),

11
src/lib/environment/object_environment_record.rs

@ -42,11 +42,12 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
// TODO: could save time here and not bother generating a new undefined object,
// only for it to be replace with the real value later. We could just add the name to a Vector instead
let bindings = &mut self.bindings;
let uninitialized = Gc::new(ValueData::Undefined);
let mut prop = Property::new(uninitialized);
prop.enumerable = true;
prop.writable = true;
prop.configurable = deletion;
let prop = Property::default()
.value(Gc::new(ValueData::Undefined))
.writable(true)
.enumerable(true)
.configurable(deletion);
bindings.set_prop(name, prop);
}

11
src/lib/js/array.rs

@ -255,14 +255,9 @@ pub fn unshift(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue
pub fn _create(global: &Value) -> Value {
let array = to_value(make_array as NativeFunctionData);
let proto = ValueData::new_obj(Some(global));
let length = Property {
configurable: false,
enumerable: false,
writable: false,
value: Gc::new(ValueData::Undefined),
get: to_value(get_array_length as NativeFunctionData),
set: Gc::new(ValueData::Undefined),
};
let length = Property::default()
.get(to_value(get_array_length as NativeFunctionData));
proto.set_prop_slice("length", length);
let concat_func = to_value(concat as NativeFunctionData);
concat_func.set_field_slice("length", to_value(1_i32));

6
src/lib/js/console.rs

@ -32,7 +32,7 @@ fn log_string_from(x: Value) -> String {
ObjectKind::Array => {
write!(s, "[").unwrap();
let len: i32 =
from_value(v.borrow().properties.get("length").unwrap().value.clone())
from_value(v.borrow().properties.get("length").unwrap().value.clone().unwrap().clone())
.unwrap();
for i in 0..len {
// Introduce recursive call to stringify any objects
@ -43,6 +43,8 @@ fn log_string_from(x: Value) -> String {
.get(&i.to_string())
.unwrap()
.value
.clone()
.unwrap()
.clone(),
);
write!(s, "{}", arr_str).unwrap();
@ -62,7 +64,7 @@ fn log_string_from(x: Value) -> String {
}
// Introduce recursive call to stringify any objects
// which are keys of the object
write!(s, "{}: {}", key, log_string_from(val.value.clone())).unwrap();
write!(s, "{}: {}", key, log_string_from(val.value.clone().unwrap().clone())).unwrap();
if key != last_key {
write!(s, ", ").unwrap();
}

4
src/lib/js/function.rs

@ -46,7 +46,7 @@ impl RegularFunction {
let mut object = Object::default();
object.properties.insert(
"arguments".to_string(),
Property::new(Gc::new(ValueData::Integer(args.len() as i32))),
Property::default().value(Gc::new(ValueData::Integer(args.len() as i32))),
);
Self { object, expr, args }
}
@ -73,7 +73,7 @@ impl Debug for NativeFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{{")?;
for (key, val) in self.object.properties.iter() {
write!(f, "{}: {}", key, val.value.clone())?;
write!(f, "{}: {}", key, val.value.as_ref().unwrap_or(&Gc::new(ValueData::Undefined)).clone())?;
}
write!(f, "}}")
}

228
src/lib/js/object.rs

@ -3,7 +3,7 @@ use crate::{
js::{
function::NativeFunctionData,
property::Property,
value::{from_value, to_value, ResultValue, Value, ValueData},
value::{from_value, same_value, to_value, ResultValue, Value, ValueData},
},
};
use gc::Gc;
@ -47,6 +47,31 @@ impl Object {
}
}
/// ObjectCreate is used to specify the runtime creation of new ordinary objects
///
/// https://tc39.es/ecma262/#sec-objectcreate
pub fn create(proto: Value) -> Object {
let mut obj = Object::default();
obj.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), proto.clone());
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);
}
/// Return a new Boolean object whose [[BooleanData]] internal slot is set to argument.
fn from_boolean(argument: &Value) -> Self {
let mut obj = Object {
@ -101,7 +126,190 @@ impl Object {
_ => 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
pub 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) {
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-isextensible
pub fn is_extensible(&self) -> bool {
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
pub fn prevent_extensions(&mut self) -> bool {
self.set_internal_slot("extensible", to_value(false));
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.
pub 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
}
}
}
/// 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
}
pub 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;
}
let mut p = Property::new();
if desc.is_generic_descriptor() || desc.is_data_descriptor() {
p.value = Some(desc.value.clone().unwrap_or_default());
p.writable = Some(desc.writable.unwrap_or_default());
p.configurable = Some(desc.configurable.unwrap_or_default());
p.enumerable = Some(desc.enumerable.unwrap_or_default());
} else {
p.get = Some(desc.get.clone().unwrap_or_default());
p.set = Some(desc.set.clone().unwrap_or_default());
p.configurable = Some(desc.configurable.unwrap_or_default());
p.enumerable = Some(desc.enumerable.unwrap_or_default());
};
self.properties.insert(property_key, p);
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.properties.insert(property_key, current);
// 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()) {
return false;
}
return true;
}
// 8
} else {
}
true
}
}
#[derive(Trace, Finalize, Clone, Debug)]
pub enum ObjectKind {
Function,
@ -121,23 +329,25 @@ pub fn make_object(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
/// Get the prototype of an object
pub fn get_proto_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).unwrap();
let obj = args.get(0).expect("Cannot get object");
Ok(obj.get_field_slice(INSTANCE_PROTOTYPE))
}
/// Set the prototype of an object
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();
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);
Ok(obj)
}
/// Define a property in an object
pub fn define_prop(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).unwrap();
let prop = from_value::<String>(args.get(1).unwrap().clone()).unwrap();
let desc = from_value::<Property>(args.get(2).unwrap().clone()).unwrap();
let obj = args.get(0).expect("Cannot get object");
let prop = from_value::<String>(args.get(1).expect("Cannot get object").clone())
.expect("Cannot get object");
let desc = from_value::<Property>(args.get(2).expect("Cannot get object").clone())
.expect("Cannot get object");
obj.set_prop(prop, desc);
Ok(Gc::new(ValueData::Undefined))
}
@ -152,10 +362,10 @@ pub fn has_own_prop(this: &Value, args: &[Value], _: &mut Interpreter) -> Result
let prop = if args.is_empty() {
None
} else {
from_value::<String>(args.get(0).unwrap().clone()).ok()
from_value::<String>(args.get(0).expect("Cannot get object").clone()).ok()
};
Ok(to_value(
prop.is_some() && this.get_prop(&prop.unwrap()).is_some(),
prop.is_some() && this.get_prop(&prop.expect("Cannot get object")).is_some(),
))
}

144
src/lib/js/property.rs

@ -5,20 +5,22 @@ use gc_derive::{Finalize, Trace};
/// A Javascript Property AKA The Property Descriptor
/// [[SPEC] - The Property Descriptor Specification Type](https://tc39.github.io/ecma262/#sec-property-descriptor-specification-type)
/// [[SPEC] - Default Attribute Values](https://tc39.github.io/ecma262/#table-4)
///
/// Any field in a JavaScript Property may be present or absent.
#[derive(Trace, Finalize, Clone, Debug)]
pub struct Property {
/// If the type of this can be changed and this can be deleted
pub configurable: bool,
pub configurable: Option<bool>,
/// If the property shows up in enumeration of the object
pub enumerable: bool,
pub enumerable: Option<bool>,
/// If this property can be changed with an assignment
pub writable: bool,
pub writable: Option<bool>,
/// The value associated with the property
pub value: Value,
pub value: Option<Value>,
/// The function serving as getter
pub get: Value,
pub get: Option<Value>,
/// The function serving as setter
pub set: Value,
pub set: Option<Value>,
}
impl Property {
@ -28,14 +30,95 @@ impl Property {
}
/// Make a new property with the given value
pub fn new(value: Value) -> Self {
/// The difference between New and Default:
///
/// New: zeros everything to make an empty object
/// Default: Defaults according to the spec
pub fn new() -> Self {
Self {
configurable: false,
enumerable: false,
writable: false,
value,
get: Gc::new(ValueData::Undefined),
set: Gc::new(ValueData::Undefined),
configurable: None,
enumerable: None,
writable: None,
value: None,
get: None,
set: None,
}
}
/// Set configurable
pub fn configurable(mut self, configurable: bool) -> Self {
self.configurable = Some(configurable);
self
}
/// Set enumerable
pub fn enumerable(mut self, enumerable: bool) -> Self {
self.enumerable = Some(enumerable);
self
}
/// Set writable
pub fn writable(mut self, writable: bool) -> Self {
self.writable = Some(writable);
self
}
/// Set value
pub fn value(mut self, value: Value) -> Self {
self.value = Some(value);
self
}
/// Set get
pub fn get(mut self, get: Value) -> Self {
self.get = Some(get);
self
}
/// Set set
pub fn set(mut self, set: Value) -> Self {
self.set = Some(set);
self
}
/// Is this an empty Property?
///
/// `true` if all fields are set to none
pub fn is_none(&self) -> bool {
self.get.is_none()
&& self.set.is_none()
&& self.writable.is_none()
&& self.configurable.is_none()
&& self.enumerable.is_none()
}
// https://tc39.es/ecma262/#sec-isaccessordescriptor
pub fn is_accessor_descriptor(&self) -> bool {
self.get.is_some() && self.set.is_some()
}
// https://tc39.es/ecma262/#sec-isdatadescriptor
pub fn is_data_descriptor(&self) -> bool {
self.value.is_some() && self.writable.is_some()
}
// https://tc39.es/ecma262/#sec-isgenericdescriptor
pub fn is_generic_descriptor(&self) -> bool {
!self.is_accessor_descriptor() && !self.is_data_descriptor()
}
}
impl Default for Property {
/// Make a default property
/// https://tc39.es/ecma262/#table-default-attribute-values
fn default() -> Self {
Self {
configurable: Some(false),
enumerable: Some(false),
writable: Some(false),
value: Some(Gc::new(ValueData::Undefined)),
get: Some(Gc::new(ValueData::Undefined)),
set: Some(Gc::new(ValueData::Undefined)),
}
}
}
@ -46,22 +129,39 @@ impl ToValue for Property {
prop.set_field_slice("configurable", to_value(self.configurable));
prop.set_field_slice("enumerable", to_value(self.enumerable));
prop.set_field_slice("writable", to_value(self.writable));
prop.set_field_slice("value", self.value.clone());
prop.set_field_slice("get", self.get.clone());
prop.set_field_slice("set", self.set.clone());
prop.set_field_slice("value", to_value(self.value.clone()));
prop.set_field_slice("get", to_value(self.get.clone()));
prop.set_field_slice("set", to_value(self.set.clone()));
prop
}
}
impl FromValue for Property {
/// Attempt to fetch values "configurable", "enumerable", "writable" from the value,
/// if they're not there default to false
fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(Self {
configurable: from_value(v.get_field_slice("configurable")).unwrap(),
enumerable: from_value(v.get_field_slice("enumerable")).unwrap(),
writable: from_value(v.get_field_slice("writable")).unwrap(),
value: v.get_field_slice("value"),
get: v.get_field_slice("get"),
set: v.get_field_slice("set"),
configurable: {
match from_value::<bool>(v.get_field_slice("configurable")) {
Ok(v) => Some(v),
Err(_) => Some(false),
}
},
enumerable: {
match from_value::<bool>(v.get_field_slice("enumerable")) {
Ok(v) => Some(v),
Err(_) => Some(false),
}
},
writable: {
match from_value(v.get_field_slice("writable")) {
Ok(v) => Some(v),
Err(_) => Some(false),
}
},
value: Some(v.get_field_slice("value")),
get: Some(v.get_field_slice("get")),
set: Some(v.get_field_slice("set")),
})
}
}

14
src/lib/js/regexp.rs

@ -181,14 +181,8 @@ fn get_unicode(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
}
fn _make_prop(getter: NativeFunctionData) -> Property {
Property {
writable: false,
enumerable: false,
configurable: true,
value: Gc::new(ValueData::Undefined),
get: to_value(getter),
set: Gc::new(ValueData::Undefined),
}
Property::default()
.get(to_value(getter))
}
/// Search for a match between this regex and a specified string
@ -240,8 +234,8 @@ pub fn exec(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
}
}
let result = to_value(result);
result.set_prop_slice("index", Property::new(to_value(m.start())));
result.set_prop_slice("input", Property::new(to_value(arg_str)));
result.set_prop_slice("index", Property::default().value(to_value(m.start())));
result.set_prop_slice("input", Property::default().value(to_value(arg_str)));
result
}
None => {

11
src/lib/js/string.rs

@ -525,14 +525,9 @@ pub fn trim_end(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue
pub fn _create(global: &Value) -> Value {
let string = to_value(make_string as NativeFunctionData);
let proto = ValueData::new_obj(Some(global));
let prop = Property {
configurable: false,
enumerable: false,
writable: false,
value: Gc::new(ValueData::Undefined),
get: to_value(get_string_length as NativeFunctionData),
set: Gc::new(ValueData::Undefined),
};
let prop = Property::default()
.get(to_value(get_string_length as NativeFunctionData));
proto.set_prop_slice("length", prop);
proto.set_field_slice("charAt", to_value(char_at as NativeFunctionData));
proto.set_field_slice("charCodeAt", to_value(char_code_at as NativeFunctionData));

112
src/lib/js/value.rs

@ -20,6 +20,10 @@ pub type ResultValue = Result<Value, Value>;
/// A Garbage-collected Javascript value as represented in the interpreter
pub type Value = Gc<ValueData>;
pub fn undefined() -> Value {
Gc::new(ValueData::Undefined)
}
/// A Javascript value
#[derive(Trace, Finalize, Debug, Clone)]
pub enum ValueData {
@ -44,17 +48,18 @@ pub enum ValueData {
impl ValueData {
/// Returns a new empty object
pub fn new_obj(global: Option<&Value>) -> Value {
let mut obj = Object::default();
match global {
Some(glob) => {
let obj_proto = glob.get_field_slice("Object").get_field_slice(PROTOTYPE);
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.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), obj_proto);
let obj = Object::create(obj_proto);
Gc::new(ValueData::Object(GcCell::new(obj)))
}
None => {
let obj = Object::default();
Gc::new(ValueData::Object(GcCell::new(obj)))
}
}
Gc::new(ValueData::Object(GcCell::new(obj)))
}
/// Similar to `new_obj`, but you can pass a prototype to create from,
@ -200,7 +205,7 @@ impl ValueData {
// This is only for primitive strings, String() objects have their lengths calculated in string.rs
if self.is_string() && field == "length" {
if let ValueData::String(ref s) = *self {
return Some(Property::new(to_value(s.len() as i32)));
return Some(Property::default().value(to_value(s.len() as i32)));
}
}
@ -252,10 +257,10 @@ impl ValueData {
if let Some(mut obj_data) = obj {
// Use value, or walk up the prototype chain
if let Some(ref mut prop) = obj_data.properties.get_mut(field) {
prop.value = value.unwrap_or_else(|| prop.value.clone());
prop.enumerable = enumerable.unwrap_or(prop.enumerable);
prop.writable = writable.unwrap_or(prop.writable);
prop.configurable = configurable.unwrap_or(prop.configurable);
prop.value = value;
prop.enumerable = enumerable;
prop.writable = writable;
prop.configurable = configurable;
}
}
}
@ -279,24 +284,23 @@ impl ValueData {
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
/// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]]
/// TODO: this function should use the get Value if its set
pub fn get_field(&self, field: &str) -> Value {
match self.get_prop(field) {
Some(prop) => {
// If the Property has [[Get]] set to a function, we should run that and return the Value
let prop_getter = match *prop.get {
ValueData::Function(ref v) => match *v.borrow() {
Function::NativeFunc(ref _ntv) => {
None // this never worked properly anyway
}
_ => None,
},
_ => None,
let prop_getter = match prop.get {
Some(_) => None,
None => None
};
// If the getter is populated, use that. If not use [[Value]] instead
match prop_getter {
Some(val) => val,
None => prop.value.clone(),
None => {
let val = prop.value.as_ref().unwrap();
val.clone()
}
}
}
None => Gc::new(ValueData::Undefined),
@ -388,18 +392,18 @@ impl ValueData {
ValueData::Object(ref obj) => {
obj.borrow_mut()
.properties
.insert(field, Property::new(val.clone()));
.insert(field, Property::default().value(val.clone()));
}
ValueData::Function(ref func) => {
match *func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut f) => f
.object
.properties
.insert(field, Property::new(val.clone())),
.insert(field, Property::default().value(val.clone())),
Function::RegularFunc(ref mut f) => f
.object
.properties
.insert(field, Property::new(val.clone())),
.insert(field, Property::default().value(val.clone())),
};
}
_ => (),
@ -476,11 +480,11 @@ impl ValueData {
for (idx, json) in vs.iter().enumerate() {
new_obj
.properties
.insert(idx.to_string(), Property::new(to_value(json.clone())));
.insert(idx.to_string(), Property::default().value(to_value(json.clone())));
}
new_obj.properties.insert(
"length".to_string(),
Property::new(to_value(vs.len() as i32)),
Property::default().value(to_value(vs.len() as i32)),
);
ValueData::Object(GcCell::new(new_obj))
}
@ -489,7 +493,7 @@ impl ValueData {
for (key, json) in obj.iter() {
new_obj
.properties
.insert(key.clone(), Property::new(to_value(json.clone())));
.insert(key.clone(), Property::default().value(to_value(json.clone())));
}
ValueData::Object(GcCell::new(new_obj))
@ -530,6 +534,12 @@ impl ValueData {
}
}
impl Default for ValueData {
fn default() -> Self {
ValueData::Undefined
}
}
impl Display for ValueData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
@ -552,7 +562,7 @@ impl Display for ValueData {
// Print public properties
if let Some((last_key, _)) = v.borrow().properties.iter().last() {
for (key, val) in v.borrow().properties.iter() {
write!(f, "{}: {}", key, val.value.clone())?;
write!(f, "{}: {}", key, val.value.as_ref().unwrap_or(&Gc::new(ValueData::Undefined)).clone())?;
if key != last_key {
write!(f, ", ")?;
}
@ -778,7 +788,7 @@ impl<'s, T: ToValue> ToValue for &'s [T] {
let mut arr = Object::default();
for (i, item) in self.iter().enumerate() {
arr.properties
.insert(i.to_string(), Property::new(item.to_value()));
.insert(i.to_string(), Property::default().value(item.to_value()));
}
to_value(arr)
}
@ -788,7 +798,7 @@ impl<T: ToValue> ToValue for Vec<T> {
let mut arr = Object::default();
for (i, item) in self.iter().enumerate() {
arr.properties
.insert(i.to_string(), Property::new(item.to_value()));
.insert(i.to_string(), Property::default().value(item.to_value()));
}
to_value(arr)
}
@ -894,6 +904,44 @@ pub fn to_value<A: ToValue>(v: A) -> Value {
v.to_value()
}
/// The internal comparison abstract operation SameValue(x, y),
/// where x and y are ECMAScript language values, produces true or false.
/// Such a comparison is performed as follows:
///
/// https://tc39.es/ecma262/#sec-samevalue
pub fn same_value(x: &Value, y: &Value) -> bool {
if x.get_type() != y.get_type() {
return false;
}
if x.get_type() == "number" {
let native_x: f64 = from_value(x.clone()).expect("failed to get value");
let native_y: f64 = from_value(y.clone()).expect("failed to get value");
return native_x.abs() - native_y.abs() == 0.0;
}
same_value_non_number(x, y)
}
pub fn same_value_non_number(x: &Value, y: &Value) -> bool {
debug_assert!(x.get_type() == y.get_type());
match x.get_type() {
"undefined" => true,
"null" => true,
"string" => {
if x.to_string() == y.to_string() {
return true;
}
false
}
"boolean" => {
from_value::<bool>(x.clone()).expect("failed to get value")
== from_value::<bool>(y.clone()).expect("failed to get value")
}
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;

3
src/lib/lib.rs

@ -29,7 +29,8 @@
clippy::print_stdout,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::non_ascii_literal
clippy::non_ascii_literal,
clippy::float_arithmetic
)]
pub mod environment;

Loading…
Cancel
Save