fix: 修复数组长度与槽位索引截断问题

This commit is contained in:
爱特每羊队
2026-06-08 22:55:52 +08:00
parent 22cbccdf6c
commit e57e140bef
16 changed files with 242 additions and 159 deletions
+2 -2
View File
@@ -167,7 +167,7 @@ fn format_object(string_forge: &StringForge, shape_forge: &ShapeForge, obj: &JsO
break;
}
}
let mut pos: u8 = 0;
let mut pos: u32 = 0;
for id in shape_ids.iter().rev() {
if let Some(shape) = shape_forge.get_shape(*id) {
if shape.property_name != 0 {
@@ -190,7 +190,7 @@ fn format_array(string_forge: &StringForge, shape_forge: &ShapeForge, obj: &JsOb
let len = obj.prop_vec_len();
let mut items = Vec::new();
for i in 0..len {
let val = obj.get_prop_at(i as u8);
let val = obj.get_prop_at(i);
items.push(format_js_value(string_forge, shape_forge, val));
}
format!("[{}]", items.join(", "))
+2 -13
View File
@@ -318,19 +318,8 @@ impl Compiler {
self.count_expression(expr, ctx);
}
}
// CALL_NATIVE: 1 opcode + 1 ext word; CALL(0x40): 1 opcode
let is_builtin = match &call.callee {
Expression::Identifier(ident) => ctx.is_builtin(ident.name.as_str()),
Expression::StaticMemberExpression(m) => {
if let Expression::Identifier(ident) = &m.object {
ctx.is_builtin(ident.name.as_str())
} else {
false
}
}
_ => false,
};
ctx.projected_pc += if is_builtin { 2 } else { 1 };
// CALL and CALL_NATIVE both carry 1 ext word for arg_count.
ctx.projected_pc += 2;
ctx.alloc_reg(); // result register
ctx.projected_pc += 1; // LOAD_VAR result <- regs[0]
}
+8 -1
View File
@@ -7,7 +7,8 @@ use crate::shape_forge::ShapeId;
#[derive(Debug, Clone)]
pub struct PropTemplate {
pub shape_id: ShapeId,
pub position: u8,
pub prop_name: u32,
pub position: u32,
pub generation: u32,
}
@@ -67,12 +68,14 @@ mod tests {
let forge = PropForge::new();
let t = PropTemplate {
shape_id: 1,
prop_name: 11,
position: 3,
generation: 10,
};
forge.upsert(1, t);
let got = forge.get_template(1).unwrap();
assert_eq!(got.shape_id, 1);
assert_eq!(got.prop_name, 11);
assert_eq!(got.position, 3);
assert_eq!(got.generation, 10);
}
@@ -84,6 +87,7 @@ mod tests {
1,
PropTemplate {
shape_id: 1,
prop_name: 11,
position: 3,
generation: 10,
},
@@ -92,6 +96,7 @@ mod tests {
1,
PropTemplate {
shape_id: 1,
prop_name: 12,
position: 7,
generation: 5,
},
@@ -107,6 +112,7 @@ mod tests {
1,
PropTemplate {
shape_id: 1,
prop_name: 11,
position: 3,
generation: 5,
},
@@ -115,6 +121,7 @@ mod tests {
1,
PropTemplate {
shape_id: 1,
prop_name: 12,
position: 7,
generation: 10,
},
+6 -6
View File
@@ -46,8 +46,8 @@ impl ShapeForge {
((parent_id as u64) << 32) | (prop_name as u64)
}
fn compute_depth(shape_id: ShapeId, shapes: &[Option<Arc<Shape>>]) -> u8 {
let mut count = 0u8;
fn compute_depth(shape_id: ShapeId, shapes: &[Option<Arc<Shape>>]) -> u32 {
let mut count = 0u32;
let mut cursor = Some(shape_id);
while let Some(id) = cursor {
match shapes.get((id - 1) as usize).and_then(|s| s.clone()) {
@@ -94,10 +94,10 @@ impl ShapeForge {
shapes.get((id - 1) as usize).and_then(|s| s.clone())
}
pub fn lookup_position(&self, shape_id: ShapeId, prop_name: StringIndex) -> Option<u8> {
pub fn lookup_position(&self, shape_id: ShapeId, prop_name: StringIndex) -> Option<u32> {
let shapes = self.shapes.read().unwrap();
let total_depth = Self::compute_depth(shape_id, &shapes);
let mut step: u8 = 0;
let mut step: u32 = 0;
let mut cursor = Some(shape_id);
while let Some(id) = cursor {
match shapes.get((id - 1) as usize).and_then(|s| s.clone()) {
@@ -131,9 +131,9 @@ impl ShapeForge {
false
}
pub fn shape_prop_count(&self, shape_id: ShapeId) -> u8 {
pub fn shape_prop_count(&self, shape_id: ShapeId) -> u32 {
let shapes = self.shapes.read().unwrap();
let mut count = 0u8;
let mut count = 0u32;
let mut cursor = Some(shape_id);
while let Some(id) = cursor {
match shapes.get((id - 1) as usize).and_then(|s| s.clone()) {
+48 -12
View File
@@ -2,6 +2,41 @@ use crate::value::JsValue;
pub type ShapeId = u32;
pub trait PropIndex {
fn to_u32(self) -> u32;
}
impl PropIndex for u8 {
fn to_u32(self) -> u32 {
self as u32
}
}
impl PropIndex for u16 {
fn to_u32(self) -> u32 {
self as u32
}
}
impl PropIndex for u32 {
fn to_u32(self) -> u32 {
self
}
}
impl PropIndex for usize {
fn to_u32(self) -> u32 {
self as u32
}
}
impl PropIndex for i32 {
fn to_u32(self) -> u32 {
debug_assert!(self >= 0, "property index must be non-negative");
self.max(0) as u32
}
}
/// Layout (56B):
/// header: u32 bits
/// [0:23] shape_id
@@ -82,19 +117,19 @@ impl JsObject {
/// Returns property count from the hash_props vec length.
/// Returns 0 if hash_props has not been allocated.
pub fn prop_count(&self) -> u8 {
pub fn prop_count(&self) -> u32 {
if self.hash_props.is_null() {
0
} else {
let vec = unsafe { &*(self.hash_props as *const Vec<Box<JsValue>>) };
vec.len() as u8
vec.len() as u32
}
}
/// Sets the length of hash_props vec. Truncates or extends with undefined.
pub fn set_prop_count(&mut self, count: u8) {
pub fn set_prop_count(&mut self, count: impl PropIndex) {
let vec = self.ensure_hash_props();
let target = count as usize;
let target = count.to_u32() as usize;
if target < vec.len() {
vec.truncate(target);
} else {
@@ -152,20 +187,20 @@ impl JsObject {
/// Get property value at position index in the vec.
/// Returns JsValue::undefined() if hash_props not allocated or position out of bounds.
pub fn get_prop_at(&self, position: u8) -> JsValue {
pub fn get_prop_at(&self, position: impl PropIndex) -> JsValue {
if self.hash_props.is_null() {
return JsValue::undefined();
}
let vec = unsafe { &*(self.hash_props as *const Vec<Box<JsValue>>) };
vec.get(position as usize)
vec.get(position.to_u32() as usize)
.map(|b| **b)
.unwrap_or(JsValue::undefined())
}
/// Set property value at position index. Vec auto-grows if needed.
pub fn set_prop_at(&mut self, position: u8, val: JsValue) {
pub fn set_prop_at(&mut self, position: impl PropIndex, val: JsValue) {
let vec = self.ensure_hash_props();
let pos = position as usize;
let pos = position.to_u32() as usize;
if pos < vec.len() {
*vec[pos] = val;
} else {
@@ -177,21 +212,22 @@ impl JsObject {
}
/// Push a value onto hash_props vec. Returns the index position.
pub fn push_prop(&mut self, val: JsValue) -> u8 {
pub fn push_prop(&mut self, val: JsValue) -> u32 {
let vec = self.ensure_hash_props();
let pos = vec.len();
vec.push(Box::new(val));
pos as u8
pos as u32
}
/// Get a stable pointer to the Box<JsValue> at position.
/// Returns None if hash_props not allocated or position out of bounds.
pub fn prop_ptr_at(&self, position: u8) -> Option<*const JsValue> {
pub fn prop_ptr_at(&self, position: impl PropIndex) -> Option<*const JsValue> {
if self.hash_props.is_null() {
return None;
}
let vec = unsafe { &*(self.hash_props as *const Vec<Box<JsValue>>) };
vec.get(position as usize).map(|b| &**b as *const JsValue)
vec.get(position.to_u32() as usize)
.map(|b| &**b as *const JsValue)
}
/// Get the length of hash_props vec (returns 0 if not allocated).
+7 -7
View File
@@ -71,8 +71,8 @@ impl ShapeStore {
new_id
}
fn lookup_position(&self, shape_id: ShapeId, prop_name: StringIndex) -> Option<u8> {
let mut depth: u8 = 0;
fn lookup_position(&self, shape_id: ShapeId, prop_name: StringIndex) -> Option<u32> {
let mut depth: u32 = 0;
let mut cursor = Some(shape_id);
while let Some(id) = cursor {
match self.get_shape(id) {
@@ -86,7 +86,7 @@ impl ShapeStore {
}
}
let total_depth = depth;
let mut step: u8 = 0;
let mut step: u32 = 0;
cursor = Some(shape_id);
while let Some(id) = cursor {
match self.get_shape(id) {
@@ -119,8 +119,8 @@ impl ShapeStore {
false
}
fn shape_prop_count(&self, shape_id: ShapeId) -> u8 {
let mut count = 0u8;
fn shape_prop_count(&self, shape_id: ShapeId) -> u32 {
let mut count = 0u32;
let mut cursor = Some(shape_id);
while let Some(id) = cursor {
match self.get_shape(id) {
@@ -151,7 +151,7 @@ pub fn make_shape(parent_id: ShapeId, prop_name: StringIndex) -> ShapeId {
store().lock().unwrap().make_shape(parent_id, prop_name)
}
pub fn lookup_position(shape_id: ShapeId, prop_name: StringIndex) -> Option<u8> {
pub fn lookup_position(shape_id: ShapeId, prop_name: StringIndex) -> Option<u32> {
store().lock().unwrap().lookup_position(shape_id, prop_name)
}
@@ -159,7 +159,7 @@ pub fn has_property(shape_id: ShapeId, prop_name: StringIndex) -> bool {
store().lock().unwrap().has_property(shape_id, prop_name)
}
pub fn shape_prop_count(shape_id: ShapeId) -> u8 {
pub fn shape_prop_count(shape_id: ShapeId) -> u32 {
store().lock().unwrap().shape_prop_count(shape_id)
}
+61 -62
View File
@@ -100,11 +100,11 @@ pub fn array_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
));
for i in 0..n_elems {
unsafe {
(*arr).set_prop_at(i as u8, vm.reg(args[1 + i]));
(*arr).set_prop_at(i, vm.reg(args[1 + i]));
}
}
unsafe {
(*arr).set_prop_count(n_elems as u8);
(*arr).set_prop_count(n_elems);
}
Ok(JsValue::from_js_object(arr))
}
@@ -112,14 +112,13 @@ pub fn array_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_push(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
let arr = unsafe { &mut *arr_ptr };
let mut len = arr.prop_count() as usize;
let mut len = arr.prop_count();
for &arg_reg in args.iter().skip(1) {
let val = vm.reg(arg_reg);
arr.set_prop_at(len as u8, val);
arr.set_prop_at(len, val);
len += 1;
}
let stored_len = (len as u8).min(31);
arr.set_prop_count(stored_len);
arr.set_prop_count(len);
Ok(JsValue::int(len as i32))
}
@@ -156,9 +155,9 @@ pub fn array_slice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let new_arr = create_new_array(vm, count);
unsafe {
for i in 0..count {
(*new_arr).set_prop_at(i as u8, arr.get_prop_at((start + i) as u8));
(*new_arr).set_prop_at(i, arr.get_prop_at(start + i));
}
(*new_arr).set_prop_count(count.min(31) as u8);
(*new_arr).set_prop_count(count);
}
Ok(JsValue::from_js_object(new_arr))
}
@@ -191,36 +190,36 @@ pub fn array_splice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let mut removed: Vec<JsValue> = Vec::new();
for i in 0..delete_count {
removed.push(arr.get_prop_at((start + i) as u8));
removed.push(arr.get_prop_at(start + i));
}
if insert_count > delete_count {
let shift = insert_count - delete_count;
for i in (start + delete_count..n).rev() {
let val = arr.get_prop_at(i as u8);
arr.set_prop_at((i + shift) as u8, val);
let val = arr.get_prop_at(i);
arr.set_prop_at(i + shift, val);
}
} else if insert_count < delete_count {
let shift = delete_count - insert_count;
for i in start + delete_count..n {
let val = arr.get_prop_at(i as u8);
arr.set_prop_at((i - shift) as u8, val);
let val = arr.get_prop_at(i);
arr.set_prop_at(i - shift, val);
}
}
for i in 0..insert_count {
arr.set_prop_at((start + i) as u8, vm.reg(args[3 + i]));
arr.set_prop_at(start + i, vm.reg(args[3 + i]));
}
let new_len = n + insert_count - delete_count;
arr.set_prop_count(new_len.min(31) as u8);
arr.set_prop_count(new_len);
let removed_arr = create_new_array(vm, removed.len());
unsafe {
for (i, val) in removed.iter().enumerate() {
(*removed_arr).set_prop_at(i as u8, *val);
(*removed_arr).set_prop_at(i, *val);
}
(*removed_arr).set_prop_count(removed.len().min(31) as u8);
(*removed_arr).set_prop_count(removed.len());
}
Ok(JsValue::from_js_object(removed_arr))
}
@@ -231,7 +230,7 @@ pub fn array_concat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = arr.prop_count() as usize;
let mut all: Vec<JsValue> = Vec::new();
for i in 0..n {
all.push(arr.get_prop_at(i as u8));
all.push(arr.get_prop_at(i));
}
for &arg_reg in args.iter().skip(1) {
let val = vm.reg(arg_reg);
@@ -242,7 +241,7 @@ pub fn array_concat(vm: &mut Vm, args: &[u8]) -> NativeResult {
if o.is_array() {
let on = o.prop_count() as usize;
for i in 0..on {
all.push(o.get_prop_at(i as u8));
all.push(o.get_prop_at(i));
}
continue;
}
@@ -253,9 +252,9 @@ pub fn array_concat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let new_arr = create_new_array(vm, all.len());
unsafe {
for (i, val) in all.iter().enumerate() {
(*new_arr).set_prop_at(i as u8, *val);
(*new_arr).set_prop_at(i, *val);
}
(*new_arr).set_prop_count(all.len().min(31) as u8);
(*new_arr).set_prop_count(all.len());
}
Ok(JsValue::from_js_object(new_arr))
}
@@ -272,7 +271,7 @@ pub fn array_join(vm: &mut Vm, args: &[u8]) -> NativeResult {
let sf = vm.kernel().string_forge().as_ref();
let parts: Vec<String> = (0..n)
.map(|i| {
let v = arr.get_prop_at(i as u8);
let v = arr.get_prop_at(i);
if v.is_undefined() || v.is_null() {
String::new()
} else {
@@ -294,7 +293,7 @@ pub fn array_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let target = vm.reg(args[1]);
for i in 0..n {
if coercion::strict_equality(arr.get_prop_at(i as u8), target) {
if coercion::strict_equality(arr.get_prop_at(i), target) {
return Ok(JsValue::int(i as i32));
}
}
@@ -310,7 +309,7 @@ pub fn array_includes(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let target = vm.reg(args[1]);
for i in 0..n {
let elem = arr.get_prop_at(i as u8);
let elem = arr.get_prop_at(i);
if elem.is_double() && target.is_double() {
let ea = elem.as_double();
let ta = target.as_double();
@@ -332,9 +331,9 @@ pub fn array_reverse(vm: &mut Vm, args: &[u8]) -> NativeResult {
let mut i = 0;
let mut j = n.saturating_sub(1);
while i < j {
let tmp = arr.get_prop_at(i as u8);
arr.set_prop_at(i as u8, arr.get_prop_at(j as u8));
arr.set_prop_at(j as u8, tmp);
let tmp = arr.get_prop_at(i);
arr.set_prop_at(i, arr.get_prop_at(j));
arr.set_prop_at(j, tmp);
i += 1;
j = j.saturating_sub(1);
}
@@ -361,7 +360,7 @@ pub fn array_flat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let o = unsafe { &*ptr };
if o.is_array() {
let on = o.prop_count() as usize;
let sub: Vec<JsValue> = (0..on).map(|i| o.get_prop_at(i as u8)).collect();
let sub: Vec<JsValue> = (0..on).map(|i| o.get_prop_at(i)).collect();
let flat = flatten(&sub, remaining_depth - 1);
out.extend(flat);
continue;
@@ -373,14 +372,14 @@ pub fn array_flat(vm: &mut Vm, args: &[u8]) -> NativeResult {
out
}
let all: Vec<JsValue> = (0..n).map(|i| arr.get_prop_at(i as u8)).collect();
let all: Vec<JsValue> = (0..n).map(|i| arr.get_prop_at(i)).collect();
let flat = flatten(&all, depth);
let new_arr = create_new_array(vm, flat.len());
unsafe {
for (i, val) in flat.iter().enumerate() {
(*new_arr).set_prop_at(i as u8, *val);
(*new_arr).set_prop_at(i, *val);
}
(*new_arr).set_prop_count(flat.len().min(31) as u8);
(*new_arr).set_prop_count(flat.len());
}
Ok(JsValue::from_js_object(new_arr))
}
@@ -401,7 +400,7 @@ pub fn array_for_each(vm: &mut Vm, args: &[u8]) -> NativeResult {
JsValue::undefined()
};
for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
let _ = invoke_native_callback(
vm,
callback_val,
@@ -429,7 +428,7 @@ pub fn array_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
};
let new_arr = create_new_array(vm, n);
for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -437,13 +436,13 @@ pub fn array_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
) {
Ok(mapped) => unsafe {
(*new_arr).set_prop_at(i as u8, mapped);
(*new_arr).set_prop_at(i, mapped);
},
Err(_) => return Err(JsValue::undefined()),
}
}
unsafe {
(*new_arr).set_prop_count(n.min(31) as u8);
(*new_arr).set_prop_count(n);
}
Ok(JsValue::from_js_object(new_arr))
}
@@ -465,7 +464,7 @@ pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult {
};
let mut kept: Vec<JsValue> = Vec::new();
for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -484,9 +483,9 @@ pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult {
let new_arr = create_new_array(vm, kept.len());
unsafe {
for (i, val) in kept.iter().enumerate() {
(*new_arr).set_prop_at(i as u8, *val);
(*new_arr).set_prop_at(i, *val);
}
(*new_arr).set_prop_count(kept.len().min(31) as u8);
(*new_arr).set_prop_count(kept.len());
}
Ok(JsValue::from_js_object(new_arr))
}
@@ -516,7 +515,7 @@ pub fn array_reduce(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
let this_val = JsValue::undefined();
for i in start_idx..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -546,7 +545,7 @@ pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult {
JsValue::undefined()
};
for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -581,7 +580,7 @@ pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult {
JsValue::undefined()
};
for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -616,7 +615,7 @@ pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult {
JsValue::undefined()
};
for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -652,7 +651,7 @@ pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
};
let mut flat: Vec<JsValue> = Vec::new();
for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -667,7 +666,7 @@ pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
if r.is_array() {
let rn = r.prop_count() as usize;
for j in 0..rn {
flat.push(r.get_prop_at(j as u8));
flat.push(r.get_prop_at(j));
}
continue;
}
@@ -681,9 +680,9 @@ pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
let new_arr = create_new_array(vm, flat.len());
unsafe {
for (i, val) in flat.iter().enumerate() {
(*new_arr).set_prop_at(i as u8, *val);
(*new_arr).set_prop_at(i, *val);
}
(*new_arr).set_prop_count(flat.len().min(31) as u8);
(*new_arr).set_prop_count(flat.len());
}
Ok(JsValue::from_js_object(new_arr))
}
@@ -710,14 +709,14 @@ pub fn array_unshift(vm: &mut Vm, args: &[u8]) -> NativeResult {
let len = arr.prop_count();
let n_items = args.len().saturating_sub(1);
for i in (0..len as usize).rev() {
let v = arr.get_prop_at(i as u8);
arr.set_prop_at(i as u8 + n_items as u8, v);
let v = arr.get_prop_at(i);
arr.set_prop_at(i + n_items, v);
}
for (j, &arg_reg) in args.iter().skip(1).enumerate() {
arr.set_prop_at(j as u8, vm.reg(arg_reg));
arr.set_prop_at(j, vm.reg(arg_reg));
}
let new_len = (len as usize + n_items).min(31);
arr.set_prop_count(new_len as u8);
let new_len = len as usize + n_items;
arr.set_prop_count(new_len);
Ok(JsValue::int(new_len as i32))
}
@@ -743,7 +742,7 @@ pub fn array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult {
len
};
for i in start..end {
arr.set_prop_at(i as u8, value);
arr.set_prop_at(i, value);
}
Ok(vm.reg(args[0]))
}
@@ -776,8 +775,8 @@ pub fn array_copy_within(vm: &mut Vm, args: &[u8]) -> NativeResult {
if to >= len {
break;
}
let v = arr.get_prop_at(from as u8);
arr.set_prop_at(to as u8, v);
let v = arr.get_prop_at(from);
arr.set_prop_at(to, v);
to += 1;
}
Ok(vm.reg(args[0]))
@@ -798,7 +797,7 @@ pub fn array_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
if index < 0 || index >= len {
return Ok(JsValue::undefined());
}
Ok(arr.get_prop_at(index as u8))
Ok(arr.get_prop_at(index))
}
pub fn array_last_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
@@ -811,7 +810,7 @@ pub fn array_last_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
JsValue::undefined()
};
for i in (0..len).rev() {
if coercion::strict_equality(arr.get_prop_at(i as u8), search) {
if coercion::strict_equality(arr.get_prop_at(i), search) {
return Ok(JsValue::int(i));
}
}
@@ -831,7 +830,7 @@ pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult {
JsValue::undefined()
};
for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -862,7 +861,7 @@ pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult {
JsValue::undefined()
};
for i in (0..n).rev() {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -893,10 +892,10 @@ pub fn array_reduce_right(vm: &mut Vm, args: &[u8]) -> NativeResult {
if n == 0 {
return Err(JsValue::undefined());
}
(unsafe { (*arr_ptr).get_prop_at(n as u8 - 1) }, n as i32 - 2)
(unsafe { (*arr_ptr).get_prop_at(n - 1) }, n as i32 - 2)
};
for i in (0..=start_idx).rev() {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
@@ -914,14 +913,14 @@ pub fn array_sort(vm: &mut Vm, _args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(_args[0]))?;
let arr = unsafe { &mut *arr_ptr };
let len = arr.prop_count() as usize;
let mut vals: Vec<JsValue> = (0..len).map(|i| arr.get_prop_at(i as u8)).collect();
let mut vals: Vec<JsValue> = (0..len).map(|i| arr.get_prop_at(i)).collect();
vals.sort_by(|a, b| {
let sa = coercion::to_string(vm.kernel().string_forge().as_ref(), *a);
let sb = coercion::to_string(vm.kernel().string_forge().as_ref(), *b);
sa.cmp(&sb)
});
for (i, &v) in vals.iter().enumerate() {
arr.set_prop_at(i as u8, v);
arr.set_prop_at(i, v);
}
Ok(vm.reg(_args[0]))
}
+2 -2
View File
@@ -58,7 +58,7 @@ fn value_to_jsvalue(vm: &mut Vm, val: &serde_json::Value) -> JsValue {
for (i, v) in arr.iter().enumerate() {
let jsv = value_to_jsvalue(vm, v);
unsafe {
(*array_obj).set_prop_at(i as u8, jsv);
(*array_obj).set_prop_at(i, jsv);
}
}
JsValue::from_js_object(array_obj)
@@ -216,7 +216,7 @@ fn stringify_array(
if i > 0 {
out.push(',');
}
let val = obj.get_prop_at(i as u8);
let val = obj.get_prop_at(i);
let is_function = val.is_object() && {
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
+25 -18
View File
@@ -7,10 +7,10 @@ use crate::coercion;
use crate::native::NativeResult;
use crate::vm::Vm;
pub(crate) fn walk_own_keys(vm: &Vm, obj: &JsObject) -> Vec<(u32, u8)> {
let mut keys: Vec<(u32, u8)> = Vec::new();
pub(crate) fn walk_own_keys(vm: &Vm, obj: &JsObject) -> Vec<(u32, u32)> {
let mut keys: Vec<(u32, u32)> = Vec::new();
let shape_id = obj.shape_id();
let mut pos: u8 = 0;
let mut pos: u32 = 0;
let mut shape_ids = Vec::new();
let mut cursor = Some(shape_id);
while let Some(id) = cursor {
@@ -67,7 +67,7 @@ pub fn object_keys(vm: &mut Vm, args: &[u8]) -> NativeResult {
.collect();
}
let n = key_names.len().min(255);
let n = key_names.len();
let array_proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.epoch().alloc(JsObject::new_array(
EMPTY_SHAPE_ID,
@@ -75,12 +75,15 @@ pub fn object_keys(vm: &mut Vm, args: &[u8]) -> NativeResult {
n,
vm.epoch().bump(),
));
for k in key_names.iter().take(n) {
for (i, k) in key_names.iter().enumerate() {
let str_val = vm.intern(k);
unsafe {
(*arr).ensure_hash_props().push(Box::new(str_val));
(*arr).set_prop_at(i, str_val);
}
}
unsafe {
(*arr).set_prop_count(n);
}
Ok(JsValue::from_js_object(arr))
}
@@ -334,7 +337,7 @@ pub fn object_get_own_property_names(vm: &mut Vm, args: &[u8]) -> NativeResult {
.collect()
};
let n = key_names.len().min(255);
let n = key_names.len();
let array_proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.epoch().alloc(JsObject::new_array(
EMPTY_SHAPE_ID,
@@ -342,12 +345,15 @@ pub fn object_get_own_property_names(vm: &mut Vm, args: &[u8]) -> NativeResult {
n,
vm.epoch().bump(),
));
for k in key_names.iter().take(n) {
for (i, k) in key_names.iter().enumerate() {
let str_val = vm.intern(k);
unsafe {
(*arr).ensure_hash_props().push(Box::new(str_val));
(*arr).set_prop_at(i, str_val);
}
}
unsafe {
(*arr).set_prop_count(n);
}
Ok(JsValue::from_js_object(arr))
}
@@ -399,7 +405,7 @@ pub fn object_entries(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj_ptr = require_obj_arg(vm, args, "entries")?;
let obj = unsafe { &*obj_ptr };
let keys = walk_own_keys(vm, obj);
let n = keys.len().min(255);
let n = keys.len();
let array_proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.epoch().alloc(JsObject::new_array(
EMPTY_SHAPE_ID,
@@ -407,7 +413,7 @@ pub fn object_entries(vm: &mut Vm, args: &[u8]) -> NativeResult {
n,
vm.epoch().bump(),
));
for (_i, (si, offset)) in keys.iter().enumerate().take(n) {
for (i, (si, offset)) in keys.iter().enumerate() {
let key_str = vm.kernel().string_forge().lookup(*si).unwrap_or_default();
let key_val = vm.intern(&key_str);
let val = obj.get_prop_at(*offset);
@@ -421,11 +427,12 @@ pub fn object_entries(vm: &mut Vm, args: &[u8]) -> NativeResult {
(*pair).set_prop_at(0, key_val);
(*pair).set_prop_at(1, val);
(*pair).set_prop_count(2);
(*arr)
.ensure_hash_props()
.push(Box::new(JsValue::from_js_object(pair)));
(*arr).set_prop_at(i, JsValue::from_js_object(pair));
}
}
unsafe {
(*arr).set_prop_count(n);
}
Ok(JsValue::from_js_object(arr))
}
@@ -433,7 +440,7 @@ pub fn object_values(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj_ptr = require_obj_arg(vm, args, "values")?;
let obj = unsafe { &*obj_ptr };
let keys = walk_own_keys(vm, obj);
let n = keys.len().min(255);
let n = keys.len();
let array_proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.epoch().alloc(JsObject::new_array(
EMPTY_SHAPE_ID,
@@ -441,14 +448,14 @@ pub fn object_values(vm: &mut Vm, args: &[u8]) -> NativeResult {
n,
vm.epoch().bump(),
));
for (i, (_si, offset)) in keys.iter().enumerate().take(n) {
for (i, (_si, offset)) in keys.iter().enumerate() {
let val = obj.get_prop_at(*offset);
unsafe {
(*arr).set_prop_at(i as u8, val);
(*arr).set_prop_at(i, val);
}
}
unsafe {
(*arr).set_prop_count(n as u8);
(*arr).set_prop_count(n);
}
Ok(JsValue::from_js_object(arr))
}
+4 -4
View File
@@ -13,7 +13,7 @@ fn this_string(vm: &Vm, args: &[u8]) -> String {
fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
let proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject;
let n = parts.len().min(31);
let n = parts.len();
let arr = vm.epoch().alloc(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(proto),
@@ -21,11 +21,11 @@ fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
vm.epoch().bump(),
));
unsafe {
for (i, s) in parts.iter().enumerate().take(n) {
for (i, s) in parts.iter().enumerate() {
let sv = vm.intern(s);
(*arr).set_prop_at(i as u8, sv);
(*arr).set_prop_at(i, sv);
}
(*arr).set_prop_count(n as u8);
(*arr).set_prop_count(n);
}
JsValue::from_js_object(arr)
}
+43 -23
View File
@@ -105,7 +105,11 @@ macro_rules! member_read_prop {
if cached_shape_id != 0 && cached_shape_id == $obj.shape_id() && cached_ptr != 0 {
unsafe { *(cached_ptr as *const JsValue) }
} else if let Some(template) = $self.kernel.prop_forge().get_template($obj.shape_id()) {
if let Some(ptr) = $self.template_prop_ptr($obj, &template) {
if template.prop_name != $prop_name_si {
$self
.resolve_property($obj, $prop_name_si)
.unwrap_or(JsValue::undefined())
} else if let Some(ptr) = $self.template_prop_ptr($obj, &template) {
$self.bytecode[$self.pc - 3] = $obj.shape_id() & 0x00FF_FFFF;
$self.bytecode[$self.pc - 2] = ptr as u32;
$self.bytecode[$self.pc - 1] = (ptr as u64 >> 32) as u32;
@@ -116,15 +120,16 @@ macro_rules! member_read_prop {
.unwrap_or(JsValue::undefined())
}
} else if let Some(val) = $self.resolve_property($obj, $prop_name_si) {
let pos = $self
if let Some(pos) = $self
.kernel
.shape_forge()
.lookup_position($obj.shape_id(), $prop_name_si)
.unwrap_or(0);
if let Some(ptr) = $obj.prop_ptr_at(pos) {
$self.bytecode[$self.pc - 3] = $obj.shape_id() & 0x00FF_FFFF;
$self.bytecode[$self.pc - 2] = ptr as u32;
$self.bytecode[$self.pc - 1] = (ptr as u64 >> 32) as u32;
{
if let Some(ptr) = $obj.prop_ptr_at(pos) {
$self.bytecode[$self.pc - 3] = $obj.shape_id() & 0x00FF_FFFF;
$self.bytecode[$self.pc - 2] = ptr as u32;
$self.bytecode[$self.pc - 1] = (ptr as u64 >> 32) as u32;
}
}
val
} else {
@@ -397,6 +402,10 @@ impl Vm {
}
fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
let length_si = self.kernel.string_forge().intern("length").0;
if obj.is_array() && prop_name_si == length_si {
return Some(JsValue::int(obj.prop_count() as i32));
}
if let Some(pos) = self
.kernel
.shape_forge()
@@ -459,6 +468,7 @@ impl Vm {
new_shape_id,
PropTemplate {
shape_id: new_shape_id,
prop_name: prop_name_si,
position: new_pos,
generation: obj.generation(),
},
@@ -1023,14 +1033,20 @@ impl Vm {
} else if let Some(template) =
self.kernel.prop_forge().get_template(obj.shape_id())
{
let pos = template.position as usize;
if let Some(vec) = obj.hash_props_vec() {
if pos < vec.len() {
let ptr = &*vec[pos] as *const JsValue;
self.bytecode[self.pc - 3] = obj.shape_id() & 0x00FF_FFFF;
self.bytecode[self.pc - 2] = ptr as u32;
self.bytecode[self.pc - 1] = (ptr as u64 >> 32) as u32;
self.regs[a] = unsafe { *(ptr) };
if template.prop_name == prop_name_si {
let pos = template.position as usize;
if let Some(vec) = obj.hash_props_vec() {
if pos < vec.len() {
let ptr = &*vec[pos] as *const JsValue;
self.bytecode[self.pc - 3] = obj.shape_id() & 0x00FF_FFFF;
self.bytecode[self.pc - 2] = ptr as u32;
self.bytecode[self.pc - 1] = (ptr as u64 >> 32) as u32;
self.regs[a] = unsafe { *(ptr) };
} else {
self.regs[a] = self
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
}
} else {
self.regs[a] = self
.resolve_property(obj, prop_name_si)
@@ -1042,15 +1058,16 @@ impl Vm {
.unwrap_or(JsValue::undefined());
}
} else if let Some(val) = self.resolve_property(obj, prop_name_si) {
let pos = self
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
.unwrap_or(0);
if let Some(ptr) = obj.prop_ptr_at(pos) {
self.bytecode[self.pc - 3] = obj.shape_id() & 0x00FF_FFFF;
self.bytecode[self.pc - 2] = ptr as u32;
self.bytecode[self.pc - 1] = (ptr as u64 >> 32) as u32;
{
if let Some(ptr) = obj.prop_ptr_at(pos) {
self.bytecode[self.pc - 3] = obj.shape_id() & 0x00FF_FFFF;
self.bytecode[self.pc - 2] = ptr as u32;
self.bytecode[self.pc - 1] = (ptr as u64 >> 32) as u32;
}
}
self.regs[a] = val;
} else {
@@ -1105,6 +1122,7 @@ impl Vm {
new_shape_id,
PropTemplate {
shape_id: new_shape_id,
prop_name: prop_name_si,
position: new_pos,
generation: obj.generation(),
},
@@ -1221,9 +1239,9 @@ impl Vm {
if obj_ptr.is_null() {
throw_err!(self, TypeError, "SET_ELEM on non-object");
}
let idx = self.regs[a].as_int() as usize;
let idx = self.regs[a].as_int().max(0) as u32;
let obj = unsafe { &mut *obj_ptr };
obj.set_prop_at(idx as u8, self.regs[b]);
obj.set_prop_at(idx, self.regs[b]);
}
OpCode::COMPOUND_ADD => {
@@ -1507,6 +1525,7 @@ impl Vm {
new_shape_id,
PropTemplate {
shape_id: new_shape_id,
prop_name: prop_name_si,
position: new_pos,
generation: obj.generation(),
},
@@ -1557,6 +1576,7 @@ impl Vm {
new_shape_id,
PropTemplate {
shape_id: new_shape_id,
prop_name: prop_name_si,
position: new_pos,
generation: obj.generation(),
},
@@ -38,6 +38,21 @@ fn array_push_returns_true_length_beyond_31() {
assert_eq!(result.as_int(), 32);
}
#[test]
fn array_length_tracks_more_than_31_elements() {
let result = eval("var a=[]; for (var i=0;i<40;i=i+1) a.push(i); a.length").unwrap();
assert_eq!(result.as_int(), 40);
}
#[test]
fn array_push_preserves_slots_beyond_255() {
let result = eval("var a=[]; for (var i=0;i<257;i=i+1) a.push(i); a").unwrap();
let obj = unsafe { &*result.as_js_object_ptr() };
assert_eq!(obj.prop_count(), 257);
let tail = obj.get_prop_at(256);
assert_eq!(tail.as_double(), 256.0);
}
#[test]
fn array_pop_returns_last() {
let result = eval("[1,2,3].pop()").unwrap();
@@ -16,11 +16,15 @@ fn eval(source: &str) -> Result<oxide_types::value::JsValue, String> {
fn object_keys_empty() {
let result = eval("Object.keys({})").unwrap();
assert!(result.is_object());
let obj = unsafe { &*result.as_js_object_ptr() };
assert_eq!(obj.prop_count(), 0);
}
#[test]
fn object_keys_has_own_property() {
eval("Object.keys({a:1})").unwrap();
let result = eval("Object.keys({a:1,b:2})").unwrap();
let obj = unsafe { &*result.as_js_object_ptr() };
assert_eq!(obj.prop_count(), 2);
}
#[test]
@@ -68,10 +72,14 @@ fn object_has_own_true() {
fn object_entries_returns_array() {
let result = eval("Object.entries({a:1,b:2})").unwrap();
assert!(result.is_object());
let obj = unsafe { &*result.as_js_object_ptr() };
assert_eq!(obj.prop_count(), 2);
}
#[test]
fn object_values_returns_array() {
let result = eval("Object.values({a:1,b:2})").unwrap();
assert!(result.is_object());
let obj = unsafe { &*result.as_js_object_ptr() };
assert_eq!(obj.prop_count(), 2);
}
@@ -227,6 +227,9 @@ fn string_split_with_regexp() {
let mut vm = Vm::new();
let result = eval(&mut vm, "'a,b,c'.split(/,/)").unwrap();
assert!(result.is_object());
let obj = unsafe { &*result.as_js_object_ptr() };
assert_eq!(obj.prop_count(), 3);
assert_eq!(to_str(&vm, obj.get_prop_at(1)), "b");
}
// --- toString ---
@@ -143,6 +143,10 @@ fn string_split_comma() {
let mut vm = Vm::new();
let result = eval(&mut vm, "'a,b,c'.split(',')").unwrap();
assert!(result.is_object());
let obj = unsafe { &*result.as_js_object_ptr() };
assert_eq!(obj.prop_count(), 3);
assert_eq!(to_str(&vm, obj.get_prop_at(0)), "a");
assert_eq!(to_str(&vm, obj.get_prop_at(2)), "c");
}
#[test]
+3 -8
View File
@@ -27,13 +27,7 @@ fn json_parse_object() {
let result = eval(&mut vm, "JSON.parse('{\"a\":1,\"b\":2}')").unwrap();
assert!(result.is_object());
let obj = unsafe { &*result.as_js_object_ptr() };
let mut count = 0;
for i in 0..obj.prop_vec_len() {
if !obj.get_prop_at(i as u8).is_undefined() {
count += 1;
}
}
assert_eq!(count, 2);
assert_eq!(obj.prop_count(), 2);
}
#[test]
@@ -43,7 +37,8 @@ fn json_parse_array() {
assert!(result.is_object());
let obj = unsafe { &*result.as_js_object_ptr() };
assert!(obj.is_array());
assert_eq!(obj.prop_vec_len(), 3);
assert_eq!(obj.prop_count(), 3);
assert_eq!(obj.get_prop_at(2).as_double(), 3.0);
}
#[test]