Skip to content

Commit

Permalink
Implement direct class comparison
Browse files Browse the repository at this point in the history
  • Loading branch information
voltrevo committed Aug 16, 2023
1 parent 5d1da13 commit d499376
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
//! test_output([true,true,true,true,true])
//! test_output([true,true,true,true,true,true])

// When functions (and therefore classes) have the same source (and references), they should compare
// equal due to the content hash system.

export default () => {
const a = new classes[0](1, 2);
const b = new classes[1](1, 2);
const a = new pointClasses.V1(3, 5);
const b = new pointClasses.V2(3, 5);

return [
a === b,
// classes[0] === classes[1], TODO: Compare actual classes
a instanceof classes[0],
a instanceof classes[1],
b instanceof classes[0],
b instanceof classes[1],
pointClasses.V1 === pointClasses.V2,
a instanceof pointClasses.V1,
a instanceof pointClasses.V2,
b instanceof pointClasses.V1,
b instanceof pointClasses.V2,
];
};

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

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

lenSq() {
return this.x ** 2 + this.y ** 2;
}
},
];
};
1 change: 1 addition & 0 deletions valuescript_compiler/src/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ impl Assembler {

fn class(&mut self, class: &Class) {
self.output.push(ValueType::Class as u8);
self.meta(&class.meta);
self.value(&class.constructor);
self.value(&class.prototype);
self.value(&class.static_);
Expand Down
5 changes: 5 additions & 0 deletions valuescript_compiler/src/optimization/kal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@ impl Kal {
}
Kal::Function(_) => return None,
Kal::Class(class) => VsClass {
name: class.meta.name,
content_hash: match class.meta.content_hashable {
asm::ContentHashable::Empty | asm::ContentHashable::Src(_, _) => None,
asm::ContentHashable::Content(hash) => Some(hash.0),
},
constructor: class.constructor.try_to_val()?,
prototype: class.prototype.try_to_val()?,
static_: class.static_.try_to_val()?,
Expand Down
7 changes: 6 additions & 1 deletion valuescript_compiler/src/optimization/try_to_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use valuescript_vm::{
vs_value::{ToVal, Val},
};

use crate::asm::{Number, Value};
use crate::asm::{ContentHashable, Number, Value};

pub trait TryToVal {
fn try_to_val(self) -> Result<Val, Val>;
Expand Down Expand Up @@ -45,6 +45,11 @@ impl TryToVal for Value {
.to_val()
}
Value::Class(class) => VsClass {
name: class.meta.name,
content_hash: match class.meta.content_hashable {
ContentHashable::Empty | ContentHashable::Src(_, _) => None,
ContentHashable::Content(hash) => Some(hash.0),
},
constructor: class.constructor.try_to_val()?,
prototype: class.prototype.try_to_val()?,
static_: class.static_.try_to_val()?,
Expand Down
2 changes: 2 additions & 0 deletions valuescript_vm/src/builtins/error_builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ impl BuiltinObject for ErrorBuiltin {

fn bo_as_class_data() -> Option<Rc<VsClass>> {
Some(Rc::new(VsClass {
name: "Error".to_string(),
content_hash: None,
constructor: SET_MESSAGE.to_val(),
prototype: make_error_prototype(),
static_: VsObject::default().to_val(),
Expand Down
2 changes: 2 additions & 0 deletions valuescript_vm/src/builtins/internal_error_builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ impl BuiltinObject for InternalErrorBuiltin {

fn bo_as_class_data() -> Option<Rc<VsClass>> {
Some(Rc::new(VsClass {
name: "InternalError".to_string(),
content_hash: None,
constructor: Val::Static(&SET_MESSAGE),
prototype: make_internal_error_prototype(),
static_: VsObject::default().to_val(),
Expand Down
2 changes: 2 additions & 0 deletions valuescript_vm/src/builtins/range_error_builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ impl BuiltinObject for RangeErrorBuiltin {

fn bo_as_class_data() -> Option<Rc<VsClass>> {
Some(Rc::new(VsClass {
name: "RangeError".to_string(),
content_hash: None,
constructor: Val::Static(&SET_MESSAGE),
prototype: make_range_error_prototype(),
static_: VsObject::default().to_val(),
Expand Down
2 changes: 2 additions & 0 deletions valuescript_vm/src/builtins/type_error_builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ impl BuiltinObject for TypeErrorBuiltin {

fn bo_as_class_data() -> Option<Rc<VsClass>> {
Some(Rc::new(VsClass {
name: "TypeError".to_string(),
content_hash: None,
constructor: Val::Static(&SET_MESSAGE),
prototype: make_type_error_prototype(),
static_: VsObject::default().to_val(),
Expand Down
43 changes: 27 additions & 16 deletions valuescript_vm/src/bytecode_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use num_bigint::BigInt;
use num_bigint::Sign;
use valuescript_common::InstructionByte;

use crate::builtins::internal_error_builtin::ToInternalError;
use crate::builtins::BUILTIN_VALS;
use crate::bytecode::Bytecode;
use crate::vs_class::VsClass;
Expand Down Expand Up @@ -147,12 +146,18 @@ impl BytecodeDecoder {
val => take(val),
},
BytecodeType::Builtin => BUILTIN_VALS[self.decode_varsize_uint()](),
BytecodeType::Class => VsClass {
constructor: self.decode_val(registers),
prototype: self.decode_val(registers),
static_: self.decode_val(registers),
BytecodeType::Class => {
let meta = self.decode_meta();

VsClass {
name: meta.name,
content_hash: meta.content_hash,
constructor: self.decode_val(registers),
prototype: self.decode_val(registers),
static_: self.decode_val(registers),
}
.to_val()
}
.to_val(),
BytecodeType::BigInt => self.decode_bigint().to_val(),
BytecodeType::GeneratorFunction => self.decode_function(true),
BytecodeType::Unrecognized => panic!("Unrecognized bytecode type at {}", self.pos - 1),
Expand Down Expand Up @@ -314,20 +319,19 @@ impl BytecodeDecoder {
InstructionByte::from_byte(self.decode_byte())
}

pub fn decode_content_hash(&mut self) -> Result<[u8; 32], Val> {
pub fn decode_meta(&mut self) -> Meta {
if self.decode_byte() != 0x16 {
return Err("Can't decode a content hash here".to_internal_error());
panic!("Expected meta");
}

if self.decode_type() != BytecodeType::String {
return Err("Expected string".to_internal_error());
panic!("Expected string");
}

let name_len = self.decode_varsize_uint();
self.pos += name_len;
let name = self.decode_string();

match self.decode_byte() {
0 | 1 => Err("Missing content hash".to_internal_error()), // Empty | Src
let content_hash = match self.decode_byte() {
0 | 1 => None, // Empty | Src
2 => {
// Hash
let mut res = [0u8; 32];
Expand All @@ -336,9 +340,16 @@ impl BytecodeDecoder {
*b = self.decode_byte();
}

Ok(res)
Some(res)
}
_ => Err("Unrecognized content_hashable case".to_internal_error()),
}
_ => panic!("Unrecognized ContentHashable case"),
};

Meta { name, content_hash }
}
}

pub struct Meta {
pub name: String,
pub content_hash: Option<[u8; 32]>,
}
10 changes: 10 additions & 0 deletions valuescript_vm/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ pub fn op_eq_impl(left: &Val, right: &Val) -> Result<bool, Val> {

true
}
(Val::Class(left), Val::Class(right)) => match (&left.content_hash, &right.content_hash) {
(None, None) => std::ptr::eq(&**left, &**right),
(None, Some(_)) | (Some(_), None) => return Ok(false),
(Some(left_hash), Some(right_hash)) => left_hash == right_hash,
},
_ => {
if left.is_truthy() != right.is_truthy() {
return Ok(false);
Expand Down Expand Up @@ -350,6 +355,11 @@ pub fn op_triple_eq_impl(left: &Val, right: &Val) -> Result<bool, Val> {
format!("TODO: op=== with special types ({}, {})", left, right).to_internal_error(),
);
}
(Val::Class(left), Val::Class(right)) => match (&left.content_hash, &right.content_hash) {
(None, None) => std::ptr::eq(&**left, &**right),
(None, Some(_)) | (Some(_), None) => return Ok(false),
(Some(left_hash), Some(right_hash)) => left_hash == right_hash,
},
_ => {
assert!(left.typeof_() != right.typeof_());
false
Expand Down
2 changes: 2 additions & 0 deletions valuescript_vm/src/vs_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use super::vs_value::Val;

#[derive(Debug)]
pub struct VsClass {
pub name: String,
pub content_hash: Option<[u8; 32]>,
pub constructor: Val,
pub prototype: Val,
pub static_: Val,
Expand Down
5 changes: 4 additions & 1 deletion valuescript_vm/src/vs_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ impl VsFunction {

pub fn content_hash(&self) -> Result<[u8; 32], Val> {
match self.meta_pos {
Some(p) => self.bytecode.decoder(p).decode_content_hash(),
Some(p) => match self.bytecode.decoder(p).decode_meta().content_hash {
Some(content_hash) => Ok(content_hash),
None => Err("content_hash missing".to_internal_error()),
},
None => Err("Can't get content_hash without meta_pos".to_internal_error()),
}
}
Expand Down

0 comments on commit d499376

Please sign in to comment.