mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 04:54:45 +00:00
JavaScript: toLocaleString writes the primitive value of Number, String and Boolean objects
`[1, 'x', true].toLocaleString()` gave `[object Number],[object String],[object Boolean]` because the wrappers' prototypes have no `toString` on this branch and the lookup reached Object.prototype.toString. Object.prototype.toLocaleString now returns the string form of the object's primitive value when `toString` is not found on the object or on its chain up to its standard prototype. A `toString` on the object or that prototype is still called.
This commit is contained in:
@@ -360,8 +360,28 @@ fn object_to_string(interp: &mut Interp, this: Value, _: &[Value], _: bool) -> J
|
||||
Ok(Value::str(&format!("[object {class}]")))
|
||||
}
|
||||
|
||||
/// The string form of a Number, String or Boolean object's [[PrimitiveValue]] (sections 8.6.2 and
|
||||
/// 9.9), when `toString` is not found on the object or on its prototype chain up to and including
|
||||
/// its standard prototype. Returns `None` otherwise, and for any other object.
|
||||
fn boxed_primitive_string(interp: &mut Interp, o: ObjId) -> JsResult<Option<Value>> {
|
||||
let (primitive, proto) = match interp.heap.object(o).map(|x| &x.class) {
|
||||
Some(Class::Number(n)) => (Value::Number(*n), interp.intrinsics.number_proto),
|
||||
Some(Class::String(s)) => (Value::String(s.clone()), interp.intrinsics.string_proto),
|
||||
Some(Class::Boolean(b)) => (Value::Bool(*b), interp.intrinsics.boolean_proto),
|
||||
_ => return Ok(None),
|
||||
};
|
||||
if !json::reaches_prototype(interp, o, proto, "toString")? {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(Value::String(interp.primitive_to_str(&primitive))))
|
||||
}
|
||||
|
||||
/// `Object.prototype.toLocaleString` (section 15.2.4.3).
|
||||
fn object_to_locale_string(interp: &mut Interp, this: Value, _: &[Value], _: bool) -> JsResult<Value> {
|
||||
let o = interp.to_object(&this)?;
|
||||
if let Some(s) = boxed_primitive_string(interp, o)? {
|
||||
return Ok(s);
|
||||
}
|
||||
let receiver = Value::Object(o);
|
||||
let f = interp.get_from(o, &key("toString"), &receiver)?;
|
||||
if !interp.is_callable(&f) {
|
||||
|
||||
@@ -574,7 +574,7 @@ fn unwrap_primitive(interp: &mut Interp, v: &Value, as_string: bool) -> JsResult
|
||||
|
||||
/// Whether looking up `name` from `o` reaches the object `proto` on the prototype chain before
|
||||
/// any object on the way, `proto` included, has its own property `name`.
|
||||
fn reaches_prototype(interp: &mut Interp, o: ObjId, proto: ObjId, name: &str) -> JsResult<bool> {
|
||||
pub(super) fn reaches_prototype(interp: &mut Interp, o: ObjId, proto: ObjId, name: &str) -> JsResult<bool> {
|
||||
let k = key(name);
|
||||
let mut cur = Some(o);
|
||||
while let Some(id) = cur {
|
||||
|
||||
@@ -380,6 +380,21 @@ fn to_locale_string_calls_to_string_on_the_boxed_value() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_locale_string_writes_the_primitive_of_a_boxed_value() {
|
||||
assert_eq!(string("[1, 'x', true, false, 1.5].toLocaleString()"), "1,x,true,false,1.5");
|
||||
assert_eq!(string("Object.prototype.toLocaleString.call(5)"), "5");
|
||||
assert_eq!(string("Object.prototype.toLocaleString.call(Object('s'))"), "s");
|
||||
// Object.prototype.toString comes after the standard prototypes on the chain.
|
||||
assert_eq!(string("Object.prototype.toString = function () { return 'z'; }; [1, 'x', true].toLocaleString()"), "1,x,true");
|
||||
// A toString on the object or on its standard prototype is called.
|
||||
assert_eq!(string("var n = Object(1); n.toString = function () { return 'own'; }; [n].toLocaleString()"), "own");
|
||||
assert_eq!(string("Object.getPrototypeOf(true).toString = function () { return 'p'; }; [true].toLocaleString()"), "p");
|
||||
assert_eq!(string("Object.getPrototypeOf(1).toString = Object.prototype.toString; [1].toLocaleString()"), "[object Number]");
|
||||
// Other objects still reach Object.prototype.toString.
|
||||
assert_eq!(string("Object.prototype.toLocaleString.call({})"), "[object 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
|
||||
|
||||
Reference in New Issue
Block a user