mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 初始化 VM 字符串表并支持字符串 ADD 指令
This commit is contained in:
Generated
+1
@@ -808,6 +808,7 @@ dependencies = [
|
|||||||
"oxide_kernel",
|
"oxide_kernel",
|
||||||
"oxide_parser",
|
"oxide_parser",
|
||||||
"proptest",
|
"proptest",
|
||||||
|
"rustc-hash",
|
||||||
"ryu",
|
"ryu",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ oxide_compiler = { path = "../oxide_compiler" }
|
|||||||
oxide_kernel = { path = "../oxide_kernel" }
|
oxide_kernel = { path = "../oxide_kernel" }
|
||||||
bumpalo.workspace = true
|
bumpalo.workspace = true
|
||||||
hashbrown.workspace = true
|
hashbrown.workspace = true
|
||||||
|
rustc-hash.workspace = true
|
||||||
fast-float.workspace = true
|
fast-float.workspace = true
|
||||||
ryu.workspace = true
|
ryu.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::value::JsValue;
|
use crate::value::JsValue;
|
||||||
|
use crate::vm::Vm;
|
||||||
|
|
||||||
pub fn to_primitive(val: JsValue) -> JsValue {
|
pub fn to_primitive(val: JsValue) -> JsValue {
|
||||||
if val.is_object() {
|
if val.is_object() {
|
||||||
@@ -29,7 +30,7 @@ pub fn to_number(val: JsValue) -> f64 {
|
|||||||
f64::NAN
|
f64::NAN
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string(val: JsValue) -> String {
|
pub fn to_string(vm: &Vm, val: JsValue) -> String {
|
||||||
if val.is_int() {
|
if val.is_int() {
|
||||||
return val.as_int().to_string();
|
return val.as_int().to_string();
|
||||||
}
|
}
|
||||||
@@ -57,6 +58,9 @@ pub fn to_string(val: JsValue) -> String {
|
|||||||
if val.is_undefined() {
|
if val.is_undefined() {
|
||||||
return "undefined".to_string();
|
return "undefined".to_string();
|
||||||
}
|
}
|
||||||
|
if val.is_string() {
|
||||||
|
return vm.lookup_str(val).unwrap_or("").to_string();
|
||||||
|
}
|
||||||
if val.is_object() {
|
if val.is_object() {
|
||||||
panic_to_object();
|
panic_to_object();
|
||||||
}
|
}
|
||||||
@@ -77,6 +81,9 @@ pub fn to_boolean(val: JsValue) -> bool {
|
|||||||
let d = val.as_double();
|
let d = val.as_double();
|
||||||
return !(d == 0.0 || d == -0.0 || d.is_nan());
|
return !(d == 0.0 || d == -0.0 || d.is_nan());
|
||||||
}
|
}
|
||||||
|
if val.is_string() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if val.is_object() {
|
if val.is_object() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -90,6 +97,9 @@ pub fn abstract_eq(lhs: JsValue, rhs: JsValue) -> bool {
|
|||||||
if lhs.is_undefined() && rhs.is_null() {
|
if lhs.is_undefined() && rhs.is_null() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if lhs.is_string() && rhs.is_string() {
|
||||||
|
return lhs == rhs;
|
||||||
|
}
|
||||||
if lhs.is_int() && rhs.is_int() {
|
if lhs.is_int() && rhs.is_int() {
|
||||||
return lhs.as_int() == rhs.as_int();
|
return lhs.as_int() == rhs.as_int();
|
||||||
}
|
}
|
||||||
@@ -142,6 +152,9 @@ pub fn strict_eq(lhs: JsValue, rhs: JsValue) -> bool {
|
|||||||
if lhs.is_undefined() && rhs.is_undefined() {
|
if lhs.is_undefined() && rhs.is_undefined() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if lhs.is_string() && rhs.is_string() {
|
||||||
|
return lhs == rhs;
|
||||||
|
}
|
||||||
if lhs.is_object() && rhs.is_object() {
|
if lhs.is_object() && rhs.is_object() {
|
||||||
return lhs.as_ptr() == rhs.as_ptr();
|
return lhs.as_ptr() == rhs.as_ptr();
|
||||||
}
|
}
|
||||||
|
|||||||
+71
-25
@@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use oxide_compiler::compiler::Constant;
|
use oxide_compiler::compiler::Constant;
|
||||||
use oxide_compiler::module::CompiledModule;
|
use oxide_compiler::module::CompiledModule;
|
||||||
use oxide_compiler::opcode::{self, OpCode};
|
use oxide_compiler::opcode::{self, OpCode};
|
||||||
@@ -17,6 +19,8 @@ pub struct Vm {
|
|||||||
bytecode: Vec<opcode::Instr>,
|
bytecode: Vec<opcode::Instr>,
|
||||||
constants: Vec<JsValue>,
|
constants: Vec<JsValue>,
|
||||||
frames: Vec<CallFrame>,
|
frames: Vec<CallFrame>,
|
||||||
|
string_table: HashMap<String, u32>,
|
||||||
|
string_reverse: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl Vm {
|
||||||
@@ -27,11 +31,61 @@ impl Vm {
|
|||||||
bytecode: Vec::new(),
|
bytecode: Vec::new(),
|
||||||
constants: Vec::new(),
|
constants: Vec::new(),
|
||||||
frames: Vec::with_capacity(128),
|
frames: Vec::with_capacity(128),
|
||||||
|
string_table: HashMap::with_capacity(64),
|
||||||
|
string_reverse: Vec::with_capacity(64),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn intern(&mut self, s: &str) -> JsValue {
|
||||||
|
if let Some(&idx) = self.string_table.get(s) {
|
||||||
|
return JsValue::string(idx, hash16(s));
|
||||||
|
}
|
||||||
|
let idx = self.string_reverse.len() as u32;
|
||||||
|
self.string_reverse.push(s.to_string());
|
||||||
|
self.string_table.insert(s.to_string(), idx);
|
||||||
|
JsValue::string(idx, hash16(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_str(&self, val: JsValue) -> Option<&str> {
|
||||||
|
if !val.is_string() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let idx = val.as_string_index() as usize;
|
||||||
|
self.string_reverse.get(idx).map(|s| s.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, module: &CompiledModule) -> Result<JsValue, String> {
|
pub fn run(&mut self, module: &CompiledModule) -> Result<JsValue, String> {
|
||||||
self.constants = module.constants.iter().map(convert_constant).collect();
|
self.string_table.clear();
|
||||||
|
self.string_reverse.clear();
|
||||||
|
|
||||||
|
let string_entries: Vec<String> = module
|
||||||
|
.constants
|
||||||
|
.iter()
|
||||||
|
.filter_map(|c| {
|
||||||
|
if let Constant::String(s) = c {
|
||||||
|
Some(s.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
for s in &string_entries {
|
||||||
|
self.intern(s);
|
||||||
|
}
|
||||||
|
self.constants = module
|
||||||
|
.constants
|
||||||
|
.iter()
|
||||||
|
.map(|c| match c {
|
||||||
|
Constant::Number(v) => JsValue::float(*v),
|
||||||
|
Constant::String(s) => {
|
||||||
|
let idx = self.string_table.get(s.as_str()).copied().unwrap_or(0);
|
||||||
|
JsValue::string(idx, hash16(s))
|
||||||
|
}
|
||||||
|
Constant::Boolean(b) => JsValue::bool(*b),
|
||||||
|
Constant::Null => JsValue::null(),
|
||||||
|
Constant::Undefined => JsValue::undefined(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
self.bytecode = module.bytecode.clone();
|
self.bytecode = module.bytecode.clone();
|
||||||
self.pc = 0;
|
self.pc = 0;
|
||||||
self.regs = [JsValue::undefined(); 256];
|
self.regs = [JsValue::undefined(); 256];
|
||||||
@@ -68,23 +122,18 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OpCode::ADD => {
|
OpCode::ADD => {
|
||||||
let lhs = coercion::to_primitive(self.regs[a]);
|
let lhs = self.regs[a];
|
||||||
let rhs = coercion::to_primitive(self.regs[b]);
|
let rhs = self.regs[b];
|
||||||
if lhs.is_object() || rhs.is_object() {
|
if lhs.is_string() || rhs.is_string() {
|
||||||
return Err("ADD with objects not yet supported".into());
|
let ls = coercion::to_string(self, lhs);
|
||||||
|
let rs = coercion::to_string(self, rhs);
|
||||||
|
let concat = format!("{ls}{rs}");
|
||||||
|
self.regs[rd] = self.intern(&concat);
|
||||||
|
} else {
|
||||||
|
let ln = coercion::to_number(lhs);
|
||||||
|
let rn = coercion::to_number(rhs);
|
||||||
|
self.regs[rd] = JsValue::float(ln + rn);
|
||||||
}
|
}
|
||||||
if lhs.is_double()
|
|
||||||
&& lhs.as_double().is_nan()
|
|
||||||
&& lhs.as_double().to_bits() & 0x000F_FFFF_FFFF_FFFF == 0
|
|
||||||
{
|
|
||||||
// Actually this is too complex. Simpler: just check if it's a string via our JsValue type system.
|
|
||||||
}
|
|
||||||
// String concat: JsValue doesn't have is_string yet (Phase 6).
|
|
||||||
// For Phase 5, strings come as NaN-boxed objects — we don't have string JsValues.
|
|
||||||
// Addition defaults to numeric.
|
|
||||||
let ln = coercion::to_number(lhs);
|
|
||||||
let rn = coercion::to_number(rhs);
|
|
||||||
self.regs[rd] = JsValue::float(ln + rn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OpCode::SUB => {
|
OpCode::SUB => {
|
||||||
@@ -212,12 +261,9 @@ impl Default for Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_constant(c: &Constant) -> JsValue {
|
fn hash16(s: &str) -> u16 {
|
||||||
match c {
|
use std::hash::{Hash, Hasher};
|
||||||
Constant::Number(v) => JsValue::float(*v),
|
let mut h = rustc_hash::FxHasher::default();
|
||||||
Constant::String(_s) => JsValue::null(),
|
s.hash(&mut h);
|
||||||
Constant::Boolean(b) => JsValue::bool(*b),
|
(h.finish() >> 48) as u16
|
||||||
Constant::Null => JsValue::null(),
|
|
||||||
Constant::Undefined => JsValue::undefined(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user