Skip to content

Commit

Permalink
Added Hash trait to Value
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed May 28, 2020
1 parent 16b29b9 commit a592aab
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 8 deletions.
14 changes: 13 additions & 1 deletion boa/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub enum ConstructorKind {
/// Defines how this references are interpreted within the formal parameters and code body of the function.
///
/// Arrow functions don't define a `this` and thus are lexical, `function`s do define a this and thus are NonLexical
#[derive(Trace, Finalize, Debug, Clone)]
#[derive(Trace, Finalize, Debug, Clone, PartialEq, PartialOrd, Hash)]
pub enum ThisMode {
Lexical,
NonLexical,
Expand All @@ -61,6 +61,18 @@ impl Debug for FunctionBody {
}
}

impl PartialEq for FunctionBody {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::BuiltIn(a), Self::BuiltIn(b)) => std::ptr::eq(a, b),
(Self::Ordinary(a), Self::Ordinary(b)) => a == b,
(_, _) => false,
}
}
}

impl Eq for FunctionBody {}

/// `Trace` implementation for `FunctionBody`.
///
/// This is indeed safe, but we need to mark this as an empty trace because neither
Expand Down
10 changes: 8 additions & 2 deletions boa/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,9 @@ impl ObjectInternalMethods for Object {
}
}

impl Object {
impl Default for Object {
/// Return a new ObjectData struct, with `kind` set to Ordinary
pub fn default() -> Self {
fn default() -> Self {
let mut object = Self {
data: ObjectData::Ordinary,
internal_slots: FxHashMap::default(),
Expand All @@ -328,6 +328,12 @@ impl Object {
object.set_internal_slot("extensible", Value::from(true));
object
}
}

impl Object {
pub fn new() -> Self {
Default::default()
}

/// Return a new ObjectData struct, with `kind` set to Ordinary
pub fn function(function: Function) -> Self {
Expand Down
52 changes: 52 additions & 0 deletions boa/src/builtins/value/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use super::*;

use crate::builtins::Number;
use std::hash::{Hash, Hasher};

impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
same_value_zero(self, other)
}
}

impl Eq for Value {}

#[derive(PartialEq, Eq, Hash)]
struct UndefinedHashable;

#[derive(PartialEq, Eq, Hash)]
struct NullHashable;

struct RationalHashable(f64);

impl PartialEq for RationalHashable {
#[inline]
fn eq(&self, other: &Self) -> bool {
Number::same_value(self.0, other.0)
}
}

impl Eq for RationalHashable {}

impl Hash for RationalHashable {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_bits().hash(state);
}
}

impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
let data = self.data();
match data {
ValueData::Undefined => UndefinedHashable.hash(state),
ValueData::Null => NullHashable.hash(state),
ValueData::String(ref string) => string.hash(state),
ValueData::Boolean(boolean) => boolean.hash(state),
ValueData::Integer(integer) => integer.hash(state),
ValueData::BigInt(ref bigint) => bigint.hash(state),
ValueData::Rational(rational) => RationalHashable(*rational).hash(state),
ValueData::Symbol(_) | ValueData::Object(_) => std::ptr::hash(data, state),
}
}
}
8 changes: 8 additions & 0 deletions boa/src/builtins/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ use std::{
};

pub mod conversions;
pub mod hash;
pub mod operations;
pub use conversions::*;
pub use hash::*;
pub use operations::*;

/// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`)
Expand All @@ -52,6 +54,12 @@ impl Value {
Self(Gc::new(ValueData::Null))
}

/// Creates a new number with `NaN` value.
#[inline]
pub fn nan() -> Self {
Self::number(NAN)
}

/// Creates a new string value.
#[inline]
pub fn string<S>(value: S) -> Self
Expand Down
2 changes: 1 addition & 1 deletion boa/src/builtins/value/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ pub fn same_value_non_number(x: &Value, y: &Value) -> bool {
}
"bigint" => BigInt::try_from(x).unwrap() == BigInt::try_from(y).unwrap(),
"boolean" => bool::from(x) == bool::from(y),
"object" => std::ptr::eq(x, y),
"object" => std::ptr::eq(x.data(), y.data()),
_ => false,
}
}
Expand Down
63 changes: 63 additions & 0 deletions boa/src/builtins/value/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use super::*;
use crate::{forward, Interpreter, Realm};

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

#[test]
fn check_is_object() {
let val = Value::new_object(None);
Expand Down Expand Up @@ -91,4 +94,64 @@ fn abstract_equality_comparison() {
assert_eq!(forward(&mut engine, "0 == NaN"), "false");
assert_eq!(forward(&mut engine, "'foo' == NaN"), "false");
assert_eq!(forward(&mut engine, "NaN == NaN"), "false");

assert_eq!(
forward(
&mut engine,
"Number.POSITIVE_INFINITY === Number.POSITIVE_INFINITY"
),
"true"
);
assert_eq!(
forward(
&mut engine,
"Number.NEGAVIVE_INFINITY === Number.NEGAVIVE_INFINITY"
),
"true"
);
}

fn hash_value(value: &Value) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}

#[test]
fn hash_undefined() {
let value1 = Value::undefined();
let value_clone = value1.clone();
assert_eq!(value1, value_clone);

let value2 = Value::undefined();
assert_eq!(value1, value2);

assert_eq!(hash_value(&value1), hash_value(&value_clone));
assert_eq!(hash_value(&value2), hash_value(&value_clone));
}

#[test]
fn hash_rational() {
let value1 = Value::rational(1.0);
let value2 = Value::rational(1.0);
assert_eq!(value1, value2);
assert_eq!(hash_value(&value1), hash_value(&value2));

let nan = Value::nan();
assert_eq!(nan, nan);
assert_eq!(hash_value(&nan), hash_value(&nan));
assert_ne!(hash_value(&nan), hash_value(&Value::rational(1.0)));
}

#[test]
fn hash_object() {
let object1 = Value::object(Object::default());
assert_eq!(object1, object1);
assert_eq!(object1, object1.clone());

let object2 = Value::object(Object::default());
assert_ne!(object1, object2);

assert_eq!(hash_value(&object1), hash_value(&object1.clone()));
assert_ne!(hash_value(&object1), hash_value(&object2));
}
8 changes: 4 additions & 4 deletions boa/src/environment/lexical_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub type Environment = Gc<GcCell<Box<dyn EnvironmentRecordTrait>>>;

/// Give each environment an easy way to declare its own type
/// This helps with comparisons
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum EnvironmentType {
Declarative,
Function,
Expand All @@ -33,21 +33,21 @@ pub enum EnvironmentType {
}

/// The scope of a given variable
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum VariableScope {
/// The variable declaration is scoped to the current block (`let` and `const`)
Block,
/// The variable declaration is scoped to the current function (`var`)
Function,
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct LexicalEnvironment {
environment_stack: VecDeque<Environment>,
}

/// An error that occurred during lexing or compiling of the source input.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EnvironmentError {
details: String,
}
Expand Down

0 comments on commit a592aab

Please sign in to comment.