summaryrefslogtreecommitdiff
path: root/Libraries/LibJS
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2020-05-22 15:04:52 +0100
committerAndreas Kling <kling@serenityos.org>2020-05-22 17:43:44 +0200
commit040c75a3cc8ae4ac87bcc03bf741b47bc688d2a0 (patch)
treeb9caf3c7ac84e5f6a2444f63fd7678a0285e43fc /Libraries/LibJS
parente9ee06b19e78a44b2715967579ab7584672eeb63 (diff)
downloadserenity-040c75a3cc8ae4ac87bcc03bf741b47bc688d2a0.zip
LibJS: Make Array.prototype.{join,toString}() generic
Diffstat (limited to 'Libraries/LibJS')
-rw-r--r--Libraries/LibJS/Runtime/ArrayPrototype.cpp55
-rw-r--r--Libraries/LibJS/Runtime/ObjectPrototype.h4
-rw-r--r--Libraries/LibJS/Tests/Array.prototype-generic-functions.js16
3 files changed, 48 insertions, 27 deletions
diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Libraries/LibJS/Runtime/ArrayPrototype.cpp
index 83be93ba4c..ecd14217c1 100644
--- a/Libraries/LibJS/Runtime/ArrayPrototype.cpp
+++ b/Libraries/LibJS/Runtime/ArrayPrototype.cpp
@@ -35,6 +35,7 @@
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/MarkedValueList.h>
+#include <LibJS/Runtime/ObjectPrototype.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
@@ -240,46 +241,48 @@ Value ArrayPrototype::shift(Interpreter& interpreter)
return array->elements().take_first().value_or(js_undefined());
}
-static Value join_array_with_separator(Interpreter& interpreter, const Array& array, StringView separator)
-{
- StringBuilder builder;
- for (size_t i = 0; i < array.elements().size(); ++i) {
- if (i != 0)
- builder.append(separator);
- auto value = array.elements()[i];
- if (!value.is_empty() && !value.is_undefined() && !value.is_null()) {
- auto string = value.to_string(interpreter);
- if (interpreter.exception())
- return {};
- builder.append(string);
- }
- }
- return js_string(interpreter, builder.to_string());
-}
-
Value ArrayPrototype::to_string(Interpreter& interpreter)
{
- auto* array = array_from(interpreter);
- if (!array)
+ auto* this_object = interpreter.this_value().to_object(interpreter);
+ if (!this_object)
return {};
-
- return join_array_with_separator(interpreter, *array, ",");
+ auto join_function = this_object->get("join");
+ if (interpreter.exception())
+ return {};
+ if (!join_function.is_function())
+ return ObjectPrototype::to_string(interpreter);
+ return interpreter.call(join_function.as_function(), this_object);
}
Value ArrayPrototype::join(Interpreter& interpreter)
{
- auto* array = array_from(interpreter);
- if (!array)
+ auto* this_object = interpreter.this_value().to_object(interpreter);
+ if (!this_object)
return {};
-
String separator = ",";
if (interpreter.argument_count()) {
separator = interpreter.argument(0).to_string(interpreter);
if (interpreter.exception())
return {};
}
-
- return join_array_with_separator(interpreter, *array, separator);
+ auto length = get_length(interpreter, *this_object);
+ if (interpreter.exception())
+ return {};
+ StringBuilder builder;
+ for (size_t i = 0; i < length; ++i) {
+ if (i > 0)
+ builder.append(separator);
+ auto value = this_object->get_by_index(i).value_or(js_undefined());
+ if (interpreter.exception())
+ return {};
+ if (value.is_undefined() || value.is_null())
+ continue;
+ auto string = value.to_string(interpreter);
+ if (interpreter.exception())
+ return {};
+ builder.append(string);
+ }
+ return js_string(interpreter, builder.to_string());
}
Value ArrayPrototype::concat(Interpreter& interpreter)
diff --git a/Libraries/LibJS/Runtime/ObjectPrototype.h b/Libraries/LibJS/Runtime/ObjectPrototype.h
index 737dadab8c..8e18b5bd3b 100644
--- a/Libraries/LibJS/Runtime/ObjectPrototype.h
+++ b/Libraries/LibJS/Runtime/ObjectPrototype.h
@@ -37,12 +37,14 @@ public:
virtual ~ObjectPrototype() override;
+ // public to serve as intrinsic function %Object.prototype.toString%
+ static Value to_string(Interpreter&);
+
private:
virtual const char* class_name() const override { return "ObjectPrototype"; }
static Value has_own_property(Interpreter&);
static Value value_of(Interpreter&);
- static Value to_string(Interpreter&);
};
}
diff --git a/Libraries/LibJS/Tests/Array.prototype-generic-functions.js b/Libraries/LibJS/Tests/Array.prototype-generic-functions.js
index aed23c4ad3..1b9848cf16 100644
--- a/Libraries/LibJS/Tests/Array.prototype-generic-functions.js
+++ b/Libraries/LibJS/Tests/Array.prototype-generic-functions.js
@@ -27,6 +27,22 @@ try {
assert(o.length === 0);
});
+ {
+ assert(Array.prototype.join.call({}) === "");
+ assert(Array.prototype.join.call({ length: "foo" }) === "");
+ assert(Array.prototype.join.call({ length: 3 }) === ",,");
+ assert(Array.prototype.join.call({ length: 2, 0: "foo", 1: "bar" }) === "foo,bar");
+ assert(Array.prototype.join.call({ length: 2, 0: "foo", 1: "bar", 2: "baz" }) === "foo,bar");
+ assert(Array.prototype.join.call({ length: 3, 1: "bar" }, "~") === "~bar~");
+ assert(Array.prototype.join.call({ length: 3, 0: "foo", 1: "bar", 2: "baz" }, "~") === "foo~bar~baz");
+ }
+
+ {
+ assert(Array.prototype.toString.call({}) === "[object Object]");
+ assert(Array.prototype.toString.call({ join: "foo" }) === "[object Object]");
+ assert(Array.prototype.toString.call({ join: () => "foo" }) === "foo");
+ }
+
const o = { length: 5, 0: "foo", 1: "bar", 3: "baz" };
{