mirror of
https://github.com/voltrevo/ValueScript.git
synced 2026-04-18 03:00:27 -04:00
Extract string constants
This commit is contained in:
@@ -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)>,
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))),
|
||||
];
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user