|
@@ -1,5 +1,6 @@
|
|
/*
|
|
/*
|
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
|
|
|
+ * Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
|
*
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
*/
|
|
@@ -73,39 +74,71 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::to_string)
|
|
{
|
|
{
|
|
auto this_value = vm.this_value(global_object);
|
|
auto this_value = vm.this_value(global_object);
|
|
|
|
|
|
|
|
+ // 1. If the this value is undefined, return "[object Undefined]".
|
|
if (this_value.is_undefined())
|
|
if (this_value.is_undefined())
|
|
return js_string(vm, "[object Undefined]");
|
|
return js_string(vm, "[object Undefined]");
|
|
|
|
+
|
|
|
|
+ // 2. If the this value is null, return "[object Null]".
|
|
if (this_value.is_null())
|
|
if (this_value.is_null())
|
|
return js_string(vm, "[object Null]");
|
|
return js_string(vm, "[object Null]");
|
|
|
|
|
|
- auto* this_object = this_value.to_object(global_object);
|
|
|
|
- VERIFY(this_object);
|
|
|
|
|
|
+ // 3. Let O be ! ToObject(this value).
|
|
|
|
+ auto* object = this_value.to_object(global_object);
|
|
|
|
+ VERIFY(object);
|
|
|
|
+
|
|
|
|
+ // 4. Let isArray be ? IsArray(O).
|
|
|
|
+ auto is_array = Value(object).is_array(global_object);
|
|
|
|
+ if (vm.exception())
|
|
|
|
+ return {};
|
|
|
|
+
|
|
|
|
+ String builtin_tag;
|
|
|
|
+
|
|
|
|
+ // 5. If isArray is true, let builtinTag be "Array".
|
|
|
|
+ if (is_array)
|
|
|
|
+ builtin_tag = "Array";
|
|
|
|
+ // 6. Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".
|
|
|
|
+ else if (object->has_parameter_map())
|
|
|
|
+ builtin_tag = "Arguments";
|
|
|
|
+ // 7. Else if O has a [[Call]] internal method, let builtinTag be "Function".
|
|
|
|
+ else if (object->is_function())
|
|
|
|
+ builtin_tag = "Function";
|
|
|
|
+ // 8. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
|
|
|
|
+ else if (is<Error>(object))
|
|
|
|
+ builtin_tag = "Error";
|
|
|
|
+ // 9. Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
|
|
|
|
+ else if (is<BooleanObject>(object))
|
|
|
|
+ builtin_tag = "Boolean";
|
|
|
|
+ // 10. Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
|
|
|
|
+ else if (is<NumberObject>(object))
|
|
|
|
+ builtin_tag = "Number";
|
|
|
|
+ // 11. Else if O has a [[StringData]] internal slot, let builtinTag be "String".
|
|
|
|
+ else if (is<StringObject>(object))
|
|
|
|
+ builtin_tag = "String";
|
|
|
|
+ // 12. Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
|
|
|
|
+ else if (is<Date>(object))
|
|
|
|
+ builtin_tag = "Date";
|
|
|
|
+ // 13. Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
|
|
|
|
+ else if (is<RegExpObject>(object))
|
|
|
|
+ builtin_tag = "RegExp";
|
|
|
|
+ // 14. Else, let builtinTag be "Object".
|
|
|
|
+ else
|
|
|
|
+ builtin_tag = "Object";
|
|
|
|
+
|
|
|
|
+ // 15. Let tag be ? Get(O, @@toStringTag).
|
|
|
|
+ auto to_string_tag = object->get(*vm.well_known_symbol_to_string_tag());
|
|
|
|
+ if (vm.exception())
|
|
|
|
+ return {};
|
|
|
|
|
|
|
|
+ // Optimization: Instead of creating another PrimitiveString from builtin_tag, we separate tag and to_string_tag and add an additional branch to step 16.
|
|
String tag;
|
|
String tag;
|
|
- auto to_string_tag = this_object->get(*vm.well_known_symbol_to_string_tag());
|
|
|
|
|
|
|
|
- if (to_string_tag.is_string()) {
|
|
|
|
|
|
+ // 16. If Type(tag) is not String, set tag to builtinTag.
|
|
|
|
+ if (!to_string_tag.is_string())
|
|
|
|
+ tag = move(builtin_tag);
|
|
|
|
+ else
|
|
tag = to_string_tag.as_string().string();
|
|
tag = to_string_tag.as_string().string();
|
|
- } else if (this_object->is_array()) {
|
|
|
|
- tag = "Array";
|
|
|
|
- } else if (this_object->is_function()) {
|
|
|
|
- tag = "Function";
|
|
|
|
- } else if (is<Error>(this_object)) {
|
|
|
|
- tag = "Error";
|
|
|
|
- } else if (is<BooleanObject>(this_object)) {
|
|
|
|
- tag = "Boolean";
|
|
|
|
- } else if (is<NumberObject>(this_object)) {
|
|
|
|
- tag = "Number";
|
|
|
|
- } else if (is<StringObject>(this_object)) {
|
|
|
|
- tag = "String";
|
|
|
|
- } else if (is<Date>(this_object)) {
|
|
|
|
- tag = "Date";
|
|
|
|
- } else if (is<RegExpObject>(this_object)) {
|
|
|
|
- tag = "RegExp";
|
|
|
|
- } else {
|
|
|
|
- tag = "Object";
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
+ // 17. Return the string-concatenation of "[object ", tag, and "]".
|
|
return js_string(vm, String::formatted("[object {}]", tag));
|
|
return js_string(vm, String::formatted("[object {}]", tag));
|
|
}
|
|
}
|
|
|
|
|