refactor: 字符串值改用 JsString 指针表示,键与字面量改用 PermInterner

This commit is contained in:
2026-06-24 20:15:15 +08:00
parent 9a62cf1610
commit ddffa8504e
73 changed files with 768 additions and 860 deletions
+9 -11
View File
@@ -10,7 +10,7 @@ use clap::{Parser, Subcommand};
use oxide_compiler::compiler::{compiled_module_hash, Compiler};
use oxide_kernel::kernel::{KernelConfig, KernelCore};
use oxide_kernel::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
use oxide_kernel::string_forge::StringForge;
use oxide_kernel::string_forge::PermInterner;
use oxide_parser::Allocator;
use oxide_types::object::JsObject;
use oxide_vm::vm_pool::VmPool;
@@ -140,7 +140,7 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
let mut guard = pool.spawn();
match guard.vm_mut().run(&module) {
Ok(result) => {
format_result(kernel.string_forge().as_ref(), kernel.shape_forge().as_ref(), result);
format_result(kernel.perm_interner().as_ref(), kernel.shape_forge().as_ref(), result);
ExitCode::SUCCESS
}
Err(err) => {
@@ -150,17 +150,15 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
}
}
fn format_result(string_forge: &StringForge, shape_forge: &ShapeForge, val: JsValue) {
fn format_result(string_forge: &PermInterner, shape_forge: &ShapeForge, val: JsValue) {
println!("{}", format_js_value(string_forge, shape_forge, val));
}
fn format_js_value(string_forge: &StringForge, shape_forge: &ShapeForge, val: JsValue) -> String {
fn format_js_value(string_forge: &PermInterner, shape_forge: &ShapeForge, val: JsValue) -> String {
if val.is_string() {
if let Some(s) = string_forge.lookup(val.as_string_index()) {
format!("\"{s}\"")
} else {
format!("{val}")
}
// SAFETY: val is a string value.
let s = unsafe { (*val.as_string_ptr()).data.clone() };
format!("\"{s}\"")
} else if val.is_object() {
let obj = unsafe { &*val.as_js_object_ptr() };
if obj.is_function() {
@@ -177,7 +175,7 @@ fn format_js_value(string_forge: &StringForge, shape_forge: &ShapeForge, val: Js
}
}
fn format_object(string_forge: &StringForge, shape_forge: &ShapeForge, obj: &JsObject) -> String {
fn format_object(string_forge: &PermInterner, shape_forge: &ShapeForge, obj: &JsObject) -> String {
let mut entries = Vec::new();
let shape_id = obj.shape_id();
let mut shape_ids = Vec::new();
@@ -214,7 +212,7 @@ fn format_object(string_forge: &StringForge, shape_forge: &ShapeForge, obj: &JsO
format!("{{{}}}", entries.join(", "))
}
fn format_array(string_forge: &StringForge, shape_forge: &ShapeForge, obj: &JsObject) -> String {
fn format_array(string_forge: &PermInterner, shape_forge: &ShapeForge, obj: &JsObject) -> String {
let len = obj.prop_vec_len();
let mut items = Vec::new();
for i in 0..len {
+21 -21
View File
@@ -4,7 +4,7 @@ use oxide_types::value::JsValue;
use crate::kernel::BuiltinDirtySet;
use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
use crate::string_forge::StringForge;
use crate::string_forge::PermInterner;
#[macro_export]
macro_rules! bind_method {
@@ -219,12 +219,12 @@ pub struct BuiltinWorld {
pub stub_objects: Vec<P<JsObject>>,
}
fn intern_label(string_forge: &StringForge, label: &str) -> u32 {
fn intern_label(string_forge: &PermInterner, label: &str) -> u32 {
string_forge.intern(label).0
}
fn make_pair(
string_forge: &StringForge, shape_forge: &ShapeForge, name: &str, si_prototype: u32, si_constructor: u32,
string_forge: &PermInterner, shape_forge: &ShapeForge, name: &str, si_prototype: u32, si_constructor: u32,
si_name: u32,
) -> (P<JsObject>, P<JsObject>) {
intern_label(string_forge, name);
@@ -258,7 +258,7 @@ struct BuiltinLabels {
name: u32,
}
fn builtin_labels(string_forge: &StringForge) -> BuiltinLabels {
fn builtin_labels(string_forge: &PermInterner) -> BuiltinLabels {
let labels = BuiltinLabels {
prototype: intern_label(string_forge, "prototype"),
constructor: intern_label(string_forge, "constructor"),
@@ -271,7 +271,7 @@ fn builtin_labels(string_forge: &StringForge) -> BuiltinLabels {
}
fn make_named_pair(
string_forge: &StringForge, shape_forge: &ShapeForge, labels: BuiltinLabels, name: &str,
string_forge: &PermInterner, shape_forge: &ShapeForge, labels: BuiltinLabels, name: &str,
) -> (P<JsObject>, P<JsObject>) {
make_pair(string_forge, shape_forge, name, labels.prototype, labels.constructor, labels.name)
}
@@ -324,7 +324,7 @@ struct TypedArrayFamily {
}
fn make_typed_array_family(
string_forge: &StringForge, shape_forge: &ShapeForge, labels: BuiltinLabels, object_proto: &P<JsObject>,
string_forge: &PermInterner, shape_forge: &ShapeForge, labels: BuiltinLabels, object_proto: &P<JsObject>,
) -> TypedArrayFamily {
let obj_proto_val = JsValue::from_js_object(object_proto.as_ptr() as *mut JsObject);
let typed_array_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, obj_proto_val));
@@ -472,7 +472,7 @@ impl BuiltinWorld {
JsValue::from_js_object(self.function_proto.as_ptr() as *mut JsObject)
}
pub fn new(string_forge: &StringForge, shape_forge: &ShapeForge) -> Self {
pub fn new(string_forge: &PermInterner, shape_forge: &ShapeForge) -> Self {
let labels = builtin_labels(string_forge);
let (object_proto, object_constructor) = make_named_pair(string_forge, shape_forge, labels, "Object");
@@ -578,7 +578,7 @@ impl BuiltinWorld {
}
pub fn rebuild_with_dirty(
current: &BuiltinWorld, string_forge: &StringForge, shape_forge: &ShapeForge, dirty: &BuiltinDirtySet,
current: &BuiltinWorld, string_forge: &PermInterner, shape_forge: &ShapeForge, dirty: &BuiltinDirtySet,
) -> BuiltinWorld {
let labels = builtin_labels(string_forge);
@@ -796,7 +796,7 @@ impl BuiltinWorld {
world
}
pub fn bind_object_methods(&self, methods: &ObjectMethods, string_forge: &StringForge, shape_forge: &ShapeForge) {
pub fn bind_object_methods(&self, methods: &ObjectMethods, string_forge: &PermInterner, shape_forge: &ShapeForge) {
let ctor_ptr = P::as_ptr(&self.object_constructor) as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
bind_methods!(
@@ -843,7 +843,7 @@ impl BuiltinWorld {
}
}
pub fn bind_array_methods(&self, methods: &ArrayMethods, string_forge: &StringForge, shape_forge: &ShapeForge) {
pub fn bind_array_methods(&self, methods: &ArrayMethods, string_forge: &PermInterner, shape_forge: &ShapeForge) {
let ctor_ptr = P::as_ptr(&self.array_constructor) as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
bind_methods!(self, ctor, string_forge, shape_forge, ("isArray", methods.is_array, 1),);
@@ -895,7 +895,7 @@ impl BuiltinWorld {
debug_assert!(shape_forge.lookup_position(proto.shape_id(), si).is_some());
}
pub fn bind_error_methods(&self, methods: &ErrorMethods, string_forge: &StringForge, shape_forge: &ShapeForge) {
pub fn bind_error_methods(&self, methods: &ErrorMethods, string_forge: &PermInterner, shape_forge: &ShapeForge) {
let ctor_ptr = P::as_ptr(&self.error_constructor) as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
bind_methods!(
@@ -924,7 +924,7 @@ impl BuiltinWorld {
);
}
pub fn bind_string_methods(&self, methods: &StringMethods, string_forge: &StringForge, shape_forge: &ShapeForge) {
pub fn bind_string_methods(&self, methods: &StringMethods, string_forge: &PermInterner, shape_forge: &ShapeForge) {
let ctor_ptr = P::as_ptr(&self.string_constructor) as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
bind_methods!(self, ctor, string_forge, shape_forge, ("fromCharCode", methods.from_char_code, 1),);
@@ -966,7 +966,7 @@ impl BuiltinWorld {
}
pub fn bind_function_methods(
&self, methods: &FunctionMethods, string_forge: &StringForge, shape_forge: &ShapeForge,
&self, methods: &FunctionMethods, string_forge: &PermInterner, shape_forge: &ShapeForge,
) {
let proto_ptr = P::as_ptr(&self.function_proto) as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
@@ -984,7 +984,7 @@ impl BuiltinWorld {
}
pub fn bind_method(
&self, proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &StringForge, method_name: &str,
&self, proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &PermInterner, method_name: &str,
native_fn_ptr: NativeFnPtr, arg_count: u8,
) -> Result<(), String> {
Self::bind_method_static(
@@ -999,7 +999,7 @@ impl BuiltinWorld {
}
pub fn bind_method_static(
proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &StringForge, method_name: &str,
proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &PermInterner, method_name: &str,
native_fn_ptr: NativeFnPtr, arg_count: u8, wrapper_proto: JsValue,
) -> Result<(), String> {
let si = string_forge.intern(method_name).0;
@@ -1022,7 +1022,7 @@ impl BuiltinWorld {
wrapper.set_shape_id(name_shape);
wrapper
.ensure_hash_props()
.push(JsValue::string(string_forge.intern(method_name).0, 0));
.push(JsValue::perm_string(string_forge.string_ptr(si)));
let wrapper_val = JsValue::from_js_object(Box::into_raw(wrapper));
let new_shape = shape_forge.make_shape(proto.shape_id(), si);
proto.set_shape_id(new_shape);
@@ -1036,10 +1036,10 @@ impl BuiltinWorld {
mod tests {
use super::*;
use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
use crate::string_forge::StringForge;
use crate::string_forge::PermInterner;
fn make_world() -> BuiltinWorld {
let sf = StringForge::new();
let sf = PermInterner::new();
let sh = ShapeForge::new();
BuiltinWorld::new(&sf, &sh)
}
@@ -1115,7 +1115,7 @@ mod tests {
#[test]
fn builtin_rebuild_with_dirty_reuses_clean_fields() {
let sf = StringForge::new();
let sf = PermInterner::new();
let sh = ShapeForge::new();
let w = BuiltinWorld::new(&sf, &sh);
let rebuilt = BuiltinWorld::rebuild_with_dirty(&w, &sf, &sh, &crate::kernel::BuiltinDirtySet::default());
@@ -1127,7 +1127,7 @@ mod tests {
#[test]
fn builtin_rebuild_with_dirty_replaces_only_dirty_group() {
let sf = StringForge::new();
let sf = PermInterner::new();
let sh = ShapeForge::new();
let w = BuiltinWorld::new(&sf, &sh);
let dirty = crate::kernel::BuiltinDirtySet {
@@ -1144,7 +1144,7 @@ mod tests {
#[test]
fn builtin_rebuild_with_dirty_repairs_ctor_proto_links() {
let sf = StringForge::new();
let sf = PermInterner::new();
let sh = ShapeForge::new();
let w = BuiltinWorld::new(&sf, &sh);
let dirty = crate::kernel::BuiltinDirtySet {
+19 -18
View File
@@ -12,7 +12,7 @@ use crate::code_forge::CodeForge;
use crate::logging::{init_logging, LogLevel, SUBSYSTEM_COUNT};
use crate::prop_forge::PropForge;
use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
use crate::string_forge::StringForge;
use crate::string_forge::PermInterner;
#[derive(Clone)]
pub struct KernelConfig {
@@ -105,7 +105,7 @@ impl Default for KernelConfig {
/// Never rebuilt after construction — forge tables are append-only.
pub struct KernelCore {
pub config: KernelConfig,
pub string_forge: Arc<StringForge>,
pub perm_interner: Arc<PermInterner>,
pub shape_forge: Arc<ShapeForge>,
pub code_forge: Arc<CodeForge>,
pub prop_forge: Arc<PropForge>,
@@ -114,7 +114,7 @@ pub struct KernelCore {
impl KernelCore {
pub fn new(config: KernelConfig) -> Arc<Self> {
init_logging(&config.log_levels);
let string_forge = Arc::new(StringForge::new());
let perm_interner = Arc::new(PermInterner::new());
let shape_forge = Arc::new(ShapeForge::new());
let code_forge = Arc::new(CodeForge::new(
NonZeroUsize::new(config.max_cached_modules).expect("max_cached_modules must be greater than zero"),
@@ -122,15 +122,15 @@ impl KernelCore {
let prop_forge = Arc::new(PropForge::new());
Arc::new(Self {
config,
string_forge,
perm_interner,
shape_forge,
code_forge,
prop_forge,
})
}
pub fn string_forge(&self) -> &Arc<StringForge> {
&self.string_forge
pub fn perm_interner(&self) -> &Arc<PermInterner> {
&self.perm_interner
}
pub fn shape_forge(&self) -> &Arc<ShapeForge> {
@@ -166,7 +166,8 @@ impl KernelCore {
}
pub fn sweep_runner_forges(&self) {
self.string_forge.maybe_sweep(self.config.max_dead_strings.or(Some(1)));
// The key interner is append-only (no per-run sweep); only the transient
// shape/prop tables need bounding at the test262 per-test boundary.
// test262 creates a fresh VM/session per test. At this boundary no JS object
// from prior tests may retain transient shapes/templates.
if self.shape_forge.len() > 50_000 {
@@ -394,9 +395,9 @@ impl KernelSession {
fn new_global_object(core: &KernelCore) -> P<JsObject> {
let mut global_obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null());
let si_nan = core.string_forge.intern("NaN").0;
let si_undef = core.string_forge.intern("undefined").0;
let si_infinity = core.string_forge.intern("Infinity").0;
let si_nan = core.perm_interner.intern("NaN").0;
let si_undef = core.perm_interner.intern("undefined").0;
let si_infinity = core.perm_interner.intern("Infinity").0;
let nan_shape = core.shape_forge.make_shape(EMPTY_SHAPE_ID, si_nan);
global_obj.set_shape_id(nan_shape);
@@ -416,7 +417,7 @@ impl KernelSession {
/// Build a fresh session from a KernelCore. All string/shape intern calls hit
/// cache on second and subsequent calls — net cost is < 0.5 ms.
pub fn new(core: &KernelCore) -> Self {
let builtin_world = Arc::new(BuiltinWorld::new(&core.string_forge, &core.shape_forge));
let builtin_world = Arc::new(BuiltinWorld::new(&core.perm_interner, &core.shape_forge));
let global_object = Self::new_global_object(core);
let builtin_snapshot = BuiltinSnapshot::new(&builtin_world, &global_object);
@@ -536,7 +537,7 @@ impl KernelSession {
if dirty.any_builtin_dirty() {
self.builtin_world = Arc::new(BuiltinWorld::rebuild_with_dirty(
&self.builtin_world,
core.string_forge.as_ref(),
core.perm_interner.as_ref(),
core.shape_forge.as_ref(),
&dirty,
));
@@ -553,8 +554,8 @@ mod tests {
#[test]
fn test_kernel_new() {
let core = KernelCore::new(KernelConfig::minimal());
let (i1, _) = core.string_forge().intern("test");
let (i2, _) = core.string_forge().intern("test");
let (i1, _) = core.perm_interner().intern("test");
let (i2, _) = core.perm_interner().intern("test");
assert_eq!(i1, i2);
}
@@ -575,8 +576,8 @@ mod tests {
#[test]
fn test_kernel_string_forge() {
let core = KernelCore::new(KernelConfig::minimal());
let (i1, _) = core.string_forge().intern("hello");
let (i2, _) = core.string_forge().intern("hello");
let (i1, _) = core.perm_interner().intern("hello");
let (i2, _) = core.perm_interner().intern("hello");
assert_eq!(i1, i2);
}
@@ -596,9 +597,9 @@ mod tests {
#[test]
fn test_session_rebuild_shares_forges() {
let core = KernelCore::new(KernelConfig::minimal());
let (i1, _) = core.string_forge().intern("hello");
let (i1, _) = core.perm_interner().intern("hello");
let _s2 = KernelSession::new(&core);
let (i2, _) = core.string_forge().intern("hello");
let (i2, _) = core.perm_interner().intern("hello");
assert_eq!(i1, i2);
}
+139 -148
View File
@@ -1,142 +1,137 @@
use std::hash::{Hash, Hasher};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::RwLock;
use hashbrown::hash_map::DefaultHashBuilder as FxBuildHasher;
use hashbrown::HashMap;
use dashmap::DashMap;
use oxide_types::object::JsString;
use rustc_hash::FxHasher;
use crate::{kernel_debug, kernel_trace};
pub fn hash16(s: &str) -> u16 {
let mut h = rustc_hash::FxHasher::default();
/// Full 64-bit content hash. Replaces the old 16-bit `hash16` so the interner's
/// hash→candidate map has negligible collision risk.
fn hash64(s: &str) -> u64 {
let mut h = FxHasher::default();
s.hash(&mut h);
(h.finish() >> 48) as u16
h.finish()
}
pub struct StringEntry {
pub(crate) data: String,
pub(crate) ref_count: AtomicU32,
pub hash: u16,
/// One interned key. `data` is a leaked `&'static str` — permanent keys are never
/// freed (append-only by design), so the leak is the storage model, not a bug.
#[derive(Clone, Copy)]
struct PermEntry {
data: &'static str,
hash: u64,
}
struct StringForgeInner {
map: HashMap<String, u32, FxBuildHasher>,
entries: Vec<StringEntry>,
}
/// Global string interning pool shared by all VMs.
/// Append-only, never-move, lock-free-read key interner shared by all VMs.
///
/// Concurrency model:
/// `StringForge` keeps a single `RwLock` around the interner state.
/// - `lookup()` takes a shared read lock and scales across readers.
/// - `intern()` uses a read-check then write-check pattern to keep hot-path hits cheap.
/// - New string insertion is serialized, which matches the workload here: most strings
/// are interned early and later accesses are dominated by reads.
pub struct StringForge {
inner: RwLock<StringForgeInner>,
/// Replaces the old ref-counted `StringForge` (and its buggy `maybe_sweep`
/// renumber path). Keys (property names, method names) are interned once and
/// addressed by a stable `u32` id that the shape/IC system keys on. Runtime
/// string *values* are no longer interned here — they are heap `JsString`
/// pointers (see `oxide_vm::Vm::new_string`).
///
/// Concurrency:
/// - `hash_map` (`DashMap`) gives sharded, lock-free reads of the hash→candidates
/// mapping on the hot intern path.
/// - `entries` sits behind a short `RwLock`; reads copy out a `&'static str`
/// (Copy) so the borrow outlives the guard.
pub struct PermInterner {
hash_map: DashMap<u64, Vec<u32>>,
entries: RwLock<Vec<PermEntry>>,
/// Lazily materialized permanent `JsString` values, indexed by entry id.
/// Used when a permanent key must also exist as a JS string *value*
/// (e.g. method names exposed as property values at builtin init).
permanent_strings: RwLock<Vec<Option<Box<JsString>>>>,
}
impl StringForge {
impl PermInterner {
pub fn new() -> Self {
Self {
inner: RwLock::new(StringForgeInner {
map: HashMap::with_hasher(FxBuildHasher::default()),
entries: Vec::new(),
}),
hash_map: DashMap::new(),
entries: RwLock::new(Vec::new()),
permanent_strings: RwLock::new(Vec::new()),
}
}
pub fn intern(&self, s: &str) -> (u32, u16) {
{
let inner = self.inner.read().unwrap();
if let Some(&idx) = inner.map.get(s) {
let entry = &inner.entries[idx as usize];
entry.ref_count.fetch_add(1, Ordering::Release);
kernel_trace!("StringForge intern hit idx={}", idx);
return (idx, entry.hash);
/// Intern a key. Returns its stable id and full 64-bit hash. Each unique
/// string is stored exactly once (single allocation, no double `to_string`).
pub fn intern(&self, s: &str) -> (u32, u64) {
let hash = hash64(s);
// Fast path: lock-free candidate read, short entries read-lock.
if let Some(candidates) = self.hash_map.get(&hash) {
let entries = self.entries.read().unwrap();
for &id in candidates.iter() {
if entries[id as usize].data == s {
kernel_trace!("PermInterner intern hit id={}", id);
return (id, hash);
}
}
}
let mut inner = self.inner.write().unwrap();
if let Some(&idx) = inner.map.get(s) {
let entry = &inner.entries[idx as usize];
entry.ref_count.fetch_add(1, Ordering::Release);
kernel_trace!("StringForge intern hit idx={}", idx);
return (idx, entry.hash);
}
let h = hash16(s);
let idx = inner.entries.len() as u32;
inner.map.insert(s.to_string(), idx);
inner.entries.push(StringEntry {
data: s.to_string(),
ref_count: AtomicU32::new(1),
hash: h,
});
kernel_debug!("StringForge intern new idx={} len={}", idx, s.len());
(idx, h)
}
pub fn lookup(&self, idx: u32) -> Option<String> {
let inner = self.inner.read().unwrap();
inner.entries.get(idx as usize).map(|e| e.data.clone())
}
pub fn get_hash(&self, idx: u32) -> Option<u16> {
let inner = self.inner.read().unwrap();
inner.entries.get(idx as usize).map(|e| e.hash)
}
pub fn decref(&self, idx: u32) {
let inner = self.inner.read().unwrap();
if let Some(entry) = inner.entries.get(idx as usize) {
entry.ref_count.fetch_sub(1, Ordering::Release);
}
}
pub fn maybe_sweep(&self, max_dead: Option<usize>) {
let threshold = match max_dead {
Some(t) => t,
None => return,
};
let mut inner = self.inner.write().unwrap();
let dead_count = inner
.entries
.iter()
.filter(|e| e.ref_count.load(Ordering::Acquire) == 0)
.count();
if dead_count < threshold {
return;
}
let mut new_map: HashMap<String, u32, FxBuildHasher> = HashMap::with_hasher(FxBuildHasher::default());
let mut new_entries = Vec::with_capacity(inner.entries.len() - dead_count);
for entry in inner.entries.drain(..) {
let rc = entry.ref_count.load(Ordering::Acquire);
if rc > 0 {
let new_idx = new_entries.len() as u32;
new_map.insert(entry.data.clone(), new_idx);
new_entries.push(entry);
// Slow path: append under write lock, re-checking for a racing insert.
let mut entries = self.entries.write().unwrap();
if let Some(candidates) = self.hash_map.get(&hash) {
for &id in candidates.iter() {
if entries[id as usize].data == s {
return (id, hash);
}
}
}
inner.map = new_map;
inner.entries = new_entries;
let id = entries.len() as u32;
let leaked: &'static str = Box::leak(s.to_string().into_boxed_str());
entries.push(PermEntry { data: leaked, hash });
drop(entries);
self.hash_map.entry(hash).or_default().push(id);
kernel_debug!("PermInterner intern new id={} len={}", id, s.len());
(id, hash)
}
pub fn len(&self) -> usize {
self.inner.read().unwrap().entries.len()
/// Resolve a key id to its text with zero clone. The returned `&'static str`
/// is valid for the program lifetime (keys are never freed).
pub fn lookup(&self, id: u32) -> Option<&'static str> {
let entries = self.entries.read().unwrap();
entries.get(id as usize).map(|e| e.data)
}
/// Full 64-bit hash for a key id.
pub fn get_hash(&self, id: u32) -> Option<u64> {
let entries = self.entries.read().unwrap();
entries.get(id as usize).map(|e| e.hash)
}
/// Total number of unique keys interned.
pub fn entry_count(&self) -> u32 {
self.entries.read().unwrap().len() as u32
}
pub fn is_empty(&self) -> bool {
self.len() == 0
self.entry_count() == 0
}
/// Materialize (once) and return a stable pointer to a permanent `JsString`
/// for the given key id. The `JsString` lives for the program lifetime.
pub fn string_ptr(&self, id: u32) -> *const JsString {
{
let perm = self.permanent_strings.read().unwrap();
if let Some(Some(boxed)) = perm.get(id as usize) {
return boxed.as_ref() as *const JsString;
}
}
let text = self.lookup(id).unwrap_or("");
let mut perm = self.permanent_strings.write().unwrap();
if perm.len() <= id as usize {
perm.resize_with(id as usize + 1, || None);
}
if perm[id as usize].is_none() {
perm[id as usize] = Some(Box::new(JsString::new(text.to_string())));
}
perm[id as usize].as_ref().unwrap().as_ref() as *const JsString
}
}
impl Default for StringForge {
impl Default for PermInterner {
fn default() -> Self {
Self::new()
}
@@ -147,67 +142,63 @@ mod tests {
use super::*;
#[test]
fn test_hash16_same() {
assert_eq!(hash16("hello"), hash16("hello"));
}
#[test]
fn test_hash16_different() {
assert_ne!(hash16("hello"), hash16("world"));
}
#[test]
fn test_intern_dedup() {
let string_forge = StringForge::new();
let (i1, h1) = string_forge.intern("abc");
let (i2, h2) = string_forge.intern("abc");
fn intern_dedup() {
let interner = PermInterner::new();
let (i1, h1) = interner.intern("abc");
let (i2, h2) = interner.intern("abc");
assert_eq!(i1, i2);
assert_eq!(h1, h2);
}
#[test]
fn test_intern_different() {
let string_forge = StringForge::new();
let (i1, _) = string_forge.intern("x");
let (i2, _) = string_forge.intern("y");
fn intern_different() {
let interner = PermInterner::new();
let (i1, _) = interner.intern("x");
let (i2, _) = interner.intern("y");
assert_ne!(i1, i2);
}
#[test]
fn test_lookup_found() {
let string_forge = StringForge::new();
let (idx, _) = string_forge.intern("hello");
assert_eq!(string_forge.lookup(idx), Some("hello".to_string()));
fn lookup_zero_clone() {
let interner = PermInterner::new();
let (id, _) = interner.intern("hello");
assert_eq!(interner.lookup(id), Some("hello"));
}
#[test]
fn test_lookup_not_found() {
let string_forge = StringForge::new();
assert_eq!(string_forge.lookup(99999), None);
fn lookup_not_found() {
let interner = PermInterner::new();
assert_eq!(interner.lookup(99999), None);
}
#[test]
fn test_ref_count_and_decref() {
let string_forge = StringForge::new();
let (idx, _) = string_forge.intern("s");
assert_eq!(string_forge.lookup(idx), Some("s".to_string()));
string_forge.decref(idx);
assert_eq!(string_forge.lookup(idx), Some("s".to_string()));
fn entry_count_monotonic() {
let interner = PermInterner::new();
assert_eq!(interner.entry_count(), 0);
interner.intern("a");
interner.intern("b");
interner.intern("a");
assert_eq!(interner.entry_count(), 2);
}
#[test]
fn test_maybe_sweep_noop_when_none() {
let string_forge = StringForge::new();
string_forge.intern("live");
string_forge.maybe_sweep(None);
assert!(string_forge.lookup(0).is_some());
fn many_unique_no_collision() {
let interner = PermInterner::new();
for i in 0..10_000 {
let s = format!("key{i}");
let (id, _) = interner.intern(&s);
assert_eq!(interner.lookup(id), Some(&*Box::leak(s.into_boxed_str())));
}
assert_eq!(interner.entry_count(), 10_000);
}
#[test]
fn test_maybe_sweep_skip_below_threshold() {
let string_forge = StringForge::new();
string_forge.intern("live");
string_forge.maybe_sweep(Some(100));
assert_eq!(string_forge.lookup(0), Some("live".to_string()));
fn string_ptr_roundtrip() {
let interner = PermInterner::new();
let (id, _) = interner.intern("perm");
let ptr = interner.string_ptr(id);
assert_eq!(unsafe { (*ptr).as_str() }, "perm");
// Second call returns the same stable pointer (materialized once).
assert_eq!(interner.string_ptr(id), ptr);
}
}
+37
View File
@@ -1,5 +1,42 @@
use std::hash::{Hash, Hasher};
use rustc_hash::FxHasher;
use crate::value::JsValue;
/// Heap-allocated JS string value.
///
/// String *values* are NaN-boxed as 48-bit pointers to a `JsString` (see
/// `JsValue::string`), replacing the old interner `(index, hash16)` pair. `hash`
/// is a full 64-bit FxHash computed once at construction and used as a fast
/// inequality reject before content comparison.
#[derive(Debug)]
pub struct JsString {
pub data: String,
pub hash: u64,
}
impl JsString {
pub fn new(data: String) -> Self {
let mut h = FxHasher::default();
data.hash(&mut h);
let hash = h.finish();
Self { data, hash }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn as_str(&self) -> &str {
self.data.as_str()
}
}
/// Type-safe opaque wrapper around a native function pointer.
///
/// Stored as `*const ()` rather than a concrete `fn` type so that `oxide_types` does not
+44 -22
View File
@@ -1,6 +1,6 @@
use std::fmt;
use crate::object::JsObject;
use crate::object::{JsObject, JsString};
/// Quiet NaN prefix — bits 63-51 = sign(1) + exponent(0x7FF) + quiet_bit(1)
const QNAN_PREFIX: u64 = 0xFFF8_0000_0000_0000;
@@ -28,11 +28,6 @@ const PTR_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
/// 32-bit integer payload mask
const INT_MASK: u64 = 0x0000_0000_FFFF_FFFF;
/// String payload: [hash_prefix:16bit][table_index:32bit]
const STRING_HASH_SHIFT: u64 = 32;
const STRING_HASH_MASK: u64 = 0xFFFF_0000_0000;
const STRING_INDEX_MASK: u64 = 0x0000_FFFF_FFFF;
fn make_tag(tag: u64) -> u64 {
QNAN_PREFIX | (tag << TAG_SHIFT)
}
@@ -71,12 +66,7 @@ impl PartialEq for JsValue {
return self.as_ptr() == other.as_ptr();
}
if self.is_string() && other.is_string() {
let ha = self.as_string_hash();
let hb = other.as_string_hash();
if ha != hb {
return false;
}
return self.as_string_index() == other.as_string_index();
return self.as_string_ptr() == other.as_string_ptr();
}
if self.is_symbol() && other.is_symbol() {
return self.as_symbol_index() == other.as_symbol_index();
@@ -122,10 +112,17 @@ impl JsValue {
Self(make_tag(TAG_OBJECT) | addr)
}
pub fn string(index: u32, hash: u16) -> Self {
let payload = ((hash as u64) << STRING_HASH_SHIFT) | (index as u64);
debug_assert!(payload & !(STRING_HASH_MASK | STRING_INDEX_MASK) == 0, "string payload overflow");
Self(make_tag(TAG_STRING) | payload)
pub fn string(ptr: *const JsString) -> Self {
let addr = ptr as u64;
debug_assert!(addr <= PTR_MASK, "string pointer must fit in 48 bits");
Self(make_tag(TAG_STRING) | addr)
}
/// Semantic alias for permanent (kernel-owned, never-collected) strings.
/// Encoding is identical to `string`; the perm-vs-session distinction is by
/// ownership (membership in the GC root set), not by the NaN-box bits.
pub fn perm_string(ptr: *const JsString) -> Self {
Self::string(ptr)
}
pub fn is_double(&self) -> bool {
@@ -214,14 +211,14 @@ impl JsValue {
Self(make_tag(TAG_OBJECT) | addr)
}
pub fn as_string_index(&self) -> u32 {
pub fn as_string_ptr(&self) -> *const JsString {
debug_assert!(self.is_string(), "JsValue is not a string");
(self.0 & STRING_INDEX_MASK) as u32
(self.0 & PTR_MASK) as *const JsString
}
pub fn as_string_hash(&self) -> u16 {
pub fn as_string_ptr_mut(&self) -> *mut JsString {
debug_assert!(self.is_string(), "JsValue is not a string");
((self.0 & STRING_HASH_MASK) >> STRING_HASH_SHIFT) as u16
(self.0 & PTR_MASK) as *mut JsString
}
pub fn symbol(index: u32) -> Self {
@@ -234,7 +231,7 @@ impl JsValue {
pub fn as_symbol_index(&self) -> u32 {
debug_assert!(self.is_symbol());
(self.0 & STRING_INDEX_MASK) as u32
(self.0 & INT_MASK) as u32
}
}
@@ -301,7 +298,7 @@ impl fmt::Debug for JsValue {
} else if self.is_object() {
write!(f, "JsValue(Object({:p}))", self.as_ptr())
} else if self.is_string() {
write!(f, "JsValue(String(idx={}, hash={:#06x}))", self.as_string_index(), self.as_string_hash())
write!(f, "JsValue(String({:p}))", self.as_string_ptr())
} else if self.is_symbol() {
write!(f, "JsValue(Symbol(idx={}))", self.as_symbol_index())
} else {
@@ -411,4 +408,29 @@ mod tests {
})
.unwrap();
}
#[test]
fn string_ptr_roundtrip() {
use crate::object::JsString;
let s = Box::new(JsString::new("test".to_string()));
let ptr: *const JsString = &*s;
let val = JsValue::string(ptr);
assert!(val.is_string());
assert_eq!(val.as_string_ptr(), ptr);
assert_eq!(unsafe { (*val.as_string_ptr()).as_str() }, "test");
}
#[test]
fn string_pointer_equality() {
use crate::object::JsString;
let a = Box::new(JsString::new("x".to_string()));
let b = Box::new(JsString::new("x".to_string()));
let va = JsValue::string(&*a);
let vb = JsValue::string(&*b);
// Distinct allocations with identical content are NOT == (pointer identity).
// Semantic content equality is handled in coercion, not PartialEq.
assert_ne!(va, vb);
assert_eq!(va, JsValue::string(&*a));
assert_eq!(unsafe { (*va.as_string_ptr()).as_str() }, unsafe { (*vb.as_string_ptr()).as_str() });
}
}
+1 -1
View File
@@ -42,7 +42,7 @@ pub fn bind_array(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
session.builtin_world().bind_array_methods(
&_array_methods,
core.string_forge().as_ref(),
core.perm_interner().as_ref(),
core.shape_forge().as_ref(),
);
+4 -4
View File
@@ -10,7 +10,7 @@ fn bind_error_subtype_constructor(
core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject, name: &str, proto_ptr: *mut JsObject,
ctor_fn: *const (),
) {
let sf = core.string_forge().as_ref();
let sf = core.perm_interner().as_ref();
let sh = core.shape_forge().as_ref();
let function_proto_ptr = session.builtin_world().function_proto.as_ptr() as *mut JsObject;
@@ -28,7 +28,7 @@ fn bind_error_subtype_constructor(
let ctor_shape2 = sh.make_shape(ctor_shape1, si_name);
ctor.set_shape_id(ctor_shape2);
ctor.ensure_hash_props().push(JsValue::from_js_object(proto_ptr));
ctor.ensure_hash_props().push(JsValue::string(name_si, 0));
ctor.ensure_hash_props().push(JsValue::perm_string(sf.string_ptr(name_si)));
let ctor_ptr = Box::into_raw(ctor);
@@ -57,11 +57,11 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
};
session.builtin_world().bind_error_methods(
&error_methods,
core.string_forge().as_ref(),
core.perm_interner().as_ref(),
core.shape_forge().as_ref(),
);
let si_err = core.string_forge().intern("Error").0;
let si_err = core.perm_interner().intern("Error").0;
let err_shape = core.shape_forge().make_shape(global.shape_id(), si_err);
let err_val = JsValue::from_js_object(session.builtin_world().error_constructor.as_ptr() as *mut JsObject);
global.set_shape_id(err_shape);
@@ -16,7 +16,7 @@ pub fn bind_function(core: &Arc<KernelCore>, session: &KernelSession, global: &m
};
session.builtin_world().bind_function_methods(
&function_methods,
core.string_forge().as_ref(),
core.perm_interner().as_ref(),
core.shape_forge().as_ref(),
);
+3 -3
View File
@@ -10,7 +10,7 @@ pub fn bind_math(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
let math = unsafe { &mut *math_ptr };
let bw = session.builtin_world();
let sf = core.string_forge().as_ref();
let sf = core.perm_interner().as_ref();
let sh = core.shape_forge().as_ref();
bind_methods!(
@@ -65,13 +65,13 @@ pub fn bind_math(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
("SQRT1_2", std::f64::consts::FRAC_1_SQRT_2),
("SQRT2", std::f64::consts::SQRT_2),
] {
let si = core.string_forge().as_ref().intern(name).0;
let si = core.perm_interner().as_ref().intern(name).0;
let sh_c = core.shape_forge().as_ref().make_shape(math.shape_id(), si);
math.set_shape_id(sh_c);
math.ensure_hash_props().push(JsValue::float(val));
}
let si_m = core.string_forge().intern("Math").0;
let si_m = core.perm_interner().intern("Math").0;
let m_shape = core.shape_forge().make_shape(global.shape_id(), si_m);
let m_val = JsValue::from_js_object(session.builtin_world().math_object.as_ptr() as *mut JsObject);
global.set_shape_id(m_shape);
+1 -1
View File
@@ -50,7 +50,7 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
("POSITIVE_INFINITY", JsValue::float(f64::INFINITY)),
] {
ctor.ensure_hash_props().push(value);
let prop_si = core.string_forge().intern(name).0;
let prop_si = core.perm_interner().intern(name).0;
let next_shape = core.shape_forge().make_shape(ctor.shape_id(), prop_si);
ctor.set_shape_id(next_shape);
}
+1 -1
View File
@@ -32,7 +32,7 @@ pub fn bind_object(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
};
session
.builtin_world()
.bind_object_methods(&methods, core.string_forge().as_ref(), core.shape_forge().as_ref());
.bind_object_methods(&methods, core.perm_interner().as_ref(), core.shape_forge().as_ref());
let ctor_ptr = session.builtin_world().object_constructor.as_ptr() as *mut JsObject;
bind_constructor!(core, global, "Object", ctor_ptr, crate::builtins::object::object_constructor, 1);
+2 -2
View File
@@ -38,7 +38,7 @@ pub fn bind_string(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
};
session.builtin_world().bind_string_methods(
&string_methods,
core.string_forge().as_ref(),
core.perm_interner().as_ref(),
core.shape_forge().as_ref(),
);
@@ -55,7 +55,7 @@ pub fn bind_string(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
&[("toString", crate::builtins::string::string_to_string as *const (), 0)],
);
let si_str = core.string_forge().intern("String").0;
let si_str = core.perm_interner().intern("String").0;
let str_shape = core.shape_forge().make_shape(global.shape_id(), si_str);
let str_val = JsValue::from_js_object(session.builtin_world().string_constructor.as_ptr() as *mut JsObject);
global.set_shape_id(str_shape);
+1 -1
View File
@@ -61,7 +61,7 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
}
fn bind_well_known_symbol(core: &Arc<KernelCore>, ctor: &mut JsObject, name: &str, val: JsValue) {
let si = core.string_forge().intern(name).0;
let si = core.perm_interner().intern(name).0;
let shape_id = core.shape_forge().make_shape(ctor.shape_id(), si);
ctor.set_shape_id(shape_id);
ctor.ensure_hash_props().push(val);
+6 -6
View File
@@ -35,7 +35,7 @@ macro_rules! bind_constructor {
bind_constructor!($core, $global, $name, $ctor_ptr, $ctor_fn, $nargs, hash: false)
}};
($core:expr, $global:expr, $name:literal, $ctor_ptr:expr, $ctor_fn:path, $nargs:literal, hash: $hash:literal) => {{
let si = $core.string_forge().intern($name).0;
let si = $core.perm_interner().intern($name).0;
let shape = $core.shape_forge().make_shape($global.shape_id(), si);
let val = $crate::JsValue::from_js_object($ctor_ptr);
$global.set_shape_id(shape);
@@ -67,7 +67,7 @@ pub(crate) fn apply_binding_table(
world: &BuiltinWorld, target: &mut JsObject, core: &Arc<KernelCore>, bindings: &[(&'static str, *const (), u8)],
) {
let shape_forge = core.shape_forge().as_ref();
let string_forge = core.string_forge().as_ref();
let string_forge = core.perm_interner().as_ref();
for (name, func, nargs) in bindings {
// SAFETY: all entries in the binding table are NativeFn fn-item pointers cast to *const ().
let fn_ptr = unsafe { oxide_types::object::NativeFnPtr::from_raw(*func) };
@@ -76,7 +76,7 @@ pub(crate) fn apply_binding_table(
}
pub(crate) fn bind_global_value(core: &Arc<KernelCore>, global: &mut JsObject, name: &str, value: JsValue) {
let si = core.string_forge().intern(name).0;
let si = core.perm_interner().intern(name).0;
let shape = core.shape_forge().make_shape(global.shape_id(), si);
global.set_shape_id(shape);
global.ensure_hash_props().push(value);
@@ -91,7 +91,7 @@ fn bind_error_subtype_global(
core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject, name: &str, proto: &P<JsObject>,
ctor_fn: *const (),
) {
let constructor_si = core.string_forge().intern("constructor").0;
let constructor_si = core.perm_interner().intern("constructor").0;
if let Some(pos) = core.shape_forge().lookup_position(proto.shape_id(), constructor_si) {
let existing_ctor = proto.get_prop_at(pos);
if existing_ctor.is_object() {
@@ -105,7 +105,7 @@ fn bind_error_subtype_global(
ctor.set_function(true);
configure_native_constructor(&mut ctor, ctor_fn, 1);
let sf = core.string_forge().as_ref();
let sf = core.perm_interner().as_ref();
let sh = core.shape_forge().as_ref();
let si_prototype = sf.intern("prototype").0;
let si_name = sf.intern("name").0;
@@ -115,7 +115,7 @@ fn bind_error_subtype_global(
ctor.set_shape_id(ctor_shape2);
ctor.ensure_hash_props()
.push(JsValue::from_js_object(proto.as_ptr() as *mut JsObject));
ctor.ensure_hash_props().push(JsValue::string(name_si, 0));
ctor.ensure_hash_props().push(JsValue::perm_string(sf.string_ptr(name_si)));
let ctor_ptr = Box::into_raw(ctor);
bind_existing_global(core, global, name, JsValue::from_js_object(ctor_ptr));
+24 -33
View File
@@ -64,7 +64,7 @@ fn create_new_array(vm: &mut Vm, n: usize) -> *mut JsObject {
}
fn array_length_arg(vm: &mut Vm, value: JsValue) -> Result<usize, JsValue> {
let n = coercion::to_number(value, vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(value);
if !n.is_finite() || n < 0.0 || n.fract() != 0.0 || n > MAX_DENSE_PROPS as f64 {
return Err(crate::builtins::error::create_range_error(vm, "Invalid array length"));
}
@@ -186,12 +186,12 @@ pub fn array_slice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize;
let start = if args.len() > 1 {
(coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as i32) as usize
(coercion::to_number(vm.reg(args[1])) as i32) as usize
} else {
0
};
let end = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as i32) as usize
(coercion::to_number(vm.reg(args[2])) as i32) as usize
} else {
n
};
@@ -215,7 +215,7 @@ pub fn array_splice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = arr.prop_count() as usize;
let start = if args.len() > 1 {
let v = coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref());
let v = coercion::to_number(vm.reg(args[1]));
let s = v as i32;
if s < 0 {
(n as i32 + s).max(0) as usize
@@ -227,7 +227,7 @@ pub fn array_splice(vm: &mut Vm, args: &[u8]) -> NativeResult {
};
let delete_count = if args.len() > 2 {
let v = coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref());
let v = coercion::to_number(vm.reg(args[2]));
(v as usize).min(n - start)
} else {
n - start
@@ -311,23 +311,22 @@ pub fn array_join(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize;
let sep = if args.len() > 1 {
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]))
coercion::to_string(vm.reg(args[1]))
} else {
",".to_string()
};
let sf = vm.kernel_core().string_forge().as_ref();
let parts: Vec<String> = (0..n)
.map(|i| {
let v = arr.get_prop_at(i);
if v.is_undefined() || v.is_null() {
String::new()
} else {
coercion::to_string(sf, v)
coercion::to_string(v)
}
})
.collect();
let joined = parts.join(&sep);
let result_val = vm.intern(&joined);
let result_val = vm.new_string(&joined);
NativeResult::Ok(result_val)
}
@@ -392,7 +391,7 @@ pub fn array_flat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize;
let depth = if args.len() > 1 {
let n = coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(vm.reg(args[1]));
if !n.is_finite() {
vm.kernel_core.config.max_call_depth
} else {
@@ -506,8 +505,7 @@ pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult {
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
NativeResult::Ok(result_val) => {
let sf = vm.kernel_core().string_forge().as_ref();
if coercion::to_boolean(result_val, sf) {
if coercion::to_boolean(result_val) {
kept.push(elem);
}
}
@@ -578,8 +576,7 @@ pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult {
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
NativeResult::Ok(result_val) => {
let sf = vm.kernel_core().string_forge().as_ref();
if coercion::to_boolean(result_val, sf) {
if coercion::to_boolean(result_val) {
return NativeResult::Ok(elem);
}
}
@@ -604,8 +601,7 @@ pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult {
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
NativeResult::Ok(result_val) => {
let sf = vm.kernel_core().string_forge().as_ref();
if coercion::to_boolean(result_val, sf) {
if coercion::to_boolean(result_val) {
return NativeResult::Ok(JsValue::bool(true));
}
}
@@ -630,8 +626,7 @@ pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult {
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
NativeResult::Ok(result_val) => {
let sf = vm.kernel_core().string_forge().as_ref();
if !coercion::to_boolean(result_val, sf) {
if !coercion::to_boolean(result_val) {
return NativeResult::Ok(JsValue::bool(false));
}
}
@@ -725,12 +720,12 @@ pub fn array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult {
let len = arr.prop_count() as usize;
let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let start = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as i32).max(0) as usize
(coercion::to_number(vm.reg(args[2])) as i32).max(0) as usize
} else {
0
};
let end = if args.len() > 3 {
let e = coercion::to_number(vm.reg(args[3]), vm.kernel_core().string_forge().as_ref()) as i32;
let e = coercion::to_number(vm.reg(args[3])) as i32;
(e as usize).min(len)
} else {
len
@@ -749,17 +744,17 @@ pub fn array_copy_within(vm: &mut Vm, args: &[u8]) -> NativeResult {
return NativeResult::Ok(vm.reg(args[0]));
}
let target = if args.len() > 1 {
(coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as i32) as usize
(coercion::to_number(vm.reg(args[1])) as i32) as usize
} else {
0
};
let start = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as i32) as usize
(coercion::to_number(vm.reg(args[2])) as i32) as usize
} else {
0
};
let end = if args.len() > 3 {
(coercion::to_number(vm.reg(args[3]), vm.kernel_core().string_forge().as_ref()) as i32 as usize).min(len)
(coercion::to_number(vm.reg(args[3])) as i32 as usize).min(len)
} else {
len
};
@@ -779,11 +774,7 @@ pub fn array_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &*arr_ptr };
let len = arr.prop_count() as i32;
let mut index = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as i32
} else {
0
};
let mut index = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) as i32 } else { 0 };
if index < 0 {
index += len;
}
@@ -820,7 +811,7 @@ pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult {
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
NativeResult::Ok(r) => {
if coercion::to_boolean(r, vm.kernel_core().string_forge().as_ref()) {
if coercion::to_boolean(r) {
return NativeResult::Ok(JsValue::int(i as i32));
}
}
@@ -848,7 +839,7 @@ pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult {
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i), vm.reg(args[0])]) {
NativeResult::Ok(r) => {
if coercion::to_boolean(r, vm.kernel_core().string_forge().as_ref()) {
if coercion::to_boolean(r) {
return NativeResult::Ok(elem);
}
}
@@ -917,7 +908,7 @@ pub fn array_sort(vm: &mut Vm, args: &[u8]) -> NativeResult {
if let Some(callback) = comparator {
match invoke_native_callback(vm, callback, JsValue::undefined(), &[*a, *b]) {
NativeResult::Ok(result) => {
let n = coercion::to_number(result, vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(result);
if n.is_nan() || n == 0.0 {
std::cmp::Ordering::Equal
} else if n < 0.0 {
@@ -937,8 +928,8 @@ pub fn array_sort(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
}
} else {
let sa = coercion::to_string(vm.kernel_core().string_forge().as_ref(), *a);
let sb = coercion::to_string(vm.kernel_core().string_forge().as_ref(), *b);
let sa = coercion::to_string(*a);
let sb = coercion::to_string(*b);
sa.cmp(&sb)
}
});
+5 -5
View File
@@ -22,7 +22,7 @@ fn type_error(vm: &mut Vm, msg: &str) -> NativeResult {
}
fn to_index(vm: &mut Vm, value: JsValue) -> Result<usize, JsValue> {
let n = coercion::to_number(value, vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(value);
if n.is_nan() {
return Ok(0);
}
@@ -32,8 +32,8 @@ fn to_index(vm: &mut Vm, value: JsValue) -> Result<usize, JsValue> {
Ok(n.trunc() as usize)
}
fn normalize_index(vm: &mut Vm, value: JsValue, len: usize) -> usize {
let n = coercion::to_number(value, vm.kernel_core().string_forge().as_ref());
fn normalize_index(_vm: &mut Vm, value: JsValue, len: usize) -> usize {
let n = coercion::to_number(value);
if n.is_nan() {
return 0;
}
@@ -46,7 +46,7 @@ fn normalize_index(vm: &mut Vm, value: JsValue, len: usize) -> usize {
}
fn set_named_prop(vm: &mut Vm, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) {
let si = vm.kernel_core().string_forge().intern(name).0;
let si = vm.kernel_core().perm_interner().intern(name).0;
let _ = vm.define_data_property(obj, si, value, attributes);
}
@@ -177,5 +177,5 @@ pub fn array_buffer_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
if array_buffer_data_ptr(vm, this_val).is_err() {
return type_error(vm, "ArrayBuffer.prototype.toString called on incompatible receiver");
}
NativeResult::Ok(vm.intern("[object ArrayBuffer]"))
NativeResult::Ok(vm.new_string("[object ArrayBuffer]"))
}
+6 -10
View File
@@ -13,12 +13,8 @@ pub fn boolean_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
} else if arg.is_bool() {
arg.as_bool()
} else if arg.is_string() {
let s = vm
.kernel_core()
.string_forge()
.lookup(arg.as_string_index())
.unwrap_or_default();
!s.is_empty()
// SAFETY: arg is a string value.
!unsafe { (*arg.as_string_ptr()).is_empty() }
} else if arg.is_object() {
true
} else if arg.is_int() {
@@ -81,9 +77,9 @@ pub fn boolean_prototype_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if this_val.is_bool() {
return if this_val.as_bool() {
NativeResult::Ok(vm.intern("true"))
NativeResult::Ok(vm.new_string("true"))
} else {
NativeResult::Ok(vm.intern("false"))
NativeResult::Ok(vm.new_string("false"))
};
}
if !this_val.is_object() {
@@ -106,8 +102,8 @@ pub fn boolean_prototype_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
!val.is_null() && !val.is_undefined()
};
if is_true {
NativeResult::Ok(vm.intern("true"))
NativeResult::Ok(vm.new_string("true"))
} else {
NativeResult::Ok(vm.intern("false"))
NativeResult::Ok(vm.new_string("false"))
}
}
+5 -9
View File
@@ -24,7 +24,7 @@ macro_rules! native_try {
}
fn to_index(vm: &mut Vm, value: JsValue, msg: &str) -> Result<usize, JsValue> {
let n = coercion::to_number(value, vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(value);
if n.is_nan() {
return Ok(0);
}
@@ -35,19 +35,15 @@ fn to_index(vm: &mut Vm, value: JsValue, msg: &str) -> Result<usize, JsValue> {
}
fn is_little_endian(vm: &mut Vm, args: &[u8], idx: usize) -> bool {
args.get(idx)
.map(|reg| coercion::to_boolean(vm.reg(*reg), vm.kernel_core().string_forge().as_ref()))
.unwrap_or(false)
args.get(idx).map(|reg| coercion::to_boolean(vm.reg(*reg))).unwrap_or(false)
}
fn numeric_arg(vm: &mut Vm, args: &[u8], idx: usize) -> f64 {
args.get(idx)
.map(|reg| coercion::to_number(vm.reg(*reg), vm.kernel_core().string_forge().as_ref()))
.unwrap_or(0.0)
args.get(idx).map(|reg| coercion::to_number(vm.reg(*reg))).unwrap_or(0.0)
}
fn set_named_prop(vm: &mut Vm, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) {
let si = vm.kernel_core().string_forge().intern(name).0;
let si = vm.kernel_core().perm_interner().intern(name).0;
let _ = vm.define_data_property(obj, si, value, attributes);
}
@@ -438,5 +434,5 @@ pub fn data_view_set_big_uint64(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn data_view_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
native_try!(get_data_view_data(vm, this_val));
NativeResult::Ok(vm.intern("[object DataView]"))
NativeResult::Ok(vm.new_string("[object DataView]"))
}
+24 -54
View File
@@ -104,33 +104,13 @@ pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let timestamp = if args.len() < 2 {
Utc::now().timestamp_millis() as f64
} else if args.len() > 2 {
let y = coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as i32;
let m = coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as u32;
let d = if args.len() > 3 {
coercion::to_number(vm.reg(args[3]), vm.kernel_core().string_forge().as_ref()) as u32
} else {
1
};
let h = if args.len() > 4 {
coercion::to_number(vm.reg(args[4]), vm.kernel_core().string_forge().as_ref()) as u32
} else {
0
};
let min = if args.len() > 5 {
coercion::to_number(vm.reg(args[5]), vm.kernel_core().string_forge().as_ref()) as u32
} else {
0
};
let sec = if args.len() > 6 {
coercion::to_number(vm.reg(args[6]), vm.kernel_core().string_forge().as_ref()) as u32
} else {
0
};
let ms = if args.len() > 7 {
coercion::to_number(vm.reg(args[7]), vm.kernel_core().string_forge().as_ref()) as u32
} else {
0
};
let y = coercion::to_number(vm.reg(args[1])) as i32;
let m = coercion::to_number(vm.reg(args[2])) as u32;
let d = if args.len() > 3 { coercion::to_number(vm.reg(args[3])) as u32 } else { 1 };
let h = if args.len() > 4 { coercion::to_number(vm.reg(args[4])) as u32 } else { 0 };
let min = if args.len() > 5 { coercion::to_number(vm.reg(args[5])) as u32 } else { 0 };
let sec = if args.len() > 6 { coercion::to_number(vm.reg(args[6])) as u32 } else { 0 };
let ms = if args.len() > 7 { coercion::to_number(vm.reg(args[7])) as u32 } else { 0 };
NaiveDate::from_ymd_opt(y, m + 1, d)
.and_then(|nd| {
nd.and_hms_milli_opt(h, min, sec, ms)
@@ -141,11 +121,8 @@ pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
} else {
let val = vm.reg(args[1]);
if val.is_string() {
let s = vm
.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default();
// SAFETY: val is a string value.
let s = unsafe { (*val.as_string_ptr()).data.clone() };
let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"];
let mut ts = f64::NAN;
for fmt in &formats {
@@ -191,7 +168,7 @@ pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
} else {
"Invalid Date".to_string()
};
return NativeResult::Ok(vm.intern(&s));
return NativeResult::Ok(vm.new_string(&s));
}
let mut obj = JsObject::new_empty(
@@ -217,11 +194,8 @@ pub fn date_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !val.is_string() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let s = vm
.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default();
// SAFETY: val is a string value.
let s = unsafe { (*val.as_string_ptr()).data.clone() };
let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"];
let mut ts = f64::NAN;
for fmt in &formats {
@@ -315,7 +289,7 @@ fn local_offset_minutes() -> i32 {
fn get_opt_arg(vm: &Vm, args: &[u8], idx: usize, default: u32) -> u32 {
if args.len() > idx {
coercion::to_number(vm.reg(args[idx]), vm.kernel_core().string_forge().as_ref()) as u32
coercion::to_number(vm.reg(args[idx])) as u32
} else {
default
}
@@ -323,18 +297,14 @@ fn get_opt_arg(vm: &Vm, args: &[u8], idx: usize, default: u32) -> u32 {
pub fn date_set_time(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let val = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref())
} else {
f64::NAN
};
let val = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) } else { f64::NAN };
set_timestamp(obj, val);
NativeResult::Ok(JsValue::float(val))
}
pub fn date_set_full_year(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
@@ -357,7 +327,7 @@ pub fn date_set_full_year(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
pub fn date_set_month(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
@@ -375,7 +345,7 @@ pub fn date_set_month(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
pub fn date_set_date(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
@@ -392,7 +362,7 @@ pub fn date_set_date(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
pub fn date_set_hours(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
@@ -417,7 +387,7 @@ pub fn date_set_hours(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
pub fn date_set_minutes(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
@@ -440,7 +410,7 @@ pub fn date_set_minutes(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
pub fn date_set_seconds(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
@@ -461,7 +431,7 @@ pub fn date_set_seconds(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
pub fn date_set_milliseconds(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
@@ -481,13 +451,13 @@ fn date_to_string_inner(vm: &mut Vm, args: &[u8], format_str: &str, invalid: &st
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(vm.intern(invalid));
return NativeResult::Ok(vm.new_string(invalid));
}
let s = match dt_from_ms(ms) {
Some(dt) => dt.format(format_str).to_string(),
None => invalid.to_string(),
};
NativeResult::Ok(vm.intern(&s))
NativeResult::Ok(vm.new_string(&s))
}
pub fn date_to_iso_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
@@ -504,7 +474,7 @@ pub fn date_to_json(vm: &mut Vm, args: &[u8]) -> NativeResult {
Some(dt) => dt.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(),
None => return NativeResult::Ok(JsValue::null()),
};
NativeResult::Ok(vm.intern(&s))
NativeResult::Ok(vm.new_string(&s))
}
pub fn date_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
+10 -16
View File
@@ -14,12 +14,12 @@ fn create_error_object(vm: &mut Vm, error_proto_ptr: *mut JsObject, name: &str,
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(error_proto_ptr)));
let sf = Arc::clone(vm.kernel_core().string_forge());
let sf = Arc::clone(vm.kernel_core().perm_interner());
let sh = Arc::clone(vm.kernel_core().shape_forge());
let si_message = sf.intern("message").0;
let sh_message = sh.make_shape(EMPTY_SHAPE_ID, si_message);
let msg_val = vm.intern(message);
let msg_val = vm.new_string(message);
unsafe {
(*obj).set_shape_id(sh_message);
(*obj).push_prop(msg_val);
@@ -27,7 +27,7 @@ fn create_error_object(vm: &mut Vm, error_proto_ptr: *mut JsObject, name: &str,
let si_name = sf.intern("name").0;
let sh_name = sh.make_shape(sh_message, si_name);
let name_val = vm.intern(name);
let name_val = vm.new_string(name);
unsafe {
(*obj).set_shape_id(sh_name);
(*obj).push_prop(name_val);
@@ -75,7 +75,7 @@ pub fn create_uri_error(vm: &mut Vm, msg: &str) -> JsValue {
fn get_msg(vm: &mut Vm, args: &[u8]) -> String {
if args.len() > 1 {
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]))
coercion::to_string(vm.reg(args[1]))
} else {
String::new()
}
@@ -145,8 +145,7 @@ pub fn error_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
("Error".to_string(), String::new())
};
let result = crate::vm::format_error_message(&name, &msg);
let si = vm.kernel_core().string_forge().intern(&result).0;
NativeResult::Ok(JsValue::string(si, 0))
NativeResult::Ok(vm.new_string(&result))
}
pub fn error_stack_getter(vm: &mut Vm, _args: &[u8]) -> NativeResult {
@@ -154,15 +153,14 @@ pub fn error_stack_getter(vm: &mut Vm, _args: &[u8]) -> NativeResult {
for frame in vm.frames.iter().rev() {
let func_name = vm
.kernel_core()
.string_forge()
.perm_interner()
.lookup(frame.function_name)
.unwrap_or_else(|| "<anonymous>".to_string());
.unwrap_or("<anonymous>");
lines.push(format!(" at {} (native)", func_name));
}
lines.push(" at <anonymous> (native)".to_string());
let result = lines.join("\n");
let si = vm.kernel_core().string_forge().intern(&result).0;
NativeResult::Ok(JsValue::string(si, 0))
NativeResult::Ok(vm.new_string(&result))
}
#[cfg(test)]
@@ -174,7 +172,7 @@ mod tests {
#[test]
fn error_stack_uses_call_frame_function_name() {
let mut vm = Vm::new();
let name_si = vm.kernel_core().string_forge().intern("foo").0;
let name_si = vm.kernel_core().perm_interner().intern("foo").0;
vm.frames.push(CallFrame {
return_addr: 0,
function_name: name_si,
@@ -190,11 +188,7 @@ mod tests {
});
let stack = error_stack_getter(&mut vm, &[0]).unwrap();
let stack_str = vm
.kernel_core()
.string_forge()
.lookup(stack.as_string_index())
.unwrap_or_default();
let stack_str = vm.lookup_str(stack).unwrap_or_default();
assert!(stack_str.contains("foo"), "expected function name in stack: {stack_str}");
}
}
+3 -10
View File
@@ -121,16 +121,10 @@ pub fn function_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
));
}
let name_si = vm.kernel_core().string_forge().intern("name").0;
let name_si = vm.kernel_core().perm_interner().intern("name").0;
let name = vm
.resolve_property(func, name_si)
.and_then(|v| {
if v.is_string() {
vm.kernel_core().string_forge().lookup(v.as_string_index())
} else {
None
}
})
.and_then(|v| vm.lookup_str(v))
.unwrap_or_else(|| {
let sub_idx = func.sub_module_index();
if sub_idx > 0 && (sub_idx as usize) <= vm.sub_modules.len() {
@@ -146,6 +140,5 @@ pub fn function_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
} else {
format!("function {name}() {{ {body} }}")
};
let si = vm.kernel_core().string_forge().intern(&result).0;
NativeResult::Ok(JsValue::string(si, 0))
NativeResult::Ok(vm.new_string(&result))
}
+1 -1
View File
@@ -9,7 +9,7 @@ pub use uri::{decode_uri, decode_uri_component, encode_uri, encode_uri_component
fn string_arg(vm: &mut Vm, args: &[u8]) -> String {
if args.len() > 1 {
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]))
coercion::to_string(vm.reg(args[1]))
} else {
"undefined".to_string()
}
@@ -87,12 +87,12 @@ fn unescape_string(input: &str) -> String {
pub fn js_escape(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.intern(&escape_string(&input)))
NativeResult::Ok(vm.new_string(&escape_string(&input)))
}
pub fn js_unescape(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.intern(&unescape_string(&input)))
NativeResult::Ok(vm.new_string(&unescape_string(&input)))
}
#[cfg(test)]
+4 -4
View File
@@ -88,18 +88,18 @@ fn uri_error(vm: &mut Vm) -> NativeResult {
pub fn encode_uri(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.intern(&encode_uri_string(&input, URI_SAFE)))
NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_SAFE)))
}
pub fn encode_uri_component(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.intern(&encode_uri_string(&input, URI_UNESCAPED)))
NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_UNESCAPED)))
}
pub fn decode_uri(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
match decode_uri_string(&input, true) {
Ok(decoded) => NativeResult::Ok(vm.intern(&decoded)),
Ok(decoded) => NativeResult::Ok(vm.new_string(&decoded)),
Err(()) => uri_error(vm),
}
}
@@ -107,7 +107,7 @@ pub fn decode_uri(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn decode_uri_component(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
match decode_uri_string(&input, false) {
Ok(decoded) => NativeResult::Ok(vm.intern(&decoded)),
Ok(decoded) => NativeResult::Ok(vm.new_string(&decoded)),
Err(()) => uri_error(vm),
}
}
+13 -13
View File
@@ -28,9 +28,9 @@ pub(crate) fn make_iterator_for_value(vm: &mut Vm, value: JsValue) -> Result<JsV
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
let inner_si = vm.kernel_core().string_forge().intern(INNER_PROP).0;
let index_si = vm.kernel_core().string_forge().intern(INDEX_PROP).0;
let next_si = vm.kernel_core().string_forge().intern("next").0;
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0;
let next_si = vm.kernel_core().perm_interner().intern("next").0;
let wrapper_obj = unsafe { &mut *wrapper };
vm.set_or_create_prop_value(wrapper_obj, inner_si, inner);
vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(0));
@@ -51,8 +51,8 @@ pub fn iterator_wrapper_next(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let wrapper = unsafe { &mut *this_val.as_js_object_ptr() };
let inner_si = vm.kernel_core().string_forge().intern(INNER_PROP).0;
let index_si = vm.kernel_core().string_forge().intern(INDEX_PROP).0;
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0;
let inner = match vm.ordinary_get(wrapper, inner_si, this_val) {
Ok(inner) if !inner.is_undefined() => inner,
_ => {
@@ -69,7 +69,7 @@ pub fn iterator_wrapper_next(vm: &mut Vm, args: &[u8]) -> NativeResult {
if inner.is_object() {
let inner_obj = unsafe { &*inner.as_js_object_ptr() };
let next_si = vm.kernel_core().string_forge().intern("next").0;
let next_si = vm.kernel_core().perm_interner().intern("next").0;
let next = match vm.ordinary_get(inner_obj, next_si, inner) {
Ok(next) => next,
Err(err) => return NativeResult::Err(crate::builtins::error::create_type_error(vm, &err)),
@@ -90,7 +90,7 @@ fn get_iterator(vm: &mut Vm, value: JsValue) -> Result<JsValue, JsValue> {
if value.is_object() {
let obj = unsafe { &*value.as_js_object_ptr() };
let next_si = vm.kernel_core().string_forge().intern("next").0;
let next_si = vm.kernel_core().perm_interner().intern("next").0;
if let Ok(next) = vm.ordinary_get(obj, next_si, value) {
if is_callable(next) {
return Ok(value);
@@ -115,11 +115,11 @@ fn next_array_like(vm: &mut Vm, wrapper: &mut JsObject, inner: JsValue, index_si
if inner.is_string() {
let index = current_index(vm, wrapper, index_si);
let source = coercion::to_string(vm.kernel_core().string_forge().as_ref(), inner);
let source = coercion::to_string(inner);
let mut chars = source.chars();
if let Some(ch) = chars.nth(index) {
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
let value = vm.intern(&ch.to_string());
let value = vm.new_string(&ch.to_string());
return Some(make_iter_result(vm, value, false));
}
return Some(make_iter_result(vm, JsValue::undefined(), true));
@@ -141,8 +141,8 @@ fn make_iter_result(vm: &mut Vm, value: JsValue, done: bool) -> JsValue {
let obj = vm
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
let value_si = vm.kernel_core().string_forge().intern("value").0;
let done_si = vm.kernel_core().string_forge().intern("done").0;
let value_si = vm.kernel_core().perm_interner().intern("value").0;
let done_si = vm.kernel_core().perm_interner().intern("done").0;
let obj_ref = unsafe { &mut *obj };
vm.set_or_create_prop_value(obj_ref, value_si, value);
vm.set_or_create_prop_value(obj_ref, done_si, JsValue::bool(done));
@@ -157,8 +157,8 @@ fn make_native_function(vm: &mut Vm, name: &str, native_fn: *const (), arg_count
func.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw(native_fn) }));
func.set_native_arg_count(arg_count);
let func = vm.alloc_object(func);
let name_si = vm.kernel_core().string_forge().intern("name").0;
let value = vm.intern(name);
let name_si = vm.kernel_core().perm_interner().intern("name").0;
let value = vm.new_string(name);
let func_ref = unsafe { &mut *func };
vm.set_or_create_prop_value(func_ref, name_si, value);
JsValue::from_js_object(func)
+9 -9
View File
@@ -21,8 +21,8 @@ pub fn json_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
));
}
let text = {
let si = val.as_string_index();
vm.kernel_core().string_forge().lookup(si).unwrap_or_default()
// SAFETY: val is a string value.
unsafe { (*val.as_string_ptr()).data.clone() }
};
let parsed: serde_json::Value = match serde_json::from_str(&text) {
@@ -44,7 +44,7 @@ fn value_to_jsvalue(vm: &mut Vm, val: &serde_json::Value) -> JsValue {
JsValue::float(0.0)
}
}
serde_json::Value::String(s) => vm.intern(s),
serde_json::Value::String(s) => vm.new_string(s),
serde_json::Value::Array(arr) => {
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let n = arr.len();
@@ -66,7 +66,7 @@ fn value_to_jsvalue(vm: &mut Vm, val: &serde_json::Value) -> JsValue {
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
for (key, val) in map {
let si = vm.kernel_core().string_forge().intern(key).0;
let si = vm.kernel_core().perm_interner().intern(key).0;
let jsv = value_to_jsvalue(vm, val);
let new_shape = vm.kernel_core().shape_forge().make_shape(obj.shape_id(), si);
obj.set_shape_id(new_shape);
@@ -91,7 +91,7 @@ pub fn json_stringify(vm: &mut Vm, args: &[u8]) -> NativeResult {
if jsvalue_to_json(vm, value, &mut visited, &mut output).is_err() {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "cyclic object value"));
};
NativeResult::Ok(vm.intern(&output))
NativeResult::Ok(vm.new_string(&output))
}
fn jsvalue_to_json(vm: &Vm, val: JsValue, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> {
@@ -113,8 +113,8 @@ fn jsvalue_to_json(vm: &Vm, val: JsValue, visited: &mut HashSet<*const u8>, out:
out.push_str(buf.format(n));
}
} else if val.is_string() {
let si = val.as_string_index();
let s = vm.kernel_core().string_forge().lookup(si).unwrap_or_default();
// SAFETY: val is a string value.
let s = unsafe { (*val.as_string_ptr()).data.clone() };
stringify_string(&s, out);
} else if val.is_object() {
let obj_ptr = val.as_js_object_ptr();
@@ -184,8 +184,8 @@ fn stringify_object(vm: &Vm, obj: &JsObject, visited: &mut HashSet<*const u8>, o
out.push(',');
}
first = false;
let name = vm.kernel_core().string_forge().lookup(si).unwrap_or_default();
stringify_string(&name, out);
let name = vm.kernel_core().perm_interner().lookup(si).unwrap_or_default();
stringify_string(name, out);
out.push(':');
jsvalue_to_json(vm, val, visited, out)?;
}
+1 -1
View File
@@ -5,7 +5,7 @@ use crate::native::NativeResult;
use crate::vm::Vm;
fn num(vm: &Vm, reg: u8) -> f64 {
coercion::to_number(vm.reg(reg), vm.kernel_core().string_forge().as_ref())
coercion::to_number(vm.reg(reg))
}
fn arg1(vm: &Vm, args: &[u8]) -> f64 {
+29 -35
View File
@@ -5,11 +5,7 @@ use crate::native::NativeResult;
use crate::vm::Vm;
pub fn number_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref())
} else {
0.0
};
let n = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) } else { 0.0 };
let number_proto = vm.session().builtin_world().number_proto.as_ptr() as *mut oxide_types::object::JsObject;
let is_ctor = if let Some(this_reg) = args.first().copied() {
let this_val = vm.reg(this_reg);
@@ -71,7 +67,7 @@ pub fn number_is_finite(vm: &mut Vm, args: &[u8]) -> NativeResult {
if val.is_double() {
return NativeResult::Ok(JsValue::bool(val.as_double().is_finite()));
}
let n = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(val);
NativeResult::Ok(JsValue::bool(n.is_finite()))
}
@@ -79,8 +75,7 @@ pub fn number_parse_int(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let sf = vm.kernel_core().string_forge().as_ref();
let s = coercion::to_string(sf, vm.reg(args[1]));
let s = coercion::to_string(vm.reg(args[1]));
let s = s.trim();
if s.is_empty() {
@@ -88,7 +83,7 @@ pub fn number_parse_int(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let radix = if args.len() > 2 {
let r = coercion::to_number(vm.reg(args[2]), sf) as i32;
let r = coercion::to_number(vm.reg(args[2])) as i32;
if r == 0 {
10
} else {
@@ -121,8 +116,7 @@ pub fn number_parse_float(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let sf = vm.kernel_core().string_forge().as_ref();
let s = coercion::to_string(sf, vm.reg(args[1]));
let s = coercion::to_string(vm.reg(args[1]));
let s = s.trim();
if s.is_empty() {
@@ -136,9 +130,9 @@ pub fn number_parse_float(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
pub fn number_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]), vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(vm.reg(args[0]));
let radix = if args.len() > 1 {
let r = coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as u32;
let r = coercion::to_number(vm.reg(args[1])) as u32;
r.clamp(2, 36)
} else {
10u32
@@ -146,22 +140,22 @@ pub fn number_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
if radix == 10 {
if n.is_nan() {
return NativeResult::Ok(vm.intern("NaN"));
return NativeResult::Ok(vm.new_string("NaN"));
}
if n.is_infinite() {
if n.is_sign_positive() {
return NativeResult::Ok(vm.intern("Infinity"));
return NativeResult::Ok(vm.new_string("Infinity"));
}
return NativeResult::Ok(vm.intern("-Infinity"));
return NativeResult::Ok(vm.new_string("-Infinity"));
}
if n.fract() == 0.0 && n >= i32::MIN as f64 && n <= i32::MAX as f64 {
return NativeResult::Ok(vm.intern(&(n as i64).to_string()));
return NativeResult::Ok(vm.new_string(&(n as i64).to_string()));
}
let mut buf = ryu::Buffer::new();
NativeResult::Ok(vm.intern(buf.format(n)))
NativeResult::Ok(vm.new_string(buf.format(n)))
} else {
if n.is_nan() {
return NativeResult::Ok(vm.intern("NaN"));
return NativeResult::Ok(vm.new_string("NaN"));
}
let nn = n as i64;
let mut result = String::new();
@@ -182,70 +176,70 @@ pub fn number_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
if nn < 0 {
result.insert(0, '-');
}
NativeResult::Ok(vm.intern(&result))
NativeResult::Ok(vm.new_string(&result))
}
}
pub fn number_to_fixed(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]), vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(vm.reg(args[0]));
let digits = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as usize
coercion::to_number(vm.reg(args[1])) as usize
} else {
0usize
}
.min(20);
let formatted = format!("{:.digits$}", n);
NativeResult::Ok(vm.intern(&formatted))
NativeResult::Ok(vm.new_string(&formatted))
}
pub fn number_is_integer(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] });
let n = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(val);
NativeResult::Ok(JsValue::bool(n.trunc() == n && n.is_finite()))
}
pub fn number_is_safe_integer(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] });
let n = coercion::to_number(val, vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(val);
let safe = n.trunc() == n && n.is_finite() && n >= -9007199254740991i64 as f64 && n <= 9007199254740991i64 as f64;
NativeResult::Ok(JsValue::bool(safe))
}
pub fn number_to_exponential(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]), vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(vm.reg(args[0]));
let digits = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as usize
coercion::to_number(vm.reg(args[1])) as usize
} else {
0usize
}
.min(100);
if n.is_nan() {
return NativeResult::Ok(vm.intern("NaN"));
return NativeResult::Ok(vm.new_string("NaN"));
}
if n.is_infinite() {
return NativeResult::Ok(vm.intern(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
return NativeResult::Ok(vm.new_string(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
}
let formatted = format!("{:.digits$e}", n);
NativeResult::Ok(vm.intern(&formatted))
NativeResult::Ok(vm.new_string(&formatted))
}
pub fn number_to_precision(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]), vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(vm.reg(args[0]));
let precision = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as usize
coercion::to_number(vm.reg(args[1])) as usize
} else {
0usize
}
.min(21);
if n.is_nan() {
return NativeResult::Ok(vm.intern("NaN"));
return NativeResult::Ok(vm.new_string("NaN"));
}
if n.is_infinite() {
return NativeResult::Ok(vm.intern(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
return NativeResult::Ok(vm.new_string(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
}
let formatted = format!("{:.precision$}", n);
NativeResult::Ok(vm.intern(&formatted))
NativeResult::Ok(vm.new_string(&formatted))
}
pub fn number_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
+21 -27
View File
@@ -1,5 +1,5 @@
use oxide_kernel::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
use oxide_kernel::string_forge::StringForge;
use oxide_kernel::string_forge::PermInterner;
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::private_key::is_private_name_key;
use oxide_types::value::JsValue;
@@ -62,7 +62,7 @@ pub fn object_keys(vm: &mut Vm, args: &[u8]) -> NativeResult {
let keys = walk_own_keys(vm, obj);
key_names = keys
.iter()
.map(|(si, _offset)| vm.kernel_core().string_forge().lookup(*si).unwrap_or_default())
.map(|(si, _offset)| vm.kernel_core().perm_interner().lookup(*si).unwrap_or("").to_string())
.collect();
}
@@ -75,7 +75,7 @@ pub fn object_keys(vm: &mut Vm, args: &[u8]) -> NativeResult {
vm.epoch().bump(),
));
for (i, k) in key_names.iter().enumerate() {
let str_val = vm.intern(k);
let str_val = vm.new_string(k);
unsafe {
(*arr).set_prop_at(i, str_val);
}
@@ -159,27 +159,23 @@ pub fn object_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
if desc_ptr.is_null() {
return NativeResult::Err(JsValue::undefined());
}
let prop_name_str = coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[2]));
let si = vm.kernel_core().string_forge().intern(&prop_name_str).0;
let prop_name_str = coercion::to_string(vm.reg(args[2]));
let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0;
let value_si = vm.kernel_core().string_forge().intern("value").0;
let get_si = vm.kernel_core().string_forge().intern("get").0;
let set_si = vm.kernel_core().string_forge().intern("set").0;
let writable_si = vm.kernel_core().string_forge().intern("writable").0;
let enumerable_si = vm.kernel_core().string_forge().intern("enumerable").0;
let configurable_si = vm.kernel_core().string_forge().intern("configurable").0;
let value_si = vm.kernel_core().perm_interner().intern("value").0;
let get_si = vm.kernel_core().perm_interner().intern("get").0;
let set_si = vm.kernel_core().perm_interner().intern("set").0;
let writable_si = vm.kernel_core().perm_interner().intern("writable").0;
let enumerable_si = vm.kernel_core().perm_interner().intern("enumerable").0;
let configurable_si = vm.kernel_core().perm_interner().intern("configurable").0;
let desc = unsafe { &*desc_ptr };
let value_field = own_field(vm, desc, value_si);
let get_field = own_field(vm, desc, get_si);
let set_field = own_field(vm, desc, set_si);
let writable_field = own_field(vm, desc, writable_si);
let enumerable = own_field(vm, desc, enumerable_si)
.map(|v| coercion::to_boolean(v, vm.kernel_core().string_forge().as_ref()))
.unwrap_or(false);
let configurable = own_field(vm, desc, configurable_si)
.map(|v| coercion::to_boolean(v, vm.kernel_core().string_forge().as_ref()))
.unwrap_or(false);
let enumerable = own_field(vm, desc, enumerable_si).map(coercion::to_boolean).unwrap_or(false);
let configurable = own_field(vm, desc, configurable_si).map(coercion::to_boolean).unwrap_or(false);
let has_data = value_field.is_some() || writable_field.is_some();
let has_accessor = get_field.is_some() || set_field.is_some();
@@ -208,9 +204,7 @@ pub fn object_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
} else {
let value = value_field.unwrap_or(JsValue::undefined());
let writable = writable_field
.map(|v| coercion::to_boolean(v, vm.kernel_core().string_forge().as_ref()))
.unwrap_or(false);
let writable = writable_field.map(coercion::to_boolean).unwrap_or(false);
if vm
.define_data_property(obj, si, value, PropAttributes::new(writable, enumerable, configurable))
.is_err()
@@ -233,8 +227,8 @@ pub fn object_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeRes
if obj_ptr.is_null() {
return NativeResult::Ok(JsValue::undefined());
}
let prop_name_str = coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[2]));
let si = vm.kernel_core().string_forge().intern(&prop_name_str).0;
let prop_name_str = coercion::to_string(vm.reg(args[2]));
let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0;
let (found_value, found_meta, found) = {
let obj = unsafe { &*obj_ptr };
@@ -257,7 +251,7 @@ pub fn object_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeRes
return NativeResult::Ok(JsValue::undefined());
}
let sf_ptr = vm.kernel_core().string_forge().as_ref() as *const StringForge;
let sf_ptr = vm.kernel_core().perm_interner().as_ref() as *const PermInterner;
let sh_ptr = vm.kernel_core().shape_forge().as_ref() as *const ShapeForge;
let desc_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
let desc = vm
@@ -406,7 +400,7 @@ pub fn object_get_own_property_names(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj = unsafe { &*obj_ptr };
let keys = walk_own_keys(vm, obj);
keys.iter()
.map(|(si, _)| vm.kernel_core().string_forge().lookup(*si).unwrap_or_default())
.map(|(si, _)| vm.kernel_core().perm_interner().lookup(*si).unwrap_or("").to_string())
.collect()
};
@@ -419,7 +413,7 @@ pub fn object_get_own_property_names(vm: &mut Vm, args: &[u8]) -> NativeResult {
vm.epoch().bump(),
));
for (i, k) in key_names.iter().enumerate() {
let str_val = vm.intern(k);
let str_val = vm.new_string(k);
unsafe {
(*arr).set_prop_at(i, str_val);
}
@@ -524,8 +518,8 @@ pub fn object_entries(vm: &mut Vm, args: &[u8]) -> NativeResult {
vm.epoch().bump(),
));
for (i, (si, offset)) in keys.iter().enumerate() {
let key_str = vm.kernel_core().string_forge().lookup(*si).unwrap_or_default();
let key_val = vm.intern(&key_str);
let key_str = vm.kernel_core().perm_interner().lookup(*si).unwrap_or_default();
let key_val = vm.new_string(key_str);
let val = obj.get_prop_at(*offset);
let pair = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
+11 -17
View File
@@ -39,23 +39,19 @@ pub fn reflect_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
let key_si = vm.property_key_si(arg(vm, args, 2));
let desc = unsafe { &*desc_ptr };
let value_si = vm.kernel_core().string_forge().intern("value").0;
let get_si = vm.kernel_core().string_forge().intern("get").0;
let set_si = vm.kernel_core().string_forge().intern("set").0;
let writable_si = vm.kernel_core().string_forge().intern("writable").0;
let enumerable_si = vm.kernel_core().string_forge().intern("enumerable").0;
let configurable_si = vm.kernel_core().string_forge().intern("configurable").0;
let value_si = vm.kernel_core().perm_interner().intern("value").0;
let get_si = vm.kernel_core().perm_interner().intern("get").0;
let set_si = vm.kernel_core().perm_interner().intern("set").0;
let writable_si = vm.kernel_core().perm_interner().intern("writable").0;
let enumerable_si = vm.kernel_core().perm_interner().intern("enumerable").0;
let configurable_si = vm.kernel_core().perm_interner().intern("configurable").0;
let value_field = own_field(vm, desc, value_si);
let get_field = own_field(vm, desc, get_si);
let set_field = own_field(vm, desc, set_si);
let writable_field = own_field(vm, desc, writable_si);
let enumerable = own_field(vm, desc, enumerable_si)
.map(|v| coercion::to_boolean(v, vm.kernel_core().string_forge().as_ref()))
.unwrap_or(false);
let configurable = own_field(vm, desc, configurable_si)
.map(|v| coercion::to_boolean(v, vm.kernel_core().string_forge().as_ref()))
.unwrap_or(false);
let enumerable = own_field(vm, desc, enumerable_si).map(coercion::to_boolean).unwrap_or(false);
let configurable = own_field(vm, desc, configurable_si).map(coercion::to_boolean).unwrap_or(false);
let has_data = value_field.is_some() || writable_field.is_some();
let has_accessor = get_field.is_some() || set_field.is_some();
@@ -73,9 +69,7 @@ pub fn reflect_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
vm.define_accessor_property(target, key_si, get, set, PropAttributes::new(false, enumerable, configurable))
} else {
let value = value_field.unwrap_or(JsValue::undefined());
let writable = writable_field
.map(|v| coercion::to_boolean(v, vm.kernel_core().string_forge().as_ref()))
.unwrap_or(false);
let writable = writable_field.map(coercion::to_boolean).unwrap_or(false);
vm.define_data_property(target, key_si, value, PropAttributes::new(writable, enumerable, configurable))
};
NativeResult::Ok(JsValue::bool(result.is_ok()))
@@ -145,7 +139,7 @@ pub fn reflect_own_keys(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target = unsafe { &*target_ptr };
let key_names: Vec<String> = walk_own_keys(vm, target)
.into_iter()
.map(|(si, _)| vm.kernel_core().string_forge().lookup(si).unwrap_or_default())
.map(|(si, _)| vm.kernel_core().perm_interner().lookup(si).unwrap_or("").to_string())
.collect();
NativeResult::Ok(make_string_array(vm, &key_names))
}
@@ -228,7 +222,7 @@ fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
vm.epoch().bump(),
));
for (idx, part) in parts.iter().enumerate() {
let value = vm.intern(part);
let value = vm.new_string(part);
unsafe { &mut *arr }.set_prop_at(idx, value);
}
unsafe { &mut *arr }.set_prop_count(parts.len());
+16 -23
View File
@@ -43,7 +43,7 @@ fn parse_flags(flags: &str) -> (bool, bool, bool) {
}
fn set_prop(obj: &mut JsObject, name: &str, val: JsValue, vm: &Vm) {
let si = vm.kernel_core().string_forge().intern(name).0;
let si = vm.kernel_core().perm_interner().intern(name).0;
let shape_id = vm.kernel_core().shape_forge().make_shape(obj.shape_id(), si);
obj.set_shape_id(shape_id);
obj.ensure_hash_props().push(val);
@@ -71,11 +71,11 @@ pub fn regexp_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (pattern, flags) = if args.len() < 2 {
(String::new(), String::new())
} else if args.len() < 3 {
let pat = coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]));
let pat = coercion::to_string(vm.reg(args[1]));
(pat, String::new())
} else {
let pat = coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]));
let fl = coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[2]));
let pat = coercion::to_string(vm.reg(args[1]));
let fl = coercion::to_string(vm.reg(args[2]));
(pat, fl)
};
@@ -109,8 +109,8 @@ pub fn regexp_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
set_prop(&mut obj, "lastIndex", JsValue::int(0), vm);
set_prop(&mut obj, "source", vm.intern(&pattern), vm);
set_prop(&mut obj, "flags", vm.intern(&flags), vm);
set_prop(&mut obj, "source", vm.new_string(&pattern), vm);
set_prop(&mut obj, "flags", vm.new_string(&flags), vm);
set_prop(&mut obj, "global", JsValue::bool(global), vm);
set_prop(&mut obj, "ignoreCase", JsValue::bool(ignore_case), vm);
set_prop(&mut obj, "multiline", JsValue::bool(multi_line), vm);
@@ -155,11 +155,8 @@ pub fn regexp_test(vm: &mut Vm, args: &[u8]) -> NativeResult {
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let haystack = coercion::to_string(
vm.kernel_core().string_forge().as_ref(),
vm.reg(if args.len() > 1 { args[1] } else { args[0] }),
);
let last_index = coercion::to_number(get_prop(re, 0), vm.kernel_core().string_forge().as_ref()) as usize;
let haystack = coercion::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] }));
let last_index = coercion::to_number(get_prop(re, 0)) as usize;
let is_global = get_prop(re, 3).as_bool();
if is_global {
@@ -188,14 +185,11 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult {
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let haystack = coercion::to_string(
vm.kernel_core().string_forge().as_ref(),
vm.reg(if args.len() > 1 { args[1] } else { args[0] }),
);
let haystack = coercion::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] }));
let (last_index, is_global) = {
let re = unsafe { &*re_ptr };
let li = coercion::to_number(get_prop(re, 0), vm.kernel_core().string_forge().as_ref()) as usize;
let li = coercion::to_number(get_prop(re, 0)) as usize;
let g = get_prop(re, 3).as_bool();
(li, g)
};
@@ -216,20 +210,20 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult {
);
let full_match = &haystack[m.start()..m.end()];
let full_si = vm.kernel_core().string_forge().intern(full_match).0;
match_obj.ensure_hash_props().push(JsValue::string(full_si, 0));
let full_val = vm.new_string(full_match);
match_obj.ensure_hash_props().push(full_val);
let captures = regex.captures(&haystack[m.start()..m.end()]);
if let Some(caps) = &captures {
for i in 1..caps.len() {
let cap_str = caps.get(i).map(|cm| cm.as_str()).unwrap_or("");
let si = vm.kernel_core().string_forge().intern(cap_str).0;
match_obj.ensure_hash_props().push(JsValue::string(si, 0));
let cap_val = vm.new_string(cap_str);
match_obj.ensure_hash_props().push(cap_val);
}
}
set_prop(&mut match_obj, "index", JsValue::int(m.start() as i32), vm);
let haystack_val = vm.intern(&haystack);
let haystack_val = vm.new_string(&haystack);
set_prop(&mut match_obj, "input", haystack_val, vm);
set_prop(&mut match_obj, "groups", JsValue::undefined(), vm);
@@ -262,6 +256,5 @@ pub fn regexp_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
vm.lookup_str(val).unwrap_or_default()
};
let result = format!("/{}/{}", source, flags);
let si = vm.kernel_core().string_forge().intern(&result).0;
NativeResult::Ok(JsValue::string(si, 0))
NativeResult::Ok(vm.new_string(&result))
}
+50 -59
View File
@@ -8,20 +8,20 @@ use crate::vm::Vm;
use memchr::memchr;
fn this_string(vm: &Vm, args: &[u8]) -> String {
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[0]))
coercion::to_string(vm.reg(args[0]))
}
pub fn string_from_char_code(vm: &mut Vm, args: &[u8]) -> NativeResult {
let mut out = String::new();
for &arg_reg in args.iter().skip(1) {
let code = coercion::to_uint32(vm.reg(arg_reg), vm.kernel_core().string_forge().as_ref()) & 0xFFFF;
let code = coercion::to_uint32(vm.reg(arg_reg)) & 0xFFFF;
if let Some(ch) = char::from_u32(code) {
out.push(ch);
} else {
out.push('\u{FFFD}');
}
}
NativeResult::Ok(vm.intern(&out))
NativeResult::Ok(vm.new_string(&out))
}
pub fn string_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
@@ -46,11 +46,11 @@ pub fn string_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn string_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = if args.len() > 1 {
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]))
coercion::to_string(vm.reg(args[1]))
} else {
String::new()
};
let str_val = vm.intern(&s);
let str_val = vm.new_string(&s);
let string_proto = vm.session().builtin_world().string_proto.as_ptr() as *mut JsObject;
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
@@ -104,7 +104,7 @@ fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
.alloc(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto), n, vm.epoch().bump()));
unsafe {
for (i, s) in parts.iter().enumerate() {
let sv = vm.intern(s);
let sv = vm.new_string(s);
(*arr).set_prop_at(i, sv);
}
(*arr).set_prop_count(n);
@@ -112,8 +112,8 @@ fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
JsValue::from_js_object(arr)
}
fn as_string(vm: &Vm, val: JsValue) -> String {
coercion::to_string(vm.kernel_core().string_forge().as_ref(), val)
fn as_string(_vm: &Vm, val: JsValue) -> String {
coercion::to_string(val)
}
fn char_len(s: &str) -> usize {
@@ -166,7 +166,7 @@ pub fn string_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let search = as_string(vm, vm.reg(args[1]));
let pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as usize).min(n)
(coercion::to_number(vm.reg(args[2])) as usize).min(n)
} else {
0
};
@@ -197,7 +197,7 @@ pub fn string_includes(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let search = as_string(vm, vm.reg(args[1]));
let pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as usize).min(n)
(coercion::to_number(vm.reg(args[2])) as usize).min(n)
} else {
0
};
@@ -218,16 +218,16 @@ pub fn string_char_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
if s.is_empty() {
return NativeResult::Ok(vm.intern(""));
return NativeResult::Ok(vm.new_string(""));
}
return NativeResult::Ok(vm.intern(&take_chars(&s, 1)));
return NativeResult::Ok(vm.new_string(&take_chars(&s, 1)));
}
let idx = coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as i32;
let idx = coercion::to_number(vm.reg(args[1])) as i32;
if idx < 0 || idx as usize >= char_len(&s) {
return NativeResult::Ok(vm.intern(""));
return NativeResult::Ok(vm.new_string(""));
}
let ch = s.chars().nth(idx as usize).map(|c| c.to_string()).unwrap_or_default();
NativeResult::Ok(vm.intern(&ch))
NativeResult::Ok(vm.new_string(&ch))
}
pub fn string_char_code_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
@@ -238,7 +238,7 @@ pub fn string_char_code_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
return NativeResult::Ok(JsValue::int(s.chars().next().unwrap() as i32));
}
let idx = coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as i32;
let idx = coercion::to_number(vm.reg(args[1])) as i32;
if idx < 0 || idx as usize >= char_len(&s) {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
@@ -247,18 +247,17 @@ pub fn string_char_code_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn string_concat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let mut result = this_string(vm, args);
let sf = vm.kernel_core().string_forge().as_ref();
for &arg_reg in args.iter().skip(1) {
result.push_str(&coercion::to_string(sf, vm.reg(arg_reg)));
result.push_str(&coercion::to_string(vm.reg(arg_reg)));
}
NativeResult::Ok(vm.intern(&result))
NativeResult::Ok(vm.new_string(&result))
}
pub fn string_slice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let n = char_len(&s) as i32;
let start = if args.len() > 1 {
let v = coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as i32;
let v = coercion::to_number(vm.reg(args[1])) as i32;
if v < 0 {
(n + v).max(0)
} else {
@@ -268,7 +267,7 @@ pub fn string_slice(vm: &mut Vm, args: &[u8]) -> NativeResult {
0
};
let end = if args.len() > 2 {
let v = coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as i32;
let v = coercion::to_number(vm.reg(args[2])) as i32;
if v < 0 {
(n + v).max(0)
} else {
@@ -280,23 +279,19 @@ pub fn string_slice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let start = start as usize;
let end = end as usize;
let result = if start < end { char_slice(&s, start, end) } else { "" };
NativeResult::Ok(vm.intern(result))
NativeResult::Ok(vm.new_string(result))
}
pub fn string_substring(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let n = char_len(&s) as i32;
let mut start = if args.len() > 1 {
(coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as i32)
.max(0)
.min(n)
(coercion::to_number(vm.reg(args[1])) as i32).max(0).min(n)
} else {
0
};
let mut end = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as i32)
.max(0)
.min(n)
(coercion::to_number(vm.reg(args[2])) as i32).max(0).min(n)
} else {
n
};
@@ -304,39 +299,39 @@ pub fn string_substring(vm: &mut Vm, args: &[u8]) -> NativeResult {
std::mem::swap(&mut start, &mut end);
}
let result = char_slice(&s, start as usize, end as usize);
NativeResult::Ok(vm.intern(result))
NativeResult::Ok(vm.new_string(result))
}
pub fn string_to_upper_case(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.intern(&s.to_uppercase()))
NativeResult::Ok(vm.new_string(&s.to_uppercase()))
}
pub fn string_to_lower_case(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.intern(&s.to_lowercase()))
NativeResult::Ok(vm.new_string(&s.to_lowercase()))
}
pub fn string_trim(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.intern(s.trim()))
NativeResult::Ok(vm.new_string(s.trim()))
}
pub fn string_repeat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let n = if args.len() > 1 {
(coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as usize).min(10000)
(coercion::to_number(vm.reg(args[1])) as usize).min(10000)
} else {
1
};
NativeResult::Ok(vm.intern(&s.repeat(n)))
NativeResult::Ok(vm.new_string(&s.repeat(n)))
}
pub fn string_pad_start(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let s_len = char_len(&s);
let target = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as usize
coercion::to_number(vm.reg(args[1])) as usize
} else {
s_len
};
@@ -345,21 +340,21 @@ pub fn string_pad_start(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() };
if s_len >= target || pad.is_empty() {
return NativeResult::Ok(vm.intern(&s));
return NativeResult::Ok(vm.new_string(&s));
}
let needed = target - s_len;
let pad_len = char_len(&pad).max(1);
let reps = needed.div_ceil(pad_len);
let mut out = take_chars(&pad.repeat(reps), needed);
out.push_str(&s);
NativeResult::Ok(vm.intern(&out))
NativeResult::Ok(vm.new_string(&out))
}
pub fn string_pad_end(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let s_len = char_len(&s);
let target = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as usize
coercion::to_number(vm.reg(args[1])) as usize
} else {
s_len
};
@@ -368,14 +363,14 @@ pub fn string_pad_end(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() };
if s_len >= target || pad.is_empty() {
return NativeResult::Ok(vm.intern(&s));
return NativeResult::Ok(vm.new_string(&s));
}
let needed = target - s_len;
let pad_len = char_len(&pad).max(1);
let reps = needed.div_ceil(pad_len);
let mut out = s;
out.push_str(&take_chars(&pad.repeat(reps), needed));
NativeResult::Ok(vm.intern(&out))
NativeResult::Ok(vm.new_string(&out))
}
pub fn string_starts_with(vm: &mut Vm, args: &[u8]) -> NativeResult {
@@ -386,7 +381,7 @@ pub fn string_starts_with(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let search = as_string(vm, vm.reg(args[1]));
let pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as usize).min(n)
(coercion::to_number(vm.reg(args[2])) as usize).min(n)
} else {
0
};
@@ -401,7 +396,7 @@ pub fn string_ends_with(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let search = as_string(vm, vm.reg(args[1]));
let end_pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as usize).min(n)
(coercion::to_number(vm.reg(args[2])) as usize).min(n)
} else {
n
};
@@ -416,7 +411,7 @@ pub fn string_split(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let sep_val = vm.reg(args[1]);
let limit = if args.len() > 2 {
coercion::to_number(vm.reg(args[2]), vm.kernel_core().string_forge().as_ref()) as usize
coercion::to_number(vm.reg(args[2])) as usize
} else {
usize::MAX
};
@@ -450,7 +445,7 @@ pub fn string_split(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn string_replace(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
return NativeResult::Ok(vm.intern(&s));
return NativeResult::Ok(vm.new_string(&s));
}
let pattern_val = vm.reg(args[1]);
let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() };
@@ -460,17 +455,17 @@ pub fn string_replace(vm: &mut Vm, args: &[u8]) -> NativeResult {
let re = unsafe { &*re_ptr };
let fn_ptr = match re.native_fn() {
Some(p) => p,
None => return NativeResult::Ok(vm.intern(&s)),
None => return NativeResult::Ok(vm.new_string(&s)),
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let result = regex.replace_all(&s, replacement.as_str()).to_string();
return NativeResult::Ok(vm.intern(&result));
return NativeResult::Ok(vm.new_string(&result));
}
let pattern = as_string(vm, pattern_val);
let result = s.replacen(&pattern, &replacement, 1);
NativeResult::Ok(vm.intern(&result))
NativeResult::Ok(vm.new_string(&result))
}
pub fn string_match_fn(vm: &mut Vm, args: &[u8]) -> NativeResult {
@@ -531,21 +526,17 @@ pub fn string_search(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn string_trim_start(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.intern(s.trim_start()))
NativeResult::Ok(vm.new_string(s.trim_start()))
}
pub fn string_trim_end(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.intern(s.trim_end()))
NativeResult::Ok(vm.new_string(s.trim_end()))
}
pub fn string_code_point_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let pos = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel_core().string_forge().as_ref()) as usize
} else {
0
};
let pos = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) as usize } else { 0 };
let chars: Vec<char> = s.chars().collect();
if pos >= chars.len() {
return NativeResult::Ok(JsValue::undefined());
@@ -571,7 +562,7 @@ pub fn string_normalize(vm: &mut Vm, args: &[u8]) -> NativeResult {
"NFKD" => s.nfkd().collect(),
_ => s.nfc().collect(),
};
NativeResult::Ok(vm.intern(&result))
NativeResult::Ok(vm.new_string(&result))
}
pub fn string_match_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
@@ -598,7 +589,7 @@ pub fn string_match_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn string_replace_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
return NativeResult::Ok(vm.intern(&s));
return NativeResult::Ok(vm.new_string(&s));
}
let pattern_val = vm.reg(args[1]);
let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() };
@@ -607,14 +598,14 @@ pub fn string_replace_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
let re = unsafe { &*re_ptr };
let fn_ptr = match re.native_fn() {
Some(p) => p,
None => return NativeResult::Ok(vm.intern(&s)),
None => return NativeResult::Ok(vm.new_string(&s)),
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let result = regex.replace_all(&s, replacement.as_str()).to_string();
return NativeResult::Ok(vm.intern(&result));
return NativeResult::Ok(vm.new_string(&result));
}
let pattern = as_string(vm, pattern_val);
let result = s.replace(&pattern, &replacement);
NativeResult::Ok(vm.intern(&result))
NativeResult::Ok(vm.new_string(&result))
}
+11 -11
View File
@@ -28,7 +28,7 @@ pub fn symbol_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let description = if args.len() > 1 {
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]))
coercion::to_string(vm.reg(args[1]))
} else {
String::new()
};
@@ -51,13 +51,12 @@ pub fn symbol_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let idx = this_val.as_symbol_index();
let desc = vm.symbol_descriptions.get(idx as usize).cloned().unwrap_or_default();
let result = format!("Symbol({})", desc);
let si = vm.kernel_core().string_forge().intern(&result).0;
NativeResult::Ok(JsValue::string(si, 0))
NativeResult::Ok(vm.new_string(&result))
}
pub fn symbol_for(vm: &mut Vm, args: &[u8]) -> NativeResult {
let key = if args.len() > 1 {
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]))
coercion::to_string(vm.reg(args[1]))
} else {
"undefined".to_string()
};
@@ -79,12 +78,13 @@ pub fn symbol_key_for(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let idx = sym.as_symbol_index();
for (key, &registered_idx) in &vm.symbol_registry {
if registered_idx == idx {
let si = vm.kernel_core().string_forge().intern(key).0;
return NativeResult::Ok(JsValue::string(si, 0));
}
let found = vm
.symbol_registry
.iter()
.find(|(_, &registered_idx)| registered_idx == idx)
.map(|(key, _)| key.clone());
match found {
Some(key) => NativeResult::Ok(vm.new_string(&key)),
None => NativeResult::Ok(JsValue::undefined()),
}
NativeResult::Ok(JsValue::undefined())
}
+7 -7
View File
@@ -33,7 +33,7 @@ fn range_error(vm: &mut Vm, msg: &str) -> JsValue {
}
fn to_index(vm: &mut Vm, value: JsValue, msg: &str) -> Result<usize, JsValue> {
let n = coercion::to_number(value, vm.kernel_core().string_forge().as_ref());
let n = coercion::to_number(value);
if n.is_nan() {
return Ok(0);
}
@@ -43,8 +43,8 @@ fn to_index(vm: &mut Vm, value: JsValue, msg: &str) -> Result<usize, JsValue> {
Ok(n.trunc() as usize)
}
fn normalize_index(vm: &mut Vm, value: JsValue, len: usize) -> usize {
let n = coercion::to_number(value, vm.kernel_core().string_forge().as_ref());
fn normalize_index(_vm: &mut Vm, value: JsValue, len: usize) -> usize {
let n = coercion::to_number(value);
if n.is_nan() {
return 0;
}
@@ -57,7 +57,7 @@ fn normalize_index(vm: &mut Vm, value: JsValue, len: usize) -> usize {
}
fn set_named_prop(vm: &mut Vm, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) {
let si = vm.kernel_core().string_forge().intern(name).0;
let si = vm.kernel_core().perm_interner().intern(name).0;
let _ = vm.define_data_property(obj, si, value, attributes);
}
@@ -236,8 +236,8 @@ fn read_element(kind: TypedArrayKind, bytes: &[u8], offset: usize) -> JsValue {
}
}
fn numeric_value(vm: &mut Vm, value: JsValue) -> f64 {
coercion::to_number(value, vm.kernel_core().string_forge().as_ref())
fn numeric_value(_vm: &mut Vm, value: JsValue) -> f64 {
coercion::to_number(value)
}
fn write_element(kind: TypedArrayKind, bytes: &mut [u8], offset: usize, value: f64) {
@@ -477,5 +477,5 @@ pub fn typed_array_set(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn typed_array_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
native_try!(get_typed_array_data(vm, this_val));
NativeResult::Ok(vm.intern("[object TypedArray]"))
NativeResult::Ok(vm.new_string("[object TypedArray]"))
}
+50 -25
View File
@@ -1,10 +1,32 @@
use crate::vm::Vm;
use oxide_kernel::string_forge::StringForge;
use oxide_types::object::JsObject;
use oxide_types::shape::EMPTY_SHAPE_ID;
use oxide_types::value::JsValue;
pub fn to_number(val: JsValue, string_forge: &StringForge) -> f64 {
/// Borrow a string value's content. Caller must hold a valid string `JsValue`.
///
/// # Safety
/// `val` must be a string `JsValue` whose `JsString` pointer is alive (session or
/// permanent string) — the same contract as object-pointer derefs in the VM.
#[inline]
unsafe fn string_data(val: JsValue) -> &'static str {
(*val.as_string_ptr()).as_str()
}
/// Content equality of two string values: pointer identity → hash+len reject →
/// byte compare. Replaces the old interner-index compare.
#[inline]
fn string_value_eq(a: JsValue, b: JsValue) -> bool {
if a.as_string_ptr() == b.as_string_ptr() {
return true;
}
// SAFETY: both are string JsValues; their JsString pointers are valid.
let sa = unsafe { &*a.as_string_ptr() };
let sb = unsafe { &*b.as_string_ptr() };
sa.hash == sb.hash && sa.data == sb.data
}
pub fn to_number(val: JsValue) -> f64 {
if val.is_int() {
return val.as_int() as f64;
}
@@ -21,7 +43,8 @@ pub fn to_number(val: JsValue, string_forge: &StringForge) -> f64 {
return f64::NAN;
}
if val.is_string() {
let s = string_forge.lookup(val.as_string_index()).unwrap_or_default();
// SAFETY: val is a string value.
let s = unsafe { string_data(val) };
return s.parse::<f64>().unwrap_or(f64::NAN);
}
if val.is_object() {
@@ -30,16 +53,16 @@ pub fn to_number(val: JsValue, string_forge: &StringForge) -> f64 {
f64::NAN
}
pub fn to_uint32(val: JsValue, string_forge: &StringForge) -> u32 {
let n = to_number(val, string_forge);
pub fn to_uint32(val: JsValue) -> u32 {
let n = to_number(val);
if n == 0.0 || !n.is_finite() {
return 0;
}
n.trunc().rem_euclid(4_294_967_296.0) as u32
}
pub fn to_int32(val: JsValue, string_forge: &StringForge) -> i32 {
let int = to_uint32(val, string_forge);
pub fn to_int32(val: JsValue) -> i32 {
let int = to_uint32(val);
if int > i32::MAX as u32 {
(int as i64 - 4_294_967_296i64) as i32
} else {
@@ -47,7 +70,7 @@ pub fn to_int32(val: JsValue, string_forge: &StringForge) -> i32 {
}
}
pub fn to_string(string_forge: &StringForge, val: JsValue) -> String {
pub fn to_string(val: JsValue) -> String {
if val.is_int() {
return val.as_int().to_string();
}
@@ -79,7 +102,8 @@ pub fn to_string(string_forge: &StringForge, val: JsValue) -> String {
return "undefined".to_string();
}
if val.is_string() {
return string_forge.lookup(val.as_string_index()).unwrap_or_default();
// SAFETY: val is a string value.
return unsafe { string_data(val) }.to_string();
}
if val.is_object() {
return "[object]".to_string();
@@ -87,7 +111,7 @@ pub fn to_string(string_forge: &StringForge, val: JsValue) -> String {
String::new()
}
pub fn to_boolean(val: JsValue, string_forge: &StringForge) -> bool {
pub fn to_boolean(val: JsValue) -> bool {
if val.is_undefined() || val.is_null() {
return false;
}
@@ -102,8 +126,8 @@ pub fn to_boolean(val: JsValue, string_forge: &StringForge) -> bool {
return !(d == 0.0 || d == -0.0 || d.is_nan());
}
if val.is_string() {
let s = string_forge.lookup(val.as_string_index()).unwrap_or_default();
return !s.is_empty();
// SAFETY: val is a string value.
return !unsafe { (*val.as_string_ptr()).is_empty() };
}
if val.is_object() {
return true;
@@ -114,7 +138,7 @@ pub fn to_boolean(val: JsValue, string_forge: &StringForge) -> bool {
false
}
pub fn abstract_eq(lhs: JsValue, rhs: JsValue, string_forge: &StringForge) -> bool {
pub fn abstract_eq(lhs: JsValue, rhs: JsValue) -> bool {
if lhs.is_null() && rhs.is_undefined() {
return true;
}
@@ -122,7 +146,7 @@ pub fn abstract_eq(lhs: JsValue, rhs: JsValue, string_forge: &StringForge) -> bo
return true;
}
if lhs.is_string() && rhs.is_string() {
return lhs == rhs;
return string_value_eq(lhs, rhs);
}
if lhs.is_int() && rhs.is_int() {
return lhs.as_int() == rhs.as_int();
@@ -140,16 +164,16 @@ pub fn abstract_eq(lhs: JsValue, rhs: JsValue, string_forge: &StringForge) -> bo
return true;
}
if (lhs.is_int() || lhs.is_double()) && (rhs.is_int() || rhs.is_double()) {
return strict_double_eq(to_number(lhs, string_forge), to_number(rhs, string_forge));
return strict_double_eq(to_number(lhs), to_number(rhs));
}
if (lhs.is_int() || lhs.is_double()) && rhs.is_bool() {
return to_number(lhs, string_forge) == to_number(rhs, string_forge);
return to_number(lhs) == to_number(rhs);
}
if lhs.is_bool() && (rhs.is_int() || rhs.is_double()) {
return to_number(lhs, string_forge) == to_number(rhs, string_forge);
return to_number(lhs) == to_number(rhs);
}
if lhs.is_bool() || rhs.is_bool() {
return to_number(lhs, string_forge) == to_number(rhs, string_forge);
return to_number(lhs) == to_number(rhs);
}
if lhs.is_null() || rhs.is_null() {
return false;
@@ -177,7 +201,7 @@ pub fn strict_eq(lhs: JsValue, rhs: JsValue) -> bool {
return true;
}
if lhs.is_string() && rhs.is_string() {
return lhs == rhs;
return string_value_eq(lhs, rhs);
}
if lhs.is_object() && rhs.is_object() {
return lhs.as_ptr() == rhs.as_ptr();
@@ -195,14 +219,15 @@ fn strict_double_eq(a: f64, b: f64) -> bool {
a == b
}
pub fn relational_compare(string_forge: &StringForge, lhs: JsValue, rhs: JsValue) -> Option<bool> {
pub fn relational_compare(lhs: JsValue, rhs: JsValue) -> Option<bool> {
if lhs.is_string() && rhs.is_string() {
let ls = string_forge.lookup(lhs.as_string_index()).unwrap_or_default();
let rs = string_forge.lookup(rhs.as_string_index()).unwrap_or_default();
// SAFETY: both are string values.
let ls = unsafe { string_data(lhs) };
let rs = unsafe { string_data(rhs) };
return Some(ls < rs);
}
let l = to_number(lhs, string_forge);
let r = to_number(rhs, string_forge);
let l = to_number(lhs);
let r = to_number(rhs);
if l.is_nan() || r.is_nan() {
return None;
}
@@ -264,7 +289,7 @@ pub fn same_value(lhs: JsValue, rhs: JsValue) -> bool {
return true;
}
if lhs.is_string() && rhs.is_string() {
return lhs == rhs;
return string_value_eq(lhs, rhs);
}
if lhs.is_object() && rhs.is_object() {
return lhs.as_ptr() == rhs.as_ptr();
+5 -5
View File
@@ -8,13 +8,13 @@ impl Vm {
let lhs = self.coerce_primitive_bounded(self.regs[a], false)?;
let rhs = self.coerce_primitive_bounded(self.regs[b], false)?;
if lhs.is_string() || rhs.is_string() {
let ls = coercion::to_string(self.kernel_core.string_forge().as_ref(), lhs);
let rs = coercion::to_string(self.kernel_core.string_forge().as_ref(), rhs);
let ls = coercion::to_string(lhs);
let rs = coercion::to_string(rhs);
let concat = format!("{ls}{rs}");
self.regs[rd] = self.intern(&concat);
self.regs[rd] = self.new_string(&concat);
} else {
let ln = coercion::to_number(lhs, self.kernel_core.string_forge().as_ref());
let rn = coercion::to_number(rhs, self.kernel_core.string_forge().as_ref());
let ln = coercion::to_number(lhs);
let rn = coercion::to_number(rhs);
self.regs[rd] = JsValue::float(ln + rn);
}
Ok(())
+2 -4
View File
@@ -45,10 +45,8 @@ impl Vm {
(err_val, self.thrown_error_kind(err_val))
} else {
let msg = if err_val.is_string() {
self.kernel_core
.string_forge()
.lookup(err_val.as_string_index())
.unwrap_or_else(|| format!("{err_val}"))
// SAFETY: err_val is a string value.
unsafe { (*err_val.as_string_ptr()).data.clone() }
} else {
format!("{err_val}")
};
+6 -6
View File
@@ -5,37 +5,37 @@ use oxide_types::value::JsValue;
impl Vm {
#[inline(always)]
pub(crate) fn dispatch_eq(&mut self, rd: usize, a: usize, b: usize) {
let eq = coercion::abstract_eq(self.regs[a], self.regs[b], self.kernel_core.string_forge().as_ref());
let eq = coercion::abstract_eq(self.regs[a], self.regs[b]);
self.regs[rd] = JsValue::bool(eq);
}
#[inline(always)]
pub(crate) fn dispatch_neq(&mut self, rd: usize, a: usize, b: usize) {
let ne = !coercion::abstract_eq(self.regs[a], self.regs[b], self.kernel_core.string_forge().as_ref());
let ne = !coercion::abstract_eq(self.regs[a], self.regs[b]);
self.regs[rd] = JsValue::bool(ne);
}
#[inline(always)]
pub(crate) fn dispatch_lt(&mut self, rd: usize, a: usize, b: usize) {
let rel = coercion::relational_compare(self.kernel_core.string_forge().as_ref(), self.regs[a], self.regs[b]);
let rel = coercion::relational_compare(self.regs[a], self.regs[b]);
self.regs[rd] = JsValue::bool(rel.unwrap_or(false));
}
#[inline(always)]
pub(crate) fn dispatch_gt(&mut self, rd: usize, a: usize, b: usize) {
let rel = coercion::relational_compare(self.kernel_core.string_forge().as_ref(), self.regs[b], self.regs[a]);
let rel = coercion::relational_compare(self.regs[b], self.regs[a]);
self.regs[rd] = JsValue::bool(rel.unwrap_or(false));
}
#[inline(always)]
pub(crate) fn dispatch_lte(&mut self, rd: usize, a: usize, b: usize) {
let rel = coercion::relational_compare(self.kernel_core.string_forge().as_ref(), self.regs[b], self.regs[a]);
let rel = coercion::relational_compare(self.regs[b], self.regs[a]);
self.regs[rd] = JsValue::bool(!rel.unwrap_or(true));
}
#[inline(always)]
pub(crate) fn dispatch_gte(&mut self, rd: usize, a: usize, b: usize) {
let rel = coercion::relational_compare(self.kernel_core.string_forge().as_ref(), self.regs[a], self.regs[b]);
let rel = coercion::relational_compare(self.regs[a], self.regs[b]);
self.regs[rd] = JsValue::bool(!rel.unwrap_or(true));
}
+3 -3
View File
@@ -6,7 +6,7 @@ impl Vm {
#[inline(always)]
pub(crate) fn dispatch_and(&mut self, rd: usize, a: usize, b: usize) {
let val = self.regs[a];
if coercion::to_boolean(val, self.kernel_core.string_forge().as_ref()) {
if coercion::to_boolean(val) {
self.regs[rd] = self.regs[b];
} else {
self.regs[rd] = val;
@@ -16,7 +16,7 @@ impl Vm {
#[inline(always)]
pub(crate) fn dispatch_or(&mut self, rd: usize, a: usize, b: usize) {
let val = self.regs[a];
if coercion::to_boolean(val, self.kernel_core.string_forge().as_ref()) {
if coercion::to_boolean(val) {
self.regs[rd] = val;
} else {
self.regs[rd] = self.regs[b];
@@ -31,7 +31,7 @@ impl Vm {
#[inline(always)]
pub(crate) fn dispatch_not(&mut self, rd: usize, a: usize) {
let b = !coercion::to_boolean(self.regs[a], self.kernel_core.string_forge().as_ref());
let b = !coercion::to_boolean(self.regs[a]);
self.regs[rd] = JsValue::bool(b);
}
}
+5 -5
View File
@@ -110,12 +110,12 @@ impl Vm {
let prop_val = self.coerce_primitive_bounded(prop_raw, false)?;
let rhs = self.coerce_primitive_bounded(self.regs[a], false)?;
let new_val = if prop_val.is_string() || rhs.is_string() {
let ls = coercion::to_string(self.kernel_core.string_forge().as_ref(), prop_val);
let rs = coercion::to_string(self.kernel_core.string_forge().as_ref(), rhs);
self.intern(&format!("{ls}{rs}"))
let ls = coercion::to_string(prop_val);
let rs = coercion::to_string(rhs);
self.new_string(&format!("{ls}{rs}"))
} else {
let ln = coercion::to_number(prop_val, self.kernel_core.string_forge().as_ref());
let rn = coercion::to_number(rhs, self.kernel_core.string_forge().as_ref());
let ln = coercion::to_number(prop_val);
let rn = coercion::to_number(rhs);
JsValue::float(ln + rn)
};
let obj = unsafe { &mut *obj_ptr };
+1 -1
View File
@@ -37,6 +37,6 @@ impl Vm {
} else {
"undefined"
};
self.regs[rd] = self.intern(result);
self.regs[rd] = self.new_string(result);
}
}
+6 -11
View File
@@ -61,15 +61,10 @@ impl Vm {
fn primitive_property_get(&mut self, val: JsValue, prop_name_si: u32) -> Result<Option<JsValue>, String> {
if val.is_string() {
let length_si = self.kernel_core.string_forge().intern("length").0;
let length_si = self.kernel_core.perm_interner().intern("length").0;
if prop_name_si == length_si {
let len = self
.kernel_core
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
.encode_utf16()
.count();
// SAFETY: val is a string value.
let len = unsafe { (*val.as_string_ptr()).data.encode_utf16().count() };
return Ok(Some(JsValue::int(len as i32)));
}
let proto_ptr = self.session.builtin_world().string_proto.as_ptr() as *mut JsObject;
@@ -245,7 +240,7 @@ impl Vm {
};
let prop_name_si = self.property_key_si(self.regs[b]);
if self.kernel_core.string_forge().lookup(prop_name_si).as_deref() == Some("__proto__") {
if self.kernel_core.perm_interner().lookup(prop_name_si) == Some("__proto__") {
let proto_value = self.promote_if_needed_for_write_ptr(obj_ptr, self.regs[a]);
if self.is_object_prototype(obj_ptr) && !proto_value.is_null() {
self.raise_type_error("Object.prototype.__proto__ is immutable")?;
@@ -328,7 +323,7 @@ impl Vm {
return Ok(());
};
let prop_name_si = self.property_key_si(self.regs[b]);
if self.kernel_core.string_forge().lookup(prop_name_si).as_deref() == Some("__proto__") {
if self.kernel_core.perm_interner().lookup(prop_name_si) == Some("__proto__") {
let proto_value = self.promote_if_needed_for_write_ptr(obj_ptr, self.regs[a]);
if self.is_object_prototype(obj_ptr) && !proto_value.is_null() {
self.raise_type_error("Object.prototype.__proto__ is immutable")?;
@@ -366,7 +361,7 @@ impl Vm {
return Ok(());
};
let prop_name_si = self.property_key_si(self.regs[a]);
if self.kernel_core.string_forge().lookup(prop_name_si).as_deref() == Some("__proto__") {
if self.kernel_core.perm_interner().lookup(prop_name_si) == Some("__proto__") {
let proto_value = self.promote_if_needed_for_write_ptr(obj_ptr, self.regs[b]);
if self.is_object_prototype(obj_ptr) && !proto_value.is_null() {
self.raise_type_error("Object.prototype.__proto__ is immutable")?;
+1 -1
View File
@@ -609,7 +609,7 @@ mod tests {
(*obj).set_prop_at(0, JsValue::int(42));
}
let old_ptr = vm.promote_object(obj);
let key = vm.kernel_core.string_forge().intern("gcRoot").0;
let key = vm.kernel_core.perm_interner().intern("gcRoot").0;
let global_ptr = vm.session.global_object().as_ptr() as *mut JsObject;
unsafe {
let global = &mut *global_ptr;
+39 -36
View File
@@ -15,7 +15,7 @@ use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::error::{JsError, JsErrorKind};
use oxide_types::mem::{Epoch, P};
use oxide_types::object::{JsObject, NativeFnPtr};
use oxide_types::object::{JsObject, JsString, NativeFnPtr};
use oxide_types::value::JsValue;
pub(crate) const MAX_PROTO_CHAIN_DEPTH: usize = 1024;
@@ -151,7 +151,7 @@ pub struct Vm {
pub for_in_iters: Vec<*mut ForInIter<'static>>,
pub(crate) kernel_core: Arc<KernelCore>,
pub(crate) session: KernelSession,
pub(crate) interned_strings: Vec<u32>,
pub(crate) session_string_ptrs: Vec<*mut JsString>,
pub epoch: Epoch,
pub(crate) session_epoch: bumpalo::Bump,
pub(crate) session_gc: SessionGc,
@@ -207,7 +207,7 @@ impl Vm {
let method_names = if prefer_string { ["toString", "valueOf"] } else { ["valueOf", "toString"] };
for method_name in method_names {
let method_si = self.kernel_core.string_forge().intern(method_name).0;
let method_si = self.kernel_core.perm_interner().intern(method_name).0;
let method = {
let obj = unsafe { &*obj_ptr };
self.ordinary_get(obj, method_si, value)?
@@ -234,7 +234,7 @@ impl Vm {
pub(crate) fn coerce_number_bounded(&mut self, value: JsValue) -> Result<f64, String> {
let primitive = self.coerce_primitive_bounded(value, false)?;
Ok(coercion::to_number(primitive, self.kernel_core.string_forge().as_ref()))
Ok(coercion::to_number(primitive))
}
pub(crate) fn coerce_int32_bounded(&mut self, value: JsValue) -> Result<i32, String> {
@@ -471,14 +471,15 @@ impl Vm {
if !val.is_string() {
return None;
}
self.kernel_core.string_forge().lookup(val.as_string_index())
// SAFETY: val is a string value; its JsString pointer is alive for its lifetime.
Some(unsafe { (*val.as_string_ptr()).data.clone() })
}
pub(crate) fn thrown_error_kind(&self, val: JsValue) -> &'static str {
if !val.is_object() {
return "Error";
}
let name_si = self.kernel_core.string_forge().intern("name").0;
let name_si = self.kernel_core.perm_interner().intern("name").0;
let obj = unsafe { &*val.as_js_object_ptr() };
let Some(name_val) = self.resolve_property(obj, name_si) else {
return "Error";
@@ -500,14 +501,16 @@ impl Vm {
pub(crate) fn property_key_si(&mut self, val: JsValue) -> u32 {
if val.is_string() {
return val.as_string_index();
// SAFETY: val is a string value; bridge its content to a permanent key id.
let s = unsafe { &(*val.as_string_ptr()).data };
return self.kernel_core.perm_interner().intern(s).0;
}
let key = coercion::to_string(self.kernel_core.string_forge().as_ref(), val);
self.kernel_core.string_forge().intern(&key).0
let key = coercion::to_string(val);
self.kernel_core.perm_interner().intern(&key).0
}
pub(crate) fn array_index_from_property_key(&self, prop_name_si: u32) -> Option<u32> {
let key = self.kernel_core.string_forge().lookup(prop_name_si)?;
let key = self.kernel_core.perm_interner().lookup(prop_name_si)?;
if key.is_empty() || (key.len() > 1 && key.starts_with('0')) {
return None;
}
@@ -515,7 +518,7 @@ impl Vm {
}
pub(crate) fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
let length_si = self.kernel_core.string_forge().intern("length").0;
let length_si = self.kernel_core.perm_interner().intern("length").0;
if obj.is_array() && prop_name_si == length_si {
return Some(JsValue::int(obj.prop_count() as i32));
}
@@ -553,7 +556,7 @@ impl Vm {
}
pub(crate) fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32> {
let length_si = self.kernel_core.string_forge().intern("length").0;
let length_si = self.kernel_core.perm_interner().intern("length").0;
if obj.is_array() && prop_name_si == length_si {
return None;
}
@@ -664,7 +667,7 @@ impl Vm {
let function_name = self.sub_modules[sub_idx]
.function_name
.as_deref()
.map(|name| self.kernel_core.string_forge().intern(name).0)
.map(|name| self.kernel_core.perm_interner().intern(name).0)
.unwrap_or(0);
self.frames.push(CallFrame {
@@ -684,7 +687,7 @@ impl Vm {
self.bytecode = sub_bytecode;
self.constants = converted_sub_constants;
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map.clone() {
let si = self.kernel_core.string_forge().intern(name.as_str()).0;
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
let global = self.session.global_object();
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
self.regs[*reg as usize] = global.get_prop_at(pos);
@@ -826,7 +829,7 @@ impl Vm {
}
OpCode::JMP_IF_FALSE => {
let cond = coercion::to_boolean(self.regs[rd], self.kernel_core.string_forge().as_ref());
let cond = coercion::to_boolean(self.regs[rd]);
if !cond {
let offset = opcode::offset16(instr) as isize;
self.pc = ((self.pc as isize) + offset - 1) as usize;
@@ -834,7 +837,7 @@ impl Vm {
}
OpCode::JMP_IF_TRUE => {
let cond = coercion::to_boolean(self.regs[rd], self.kernel_core.string_forge().as_ref());
let cond = coercion::to_boolean(self.regs[rd]);
if cond {
let offset = opcode::offset16(instr) as isize;
self.pc = ((self.pc as isize) + offset - 1) as usize;
@@ -987,7 +990,7 @@ impl Vm {
function_name: self.sub_modules[sub_idx]
.function_name
.as_deref()
.map(|name| self.kernel_core.string_forge().intern(name).0)
.map(|name| self.kernel_core.perm_interner().intern(name).0)
.unwrap_or(0),
caller_reg_limit,
saved_regs,
@@ -1003,7 +1006,7 @@ impl Vm {
self.bytecode = sub_bytecode;
self.constants = converted_sub_constants;
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map {
let si = self.kernel_core.string_forge().intern(name.as_str()).0;
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
let global = self.session.global_object();
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
self.regs[*reg as usize] = global.get_prop_at(pos);
@@ -1107,13 +1110,13 @@ impl Vm {
let lhs = self.coerce_primitive_bounded(self.regs[rd], false)?;
let rhs = self.coerce_primitive_bounded(self.regs[a], false)?;
if lhs.is_string() || rhs.is_string() {
let ls = coercion::to_string(self.kernel_core.string_forge().as_ref(), lhs);
let rs = coercion::to_string(self.kernel_core.string_forge().as_ref(), rhs);
let ls = coercion::to_string(lhs);
let rs = coercion::to_string(rhs);
let concat = format!("{ls}{rs}");
self.regs[rd] = self.intern(&concat);
self.regs[rd] = self.new_string(&concat);
} else {
let ln = coercion::to_number(lhs, self.kernel_core.string_forge().as_ref());
let rn = coercion::to_number(rhs, self.kernel_core.string_forge().as_ref());
let ln = coercion::to_number(lhs);
let rn = coercion::to_number(rhs);
self.regs[rd] = JsValue::float(ln + rn);
}
}
@@ -1357,7 +1360,7 @@ mod tests {
if !this_val.is_object() {
return NativeResult::Ok(JsValue::undefined());
}
let marker_si = vm.kernel_core.string_forge().intern("marker").0;
let marker_si = vm.kernel_core.perm_interner().intern("marker").0;
let obj = unsafe { &*this_val.as_js_object_ptr() };
NativeResult::Ok(vm.resolve_property(obj, marker_si).unwrap_or(JsValue::undefined()))
}
@@ -1368,7 +1371,7 @@ mod tests {
if !this_val.is_object() {
return NativeResult::Ok(JsValue::undefined());
}
let marker_si = vm.kernel_core.string_forge().intern("marker").0;
let marker_si = vm.kernel_core.perm_interner().intern("marker").0;
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
vm.set_or_create_prop_value(obj, marker_si, value);
NativeResult::Ok(JsValue::undefined())
@@ -1399,7 +1402,7 @@ mod tests {
}
fn add_accessor(vm: &mut Vm, obj_val: JsValue, name: &str, get: JsValue, set: JsValue) {
let si = vm.kernel_core.string_forge().intern(name).0;
let si = vm.kernel_core.perm_interner().intern(name).0;
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
let shape_id = vm.kernel_core.shape_forge().make_shape(obj.shape_id(), si);
obj.set_shape_id(shape_id);
@@ -1409,7 +1412,7 @@ mod tests {
}
fn set_data(vm: &mut Vm, obj_val: JsValue, name: &str, val: JsValue) {
let si = vm.kernel_core.string_forge().intern(name).0;
let si = vm.kernel_core.perm_interner().intern(name).0;
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
vm.set_or_create_prop_value(obj, si, val);
}
@@ -1568,7 +1571,7 @@ mod tests {
let getter = native_function(&mut vm, native_return_7);
add_accessor(&mut vm, obj_val, "x", getter, JsValue::undefined());
let x_si = vm.kernel_core.string_forge().intern("x").0;
let x_si = vm.kernel_core.perm_interner().intern("x").0;
let obj = unsafe { &*obj_val.as_js_object_ptr() };
let value = vm.ordinary_get(obj, x_si, obj_val).expect("getter");
assert_eq!(value, JsValue::int(7));
@@ -1581,11 +1584,11 @@ mod tests {
let setter = native_function(&mut vm, native_set_marker);
add_accessor(&mut vm, obj_val, "x", JsValue::undefined(), setter);
let x_si = vm.kernel_core.string_forge().intern("x").0;
let x_si = vm.kernel_core.perm_interner().intern("x").0;
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
vm.ordinary_set(obj, x_si, JsValue::int(9), obj_val).expect("setter");
let marker_si = vm.kernel_core.string_forge().intern("marker").0;
let marker_si = vm.kernel_core.perm_interner().intern("marker").0;
let obj = unsafe { &*obj_val.as_js_object_ptr() };
assert_eq!(vm.resolve_property(obj, marker_si), Some(JsValue::int(9)));
}
@@ -1602,7 +1605,7 @@ mod tests {
(*child_val.as_js_object_ptr()).set_proto(proto_val).expect("proto");
}
let x_si = vm.kernel_core.string_forge().intern("x").0;
let x_si = vm.kernel_core.perm_interner().intern("x").0;
let child = unsafe { &*child_val.as_js_object_ptr() };
let value = vm.ordinary_get(child, x_si, child_val).expect("getter");
assert_eq!(value, JsValue::int(42));
@@ -1619,11 +1622,11 @@ mod tests {
(*child_val.as_js_object_ptr()).set_proto(proto_val).expect("proto");
}
let x_si = vm.kernel_core.string_forge().intern("x").0;
let x_si = vm.kernel_core.perm_interner().intern("x").0;
let child = unsafe { &mut *child_val.as_js_object_ptr() };
vm.ordinary_set(child, x_si, JsValue::int(12), child_val).expect("setter");
let marker_si = vm.kernel_core.string_forge().intern("marker").0;
let marker_si = vm.kernel_core.perm_interner().intern("marker").0;
let child = unsafe { &*child_val.as_js_object_ptr() };
let proto = unsafe { &*proto_val.as_js_object_ptr() };
assert_eq!(vm.resolve_property(child, marker_si), Some(JsValue::int(12)));
@@ -1643,11 +1646,11 @@ mod tests {
(*child_val.as_js_object_ptr()).set_proto(proto_val).expect("proto");
}
let x_si = vm.kernel_core.string_forge().intern("x").0;
let x_si = vm.kernel_core.perm_interner().intern("x").0;
let child = unsafe { &mut *child_val.as_js_object_ptr() };
vm.ordinary_set(child, x_si, JsValue::int(15), child_val).expect("setter");
let marker_si = vm.kernel_core.string_forge().intern("marker").0;
let marker_si = vm.kernel_core.perm_interner().intern("marker").0;
let child = unsafe { &*child_val.as_js_object_ptr() };
assert_eq!(vm.resolve_property(child, marker_si), Some(JsValue::int(15)));
}
@@ -1658,7 +1661,7 @@ mod tests {
let obj_val = plain_object(&mut vm);
set_data(&mut vm, obj_val, "x", JsValue::int(1));
let x_si = vm.kernel_core.string_forge().intern("x").0;
let x_si = vm.kernel_core.perm_interner().intern("x").0;
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
assert!(!obj.has_prop_meta());
assert_eq!(vm.ordinary_get(obj, x_si, obj_val).expect("get"), JsValue::int(1));
+21 -25
View File
@@ -42,7 +42,7 @@ impl Vm {
JsValue::from_js_object(proto_ptr),
));
let proto_si = self.kernel_core.string_forge().intern("prototype").0;
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
if let Some(proto_val) = self.resolve_property(ctor_obj, proto_si) {
if proto_val.is_object() {
let new_obj_mut = unsafe { &mut *new_obj };
@@ -123,7 +123,7 @@ impl Vm {
function_name: self.sub_modules[sub_idx]
.function_name
.as_deref()
.map(|name| self.kernel_core.string_forge().intern(name).0)
.map(|name| self.kernel_core.perm_interner().intern(name).0)
.unwrap_or(0),
caller_reg_limit,
saved_regs,
@@ -140,7 +140,7 @@ impl Vm {
self.constants = converted_sub_constants;
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map {
let si = self.kernel_core.string_forge().intern(name.as_str()).0;
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
let global = self.session.global_object();
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
self.regs[*reg as usize] = global.get_prop_at(pos);
@@ -173,10 +173,8 @@ impl Vm {
let reg = (seg & 0x7F) as u8;
let val = self.regs[reg as usize];
let s = if val.is_string() {
self.kernel_core
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
// SAFETY: val is a string value.
unsafe { (*val.as_string_ptr()).data.clone() }
} else {
format!("{}", val)
};
@@ -186,18 +184,14 @@ impl Vm {
if const_idx < self.constants.len() {
let val = self.constants[const_idx];
if val.is_string() {
let s = self
.kernel_core
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default();
// SAFETY: val is a string value.
let s = unsafe { (*val.as_string_ptr()).data.clone() };
result.push_str(&s);
}
}
}
}
let si = self.kernel_core.string_forge().intern(&result).0;
self.regs[rd] = JsValue::string(si, 0);
self.regs[rd] = self.new_string(&result);
}
pub(crate) fn dispatch_instanceof(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
@@ -213,7 +207,7 @@ impl Vm {
}
let rhs_obj = unsafe { &*rhs_val.as_js_object_ptr() };
let proto_si = self.kernel_core.string_forge().intern("prototype").0;
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
let ctor_proto = self.resolve_property(rhs_obj, proto_si);
let ctor_proto_ptr = match ctor_proto {
@@ -297,8 +291,9 @@ impl Vm {
.map(|meta| meta.attributes.enumerable())
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable());
if enumerable {
let hash = self.kernel_core.string_forge().get_hash(shape.property_name).unwrap_or(0);
keys_vec.push(JsValue::string(shape.property_name, hash));
keys_vec.push(JsValue::perm_string(
self.kernel_core.perm_interner().string_ptr(shape.property_name),
));
}
}
cursor = shape.parent;
@@ -368,7 +363,7 @@ impl Vm {
}
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
let next_si = self.kernel_core.string_forge().intern("next").0;
let next_si = self.kernel_core.perm_interner().intern("next").0;
let next_fn = self.ordinary_get(iter_obj, next_si, iterator)?;
let result = self.call_function_sync(next_fn, iterator, &[])?;
if !result.is_object() {
@@ -376,9 +371,9 @@ impl Vm {
}
let result_obj = unsafe { &*result.as_js_object_ptr() };
let done_si = self.kernel_core.string_forge().intern("done").0;
let done_si = self.kernel_core.perm_interner().intern("done").0;
let done_val = self.ordinary_get(result_obj, done_si, result)?;
let done = crate::coercion::to_boolean(done_val, self.kernel_core.string_forge().as_ref());
let done = crate::coercion::to_boolean(done_val);
self.last_for_of_result = result;
self.regs[rd] = JsValue::bool(!done);
Ok(())
@@ -391,7 +386,7 @@ impl Vm {
return Ok(());
}
let result_obj = unsafe { &*result.as_js_object_ptr() };
let value_si = self.kernel_core.string_forge().intern("value").0;
let value_si = self.kernel_core.perm_interner().intern("value").0;
self.regs[rd] = self.ordinary_get(result_obj, value_si, result)?;
Ok(())
}
@@ -404,7 +399,7 @@ impl Vm {
return Ok(());
}
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
let return_si = self.kernel_core.string_forge().intern("return").0;
let return_si = self.kernel_core.perm_interner().intern("return").0;
let return_fn = self.ordinary_get(iter_obj, return_si, iterator)?;
if return_fn.is_object() {
let return_obj = unsafe { &*return_fn.as_js_object_ptr() };
@@ -427,7 +422,8 @@ impl Vm {
.get(excluded_idx)
.and_then(|v| {
if v.is_string() {
self.kernel_core.string_forge().lookup(v.as_string_index())
// SAFETY: v is a string constant value.
Some(unsafe { (*v.as_string_ptr()).data.clone() })
} else {
None
}
@@ -450,8 +446,8 @@ impl Vm {
break;
};
if shape.property_name != u32::MAX && !is_private_name_key(shape.property_name) {
if let Some(name) = self.kernel_core.string_forge().lookup(shape.property_name) {
if !excluded.contains(name.as_str()) {
if let Some(name) = self.kernel_core.perm_interner().lookup(shape.property_name) {
if !excluded.contains(name) {
if let Some(pos) = self
.kernel_core
.shape_forge()
-8
View File
@@ -22,7 +22,6 @@ pub struct VmGuard {
vm: Option<Vm>,
pool: Arc<VmPool>,
dirty: bool,
interned_strings: Vec<u32>,
}
impl VmPool {
@@ -55,7 +54,6 @@ impl VmPool {
vm: Some(vm),
pool: Arc::clone(self),
dirty: false,
interned_strings: Vec::new(),
};
}
@@ -72,7 +70,6 @@ impl VmPool {
vm: Some(vm),
pool: Arc::clone(self),
dirty: false,
interned_strings: Vec::new(),
};
}
@@ -86,7 +83,6 @@ impl VmPool {
vm: Some(vm),
pool: Arc::clone(self),
dirty: false,
interned_strings: Vec::new(),
};
}
}
@@ -115,10 +111,6 @@ impl Drop for VmGuard {
let new_vm = self.pool.replace_vm();
inner.available.push(new_vm);
} else {
for &idx in &self.interned_strings {
self.pool.kernel_core.string_forge().decref(idx);
}
vm.full_reset();
inner.available.push(vm);
}
+1 -1
View File
@@ -19,7 +19,7 @@ impl Vm {
fn ordinary_get_inner(
&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue, target_reg: Option<u8>,
) -> Result<JsValue, String> {
let length_si = self.kernel_core.string_forge().intern("length").0;
let length_si = self.kernel_core.perm_interner().intern("length").0;
let mut current = Some(obj);
let mut depth = 0usize;
while let Some(obj) = current {
+2 -2
View File
@@ -64,7 +64,7 @@ impl Vm {
self.regs[254] = if sub.is_arrow { callee_obj.captured_this() } else { receiver };
self.regs[255] = JsValue::undefined();
for (name, reg) in &sub.builtin_reg_map {
let si = self.kernel_core.string_forge().intern(name.as_str()).0;
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
let global = self.session.global_object();
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
self.regs[*reg as usize] = global.get_prop_at(pos);
@@ -144,7 +144,7 @@ impl Vm {
self.active_reg_limit = self.root_reg_limit;
for (name, reg) in &module.builtin_reg_map {
let si = self.kernel_core.string_forge().intern(name.as_str()).0;
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
let global = self.session.global_object();
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
self.regs[*reg as usize] = global.get_prop_at(pos);
+47 -20
View File
@@ -10,7 +10,7 @@ use oxide_kernel::kernel::{KernelConfig, KernelCore, KernelSession};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::error::JsError;
use oxide_types::mem::{Epoch, P};
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::object::{JsObject, JsString, PropAttributes};
use oxide_types::value::JsValue;
impl Vm {
@@ -28,7 +28,7 @@ impl Vm {
for_in_iters: Vec::new(),
kernel_core: core,
session,
interned_strings: Vec::new(),
session_string_ptrs: Vec::new(),
epoch: Epoch::new(),
session_epoch: bumpalo::Bump::new(),
session_gc: crate::session_gc::SessionGc::new(),
@@ -74,7 +74,7 @@ impl Vm {
for_in_iters: Vec::new(),
kernel_core: core,
session,
interned_strings: Vec::new(),
session_string_ptrs: Vec::new(),
epoch: Epoch::new(),
session_epoch: bumpalo::Bump::new(),
session_gc: crate::session_gc::SessionGc::new(),
@@ -141,7 +141,7 @@ impl Vm {
self.session_object_ptrs.clear();
self.session_bytes_allocated = 0;
self.session_gc = crate::session_gc::SessionGc::new();
self.interned_strings.clear();
self.free_session_string_heap_data();
self.symbol_counter = 0;
self.symbol_descriptions.clear();
self.symbol_registry.clear();
@@ -189,15 +189,44 @@ impl Vm {
self.free_epoch_object_heap_data();
self.epoch.reset();
self.epoch_object_ptrs.clear();
self.interned_strings.clear();
self.root_reg_limit = 0;
self.active_reg_limit = 0;
}
pub fn intern(&mut self, s: &str) -> JsValue {
let (idx, hash) = self.kernel_core.string_forge().intern(s);
self.interned_strings.push(idx);
JsValue::string(idx, hash)
pub fn new_string(&mut self, s: &str) -> JsValue {
let ptr = Box::into_raw(Box::new(JsString::new(s.to_string())));
self.session_string_ptrs.push(ptr);
self.session_bytes_allocated += std::mem::size_of::<JsString>() + s.len();
JsValue::string(ptr)
}
pub fn intern_key(&self, s: &str) -> u32 {
self.kernel_core.perm_interner().intern(s).0
}
/// Intern a compile-time string literal as a permanent, process-lifetime
/// `JsString` value shared across sessions. Source literals and RegExp
/// source/flags recur (templated/repetitive code) and are immutable, so
/// sharing them via `PermInterner` restores cross-session string reuse —
/// without interning transient computed values, which stay session-heap
/// (`new_string`) so they remain collectable.
pub fn perm_string(&self, s: &str) -> JsValue {
let id = self.kernel_core.perm_interner().intern(s).0;
JsValue::perm_string(self.kernel_core.perm_interner().string_ptr(id))
}
/// Drop all session-heap `JsString` values. Called only on full isolation reset
/// (`full_reset` / `clear_full_reset_state`), where no surviving session object
/// can reference them. The lighter `reset()` deliberately keeps them alive,
/// mirroring session-object survival across evals.
fn free_session_string_heap_data(&mut self) {
for ptr in self.session_string_ptrs.drain(..) {
// SAFETY: each ptr came from Box::into_raw(Box::new(JsString)) in new_string
// and is dropped exactly once here.
unsafe {
drop(Box::from_raw(ptr));
}
}
}
/// Create a function JsObject for a BytecodeFunc constant.
@@ -230,7 +259,7 @@ impl Vm {
self.epoch_object_ptrs.push(prototype_obj);
let prototype_val = JsValue::from_js_object(prototype_obj);
let constructor_si = self.kernel_core.string_forge().intern("constructor").0;
let constructor_si = self.kernel_core.perm_interner().intern("constructor").0;
let constructor_shape = self.kernel_core.shape_forge().make_shape(EMPTY_SHAPE_ID, constructor_si);
let prototype = unsafe { &mut *prototype_obj };
prototype.set_shape_id(constructor_shape);
@@ -238,7 +267,7 @@ impl Vm {
prototype.set_data_meta(constructor_pos, PropAttributes::new(true, false, true));
prototype.bump_generation();
let prototype_si = self.kernel_core.string_forge().intern("prototype").0;
let prototype_si = self.kernel_core.perm_interner().intern("prototype").0;
let func = unsafe { &mut *obj_ptr };
let prototype_shape = self.kernel_core.shape_forge().make_shape(func.shape_id(), prototype_si);
func.set_shape_id(prototype_shape);
@@ -255,8 +284,8 @@ impl Vm {
}
if val.is_object() {
let obj = unsafe { &*val.as_js_object_ptr() };
let name_si = self.kernel_core.string_forge().intern("name").0;
let message_si = self.kernel_core.string_forge().intern("message").0;
let name_si = self.kernel_core.perm_interner().intern("name").0;
let message_si = self.kernel_core.perm_interner().intern("message").0;
let name = self
.resolve_property(obj, name_si)
.and_then(|v| self.lookup_str(v))
@@ -274,7 +303,7 @@ impl Vm {
match constant {
Constant::Number(v) => Ok(JsValue::float(*v)),
Constant::Int(v) => Ok(JsValue::int(*v)),
Constant::String(s) => Ok(self.intern(s)),
Constant::String(s) => Ok(self.perm_string(s)),
Constant::Boolean(b) => Ok(JsValue::bool(*b)),
Constant::Null => Ok(JsValue::null()),
Constant::Undefined => Ok(JsValue::undefined()),
@@ -301,10 +330,8 @@ impl Vm {
))
}
Constant::RegExp(pattern, flags) => {
let pat_si = self.kernel_core.string_forge().intern(pattern).0;
let flags_si = self.kernel_core.string_forge().intern(flags).0;
let pat_val = JsValue::string(pat_si, 0);
let flags_val = JsValue::string(flags_si, 0);
let pat_val = self.perm_string(pattern);
let flags_val = self.perm_string(flags);
let ctor_ptr = self.session.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
let ctor = unsafe { &*ctor_ptr };
@@ -347,7 +374,7 @@ mod tests {
fn global_prop_opt(vm: &Vm, name: &str) -> Option<JsValue> {
let global = vm.session.global_object();
let si = vm.kernel_core.string_forge().intern(name).0;
let si = vm.kernel_core.perm_interner().intern(name).0;
vm.kernel_core
.shape_forge()
.lookup_position(global.shape_id(), si)
@@ -418,7 +445,7 @@ mod tests {
global_prop(&vm, "Array").as_js_object_ptr(),
vm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject
));
let constructor_si = vm.kernel_core.string_forge().intern("constructor").0;
let constructor_si = vm.kernel_core.perm_interner().intern("constructor").0;
let array_proto = &*vm.session.builtin_world().array_proto;
let constructor = vm
.resolve_property(array_proto, constructor_si)
+1 -4
View File
@@ -37,10 +37,7 @@ fn eval_val(source: &str) -> (Vm, Result<JsValue, String>) {
fn to_str(vm: &Vm, val: JsValue) -> String {
if val.is_string() {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
} else {
format!("{}", val)
}
@@ -10,10 +10,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
#[test]
@@ -13,10 +13,7 @@ fn eval(source: &str) -> Result<(Vm, JsValue), String> {
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
fn assert_num_eq(val: JsValue, expected: f64) {
@@ -14,10 +14,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn str_val(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
// -- direct native fn tests --
@@ -47,7 +44,7 @@ fn tboolean_call_true_false() {
#[test]
fn tboolean_call_empty_string() {
let mut vm = Vm::new();
let empty = vm.intern("");
let empty = vm.new_string("");
assert!(!bool_ctor_call(&mut vm, empty).as_bool());
}
@@ -10,10 +10,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
#[test]
+1 -4
View File
@@ -9,10 +9,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<oxide_types::value::JsValue, String
}
fn str_val(vm: &Vm, val: oxide_types::value::JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
// -- static methods --
@@ -25,7 +25,7 @@ fn error_constructor_creates_object() {
#[test]
fn error_has_message() {
let mut vm = make_vm();
let msg = vm.intern("test message");
let msg = vm.new_string("test message");
vm.set_reg(1, msg);
let result = error::error_constructor(&mut vm, &[0u8, 1u8]).unwrap();
assert!(result.is_object());
@@ -34,11 +34,7 @@ fn function_bind_creates_wrapper() {
fn function_constructor_is_global() {
let mut vm = Vm::new();
let result = eval(&mut vm, "typeof Function").unwrap();
let rendered = vm
.kernel_core()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
let rendered = vm.lookup_str(result).unwrap_or_default();
assert_eq!(rendered, "function");
}
@@ -86,11 +82,7 @@ fn function_to_string_includes_function() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Math.max.toString()").unwrap();
assert!(result.is_string());
let rendered = vm
.kernel_core()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
let rendered = vm.lookup_str(result).unwrap_or_default();
assert_eq!(rendered, "function max() { [native code] }");
}
@@ -21,10 +21,7 @@ fn eval_with_vm(source: &str) -> (Vm, Result<JsValue, String>) {
}
fn str_val(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
#[test]
@@ -10,10 +10,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
#[test]
+1 -4
View File
@@ -11,10 +11,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn str_val(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
// -- direct native fn tests --
+4 -16
View File
@@ -76,11 +76,7 @@ fn number_constructor() {
fn to_string_of_number() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var n = 42; n.toString()").unwrap();
let s = vm
.kernel_core()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
let s = vm.lookup_str(result).unwrap_or_default();
assert_eq!(s, "42");
}
@@ -88,11 +84,7 @@ fn to_string_of_number() {
fn to_fixed_of_number() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var n = 3.14159; n.toFixed(2)").unwrap();
let s = vm
.kernel_core()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
let s = vm.lookup_str(result).unwrap_or_default();
assert_eq!(s, "3.14");
}
@@ -121,11 +113,7 @@ fn number_epsilon_is_positive() {
fn number_to_exponential() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var n = 123.456; n.toExponential(2)").unwrap();
let s = vm
.kernel_core()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
let s = vm.lookup_str(result).unwrap_or_default();
assert_eq!(s, "1.23e2");
}
@@ -155,7 +143,7 @@ fn boxed_number_valueof_preserves_int_and_float() {
fn boxed_number_is_object_and_call_stays_primitive() {
let mut vm = Vm::new();
let ty = eval(&mut vm, "typeof new Number(5)").unwrap();
assert_eq!(vm.kernel_core().string_forge().lookup(ty.as_string_index()).unwrap(), "object");
assert_eq!(vm.lookup_str(ty).unwrap(), "object");
let n = eval(&mut vm, "Number('7')").unwrap();
assert_eq!(n.as_int(), 7);
@@ -63,11 +63,7 @@ fn object_assign_copies() {
fn object_assign_boxes_primitive_target() {
let (_vm, result) = eval("var result = Object.assign('a'); result.valueOf()").unwrap();
assert!(result.is_string());
let rendered = _vm
.kernel_core()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
let rendered = _vm.lookup_str(result).unwrap_or_default();
assert_eq!(rendered, "a");
}
@@ -10,10 +10,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
#[test]
@@ -10,10 +10,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
// --- RegExp constructor ---
@@ -10,10 +10,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
fn assert_num_eq(val: JsValue, expected: f64) {
@@ -10,10 +10,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
// --- Symbol constructor ---
@@ -10,10 +10,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
#[test]
+11 -18
View File
@@ -85,25 +85,18 @@ fn test_strict_equality_null_undefined() {
#[test]
fn test_to_int32_and_to_uint32() {
let vm = Vm::new();
let sf = vm.kernel_core().string_forge().as_ref();
let string_three = {
let (idx, hash) = vm.kernel_core().string_forge().intern("3");
JsValue::string(idx, hash)
};
let string_bad = {
let (idx, hash) = vm.kernel_core().string_forge().intern("x");
JsValue::string(idx, hash)
};
let mut vm = Vm::new();
let string_three = vm.new_string("3");
let string_bad = vm.new_string("x");
assert_eq!(coercion::to_int32(string_three, sf), 3);
assert_eq!(coercion::to_int32(string_bad, sf), 0);
assert_eq!(coercion::to_int32(JsValue::undefined(), sf), 0);
assert_eq!(coercion::to_int32(JsValue::float(1.9), sf), 1);
assert_eq!(coercion::to_int32(JsValue::float(-1.9), sf), -1);
assert_eq!(coercion::to_int32(JsValue::float(f64::NAN), sf), 0);
assert_eq!(coercion::to_int32(JsValue::float(f64::INFINITY), sf), 0);
assert_eq!(coercion::to_uint32(JsValue::int(-1), sf), 4_294_967_295);
assert_eq!(coercion::to_int32(string_three), 3);
assert_eq!(coercion::to_int32(string_bad), 0);
assert_eq!(coercion::to_int32(JsValue::undefined()), 0);
assert_eq!(coercion::to_int32(JsValue::float(1.9)), 1);
assert_eq!(coercion::to_int32(JsValue::float(-1.9)), -1);
assert_eq!(coercion::to_int32(JsValue::float(f64::NAN)), 0);
assert_eq!(coercion::to_int32(JsValue::float(f64::INFINITY)), 0);
assert_eq!(coercion::to_uint32(JsValue::int(-1)), 4_294_967_295);
}
#[test]
+1 -5
View File
@@ -204,10 +204,6 @@ fn function_default_prototype_accepts_member_assignment() {
"function Test262Error(message) { this.message = message || ''; } Test262Error.prototype.toString = function () { return this.message; }; new Test262Error('ok').toString()",
)
.unwrap();
let rendered = vm
.kernel_core()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
let rendered = vm.lookup_str(result).unwrap_or_default();
assert_eq!(rendered, "ok");
}
+1 -4
View File
@@ -10,10 +10,7 @@ fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
}
fn string_value(vm: &Vm, val: JsValue) -> String {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
}
// -- JSON.parse --
+1 -1
View File
@@ -11,7 +11,7 @@ fn run_source(vm: &mut Vm, source: &str) -> JsValue {
}
fn global_prop(vm: &Vm, name: &str) -> JsValue {
let si = vm.kernel_core().string_forge().intern(name).0;
let si = vm.kernel_core().perm_interner().intern(name).0;
let global = vm.session().global_object();
let pos = vm
.kernel_core()
+1 -4
View File
@@ -37,10 +37,7 @@ fn eval_val(source: &str) -> (Vm, Result<JsValue, String>) {
fn to_str(vm: &Vm, val: JsValue) -> String {
if val.is_string() {
vm.kernel_core()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
vm.lookup_str(val).unwrap_or_default()
} else {
format!("{}", val)
}