mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
refactor: 哈希表存储属性以支持无上限属性个数
This commit is contained in:
@@ -126,17 +126,23 @@ fn make_pair(
|
||||
|
||||
let proto_shape = shape_forge.make_shape(EMPTY_SHAPE_ID, si_constructor);
|
||||
proto.set_shape_id(proto_shape);
|
||||
proto.set_prop_count(1);
|
||||
// proto shape: EMPTY -> "constructor" (slot 0)
|
||||
|
||||
let ctor_shape1 = shape_forge.make_shape(EMPTY_SHAPE_ID, si_prototype);
|
||||
let ctor_shape2 = shape_forge.make_shape(ctor_shape1, si_name);
|
||||
ctor.set_shape_id(ctor_shape2);
|
||||
ctor.set_prop_count(2);
|
||||
// ctor shape: EMPTY -> "prototype" (slot 0) -> "name" (slot 1)
|
||||
ctor.set_function(true);
|
||||
|
||||
proto.set_inline_prop(0, JsValue::undefined());
|
||||
ctor.set_inline_prop(0, JsValue::undefined());
|
||||
ctor.set_inline_prop(1, JsValue::undefined());
|
||||
// Pre-allocate Vec slots so wire_ctor_proto can overwrite vec[0] later.
|
||||
// Proto: 1 property ("constructor"), Ctor: 2 properties ("prototype", "name").
|
||||
proto
|
||||
.ensure_hash_props()
|
||||
.push(Box::new(JsValue::undefined())); // slot 0: "constructor" placeholder
|
||||
ctor.ensure_hash_props()
|
||||
.push(Box::new(JsValue::undefined())); // slot 0: "prototype" placeholder
|
||||
ctor.ensure_hash_props()
|
||||
.push(Box::new(JsValue::undefined())); // slot 1: "name" placeholder
|
||||
|
||||
(P::new(proto), P::new(ctor))
|
||||
}
|
||||
@@ -229,13 +235,22 @@ impl BuiltinWorld {
|
||||
|
||||
let math_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||
|
||||
/// Overwrite placeholder slots (set up by make_pair) with real values.
|
||||
/// ctor.vec[0] = constructor.prototype -> proto
|
||||
/// proto.vec[0] = proto.constructor -> ctor
|
||||
fn wire_ctor_proto(ctor: &P<JsObject>, proto: &P<JsObject>) {
|
||||
let ctor_ptr = ctor.as_ptr() as *mut JsObject;
|
||||
let ctor = unsafe { &mut *ctor_ptr };
|
||||
ctor.set_inline_prop(0, JsValue::from_js_object(proto.as_ptr() as *mut JsObject));
|
||||
let vec = ctor.ensure_hash_props();
|
||||
if !vec.is_empty() {
|
||||
*vec[0] = JsValue::from_js_object(proto.as_ptr() as *mut JsObject);
|
||||
}
|
||||
let proto_ptr = proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
proto.set_inline_prop(0, JsValue::from_js_object(ctor_ptr));
|
||||
let pvec = proto.ensure_hash_props();
|
||||
if !pvec.is_empty() {
|
||||
*pvec[0] = JsValue::from_js_object(ctor_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
wire_ctor_proto(&object_constructor, &object_proto);
|
||||
@@ -735,11 +750,9 @@ impl BuiltinWorld {
|
||||
wrapper.set_native_fn(Some(native_fn_ptr));
|
||||
wrapper.set_native_arg_count(arg_count);
|
||||
let wrapper_val = JsValue::from_js_object(Box::into_raw(wrapper));
|
||||
let new_offset = proto.prop_count();
|
||||
let new_shape = shape_forge.make_shape(proto.shape_id(), si);
|
||||
proto.set_shape_id(new_shape);
|
||||
proto.set_prop_count(new_offset + 1);
|
||||
proto.set_prop_expand_heap(new_offset, wrapper_val);
|
||||
proto.ensure_hash_props().push(Box::new(wrapper_val));
|
||||
proto.bump_generation();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -87,13 +87,15 @@ impl OxideKernel {
|
||||
|
||||
let nan_shape = shape_forge.make_shape(EMPTY_SHAPE_ID, si_nan);
|
||||
global_obj.set_shape_id(nan_shape);
|
||||
global_obj.set_prop_count(1);
|
||||
global_obj.set_inline_prop(0, JsValue::float(f64::NAN));
|
||||
global_obj
|
||||
.ensure_hash_props()
|
||||
.push(Box::new(JsValue::float(f64::NAN)));
|
||||
|
||||
let undef_shape = shape_forge.make_shape(nan_shape, si_undef);
|
||||
global_obj.set_shape_id(undef_shape);
|
||||
global_obj.set_prop_count(2);
|
||||
global_obj.set_inline_prop(1, JsValue::undefined());
|
||||
global_obj
|
||||
.ensure_hash_props()
|
||||
.push(Box::new(JsValue::undefined()));
|
||||
|
||||
let global_object = P::new(global_obj);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::shape_forge::ShapeId;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PropTemplate {
|
||||
pub shape_id: ShapeId,
|
||||
pub offset: u8,
|
||||
pub ptr_bits: u64,
|
||||
pub generation: u32,
|
||||
}
|
||||
|
||||
@@ -67,13 +67,13 @@ mod tests {
|
||||
let forge = PropForge::new();
|
||||
let t = PropTemplate {
|
||||
shape_id: 1,
|
||||
offset: 3,
|
||||
ptr_bits: 3,
|
||||
generation: 10,
|
||||
};
|
||||
forge.upsert(1, t);
|
||||
let got = forge.get_template(1).unwrap();
|
||||
assert_eq!(got.shape_id, 1);
|
||||
assert_eq!(got.offset, 3);
|
||||
assert_eq!(got.ptr_bits, 3);
|
||||
assert_eq!(got.generation, 10);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ mod tests {
|
||||
1,
|
||||
PropTemplate {
|
||||
shape_id: 1,
|
||||
offset: 3,
|
||||
ptr_bits: 3,
|
||||
generation: 10,
|
||||
},
|
||||
);
|
||||
@@ -92,7 +92,7 @@ mod tests {
|
||||
1,
|
||||
PropTemplate {
|
||||
shape_id: 1,
|
||||
offset: 7,
|
||||
ptr_bits: 7,
|
||||
generation: 5,
|
||||
},
|
||||
);
|
||||
@@ -107,7 +107,7 @@ mod tests {
|
||||
1,
|
||||
PropTemplate {
|
||||
shape_id: 1,
|
||||
offset: 3,
|
||||
ptr_bits: 3,
|
||||
generation: 5,
|
||||
},
|
||||
);
|
||||
@@ -115,12 +115,12 @@ mod tests {
|
||||
1,
|
||||
PropTemplate {
|
||||
shape_id: 1,
|
||||
offset: 7,
|
||||
ptr_bits: 7,
|
||||
generation: 10,
|
||||
},
|
||||
);
|
||||
let got = forge.get_template(1).unwrap();
|
||||
assert_eq!(got.generation, 10);
|
||||
assert_eq!(got.offset, 7);
|
||||
assert_eq!(got.ptr_bits, 7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ const EMPTY_SENTINEL: StringIndex = u32::MAX;
|
||||
pub struct Shape {
|
||||
pub id: ShapeId,
|
||||
pub property_name: StringIndex,
|
||||
pub property_offset: u8,
|
||||
pub parent: Option<ShapeId>,
|
||||
}
|
||||
|
||||
@@ -35,7 +34,6 @@ impl ShapeForge {
|
||||
let empty = Arc::new(Shape {
|
||||
id: EMPTY_SHAPE_ID,
|
||||
property_name: EMPTY_SENTINEL,
|
||||
property_offset: 0,
|
||||
parent: None,
|
||||
});
|
||||
debug_assert_eq!(shapes.len(), 0);
|
||||
@@ -48,7 +46,7 @@ impl ShapeForge {
|
||||
((parent_id as u64) << 32) | (prop_name as u64)
|
||||
}
|
||||
|
||||
fn compute_offset_inner(shape_id: ShapeId, shapes: &[Option<Arc<Shape>>]) -> u8 {
|
||||
fn compute_depth(shape_id: ShapeId, shapes: &[Option<Arc<Shape>>]) -> u8 {
|
||||
let mut count = 0u8;
|
||||
let mut cursor = Some(shape_id);
|
||||
while let Some(id) = cursor {
|
||||
@@ -62,7 +60,7 @@ impl ShapeForge {
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
count.min(31)
|
||||
count
|
||||
}
|
||||
|
||||
pub fn make_shape(&self, parent_id: ShapeId, prop_name: StringIndex) -> ShapeId {
|
||||
@@ -72,16 +70,11 @@ impl ShapeForge {
|
||||
return *entry;
|
||||
}
|
||||
|
||||
let shapes_read = self.shapes.read().unwrap();
|
||||
let prop_offset = Self::compute_offset_inner(parent_id, &shapes_read);
|
||||
drop(shapes_read);
|
||||
|
||||
let new_id = self.next_id.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let shape = Arc::new(Shape {
|
||||
id: new_id,
|
||||
property_name: prop_name,
|
||||
property_offset: prop_offset,
|
||||
parent: Some(parent_id),
|
||||
});
|
||||
|
||||
@@ -101,21 +94,41 @@ impl ShapeForge {
|
||||
shapes.get((id - 1) as usize).and_then(|s| s.clone())
|
||||
}
|
||||
|
||||
pub fn lookup_offset(&self, shape_id: ShapeId, prop_name: StringIndex) -> Option<u8> {
|
||||
pub fn lookup_position(&self, shape_id: ShapeId, prop_name: StringIndex) -> Option<u8> {
|
||||
let shapes = self.shapes.read().unwrap();
|
||||
let total_depth = Self::compute_depth(shape_id, &shapes);
|
||||
let mut step: u8 = 0;
|
||||
let mut cursor = Some(shape_id);
|
||||
while let Some(id) = cursor {
|
||||
match shapes.get((id - 1) as usize).and_then(|s| s.clone()) {
|
||||
Some(s) => {
|
||||
if s.property_name == prop_name && s.property_name != EMPTY_SENTINEL {
|
||||
return Some(total_depth - step - 1);
|
||||
}
|
||||
cursor = s.parent;
|
||||
step += 1;
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn has_property(&self, shape_id: ShapeId, prop_name: StringIndex) -> bool {
|
||||
let shapes = self.shapes.read().unwrap();
|
||||
let mut cursor = Some(shape_id);
|
||||
while let Some(id) = cursor {
|
||||
match shapes.get((id - 1) as usize).and_then(|s| s.clone()) {
|
||||
Some(s) => {
|
||||
if s.property_name == prop_name && s.property_name != EMPTY_SENTINEL {
|
||||
return Some(s.property_offset);
|
||||
return true;
|
||||
}
|
||||
cursor = s.parent;
|
||||
}
|
||||
None => return None,
|
||||
None => return false,
|
||||
}
|
||||
}
|
||||
None
|
||||
false
|
||||
}
|
||||
|
||||
pub fn shape_prop_count(&self, shape_id: ShapeId) -> u8 {
|
||||
@@ -195,10 +208,10 @@ mod tests {
|
||||
|
||||
assert_eq!(forge.shape_prop_count(s3), 3);
|
||||
|
||||
assert_eq!(forge.lookup_offset(s3, 1_000_030), Some(2));
|
||||
assert_eq!(forge.lookup_offset(s3, 1_000_020), Some(1));
|
||||
assert_eq!(forge.lookup_offset(s3, 1_000_010), Some(0));
|
||||
assert_eq!(forge.lookup_offset(s3, 99), None);
|
||||
assert_eq!(forge.lookup_position(s3, 1_000_030), Some(2));
|
||||
assert_eq!(forge.lookup_position(s3, 1_000_020), Some(1));
|
||||
assert_eq!(forge.lookup_position(s3, 1_000_010), Some(0));
|
||||
assert_eq!(forge.lookup_position(s3, 99), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -223,8 +236,8 @@ mod tests {
|
||||
let s4 = forge.make_shape(s3, 1_000_100);
|
||||
assert_ne!(s1, s3);
|
||||
assert_ne!(s2, s4);
|
||||
assert_eq!(forge.lookup_offset(s2, 1_000_070), Some(0));
|
||||
assert_eq!(forge.lookup_offset(s4, 1_000_090), Some(0));
|
||||
assert_eq!(forge.lookup_position(s2, 1_000_070), Some(0));
|
||||
assert_eq!(forge.lookup_position(s4, 1_000_090), Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -252,4 +265,14 @@ mod tests {
|
||||
assert_eq!(id1, id2);
|
||||
assert!(id1 > EMPTY_SHAPE_ID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_property_works() {
|
||||
let forge = ShapeForge::new();
|
||||
let s1 = forge.make_shape(EMPTY_SHAPE_ID, 1_000_300);
|
||||
let s2 = forge.make_shape(s1, 1_000_310);
|
||||
assert!(forge.has_property(s2, 1_000_300));
|
||||
assert!(forge.has_property(s2, 1_000_310));
|
||||
assert!(!forge.has_property(s2, 99));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user