diff --git a/inputs/passing/contentHashing/compareClassInstancesWithEqualSources.ts b/inputs/passing/contentHashing/compareClassInstancesWithEqualSources.ts index 5802810..45339d5 100644 --- a/inputs/passing/contentHashing/compareClassInstancesWithEqualSources.ts +++ b/inputs/passing/contentHashing/compareClassInstancesWithEqualSources.ts @@ -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; } }, -]; +}; diff --git a/valuescript_compiler/src/assembler.rs b/valuescript_compiler/src/assembler.rs index bd6d16c..ed326dd 100644 --- a/valuescript_compiler/src/assembler.rs +++ b/valuescript_compiler/src/assembler.rs @@ -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_); diff --git a/valuescript_compiler/src/optimization/kal.rs b/valuescript_compiler/src/optimization/kal.rs index 7f04bd2..1528887 100644 --- a/valuescript_compiler/src/optimization/kal.rs +++ b/valuescript_compiler/src/optimization/kal.rs @@ -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()?, diff --git a/valuescript_compiler/src/optimization/try_to_val.rs b/valuescript_compiler/src/optimization/try_to_val.rs index 5b9134e..b5b391a 100644 --- a/valuescript_compiler/src/optimization/try_to_val.rs +++ b/valuescript_compiler/src/optimization/try_to_val.rs @@ -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; @@ -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()?, diff --git a/valuescript_vm/src/builtins/error_builtin.rs b/valuescript_vm/src/builtins/error_builtin.rs index 69c3b55..780f839 100644 --- a/valuescript_vm/src/builtins/error_builtin.rs +++ b/valuescript_vm/src/builtins/error_builtin.rs @@ -40,6 +40,8 @@ impl BuiltinObject for ErrorBuiltin { fn bo_as_class_data() -> Option> { 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(), diff --git a/valuescript_vm/src/builtins/internal_error_builtin.rs b/valuescript_vm/src/builtins/internal_error_builtin.rs index c1df770..4a3fff2 100644 --- a/valuescript_vm/src/builtins/internal_error_builtin.rs +++ b/valuescript_vm/src/builtins/internal_error_builtin.rs @@ -39,6 +39,8 @@ impl BuiltinObject for InternalErrorBuiltin { fn bo_as_class_data() -> Option> { 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(), diff --git a/valuescript_vm/src/builtins/range_error_builtin.rs b/valuescript_vm/src/builtins/range_error_builtin.rs index 470e4a7..c3da412 100644 --- a/valuescript_vm/src/builtins/range_error_builtin.rs +++ b/valuescript_vm/src/builtins/range_error_builtin.rs @@ -31,6 +31,8 @@ impl BuiltinObject for RangeErrorBuiltin { fn bo_as_class_data() -> Option> { 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(), diff --git a/valuescript_vm/src/builtins/type_error_builtin.rs b/valuescript_vm/src/builtins/type_error_builtin.rs index 634946b..f8045c7 100644 --- a/valuescript_vm/src/builtins/type_error_builtin.rs +++ b/valuescript_vm/src/builtins/type_error_builtin.rs @@ -39,6 +39,8 @@ impl BuiltinObject for TypeErrorBuiltin { fn bo_as_class_data() -> Option> { 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(), diff --git a/valuescript_vm/src/bytecode_decoder.rs b/valuescript_vm/src/bytecode_decoder.rs index ca56105..a0a6dc8 100644 --- a/valuescript_vm/src/bytecode_decoder.rs +++ b/valuescript_vm/src/bytecode_decoder.rs @@ -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; @@ -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), @@ -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]; @@ -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]>, +} diff --git a/valuescript_vm/src/operations.rs b/valuescript_vm/src/operations.rs index f4d0f14..92fabd0 100644 --- a/valuescript_vm/src/operations.rs +++ b/valuescript_vm/src/operations.rs @@ -205,6 +205,11 @@ pub fn op_eq_impl(left: &Val, right: &Val) -> Result { 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); @@ -350,6 +355,11 @@ pub fn op_triple_eq_impl(left: &Val, right: &Val) -> Result { 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 diff --git a/valuescript_vm/src/vs_class.rs b/valuescript_vm/src/vs_class.rs index d26dd9c..8cd66d4 100644 --- a/valuescript_vm/src/vs_class.rs +++ b/valuescript_vm/src/vs_class.rs @@ -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, diff --git a/valuescript_vm/src/vs_function.rs b/valuescript_vm/src/vs_function.rs index 8b11a22..182213b 100644 --- a/valuescript_vm/src/vs_function.rs +++ b/valuescript_vm/src/vs_function.rs @@ -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()), } }