This commit is contained in:
Andrew Morris
2023-08-14 17:35:24 +10:00
parent 5abd1c472e
commit 3ffafda8d2
12 changed files with 293 additions and 137 deletions

View File

@@ -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<Value>),
Hash([u8; 32]),

View File

@@ -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<u8> {
@@ -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,
}

View File

@@ -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<u8> {
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 {

View File

@@ -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),
});
}

View File

@@ -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,
}

View File

@@ -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(_) => {}
}

View File

@@ -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(

View File

@@ -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)
}

View File

@@ -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<Visitor>(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);
}