Extract string constants

This commit is contained in:
Andrew Morris
2023-06-23 13:48:58 +10:00
parent f49762c040
commit c880dda7c9
6 changed files with 259 additions and 19 deletions

View File

@@ -1,3 +1,5 @@
use std::hash::{Hash as HashTrait, Hasher};
use num_bigint::BigInt;
use valuescript_common::InstructionByte;
@@ -387,6 +389,120 @@ pub enum Instruction {
YieldStar(Value, Register),
}
pub enum InstructionFieldMut<'a> {
Value(&'a mut Value),
Register(&'a mut Register),
LabelRef(&'a mut LabelRef),
}
impl Instruction {
pub fn visit_fields_mut<F>(&mut self, visit: &mut F)
where
F: FnMut(InstructionFieldMut) -> (),
{
use Instruction::*;
match self {
End => {}
Mov(arg, dst)
| OpNot(arg, dst)
| OpBitNot(arg, dst)
| TypeOf(arg, dst)
| UnaryPlus(arg, dst)
| UnaryMinus(arg, dst)
| Import(arg, dst)
| ImportStar(arg, dst)
| Cat(arg, dst)
| Yield(arg, dst)
| YieldStar(arg, dst) => {
visit(InstructionFieldMut::Value(arg));
visit(InstructionFieldMut::Register(dst));
}
OpInc(arg) | OpDec(arg) => {
visit(InstructionFieldMut::Register(arg));
}
OpPlus(left, right, dst)
| OpMinus(left, right, dst)
| OpMul(left, right, dst)
| OpDiv(left, right, dst)
| OpMod(left, right, dst)
| OpExp(left, right, dst)
| OpEq(left, right, dst)
| OpNe(left, right, dst)
| OpTripleEq(left, right, dst)
| OpTripleNe(left, right, dst)
| OpAnd(left, right, dst)
| OpOr(left, right, dst)
| OpLess(left, right, dst)
| OpLessEq(left, right, dst)
| OpGreater(left, right, dst)
| OpGreaterEq(left, right, dst)
| OpNullishCoalesce(left, right, dst)
| OpOptionalChain(left, right, dst)
| OpBitAnd(left, right, dst)
| OpBitOr(left, right, dst)
| OpBitXor(left, right, dst)
| OpLeftShift(left, right, dst)
| OpRightShift(left, right, dst)
| OpRightShiftUnsigned(left, right, dst)
| InstanceOf(left, right, dst)
| In(left, right, dst)
| Call(left, right, dst)
| Bind(left, right, dst)
| Sub(left, right, dst)
| SubMov(left, right, dst)
| New(left, right, dst) => {
visit(InstructionFieldMut::Value(left));
visit(InstructionFieldMut::Value(right));
visit(InstructionFieldMut::Register(dst));
}
Apply(a1, a2, a3, dst)
| SubCall(a1, a2, a3, dst)
| ConstSubCall(a1, a2, a3, dst)
| ThisSubCall(a1, a2, a3, dst) => {
visit(InstructionFieldMut::Value(a1));
visit(InstructionFieldMut::Value(a2));
visit(InstructionFieldMut::Value(a3));
visit(InstructionFieldMut::Register(dst));
}
Jmp(label_ref) => {
visit(InstructionFieldMut::LabelRef(label_ref));
}
JmpIf(cond, label_ref) => {
visit(InstructionFieldMut::Value(cond));
visit(InstructionFieldMut::LabelRef(label_ref));
}
Throw(ex) => {
visit(InstructionFieldMut::Value(ex));
}
SetCatch(label_ref, dst) => {
visit(InstructionFieldMut::LabelRef(label_ref));
visit(InstructionFieldMut::Register(dst));
}
Next(iterable, dst) => {
visit(InstructionFieldMut::Register(iterable));
visit(InstructionFieldMut::Register(dst));
}
UnpackIterRes(iter_res, value_dst, done_dst) => {
visit(InstructionFieldMut::Register(iter_res));
visit(InstructionFieldMut::Register(value_dst));
visit(InstructionFieldMut::Register(done_dst));
}
UnsetCatch | RequireMutableThis => {}
}
}
}
impl std::fmt::Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
@@ -630,13 +746,13 @@ impl Instruction {
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Value {
Void,
Undefined,
Null,
Bool(bool),
Number(f64),
Number(Number),
BigInt(BigInt),
String(String),
Array(Box<Array>),
@@ -646,10 +762,66 @@ pub enum Value {
Builtin(Builtin),
}
impl Default for Value {
fn default() -> Self {
Value::Void
}
}
#[derive(Debug, Clone)]
pub struct Number(pub f64);
impl PartialEq for Number {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for Number {
fn assert_receiver_is_total_eq(&self) {}
}
impl HashTrait for Number {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.0.to_bits());
}
}
impl Value {
pub fn to_ce(self) -> CompiledExpression {
CompiledExpression::new(self, vec![])
}
pub fn visit_values_mut<F>(&mut self, visit: &mut F)
where
F: FnMut(&mut Value) -> (),
{
visit(self);
match self {
Value::Array(array) => {
for item in &mut array.values {
item.visit_values_mut(visit);
}
}
Value::Object(object) => {
for (k, v) in &mut object.properties {
k.visit_values_mut(visit);
v.visit_values_mut(visit);
}
}
Value::Void => {}
Value::Undefined => {}
Value::Null => {}
Value::Bool(..) => {}
Value::Number(..) => {}
Value::BigInt(..) => {}
Value::String(..) => {}
Value::Register(..) => {}
Value::Pointer(..) => {}
Value::Builtin(..) => {}
}
}
}
impl std::fmt::Display for Value {
@@ -659,7 +831,7 @@ impl std::fmt::Display for Value {
Value::Undefined => write!(f, "undefined"),
Value::Null => write!(f, "null"),
Value::Bool(value) => write!(f, "{}", value),
Value::Number(value) => {
Value::Number(Number(value)) => {
if value.is_infinite() {
if value.is_sign_positive() {
write!(f, "Infinity")
@@ -718,7 +890,7 @@ impl std::fmt::Display for Builtin {
}
}
#[derive(Default, Debug, Clone)]
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Array {
pub values: Vec<Value>,
}
@@ -736,7 +908,7 @@ impl std::fmt::Display for Array {
}
}
#[derive(Default, Debug, Clone)]
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Object {
pub properties: Vec<(Value, Value)>,
}

View File

@@ -9,7 +9,7 @@ use valuescript_common::BuiltinName;
use crate::asm::{
Array, Builtin, Class, Definition, DefinitionContent, FnLine, Function, Instruction, Label,
LabelRef, Lazy, Module, Object, Pointer, Register, Value,
LabelRef, Lazy, Module, Number, Object, Pointer, Register, Value,
};
pub fn assemble(module: &Module) -> Vec<u8> {
@@ -267,7 +267,7 @@ impl Assembler {
self.output.push(register.value_type() as u8);
self.register(register);
}
Value::Number(number) => self.number(*number),
Value::Number(Number(number)) => self.number(*number),
Value::BigInt(bigint) => self.bigint(bigint),
Value::String(string) => self.string(string),
Value::Bool(boolean) => match boolean {

View File

@@ -6,7 +6,7 @@ use valuescript_common::{InstructionByte, BUILTIN_NAMES};
use crate::asm::{
Array, Builtin, Class, Definition, DefinitionContent, FnLine, Function, Instruction, Label,
LabelRef, Module, Object, Pointer, Register, Value,
LabelRef, Module, Number, Object, Pointer, Register, Value,
};
pub struct AssemblyParser<'a> {
@@ -797,8 +797,8 @@ impl<'a> AssemblyParser<'a> {
"null" => Value::Null,
"false" => Value::Bool(false),
"true" => Value::Bool(true),
"Infinity" => Value::Number(f64::INFINITY),
"NaN" => Value::Number(f64::NAN),
"Infinity" => Value::Number(Number(f64::INFINITY)),
"NaN" => Value::Number(Number(f64::NAN)),
// TODO: Finish implementing the different values
_ => {
@@ -912,7 +912,7 @@ impl<'a> AssemblyParser<'a> {
fn assemble_number(&mut self) -> Value {
if self.parse_one_of(&["-Infinity", ""]) == "-Infinity" {
return Value::Number(f64::NEG_INFINITY);
return Value::Number(Number(f64::NEG_INFINITY));
}
let mut num_string = "".to_string();
@@ -957,7 +957,7 @@ impl<'a> AssemblyParser<'a> {
);
}
Value::Number(value_result.unwrap())
Value::Number(Number(value_result.unwrap()))
}
fn assemble_object(&mut self) -> Object {

View File

@@ -1,6 +1,6 @@
use crate::asm::Value;
use crate::asm::{Number, Value};
pub const CONSTANTS: [(&'static str, Value); 2] = [
("NaN", Value::Number(std::f64::NAN)),
("Infinity", Value::Number(std::f64::INFINITY)),
("NaN", Value::Number(Number(std::f64::NAN))),
("Infinity", Value::Number(Number(std::f64::INFINITY))),
];

View File

@@ -3,7 +3,7 @@ use std::mem::take;
use queues::*;
use swc_common::Spanned;
use crate::asm::{Array, Builtin, Instruction, Label, Object, Register, Value};
use crate::asm::{Array, Builtin, Instruction, Label, Number, Object, Register, Value};
use crate::diagnostic::{Diagnostic, DiagnosticLevel};
use crate::function_compiler::{FunctionCompiler, Functionish, QueuedFunction};
use crate::scope::{NameId, OwnerId};
@@ -1397,7 +1397,7 @@ impl<'a> ExpressionCompiler<'a> {
Str(str_) => return Value::String(str_.value.to_string()),
Bool(bool_) => return Value::Bool(bool_.value),
Null(_) => return Value::Null,
Num(num) => return Value::Number(num.value),
Num(num) => return Value::Number(Number(num.value)),
BigInt(bigint) => return Value::BigInt(bigint.value.clone()),
Regex(_) => ("_todo_regex_literal", "Regex literals"),
JSXText(_) => ("_todo_jsxtext_literal", "JSXText literals"),
@@ -1457,7 +1457,7 @@ impl<'a> ExpressionCompiler<'a> {
self.fnc.push(Instruction::Sub(
Value::Register(register.clone()),
Value::Number(i as f64),
Value::Number(Number(i as f64)),
elem_reg.clone(),
));

View File

@@ -1,7 +1,9 @@
use std::collections::{HashMap, HashSet};
use std::mem::take;
use crate::asm::{
Array, Definition, DefinitionContent, FnLine, Instruction, Object, Pointer, Value,
Array, Definition, DefinitionContent, FnLine, Instruction, InstructionFieldMut, Object, Pointer,
Value,
};
use crate::gather_modules::PathAndModule;
use crate::import_pattern::{ImportKind, ImportPattern};
@@ -108,6 +110,7 @@ pub fn link_module(
collapse_pointers_of_pointers(&mut path_and_module.module);
shake_tree(&mut path_and_module.module);
extract_constants(&mut path_and_module.module, &mut pointer_allocator);
result.module = Some(path_and_module.module);
result
@@ -469,3 +472,68 @@ fn shake_tree(module: &mut Module) {
module.definitions = new_definitions;
}
fn extract_constants(module: &mut Module, pointer_allocator: &mut NameAllocator) {
let mut constants = HashMap::<Value, Pointer>::new();
for defn in &mut module.definitions {
if let DefinitionContent::Function(f) = &mut defn.content {
for line in &mut f.body {
if let FnLine::Instruction(instr) = line {
instr.visit_fields_mut(&mut |field| match field {
InstructionFieldMut::Value(value) => {
value.visit_values_mut(&mut |sub_value| {
if let Some(p) = constants.get(&sub_value) {
*sub_value = Value::Pointer(p.clone());
return;
}
if let Some(name) = should_extract_value_as_constant(&sub_value) {
let p = Pointer {
name: pointer_allocator.allocate(&name),
};
let existing_p = constants.insert(take(sub_value), p.clone());
assert!(existing_p.is_none());
*sub_value = Value::Pointer(p);
}
});
}
InstructionFieldMut::Register(_) | InstructionFieldMut::LabelRef(_) => {}
});
}
}
}
}
for (value, pointer) in constants {
module.definitions.push(Definition {
pointer,
content: DefinitionContent::Value(value),
});
}
}
fn should_extract_value_as_constant(value: &Value) -> Option<String> {
if let Value::String(s) = value {
if s.len() >= 4 {
return Some(mangle_string(s));
}
}
None
}
fn mangle_string(s: &String) -> String {
let mut res = "s_".to_string();
for c in s.chars() {
if c.is_ascii_alphanumeric() {
res.push(c);
} else {
res.push('_');
}
}
res
}