Skip to content

Commit

Permalink
Add fake hash and use it to compare functions and class instances
Browse files Browse the repository at this point in the history
  • Loading branch information
voltrevo committed Jul 26, 2023
1 parent b683e47 commit 6966319
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 2 deletions.
12 changes: 12 additions & 0 deletions inputs/failing/collidingClasses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// //! test_output("foo")

export default () => {
class X {
foo = "foo";
}

return new X().foo;
};

// deno-lint-ignore no-unused-vars
class X {}
31 changes: 31 additions & 0 deletions inputs/failing/compareClassInstancesWithEqualSources.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! test_output(false)
// Should be: true

// When functions (and therefore classes) have the same source (and references), they should compare
// equal due to the content hash system. This isn't implemented yet - the content hash is just faked
// using its location in the bytecode, which means you only get equal comparison when the functions
// are referentially equal (inconsistent with value semantics).

export default () => {
const a = new classes[0](1, 2);
const b = new classes[1](1, 2);

return a === b;
};

const classes = [
class Point {
constructor(public x: number, public y: number) {}

lenSq() {
return this.x ** 2 + this.y ** 2;
}
},
class Point {
constructor(public x: number, public y: number) {}

lenSq() {
return this.x ** 2 + this.y ** 2;
}
},
];
22 changes: 22 additions & 0 deletions inputs/passing/compareFunctionsAndClasses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! test_output([true,true,false,true])

export default () => {
const a = new Point(1, 2);
const b = new Point(1, 2);
const c = new Point(1, 3);

return [
a.lenSq === b.lenSq,
a === b,
a === c,
c === c,
];
};

class Point {
constructor(public x: number, public y: number) {}

lenSq() {
return this.x ** 2 + this.y ** 2;
}
}
6 changes: 6 additions & 0 deletions valuescript_vm/src/bytecode_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,18 @@ impl BytecodeDecoder {
}

pub fn decode_function(&mut self, is_generator: bool) -> Val {
// TODO: Use actual content hash
let mut hash = [0u8; 32];
hash[0] = (self.pos & 0xff) as u8;
hash[1] = ((self.pos >> 8) & 0xff) as u8;

// TODO: Support >256
let register_count = self.decode_byte() as usize;
let parameter_count = self.decode_byte() as usize;

VsFunction {
bytecode: self.bytecode.clone(),
hash,
is_generator,
register_count,
parameter_count,
Expand Down
48 changes: 46 additions & 2 deletions valuescript_vm/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,17 @@ pub fn op_eq_impl(left: &Val, right: &Val) -> Result<bool, Val> {
true
}
(Val::Object(left_object), Val::Object(right_object)) => 'b: {
match (&left_object.prototype, &right_object.prototype) {
(None, None) => {}
(None, Some(_)) => return Ok(false),
(Some(_), None) => return Ok(false),
(Some(ref left_proto), Some(ref right_proto)) => {
if !op_eq_impl(left_proto, right_proto)? {
return Ok(false);
}
}
}

if left_object.prototype.is_some() || right_object.prototype.is_some() {
return Err("TODO: class instance comparison".to_internal_error());
}
Expand All @@ -172,6 +183,19 @@ pub fn op_eq_impl(left: &Val, right: &Val) -> Result<bool, Val> {

true
}
(Val::Function(left), Val::Function(right)) => {
if left.binds.len() != right.binds.len() {
return Ok(false);
}

for i in 0..left.binds.len() {
if !op_eq_impl(&left.binds[i], &right.binds[i])? {
return Ok(false);
}
}

left.hash == right.hash
}
_ => {
if left.is_truthy() != right.is_truthy() {
return Ok(false);
Expand Down Expand Up @@ -248,8 +272,15 @@ pub fn op_triple_eq_impl(left: &Val, right: &Val) -> Result<bool, Val> {
true
}
(Val::Object(left_object), Val::Object(right_object)) => 'b: {
if left_object.prototype.is_some() || right_object.prototype.is_some() {
return Err("TODO: class instance comparison".to_internal_error());
match (&left_object.prototype, &right_object.prototype) {
(None, None) => {}
(None, Some(_)) => return Ok(false),
(Some(_), None) => return Ok(false),
(Some(ref left_proto), Some(ref right_proto)) => {
if !op_triple_eq_impl(left_proto, right_proto)? {
return Ok(false);
}
}
}

if std::ptr::eq(&**left_object, &**right_object) {
Expand All @@ -274,6 +305,19 @@ pub fn op_triple_eq_impl(left: &Val, right: &Val) -> Result<bool, Val> {

true
}
(Val::Function(left), Val::Function(right)) => {
if left.binds.len() != right.binds.len() {
return Ok(false);
}

for i in 0..left.binds.len() {
if !op_triple_eq_impl(&left.binds[i], &right.binds[i])? {
return Ok(false);
}
}

left.hash == right.hash
}
(Val::Static(..) | Val::Dynamic(..) | Val::CopyCounter(..), _)
| (_, Val::Static(..) | Val::Dynamic(..) | Val::CopyCounter(..)) => {
if left.typeof_() != right.typeof_() {
Expand Down
2 changes: 2 additions & 0 deletions valuescript_vm/src/vs_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use super::vs_value::Val;
#[derive(Debug, Clone)]
pub struct VsFunction {
pub bytecode: Rc<Bytecode>,
pub hash: [u8; 32],
pub is_generator: bool,
pub register_count: usize,
pub parameter_count: usize,
Expand All @@ -29,6 +30,7 @@ impl VsFunction {

VsFunction {
bytecode: self.bytecode.clone(),
hash: self.hash,
is_generator: self.is_generator,
register_count: self.register_count,
parameter_count: self.parameter_count,
Expand Down

0 comments on commit 6966319

Please sign in to comment.