njs_value_t *args, nxt_uint_t nargs);
static njs_ret_t njs_string_from_char_code(njs_vm_t *vm,
njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_string_starts_or_ends_with(njs_vm_t *vm, njs_value_t *args,
+ nxt_uint_t nargs, nxt_bool_t starts);
static njs_ret_t njs_string_match_multiple(njs_vm_t *vm, njs_value_t *args,
njs_regexp_pattern_t *pattern);
static njs_ret_t njs_string_split_part_add(njs_vm_t *vm, njs_array_t *array,
}
+static njs_ret_t
+njs_string_prototype_starts_with(njs_vm_t *vm, njs_value_t *args,
+ nxt_uint_t nargs, njs_index_t unused)
+{
+ return njs_string_starts_or_ends_with(vm, args, nargs, 1);
+}
+
+
+static njs_ret_t
+njs_string_prototype_ends_with(njs_vm_t *vm, njs_value_t *args,
+ nxt_uint_t nargs, njs_index_t unused)
+{
+ return njs_string_starts_or_ends_with(vm, args, nargs, 0);
+}
+
+
+static njs_ret_t
+njs_string_starts_or_ends_with(njs_vm_t *vm, njs_value_t *args,
+ nxt_uint_t nargs, nxt_bool_t starts)
+{
+ ssize_t index, length, search_length;
+ const u_char *p, *end;
+ const njs_value_t *retval;
+ njs_string_prop_t string, search;
+
+ retval = &njs_value_true;
+
+ if (nargs > 1) {
+ search_length = njs_string_prop(&search, &args[1]);
+
+ if (search_length == 0) {
+ goto done;
+ }
+
+ length = njs_string_prop(&string, &args[0]);
+
+ index = (nargs > 2) ? args[2].data.u.number : -1;
+
+ if (starts) {
+ if (index < 0) {
+ index = 0;
+ }
+
+ if (length - index < search_length) {
+ goto small;
+ }
+
+ } else {
+ if (index < 0 || index > length) {
+ index = length;
+ }
+
+ index -= search_length;
+
+ if (index < 0) {
+ goto small;
+ }
+ }
+
+ end = string.start + string.size;
+
+ if (string.size == (size_t) length) {
+ /* Byte or ASCII string. */
+ p = string.start + index;
+
+ } else {
+ /* UTF-8 string. */
+ p = njs_string_offset(string.start, end, index);
+ }
+
+ if ((size_t) (end - p) >= search.size
+ && memcmp(p, search.start, search.size) == 0)
+ {
+ goto done;
+ }
+ }
+
+small:
+
+ retval = &njs_value_false;
+
+done:
+
+ vm->retval = *retval;
+
+ return NXT_OK;
+}
+
+
/*
* njs_string_offset() assumes that index is correct
* and the optional offset map has been initialized.
NJS_STRING_OBJECT_ARG, NJS_STRING_ARG, NJS_INTEGER_ARG),
},
+ /* ES6. */
+ {
+ .type = NJS_METHOD,
+ .name = njs_string("startsWith"),
+ .value = njs_native_function(njs_string_prototype_starts_with, 0,
+ NJS_STRING_OBJECT_ARG, NJS_STRING_ARG, NJS_INTEGER_ARG),
+ },
+
+ /* ES6. */
+ {
+ .type = NJS_METHOD,
+ .name = njs_string("endsWith"),
+ .value = njs_native_function(njs_string_prototype_ends_with, 0,
+ NJS_STRING_OBJECT_ARG, NJS_STRING_ARG, NJS_INTEGER_ARG),
+ },
+
{
.type = NJS_METHOD,
.name = njs_string("toLowerCase"),
{ nxt_string("'абв абв абвгдежз'.includes('абвгд', 9)"),
nxt_string("false") },
+ { nxt_string("''.startsWith('')"),
+ nxt_string("true") },
+
+ { nxt_string("'12345'.startsWith()"),
+ nxt_string("false") },
+
+ { nxt_string("'abc'.startsWith('abc')"),
+ nxt_string("true") },
+
+ { nxt_string("'abc'.startsWith('abc', 1)"),
+ nxt_string("false") },
+
+ { nxt_string("'abc'.startsWith('abc', -1)"),
+ nxt_string("true") },
+
+ { nxt_string("'абв абв абвгдежз'.startsWith('абвгд', 8)"),
+ nxt_string("true") },
+
+ { nxt_string("'абв абв абвгдежз'.startsWith('абвгд', 9)"),
+ nxt_string("false") },
+
+ { nxt_string("''.endsWith('')"),
+ nxt_string("true") },
+
+ { nxt_string("'12345'.endsWith()"),
+ nxt_string("false") },
+
+ { nxt_string("'abc'.endsWith('abc')"),
+ nxt_string("true") },
+
+ { nxt_string("'abc'.endsWith('abc', 4)"),
+ nxt_string("true") },
+
+ { nxt_string("'abc'.endsWith('abc', 1)"),
+ nxt_string("false") },
+
+ { nxt_string("'abc'.endsWith('abc', -1)"),
+ nxt_string("true") },
+
+ { nxt_string("'абв абв абвгдежз'.endsWith('абвгд', 13)"),
+ nxt_string("true") },
+
+ { nxt_string("'абв абв абвгдежз'.endsWith('абвгд', 14)"),
+ nxt_string("false") },
+
{ nxt_string("'ABC'.toLowerCase()"),
nxt_string("abc") },