diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 7e488e1b26..429303797e 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -115,9 +115,33 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString #[allow(clippy::wrong_self_convention)] - pub fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result { - // FIXME: it should not display the object. - Ok(this.display().to_string().into()) + pub fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result { + if this.is_undefined() { + Ok("[object Undefined]".into()) + } else if this.is_null() { + Ok("[object Null]".into()) + } else { + let gc_o = this.to_object(ctx)?; + let o = gc_o.borrow(); + let builtin_tag = match &o.data { + ObjectData::Array => "Array", + // TODO: Arguments Exotic Objects are currently not supported + ObjectData::Function(_) => "Function", + ObjectData::Error => "Error", + ObjectData::Boolean(_) => "Boolean", + ObjectData::Number(_) => "Number", + ObjectData::String(_) => "String", + ObjectData::Date(_) => "Date", + ObjectData::RegExp(_) => "RegExp", + _ => "Object", + }; + + let tag = o.get(&ctx.well_known_symbols().to_string_tag_symbol().into()); + + let tag_str = tag.as_string().map(|s| s.as_str()).unwrap_or(builtin_tag); + + Ok(format!("[object {}]", tag_str).into()) + } } /// `Object.prototype.hasOwnPrototype( property )` diff --git a/boa/src/builtins/object/tests.rs b/boa/src/builtins/object/tests.rs index 7ea3f6478b..5c42f85fc9 100644 --- a/boa/src/builtins/object/tests.rs +++ b/boa/src/builtins/object/tests.rs @@ -55,7 +55,7 @@ fn object_create_with_number() { #[test] #[ignore] -// to test on __proto__ somehow. __proto__ getter is not working as expected currently +// TODO: to test on __proto__ somehow. __proto__ getter is not working as expected currently fn object_create_with_function() { let mut engine = Context::new(); @@ -135,3 +135,45 @@ fn object_property_is_enumerable() { ); assert_eq!(forward(&mut engine, r#"x.propertyIsEnumerable()"#), "false",) } + +#[test] +fn object_to_string() { + let mut ctx = Context::new(); + let init = r#" + let u = undefined; + let n = null; + let a = []; + Array.prototype.toString = Object.prototype.toString; + let f = () => {}; + Function.prototype.toString = Object.prototype.toString; + let b = Boolean(); + Boolean.prototype.toString = Object.prototype.toString; + let i = Number(42); + Number.prototype.toString = Object.prototype.toString; + let s = String('boa'); + String.prototype.toString = Object.prototype.toString; + let d = new Date(Date.now()); + Date.prototype.toString = Object.prototype.toString; + let re = /boa/; + RegExp.prototype.toString = Object.prototype.toString; + let o = Object(); + "#; + eprintln!("{}", forward(&mut ctx, init)); + // TODO: need Function.prototype.call to be implemented + // assert_eq!( + // forward(&mut ctx, "Object.prototype.toString.call(u)"), + // "\"[object Undefined]\"" + // ); + // assert_eq!( + // forward(&mut ctx, "Object.prototype.toString.call(n)"), + // "\"[object Null]\"" + // ); + assert_eq!(forward(&mut ctx, "a.toString()"), "\"[object Array]\""); + assert_eq!(forward(&mut ctx, "f.toString()"), "\"[object Function]\""); + assert_eq!(forward(&mut ctx, "b.toString()"), "\"[object Boolean]\""); + assert_eq!(forward(&mut ctx, "i.toString()"), "\"[object Number]\""); + assert_eq!(forward(&mut ctx, "s.toString()"), "\"[object String]\""); + assert_eq!(forward(&mut ctx, "d.toString()"), "\"[object Date]\""); + assert_eq!(forward(&mut ctx, "re.toString()"), "\"[object RegExp]\""); + assert_eq!(forward(&mut ctx, "o.toString()"), "\"[object Object]\""); +}