fix(storage): build name arrays without inherited numeric setters

This commit is contained in:
ldm0
2026-10-07 14:22:05 +08:00
committed by Donough Liu
parent fea8bba3d2
commit ae2e7ca54d
3 changed files with 52 additions and 7 deletions
@@ -3345,14 +3345,15 @@ fn strings_to_array<'s>(
scope: &mut v8::PinScope<'s, '_>,
values: &[String],
) -> v8::Local<'s, v8::Array> {
let array = v8::Array::new(scope, values.len() as i32);
for (index, value) in values.iter().enumerate() {
let Some(value) = v8_string(scope, value) else {
continue;
};
let _ = array.set_index(scope, index as u32, value.into());
let mut elements = Vec::with_capacity(values.len());
for value in values {
elements.push(
v8_string(scope, value)
.expect("storage name should allocate")
.into(),
);
}
array
v8::Array::new_with_elements(scope, &elements)
}
pub(in crate::context_bootstrap) fn navigator_ua_data_to_json_callback<'s>(
@@ -2167,6 +2167,7 @@ mod readable_algorithm_arrays;
mod remote_playback_interface;
mod rendering_update;
mod script_terminal_completion;
mod storage_dense_name_arrays;
mod streams;
mod svg_filter_interfaces;
mod svg_number_validation;
@@ -0,0 +1,43 @@
use super::*;
#[tokio::test(flavor = "current_thread")]
async fn storage_name_arrays_ignore_numeric_prototype_properties() {
let loader = static_http_loader(std::iter::empty::<String>());
let mut vm = new_storage_page_task_executor_test_vm_with_loader(
"https://storage-dense-name-arrays.test/",
&loader,
);
assert_eq!(vm.eval(r#"(() => {
const own=Object.getOwnPropertyDescriptor,define=Object.defineProperty;
globalThis.__storageNameArrayFacts={complete:false,error:null};
(async()=>{
const apis=[navigator.storageBuckets,caches];
for(let a=0;a<apis.length;a++) {
const api=apis[a],name='dense-names-'+a,second=name+'-second';await api.open(name);await api.open(second);
const expected=await api.keys();if(expected.length<2)throw Error('fixture needs both indices');
for(const parent of [Array.prototype,Object.prototype])for(const index of ['0','1'])for(const kind of ['setter','throwing','getter','readonly']) {
const old=own(parent,index);let calls=0,result;
const descriptor=kind==='readonly'?{configurable:true,value:99,writable:false}:kind==='getter'?{configurable:true,get(){calls++;throw 42;}}:{configurable:true,set(){calls++;if(kind==='throwing')throw 43;}};
define(parent,index,descriptor);
try{result=await api.keys();}finally{if(old)define(parent,index,old);else delete parent[index];}
if(calls||!Array.isArray(result)||Object.isFrozen(result)||JSON.stringify(result)!==JSON.stringify(expected))throw Error('dense names');
for(let i=0;i<result.length;i++){const d=own(result,String(i));if(!d||!d.writable||!d.configurable||!d.enumerable||!('value' in d))throw Error('own item');}
}
await api.delete(name);await api.delete(second);
}
})().catch(error=>{__storageNameArrayFacts.error=String(error);}).finally(()=>{__storageNameArrayFacts.complete=true;});
return 'ok';
})()"#).expect("native regression"), "ok");
advance_page_task_executor_until_eval_equals(
&mut vm,
&loader,
"String(__storageNameArrayFacts.complete)",
"true",
"dense-storage-names",
)
.await;
assert_eq!(
vm.eval("String(__storageNameArrayFacts.error)").unwrap(),
"null"
);
}