mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 06:06:38 +00:00
JavaScript: toLocaleString calls toString on the boxed value, and overwriting an element reserves no heap
Object.prototype.toLocaleString passed the primitive this value to toString; section 15.2.4.3 calls it on ToObject(this), so a strict toString now sees the object. The array methods' element write reserved room for a new element even when it only replaced an own property, so a heap at its limit stopped reverse or sort writes that add nothing.
This commit is contained in:
@@ -159,12 +159,17 @@ fn has_at(interp: &mut Interp, o: ObjId, k: f64) -> JsResult<bool> {
|
||||
/// The estimated bytes a new element adds.
|
||||
const ELEMENT_BYTES: usize = PROP_BYTES + 32 + 2 * 10;
|
||||
|
||||
/// `o[k] = v` as the array methods assign: a failure throws. `v` stays rooted while heap is
|
||||
/// reserved for it, since the caller may hold it nowhere else.
|
||||
fn put_at(interp: &mut Interp, o: ObjId, k: f64, v: Value) -> JsResult<()> {
|
||||
/// `o[k] = v` as the array methods assign: a failure throws. When `o` has no own property `k`, `v`
|
||||
/// stays rooted while heap is reserved for the new element, since the caller may hold it nowhere
|
||||
/// else; overwriting an own property reserves nothing.
|
||||
pub(crate) fn put_at(interp: &mut Interp, o: ObjId, k: f64, v: Value) -> JsResult<()> {
|
||||
let k = key_of(k);
|
||||
if interp.heap.object(o).is_some_and(|obj| obj.props.contains(&k)) {
|
||||
return interp.put(o, k, v, true);
|
||||
}
|
||||
interp.rooted(std::slice::from_ref(&v), |i| {
|
||||
i.reserve_heap(ELEMENT_BYTES)?;
|
||||
i.put(o, key_of(k), v.clone(), true)
|
||||
i.put(o, k.clone(), v.clone(), true)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -357,11 +362,12 @@ fn object_to_string(interp: &mut Interp, this: Value, _: &[Value], _: bool) -> J
|
||||
|
||||
fn object_to_locale_string(interp: &mut Interp, this: Value, _: &[Value], _: bool) -> JsResult<Value> {
|
||||
let o = interp.to_object(&this)?;
|
||||
let f = interp.get_from(o, &key("toString"), &this)?;
|
||||
let receiver = Value::Object(o);
|
||||
let f = interp.get_from(o, &key("toString"), &receiver)?;
|
||||
if !interp.is_callable(&f) {
|
||||
return Err(interp.type_error("toString is not a function"));
|
||||
}
|
||||
interp.call(&f, &this, &[])
|
||||
interp.call(&f, &receiver, &[])
|
||||
}
|
||||
|
||||
fn object_value_of(interp: &mut Interp, this: Value, _: &[Value], _: bool) -> JsResult<Value> {
|
||||
|
||||
@@ -369,6 +369,38 @@ fn a_heap_limit_smaller_than_the_built_ins_still_installs_every_built_in() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_locale_string_calls_to_string_on_the_boxed_value() {
|
||||
assert_eq!(
|
||||
string(
|
||||
"Object.defineProperty(Object.getPrototypeOf(Object(5)), 'toString', {value: function () { 'use strict'; return typeof this; }}); \
|
||||
Object.prototype.toLocaleString.call(5)"
|
||||
),
|
||||
"object"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_methods_overwriting_elements_need_no_heap_for_new_ones() {
|
||||
// With the heap exactly at its limit, an array method's write over an existing element
|
||||
// succeeds, and a write that adds an element stops with the heap limit.
|
||||
use crate::heap::key;
|
||||
use crate::interp::Abrupt;
|
||||
use crate::value::Value;
|
||||
let mut realm = small(Limits::default());
|
||||
assert_eq!(realm.run("var a = [1, 2]; 0"), Ok(JsValue::Number(0.0)));
|
||||
let interp = realm.interp();
|
||||
let global = interp.global;
|
||||
let a = match interp.get_from(global, &key("a"), &Value::Object(global)) {
|
||||
Ok(Value::Object(a)) => a,
|
||||
other => panic!("{other:?}"),
|
||||
};
|
||||
interp.collect_garbage();
|
||||
interp.limits.heap_bytes = interp.heap.bytes;
|
||||
assert!(crate::builtins::put_at(interp, a, 0.0, Value::Number(5.0)).is_ok());
|
||||
assert!(matches!(crate::builtins::put_at(interp, a, 2.0, Value::Number(5.0)), Err(Abrupt::Limit(LimitKind::Heap))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_realm_whose_built_ins_failed_to_install_runs_nothing() {
|
||||
let mut realm = Realm::new();
|
||||
|
||||
Reference in New Issue
Block a user