summaryrefslogtreecommitdiff
path: root/Libraries/LibJS
diff options
context:
space:
mode:
authorXavier Cooney <xavier.cooney03@gmail.com>2020-12-25 12:58:34 +1100
committerAndreas Kling <kling@serenityos.org>2020-12-26 01:09:04 +0100
commit1cf92d39ebce21832ad994054ff33a9ce07c8e72 (patch)
tree7ef4913cdfe4c99d1694b0b1e42ee675209ec080 /Libraries/LibJS
parent43f948b35735ae95e827a3d66a5c68c087afb638 (diff)
downloadserenity-1cf92d39ebce21832ad994054ff33a9ce07c8e72.zip
LibJS: Implement String.prototype.endsWith()
Diffstat (limited to 'Libraries/LibJS')
-rw-r--r--Libraries/LibJS/Runtime/CommonPropertyNames.h1
-rw-r--r--Libraries/LibJS/Runtime/StringPrototype.cpp46
-rw-r--r--Libraries/LibJS/Runtime/StringPrototype.h1
-rw-r--r--Libraries/LibJS/Tests/builtins/String/String.prototype-generic-functions.js1
-rw-r--r--Libraries/LibJS/Tests/builtins/String/String.prototype.endsWith.js39
5 files changed, 88 insertions, 0 deletions
diff --git a/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Libraries/LibJS/Runtime/CommonPropertyNames.h
index aa3d1b8ea6..c9a31bd467 100644
--- a/Libraries/LibJS/Runtime/CommonPropertyNames.h
+++ b/Libraries/LibJS/Runtime/CommonPropertyNames.h
@@ -93,6 +93,7 @@ namespace JS {
P(description) \
P(done) \
P(dotAll) \
+ P(endsWith) \
P(entries) \
P(enumerable) \
P(error) \
diff --git a/Libraries/LibJS/Runtime/StringPrototype.cpp b/Libraries/LibJS/Runtime/StringPrototype.cpp
index bcfcb074cb..2799bf79aa 100644
--- a/Libraries/LibJS/Runtime/StringPrototype.cpp
+++ b/Libraries/LibJS/Runtime/StringPrototype.cpp
@@ -75,6 +75,7 @@ void StringPrototype::initialize(GlobalObject& global_object)
define_native_function(vm.names.charCodeAt, char_code_at, 1, attr);
define_native_function(vm.names.repeat, repeat, 1, attr);
define_native_function(vm.names.startsWith, starts_with, 1, attr);
+ define_native_function(vm.names.endsWith, ends_with, 1, attr);
define_native_function(vm.names.indexOf, index_of, 1, attr);
define_native_function(vm.names.toLowerCase, to_lowercase, 0, attr);
define_native_function(vm.names.toUpperCase, to_uppercase, 0, attr);
@@ -185,6 +186,51 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::starts_with)
return Value(string.substring(start, search_string_length) == search_string);
}
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::ends_with)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+
+ auto search_string_value = vm.argument(0);
+
+ bool search_is_regexp = search_string_value.is_regexp(global_object);
+ if (vm.exception())
+ return {};
+ if (search_is_regexp) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::IsNotA, "searchString", "string, but a regular expression");
+ return {};
+ }
+
+ auto search_string = search_string_value.to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ auto string_length = string.length();
+ auto search_string_length = search_string.length();
+
+ size_t pos = string_length;
+
+ auto end_position_value = vm.argument(1);
+ if (!end_position_value.is_undefined()) {
+ auto number = end_position_value.to_number(global_object);
+ if (vm.exception())
+ return {};
+ double pos_as_double = number.to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ pos = clamp(pos_as_double, static_cast<double>(0), static_cast<double>(string_length));
+ }
+
+ if (search_string_length == 0)
+ return Value(true);
+ if (pos < search_string_length)
+ return Value(false);
+
+ auto start = pos - search_string_length;
+ return Value(string.substring(start, search_string_length) == search_string);
+}
+
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::index_of)
{
auto string = ak_string_from(vm, global_object);
diff --git a/Libraries/LibJS/Runtime/StringPrototype.h b/Libraries/LibJS/Runtime/StringPrototype.h
index e412b713ef..f443f73c2d 100644
--- a/Libraries/LibJS/Runtime/StringPrototype.h
+++ b/Libraries/LibJS/Runtime/StringPrototype.h
@@ -43,6 +43,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(char_code_at);
JS_DECLARE_NATIVE_FUNCTION(repeat);
JS_DECLARE_NATIVE_FUNCTION(starts_with);
+ JS_DECLARE_NATIVE_FUNCTION(ends_with);
JS_DECLARE_NATIVE_FUNCTION(index_of);
JS_DECLARE_NATIVE_FUNCTION(to_lowercase);
JS_DECLARE_NATIVE_FUNCTION(to_uppercase);
diff --git a/Libraries/LibJS/Tests/builtins/String/String.prototype-generic-functions.js b/Libraries/LibJS/Tests/builtins/String/String.prototype-generic-functions.js
index f5c0ffa854..a05ca53d8c 100644
--- a/Libraries/LibJS/Tests/builtins/String/String.prototype-generic-functions.js
+++ b/Libraries/LibJS/Tests/builtins/String/String.prototype-generic-functions.js
@@ -4,6 +4,7 @@ test("basic functionality", () => {
"charCodeAt",
"repeat",
"startsWith",
+ "endsWith",
"indexOf",
"toLowerCase",
"toUpperCase",
diff --git a/Libraries/LibJS/Tests/builtins/String/String.prototype.endsWith.js b/Libraries/LibJS/Tests/builtins/String/String.prototype.endsWith.js
new file mode 100644
index 0000000000..ef39541ce1
--- /dev/null
+++ b/Libraries/LibJS/Tests/builtins/String/String.prototype.endsWith.js
@@ -0,0 +1,39 @@
+test("basic functionality", () => {
+ expect(String.prototype.endsWith).toHaveLength(1);
+
+ var s = "foobar";
+ expect(s.endsWith("r")).toBeTrue();
+ expect(s.endsWith("ar")).toBeTrue();
+ expect(s.endsWith("bar")).toBeTrue();
+ expect(s.endsWith("obar")).toBeTrue();
+ expect(s.endsWith("oobar")).toBeTrue();
+ expect(s.endsWith("foobar")).toBeTrue();
+ expect(s.endsWith("1foobar")).toBeFalse();
+ expect(s.endsWith("r", 6)).toBeTrue();
+ expect(s.endsWith("ar", 6)).toBeTrue();
+ expect(s.endsWith("bar", 6)).toBeTrue();
+ expect(s.endsWith("obar", 6)).toBeTrue();
+ expect(s.endsWith("oobar", 6)).toBeTrue();
+ expect(s.endsWith("foobar", 6)).toBeTrue();
+ expect(s.endsWith("1foobar", 6)).toBeFalse();
+ expect(s.endsWith("bar", [])).toBeFalse();
+ expect(s.endsWith("bar", null)).toBeFalse();
+ expect(s.endsWith("bar", false)).toBeFalse();
+ expect(s.endsWith("bar", true)).toBeFalse();
+ expect(s.endsWith("f", true)).toBeTrue();
+ expect(s.endsWith("bar", -1)).toBeFalse();
+ expect(s.endsWith("bar", 42)).toBeTrue();
+ expect(s.endsWith("foo", 3)).toBeTrue();
+ expect(s.endsWith("foo", "3")).toBeTrue();
+ expect(s.endsWith("foo1", 3)).toBeFalse();
+ expect(s.endsWith("foo", 3.7)).toBeTrue();
+ expect(s.endsWith()).toBeFalse();
+ expect(s.endsWith("")).toBeTrue();
+ expect(s.endsWith("", 0)).toBeTrue();
+ expect(s.endsWith("", 1)).toBeTrue();
+ expect(s.endsWith("", -1)).toBeTrue();
+ expect(s.endsWith("", 42)).toBeTrue();
+ expect("12undefined".endsWith()).toBeTrue();
+ expect(() => s.endsWith(/foobar/)).toThrowWithMessage(TypeError, "searchString is not a string, but a regular expression");
+ expect(s.endsWith("bar", undefined)).toBeTrue();
+});