diff --git a/valuescript_compiler/src/asm.rs b/valuescript_compiler/src/asm.rs index 1a9c595..01e403a 100644 --- a/valuescript_compiler/src/asm.rs +++ b/valuescript_compiler/src/asm.rs @@ -112,19 +112,19 @@ impl std::fmt::Display for DefinitionContent { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct FnMeta { - name: String, - content_hashable: ContentHashable, + pub name: String, + pub content_hashable: ContentHashable, } impl std::fmt::Display for FnMeta { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "fn_meta {{\n")?; + writeln!(f, "fn_meta {{")?; - write!( + writeln!( f, - " name: {}", + " name: {},", serde_json::to_string(&self.name).expect("Failed json serialization") )?; @@ -137,11 +137,11 @@ impl std::fmt::Display for FnMeta { write!(f, "{:02x}", b)?; } - write!(f, ",\n")?; + writeln!(f, ",")?; - write!( + writeln!( f, - " deps: {},\n", + " deps: {},", Array { values: deps.clone() } @@ -154,7 +154,7 @@ impl std::fmt::Display for FnMeta { write!(f, "{:02x}", b)?; } - write!(f, ",\n")?; + writeln!(f, ",")?; } } @@ -162,8 +162,9 @@ impl std::fmt::Display for FnMeta { } } -#[derive(Debug, Clone)] -enum ContentHashable { +#[derive(Debug, Clone, Default)] +pub enum ContentHashable { + #[default] Empty, Src([u8; 32], Vec), Hash([u8; 32]), diff --git a/valuescript_compiler/src/assembler.rs b/valuescript_compiler/src/assembler.rs index 05f9833..6b3cf00 100644 --- a/valuescript_compiler/src/assembler.rs +++ b/valuescript_compiler/src/assembler.rs @@ -8,8 +8,8 @@ use num_bigint::{BigInt, Sign}; use valuescript_common::BuiltinName; use crate::asm::{ - Array, Builtin, Class, Definition, DefinitionContent, FnLine, Function, Instruction, Label, - LabelRef, Lazy, Module, Number, Object, Pointer, Register, Value, + Array, Builtin, Class, ContentHashable, Definition, DefinitionContent, FnLine, FnMeta, Function, + Instruction, Label, LabelRef, Lazy, Module, Number, Object, Pointer, Register, Value, }; pub fn assemble(module: &Module) -> Vec { @@ -62,6 +62,9 @@ impl Assembler { DefinitionContent::Function(function) => { self.function(function); } + DefinitionContent::FnMeta(fn_meta) => { + self.fn_meta(fn_meta); + } DefinitionContent::Value(value) => { self.value(value); } @@ -77,7 +80,15 @@ impl Assembler { true => ValueType::GeneratorFunction, } as u8); - self.value(&function.metadata); + match &function.metadata { + Some(p) => { + self.output.push(0x01); + self.pointer(p); + } + None => { + self.output.push(0x00); + } + } self.fn_data = Default::default(); @@ -121,6 +132,36 @@ impl Assembler { self.fn_data.labels_map.resolve(&mut self.output); } + fn fn_meta(&mut self, fn_meta: &FnMeta) { + self.output.push(ValueType::FnMeta as u8); + + self.string(&fn_meta.name); + + match &fn_meta.content_hashable { + ContentHashable::Empty => self.output.push(0x00), + ContentHashable::Src(src_hash, deps) => { + self.output.push(0x01); + + for b in src_hash { + self.output.push(*b); + } + + self.varsize_uint(deps.len()); + + for dep in deps { + self.value(dep); + } + } + ContentHashable::Hash(content_hash) => { + self.output.push(0x02); + + for b in content_hash { + self.output.push(*b); + } + } + } + } + fn lazy(&mut self, lazy: &Lazy) { self.output.push(ValueType::Lazy as u8); @@ -458,6 +499,7 @@ pub enum ValueType { BigInt = 0x13, GeneratorFunction = 0x14, ExportStar = 0x15, + FnMeta = 0x16, // External = TBD, } diff --git a/valuescript_compiler/src/assembly_parser.rs b/valuescript_compiler/src/assembly_parser.rs index e1602ed..7fd50c5 100644 --- a/valuescript_compiler/src/assembly_parser.rs +++ b/valuescript_compiler/src/assembly_parser.rs @@ -5,8 +5,8 @@ use num_bigint::BigInt; use valuescript_common::{InstructionByte, BUILTIN_NAMES}; use crate::asm::{ - Array, Builtin, Class, Definition, DefinitionContent, ExportStar, FnLine, Function, Instruction, - Label, LabelRef, Module, Number, Object, Pointer, Register, Value, + Array, Builtin, Class, ContentHashable, Definition, DefinitionContent, ExportStar, FnLine, + FnMeta, Function, Instruction, Label, LabelRef, Module, Number, Object, Pointer, Register, Value, }; pub struct AssemblyParser<'a> { @@ -85,7 +85,7 @@ impl<'a> AssemblyParser<'a> { return self.content.split('\n').collect(); } - fn render_pos(&self, offset: isize, message: &String) -> String { + fn render_pos(&self, offset: isize, message: &str) -> String { let LineCol { line, col } = self.get_line_col(offset); let source_lines = self.get_lines(); @@ -134,7 +134,7 @@ impl<'a> AssemblyParser<'a> { } if count == 0 { - panic!("{}", self.render_pos(0, &"Expected whitespace".to_string())); + panic!("{}", self.render_pos(0, "Expected whitespace")); } } @@ -180,11 +180,16 @@ impl<'a> AssemblyParser<'a> { self.parse_exact("="); self.parse_optional_whitespace(); - let c = *self.pos.peek().expect("Expected value for definition"); + let content = 'b: { + if self.test_chars("function") { + break 'b DefinitionContent::Function(self.assemble_function()); + } - let content = match c { - 'f' => DefinitionContent::Function(self.assemble_function()), - _ => DefinitionContent::Value(self.assemble_value()), + if self.test_chars("fn_meta") { + break 'b DefinitionContent::FnMeta(self.assemble_fn_meta()); + } + + DefinitionContent::Value(self.assemble_value()) }; Definition { @@ -268,10 +273,7 @@ impl<'a> AssemblyParser<'a> { } } - panic!( - "{}", - self.render_pos(0, &"Failed to parse instruction".to_string()) - ); + panic!("{}", self.render_pos(0, "Failed to parse instruction")); } fn test_instruction_word(&self, word: &str) -> bool { @@ -327,7 +329,7 @@ impl<'a> AssemblyParser<'a> { let optional_identifier = self.test_identifier(); if optional_identifier.is_none() { - panic!("{}", self.render_pos(0, &"Invalid identifier".to_string())); + panic!("{}", self.render_pos(0, "Invalid identifier")); } let identifier = optional_identifier.unwrap(); @@ -383,10 +385,7 @@ impl<'a> AssemblyParser<'a> { } else if c == 't' { result.push('\t'); } else { - panic!( - "{}", - self.render_pos(-1, &"Unimplemented escape sequence".to_string()) - ); + panic!("{}", self.render_pos(-1, "Unimplemented escape sequence")); } escaping = false; @@ -402,10 +401,7 @@ impl<'a> AssemblyParser<'a> { if escaping { panic!( "{}", - self.render_pos( - 0, - &"Unexpected end of input after escape character".to_string() - ) + self.render_pos(0, "Unexpected end of input after escape character") ); } @@ -428,7 +424,7 @@ impl<'a> AssemblyParser<'a> { // Leave metadata as void self.parse_exact("("); } else { - function.metadata = self.assemble_value(); + function.metadata = Some(self.assemble_pointer()); self.parse_optional_whitespace(); self.parse_exact("("); } @@ -525,6 +521,60 @@ impl<'a> AssemblyParser<'a> { function } + fn assemble_fn_meta(&mut self) -> FnMeta { + self.parse_exact("fn_meta {"); + self.parse_optional_whitespace(); + + self.parse_exact("name: "); + + let name = self.parse_string_literal(); + + self.parse_exact(","); + self.parse_optional_whitespace(); + + let content_hashable = 'b: { + if self.test_chars("}") { + break 'b ContentHashable::Empty; + } + + if self.test_chars("srcHash:") { + self.parse_exact("srcHash: "); + + let src_hash = self.assemble_hash(); + + self.parse_exact(","); + self.parse_optional_whitespace(); + + self.parse_exact("deps: "); + let deps = self.assemble_array().values; + self.parse_exact(","); + self.parse_optional_whitespace(); + + break 'b ContentHashable::Src(src_hash, deps); + } + + if self.test_chars("contentHash:") { + self.parse_exact("contentHash: "); + + let content_hash = self.assemble_hash(); + + self.parse_exact(","); + self.parse_optional_whitespace(); + + break 'b ContentHashable::Hash(content_hash); + } + + panic!("{}", self.render_pos(-1, "Expected ContentHashable")); + }; + + self.parse_exact("}"); + + FnMeta { + name, + content_hashable, + } + } + fn assemble_class(&mut self) -> Class { self.parse_exact("class {"); self.parse_optional_whitespace(); @@ -783,7 +833,7 @@ impl<'a> AssemblyParser<'a> { match self.pos.peek() { None => { - panic!("{}", self.render_pos(0, &"Expected value".to_string())); + panic!("{}", self.render_pos(0, "Expected value")); } Some('%') => Value::Register(self.assemble_register()), Some('@') => Value::Pointer(self.assemble_pointer()), @@ -849,10 +899,7 @@ impl<'a> AssemblyParser<'a> { match self.pos.peek() { None => { - panic!( - "{}", - self.render_pos(0, &"Expected value or array end".to_string()) - ); + panic!("{}", self.render_pos(0, "Expected value or array end")); } Some(']') => { self.pos.next(); @@ -945,10 +992,7 @@ impl<'a> AssemblyParser<'a> { None => { panic!( "{}", - self.render_pos( - -(num_string.len() as isize + 1), - &"Expected valid number".to_string() - ) + self.render_pos(-(num_string.len() as isize + 1), "Expected valid number") ); } } @@ -959,10 +1003,7 @@ impl<'a> AssemblyParser<'a> { if value_result.is_err() { panic!( "{}", - self.render_pos( - -(num_string.len() as isize), - &"Expected valid number".to_string() - ) + self.render_pos(-(num_string.len() as isize), "Expected valid number") ); } @@ -1082,6 +1123,28 @@ impl<'a> AssemblyParser<'a> { } } } + + fn assemble_hash(&mut self) -> [u8; 32] { + self.parse_exact("0x"); + + let mut res = [0u8; 32]; + + for res_byte in &mut res { + *res_byte = match self.assemble_hex_byte() { + Some(b) => b, + None => panic!("{}", self.render_pos(0, "Expected hex byte")), + } + } + + res + } + + fn assemble_hex_byte(&mut self) -> Option { + let high_nibble = self.pos.next()?.to_digit(16)? as u8; + let low_nibble = self.pos.next()?.to_digit(16)? as u8; + + Some((high_nibble << 4) | low_nibble) + } } pub fn parse_module(content: &str) -> Module { diff --git a/valuescript_compiler/src/function_compiler.rs b/valuescript_compiler/src/function_compiler.rs index 7da7f4d..3a51323 100644 --- a/valuescript_compiler/src/function_compiler.rs +++ b/valuescript_compiler/src/function_compiler.rs @@ -5,8 +5,8 @@ use std::mem::take; use swc_common::Spanned; use crate::asm::{ - Array, Builtin, Definition, DefinitionContent, FnLine, Function, Instruction, Label, Object, - Pointer, Register, Value, + Builtin, ContentHashable, Definition, DefinitionContent, FnLine, FnMeta, Function, Instruction, + Label, Pointer, Register, Value, }; use crate::diagnostic::{Diagnostic, DiagnosticContainer, DiagnosticReporter}; use crate::expression_compiler::CompiledExpression; @@ -16,7 +16,7 @@ use crate::module_compiler::ModuleCompiler; use crate::name_allocator::{NameAllocator, RegAllocator}; use crate::scope::{NameId, OwnerId}; use crate::scope_analysis::{fn_to_owner_id, Name}; -use crate::src_hash::src_hash_asm; +use crate::src_hash::src_hash; #[derive(Clone, Debug)] pub enum Functionish { @@ -34,49 +34,28 @@ impl Functionish { } } - pub fn metadata(&self, mc: &ModuleCompiler) -> Value { + pub fn metadata(&self, mc: &ModuleCompiler) -> FnMeta { match self { - Functionish::Fn(ident, fn_) => Value::Object(Box::new(Object { - properties: vec![ - ( - Value::String("name".to_string()), - Value::String( - ident - .as_ref() - .map_or_else(|| "".to_string(), |ident| ident.sym.to_string()), - ), - ), - ( - Value::String("srcHash".to_string()), - src_hash_asm(&mc.source, fn_.span), - ), - ( - Value::String("deps".to_string()), - Value::Array(Box::new(Array { - values: mc.scope_analysis.get_deps(fn_.span), - })), - ), - ], - })), - Functionish::Arrow(arrow) => Value::Object(Box::new(Object { - properties: vec![ - ( - Value::String("name".to_string()), - Value::String("".to_string()), - ), - ( - Value::String("srcHash".to_string()), - src_hash_asm(&mc.source, arrow.span), - ), - // ( - // Value::String("deps".to_string()), - // Value::Array(Box::new(Array { - // values: mc.scope_analysis.get_deps(arrow.span), - // })), - // ), - ], - })), - Functionish::Constructor(_, _, _) => Value::Void, + Functionish::Fn(ident, fn_) => FnMeta { + name: ident + .as_ref() + .map_or_else(|| "".to_string(), |ident| ident.sym.to_string()), + content_hashable: ContentHashable::Src( + src_hash(&mc.source, fn_.span), + mc.scope_analysis.get_deps(fn_.span), + ), + }, + Functionish::Arrow(arrow) => FnMeta { + name: "".to_string(), + content_hashable: ContentHashable::Src( + src_hash(&mc.source, arrow.span), + mc.scope_analysis.get_deps(arrow.span), + ), + }, + Functionish::Constructor(_, _, _constructor) => FnMeta { + name: "".to_string(), // TODO: Use class name? + content_hashable: ContentHashable::Empty, // TODO + }, } } } @@ -250,9 +229,12 @@ impl<'a> FunctionCompiler<'a> { Functionish::Constructor(..) => false, }; - let metadata_pointer = self.mc.allocate_defn_numbered("meta"); + let metadata_pointer = self + .mc + .allocate_defn(&format!("{}_meta", definition_pointer.name)); + let metadata = functionish.metadata(self.mc); - self.fn_.metadata = Value::Pointer(metadata_pointer.clone()); + self.fn_.metadata = Some(metadata_pointer.clone()); self.set_owner_id(functionish.owner_id()); @@ -343,7 +325,7 @@ impl<'a> FunctionCompiler<'a> { self.mc.module.definitions.push(Definition { pointer: metadata_pointer, - content: DefinitionContent::Value(metadata), + content: DefinitionContent::FnMeta(metadata), }); } diff --git a/valuescript_compiler/src/optimization/shake_tree.rs b/valuescript_compiler/src/optimization/shake_tree.rs index 6b57c47..1321442 100644 --- a/valuescript_compiler/src/optimization/shake_tree.rs +++ b/valuescript_compiler/src/optimization/shake_tree.rs @@ -72,7 +72,9 @@ pub fn shake_tree(module: &mut Module) { // First include pointers that are allowed to be circular match &defn.content { - DefinitionContent::Function(..) | DefinitionContent::Value(Value::Class(..)) => {} + DefinitionContent::Function(..) + | DefinitionContent::Value(Value::Class(..)) + | DefinitionContent::FnMeta(..) => {} DefinitionContent::Value(..) | DefinitionContent::Lazy(..) => continue, } diff --git a/valuescript_compiler/src/optimization/simplify.rs b/valuescript_compiler/src/optimization/simplify.rs index 8d47058..f7d8724 100644 --- a/valuescript_compiler/src/optimization/simplify.rs +++ b/valuescript_compiler/src/optimization/simplify.rs @@ -15,6 +15,7 @@ pub fn simplify(module: &mut Module, take_registers: bool) { Kal::from_function(defn.pointer.clone(), fn_), ); } + DefinitionContent::FnMeta(_) => {} DefinitionContent::Value(value) => { pointer_kals.insert(defn.pointer.clone(), Kal::from_value(value)); } @@ -27,6 +28,7 @@ pub fn simplify(module: &mut Module, take_registers: bool) { DefinitionContent::Function(fn_) => { simplify_fn(FnState::new(fn_, pointer_kals.clone()), fn_, take_registers) } + DefinitionContent::FnMeta(_) => {} DefinitionContent::Value(_) => {} DefinitionContent::Lazy(_) => {} } diff --git a/valuescript_compiler/src/scope_analysis.rs b/valuescript_compiler/src/scope_analysis.rs index ebd01f1..53dd4c0 100644 --- a/valuescript_compiler/src/scope_analysis.rs +++ b/valuescript_compiler/src/scope_analysis.rs @@ -468,7 +468,7 @@ impl ScopeAnalysis { Decl::Class(class_decl) => { self.class_(scope, &Some(class_decl.ident.clone()), &class_decl.class) } - Decl::Fn(fn_decl) => self.function(scope, Some(&fn_decl.ident), &fn_decl.function), + Decl::Fn(fn_decl) => self.function(scope, Some(&fn_decl.ident), &fn_decl.function, false), Decl::Var(var_decl) => { for decl in &var_decl.decls { self.var_declarator(scope, decl); @@ -496,14 +496,17 @@ impl ScopeAnalysis { scope: &Scope, name: Option<&swc_ecma_ast::Ident>, function: &swc_ecma_ast::Function, + is_method: bool, ) { let owner_span = fn_owner_span(name, function); let child_scope = scope.nest(Some(OwnerId::Span(owner_span))); self.insert_this_name(&child_scope, owner_span); - if let Some(name) = name { - self.insert_pointer_name(&child_scope, NameType::Function, name); + if !is_method { + if let Some(name) = name { + self.insert_pointer_name(&child_scope, NameType::Function, name); + } } for param in &function.params { @@ -950,7 +953,7 @@ impl ScopeAnalysis { _ => None, }; - self.function(scope, method_id, &method.function); + self.function(scope, method_id, &method.function, true); } ClassProp(class_prop) => { self.prop_key(scope, &class_prop.key); @@ -971,6 +974,7 @@ impl ScopeAnalysis { scope, Some(&private_method.key.id), &private_method.function, + true, ); } PrivateProp(private_prop) => { @@ -989,7 +993,7 @@ impl ScopeAnalysis { } fn fn_expr(&mut self, scope: &Scope, fn_expr: &swc_ecma_ast::FnExpr) { - self.function(scope, fn_expr.ident.as_ref(), &fn_expr.function); + self.function(scope, fn_expr.ident.as_ref(), &fn_expr.function, false); } fn expr(&mut self, scope: &Scope, expr: &swc_ecma_ast::Expr) { @@ -1494,7 +1498,7 @@ impl ScopeAnalysis { _ => None, }; - self.function(scope, method_ident, &method.function); + self.function(scope, method_ident, &method.function, true); } swc_ecma_ast::Prop::Assign(assign) => { self.todo( diff --git a/valuescript_compiler/src/src_hash.rs b/valuescript_compiler/src/src_hash.rs index b6c0e0d..c454358 100644 --- a/valuescript_compiler/src/src_hash.rs +++ b/valuescript_compiler/src/src_hash.rs @@ -1,8 +1,6 @@ use swc_common::BytePos; use tiny_keccak::{Hasher, Keccak}; -use crate::asm::Value; - pub fn src_hash(source: &str, span: swc_common::Span) -> [u8; 32] { let BytePos(start) = span.lo; let BytePos(end) = span.hi; @@ -20,14 +18,3 @@ pub fn src_hash(source: &str, span: swc_common::Span) -> [u8; 32] { output } - -pub fn src_hash_asm(source: &str, span: swc_common::Span) -> Value { - let mut result = String::with_capacity(66); - result.push_str("0x"); - - for byte in &src_hash(source, span) { - result.push_str(&format!("{:02x}", byte)); - } - - Value::String(result) -} diff --git a/valuescript_compiler/src/visit_pointers.rs b/valuescript_compiler/src/visit_pointers.rs index a46b762..2c01f8d 100644 --- a/valuescript_compiler/src/visit_pointers.rs +++ b/valuescript_compiler/src/visit_pointers.rs @@ -1,6 +1,6 @@ use crate::asm::{ - Array, Definition, DefinitionContent, ExportStar, FnLine, Instruction, Module, Object, Pointer, - Value, + Array, ContentHashable, Definition, DefinitionContent, ExportStar, FnLine, Instruction, Module, + Object, Pointer, Value, }; pub fn visit_pointers(module: &mut Module, visitor: Visitor) @@ -47,9 +47,21 @@ where match &mut definition.content { DefinitionContent::Function(function) => { - self.value(Some(&definition.pointer), &mut function.metadata); + if let Some(metadata) = &mut function.metadata { + self.pointer(Some(&definition.pointer), metadata); + } + self.body(&definition.pointer, &mut function.body); } + DefinitionContent::FnMeta(fn_meta) => match &mut fn_meta.content_hashable { + ContentHashable::Empty => {} + ContentHashable::Src(_, deps) => { + for dep in deps { + self.value(Some(&definition.pointer), dep); + } + } + ContentHashable::Hash(_) => {} + }, DefinitionContent::Value(value) => { self.value(Some(&definition.pointer), value); } diff --git a/valuescript_vm/src/bytecode_decoder.rs b/valuescript_vm/src/bytecode_decoder.rs index 7eb3744..0f5884c 100644 --- a/valuescript_vm/src/bytecode_decoder.rs +++ b/valuescript_vm/src/bytecode_decoder.rs @@ -6,6 +6,7 @@ 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; @@ -44,6 +45,8 @@ pub enum BytecodeType { Class = 0x11, BigInt = 0x13, GeneratorFunction = 0x14, + // ExportStar = 0x15, + // FnMeta = 0x16, Unrecognized = 0xff, } @@ -133,7 +136,7 @@ impl BytecodeDecoder { } .to_val() } - BytecodeType::Function => self.decode_function(false, registers), + BytecodeType::Function => self.decode_function(false), BytecodeType::Pointer => self.decode_pointer(registers), BytecodeType::Register => match registers[self.decode_register_index().unwrap()].clone() { Val::Void => Val::Undefined, @@ -151,7 +154,7 @@ impl BytecodeDecoder { } .to_val(), BytecodeType::BigInt => self.decode_bigint().to_val(), - BytecodeType::GeneratorFunction => self.decode_function(true, registers), + BytecodeType::GeneratorFunction => self.decode_function(true), BytecodeType::Unrecognized => panic!("Unrecognized bytecode type at {}", self.pos - 1), } } @@ -280,8 +283,16 @@ impl BytecodeDecoder { } } - pub fn decode_function(&mut self, is_generator: bool, registers: &mut Vec) -> Val { - let metadata = self.decode_val(registers); + pub fn decode_function(&mut self, is_generator: bool) -> Val { + let metadata_pos = if self.decode_byte() == 0 { + None + } else { + if self.decode_type() != BytecodeType::Pointer { + panic!("Unexpected non-pointer function metadata"); + } + + Some(self.decode_pos()) + }; // TODO: Support >256 let register_count = self.decode_byte() as usize; @@ -289,7 +300,7 @@ impl BytecodeDecoder { VsFunction { bytecode: self.bytecode.clone(), - metadata, + metadata_pos, is_generator, register_count, parameter_count, @@ -302,4 +313,34 @@ impl BytecodeDecoder { pub fn decode_instruction(&mut self) -> InstructionByte { InstructionByte::from_byte(self.decode_byte()) } + + pub fn decode_content_hash(&mut self) -> Result<[u8; 32], Val> { + if self.decode_byte() != 0x16 { + return Err("Can't decode a content hash here".to_internal_error()); + } + + if self.decode_type() != BytecodeType::String { + return Err("Expected string".to_internal_error()); + } + + let name_len = self.decode_varsize_uint(); + self.pos += name_len; + + match self.decode_byte() { + 0 => Err("Missing content_hashable".to_internal_error()), // Empty + 1 | 2 => { + // Src | Hash + // TODO: In the Src case, we should be calculating the content hash, or throwing a todo + // error + let mut res = [0u8; 32]; + + for b in &mut res { + *b = self.decode_byte(); + } + + Ok(res) + } + _ => Err("Unrecognized content_hashable case".to_internal_error()), + } + } } diff --git a/valuescript_vm/src/operations.rs b/valuescript_vm/src/operations.rs index 4a8067b..f4d0f14 100644 --- a/valuescript_vm/src/operations.rs +++ b/valuescript_vm/src/operations.rs @@ -194,10 +194,16 @@ pub fn op_eq_impl(left: &Val, right: &Val) -> Result { } } - op_triple_eq_impl( - &left.metadata.sub(&"srcHash".to_val())?, - &right.metadata.sub(&"srcHash".to_val())?, - )? + let left_hash = left.content_hash()?; + let right_hash = right.content_hash()?; + + for i in 0..32 { + if left_hash[i] != right_hash[i] { + return Ok(false); + } + } + + true } _ => { if left.is_truthy() != right.is_truthy() { @@ -319,10 +325,16 @@ pub fn op_triple_eq_impl(left: &Val, right: &Val) -> Result { } } - op_triple_eq_impl( - &left.metadata.sub(&"srcHash".to_val())?, - &right.metadata.sub(&"srcHash".to_val())?, - )? + let left_hash = left.content_hash()?; + let right_hash = right.content_hash()?; + + for i in 0..32 { + if left_hash[i] != right_hash[i] { + return Ok(false); + } + } + + true } #[allow(clippy::vtable_address_comparisons)] // TODO: Is this ok? (Val::Static(left), Val::Static(right)) => std::ptr::eq(&**left, &**right), diff --git a/valuescript_vm/src/vs_function.rs b/valuescript_vm/src/vs_function.rs index 3de72f7..6df4873 100644 --- a/valuescript_vm/src/vs_function.rs +++ b/valuescript_vm/src/vs_function.rs @@ -1,6 +1,7 @@ use std::rc::Rc; -use crate::bytecode::Bytecode; +use crate::builtins::internal_error_builtin::ToInternalError; +use crate::bytecode::{Bytecode, DecoderMaker}; use crate::make_generator_frame::MakeGeneratorFrame; use crate::vs_value::ToVal; @@ -12,7 +13,7 @@ use super::vs_value::Val; #[derive(Debug, Clone)] pub struct VsFunction { pub bytecode: Rc, - pub metadata: Val, + pub metadata_pos: Option, pub is_generator: bool, pub register_count: usize, pub parameter_count: usize, @@ -30,7 +31,7 @@ impl VsFunction { VsFunction { bytecode: self.bytecode.clone(), - metadata: self.metadata.clone(), + metadata_pos: self.metadata_pos, is_generator: self.is_generator, register_count: self.register_count, parameter_count: self.parameter_count, @@ -39,6 +40,13 @@ impl VsFunction { } } + pub fn content_hash(&self) -> Result<[u8; 32], Val> { + match self.metadata_pos { + Some(p) => self.bytecode.decoder(p).decode_content_hash(), + None => Err("Can't get content_hash without metadata_pos".to_internal_error()), + } + } + pub fn make_bytecode_frame(&self) -> BytecodeStackFrame { let mut registers: Vec = Vec::with_capacity(self.register_count - 1);