Skip to content

Commit 554305d

Browse files
lvidacskkristof
authored andcommitted
Implement String.prototype.lastIndexOf()
JerryScript-DCO-1.0-Signed-off-by: Laszlo Vidacs [email protected]
1 parent 7aa1b16 commit 554305d

File tree

4 files changed

+280
-7
lines changed

4 files changed

+280
-7
lines changed

jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp

Lines changed: 170 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "ecma-builtin-helpers.h"
1818

19+
#include "ecma-alloc.h"
1920
#include "ecma-array-object.h"
2021
#include "ecma-builtins.h"
2122
#include "ecma-conversion.h"
@@ -474,22 +475,33 @@ ecma_builtin_helper_array_concat_value (ecma_object_t *obj_p, /**< array */
474475
* This function clamps the given index to the [0, length] range.
475476
* If the index is negative, 0 value is used.
476477
* If the index is greater than the length of the string, the normalized index will be the length of the string.
478+
* NaN is mapped to zero or length depending on the nan_to_zero parameter.
477479
*
478480
* See also:
479481
* ECMA-262 v5, 15.5.4.15
480482
*
481483
* Used by:
482484
* - The String.prototype.substring routine.
485+
* - The String.prototype.indexOf routine.
486+
* - The ecma_builtin_helper_string_prototype_object_index_of helper routine.
483487
*
484488
* @return uint32_t - the normalized value of the index
485489
*/
486490
uint32_t
487491
ecma_builtin_helper_string_index_normalize (ecma_number_t index, /**< index */
488-
uint32_t length) /**< string's length */
492+
uint32_t length, /**< string's length */
493+
bool nan_to_zero) /**< whether NaN is mapped to zero (t) or length (f) */
489494
{
490495
uint32_t norm_index = 0;
491496

492-
if (!ecma_number_is_nan (index) && !ecma_number_is_negative (index))
497+
if (ecma_number_is_nan (index))
498+
{
499+
if (!nan_to_zero)
500+
{
501+
norm_index = length;
502+
}
503+
}
504+
else if (!ecma_number_is_negative (index))
493505
{
494506
if (ecma_number_is_infinity (index))
495507
{
@@ -509,6 +521,162 @@ ecma_builtin_helper_string_index_normalize (ecma_number_t index, /**< index */
509521
return norm_index;
510522
} /* ecma_builtin_helper_string_index_normalize */
511523

524+
/*
525+
* Helper function for string indexOf and lastIndexOf functions
526+
*
527+
* This function implements string indexOf and lastIndexOf with required checks and conversions.
528+
*
529+
* See also:
530+
* ECMA-262 v5, 15.5.4.7
531+
* ECMA-262 v5, 15.5.4.8
532+
*
533+
* Used by:
534+
* - The String.prototype.indexOf routine.
535+
* - The String.prototype.lastIndexOf routine.
536+
*
537+
* @return uint32_t - (last)index of search string
538+
*/
539+
ecma_completion_value_t
540+
ecma_builtin_helper_string_prototype_object_index_of (ecma_value_t this_arg, /**< this argument */
541+
ecma_value_t arg1, /**< routine's first argument */
542+
ecma_value_t arg2, /**< routine's second argument */
543+
bool firstIndex) /**< routine's third argument */
544+
{
545+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
546+
547+
/* 1 */
548+
ECMA_TRY_CATCH (check_coercible_val,
549+
ecma_op_check_object_coercible (this_arg),
550+
ret_value);
551+
552+
/* 2 */
553+
ECMA_TRY_CATCH (to_str_val,
554+
ecma_op_to_string (this_arg),
555+
ret_value);
556+
557+
/* 3 */
558+
ECMA_TRY_CATCH (search_str_val,
559+
ecma_op_to_string (arg1),
560+
ret_value);
561+
562+
/* 4 */
563+
ECMA_OP_TO_NUMBER_TRY_CATCH (pos_num,
564+
arg2,
565+
ret_value);
566+
567+
/* 6 */
568+
ecma_string_t *original_str_p = ecma_get_string_from_value (to_str_val);
569+
const ecma_length_t original_len = ecma_string_get_length (original_str_p);
570+
const lit_utf8_size_t original_size = ecma_string_get_size (original_str_p);
571+
572+
/* 4b, 5, 7 */
573+
ecma_length_t start = ecma_builtin_helper_string_index_normalize (pos_num, original_len, firstIndex);
574+
575+
/* 8 */
576+
ecma_string_t *search_str_p = ecma_get_string_from_value (search_str_val);
577+
const ecma_length_t search_len = ecma_string_get_length (search_str_p);
578+
const lit_utf8_size_t search_size = ecma_string_get_size (search_str_p);
579+
580+
ecma_number_t *ret_num_p = ecma_alloc_number ();
581+
*ret_num_p = ecma_int32_to_number (-1);
582+
583+
/* 9 */
584+
if (search_len <= original_len)
585+
{
586+
if (!search_len)
587+
{
588+
*ret_num_p = ecma_uint32_to_number (firstIndex ? 0 : original_len);
589+
}
590+
else
591+
{
592+
/* create utf8 string from original string and advance to position */
593+
MEM_DEFINE_LOCAL_ARRAY (original_str_utf8_p,
594+
original_size,
595+
lit_utf8_byte_t);
596+
597+
ecma_string_to_utf8_string (original_str_p,
598+
original_str_utf8_p,
599+
(ssize_t) (original_size));
600+
601+
lit_utf8_iterator_t original_it = lit_utf8_iterator_create (original_str_utf8_p, original_size);
602+
603+
ecma_length_t index = start;
604+
lit_utf8_iterator_advance (&original_it, index);
605+
606+
/* create utf8 string from search string */
607+
MEM_DEFINE_LOCAL_ARRAY (search_str_utf8_p,
608+
search_size,
609+
lit_utf8_byte_t);
610+
611+
ecma_string_to_utf8_string (search_str_p,
612+
search_str_utf8_p,
613+
(ssize_t) (search_size));
614+
615+
lit_utf8_iterator_t search_it = lit_utf8_iterator_create (search_str_utf8_p, search_size);
616+
617+
/* iterate original string and try to match at each position */
618+
bool searching = true;
619+
620+
while (searching)
621+
{
622+
/* match as long as possible */
623+
ecma_length_t match_len = 0;
624+
lit_utf8_iterator_t stored_original_it = original_it;
625+
626+
while (match_len < search_len &&
627+
index + match_len < original_len &&
628+
lit_utf8_iterator_read_next (&original_it) == lit_utf8_iterator_read_next (&search_it))
629+
{
630+
match_len++;
631+
}
632+
633+
/* check for match */
634+
if (match_len == search_len)
635+
{
636+
*ret_num_p = ecma_uint32_to_number (index);
637+
break;
638+
}
639+
else
640+
{
641+
/* inc/dec index and update iterators and search condition */
642+
lit_utf8_iterator_seek_bos (&search_it);
643+
original_it = stored_original_it;
644+
645+
if (firstIndex)
646+
{
647+
if ((searching = (index <= original_len - search_len)))
648+
{
649+
lit_utf8_iterator_incr (&original_it);
650+
index++;
651+
}
652+
}
653+
else
654+
{
655+
if ((searching = (index > 0)))
656+
{
657+
lit_utf8_iterator_decr (&original_it);
658+
index--;
659+
}
660+
}
661+
}
662+
}
663+
664+
MEM_FINALIZE_LOCAL_ARRAY (search_str_utf8_p);
665+
MEM_FINALIZE_LOCAL_ARRAY (original_str_utf8_p);
666+
}
667+
}
668+
669+
ecma_value_t new_value = ecma_make_number_value (ret_num_p);
670+
ret_value = ecma_make_normal_completion_value (new_value);
671+
672+
ECMA_OP_TO_NUMBER_FINALIZE (pos_num);
673+
ECMA_FINALIZE (search_str_val);
674+
ECMA_FINALIZE (to_str_val);
675+
ECMA_FINALIZE (check_coercible_val);
676+
677+
return ret_value;
678+
} /* ecma_builtin_helper_string_index_normalize */
679+
512680
/**
513681
* Helper function for using [[DefineOwnProperty]].
514682
*

jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ extern ecma_completion_value_t ecma_builtin_helper_array_concat_value (ecma_obje
3434
uint32_t *length,
3535
ecma_value_t);
3636
extern uint32_t ecma_builtin_helper_array_index_normalize (ecma_number_t index, uint32_t length);
37-
extern uint32_t ecma_builtin_helper_string_index_normalize (ecma_number_t index, uint32_t length);
37+
extern uint32_t ecma_builtin_helper_string_index_normalize (ecma_number_t index, uint32_t length, bool nan_to_zero);
38+
extern ecma_completion_value_t ecma_builtin_helper_string_prototype_object_index_of (ecma_value_t this_arg,
39+
ecma_value_t arg1,
40+
ecma_value_t arg2,
41+
bool firstIndex);
3842
extern ecma_completion_value_t ecma_builtin_helper_def_prop (ecma_object_t *obj_p,
3943
ecma_string_t *index_p,
4044
ecma_value_t value,

jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ ecma_builtin_string_prototype_object_index_of (ecma_value_t this_arg, /**< this
340340
const lit_utf8_size_t original_size = ecma_string_get_size (original_str_p);
341341

342342
/* 4b, 6 */
343-
ecma_length_t start = ecma_builtin_helper_string_index_normalize (pos_num, original_len);
343+
ecma_length_t start = ecma_builtin_helper_string_index_normalize (pos_num, original_len, true);
344344

345345
/* 7 */
346346
ecma_string_t *search_str_p = ecma_get_string_from_value (search_str_val);
@@ -444,7 +444,7 @@ ecma_builtin_string_prototype_object_last_index_of (ecma_value_t this_arg, /**<
444444
ecma_value_t arg1, /**< routine's first argument */
445445
ecma_value_t arg2) /**< routine's second argument */
446446
{
447-
ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, arg1, arg2);
447+
return ecma_builtin_helper_string_prototype_object_index_of (this_arg, arg1, arg2, false);
448448
} /* ecma_builtin_string_prototype_object_last_index_of */
449449

450450
/**
@@ -1722,7 +1722,7 @@ ecma_builtin_string_prototype_object_substring (ecma_value_t this_arg, /**< this
17221722

17231723
ecma_length_t start = 0, end = len;
17241724

1725-
start = ecma_builtin_helper_string_index_normalize (start_num, len);
1725+
start = ecma_builtin_helper_string_index_normalize (start_num, len, true);
17261726

17271727
/* 5, 7 */
17281728
if (ecma_is_value_undefined (arg2))
@@ -1735,7 +1735,7 @@ ecma_builtin_string_prototype_object_substring (ecma_value_t this_arg, /**< this
17351735
arg2,
17361736
ret_value);
17371737

1738-
end = ecma_builtin_helper_string_index_normalize (end_num, len);
1738+
end = ecma_builtin_helper_string_index_normalize (end_num, len, true);
17391739

17401740
ECMA_OP_TO_NUMBER_FINALIZE (end_num);
17411741
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2015 Samsung Electronics Co., Ltd.
2+
// Copyright 2015 University of Szeged.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
// check properties
17+
assert(Object.getOwnPropertyDescriptor(String.prototype.lastIndexOf, 'length').configurable === false);
18+
19+
assert(Object.getOwnPropertyDescriptor(String.prototype.lastIndexOf, 'length').enumerable === false);
20+
21+
assert(Object.getOwnPropertyDescriptor(String.prototype.lastIndexOf, 'length').writable === false);
22+
23+
assert(String.prototype.lastIndexOf.length === 1);
24+
25+
// simple checks
26+
assert("Hello welcome, welcome to the universe.".lastIndexOf("welcome") === 15);
27+
28+
assert("Hello world, welcome to the universe.".lastIndexOf("Hello world, welcome to the universe.") === 0);
29+
30+
assert("Hello welcome, welcome to the universe.".lastIndexOf("welcome", 5) === -1);
31+
32+
assert("Hello welcome, welcome to the universe.".lastIndexOf("welcome", -100) == -1);
33+
34+
assert("Hello welcome, welcome to the universe.".lastIndexOf("welcome", 15) === 15);
35+
36+
assert("Hello welcome, welcome to the universe o.".lastIndexOf("o", 10) === 10);
37+
38+
assert("Hello welcome, welcome to the universe o.".lastIndexOf("o", 25) === 24);
39+
40+
assert("Helloooo woooorld".lastIndexOf("oooo", 6) === 4);
41+
42+
// check empty string
43+
assert(String.prototype.lastIndexOf.call(new String()) === -1);
44+
45+
assert(String.prototype.lastIndexOf.call("Hello world, welcome to the universe.","") === 37);
46+
47+
assert(String.prototype.lastIndexOf.call("","") === 0);
48+
49+
// check NaN
50+
assert("Hello world, welcome to the universe.".lastIndexOf(NaN) === -1);
51+
52+
assert("Hello world, welcome to the universe.".lastIndexOf("o", NaN) === 22);
53+
54+
// check Object
55+
assert(String.prototype.lastIndexOf.call({}) === -1);
56+
57+
// check +-Inf
58+
assert("hello world!".lastIndexOf("world", -Infinity) === -1);
59+
60+
assert("hello world!".lastIndexOf("world", Infinity) === 6);
61+
62+
// check numbers
63+
assert("hello world!".lastIndexOf(-1) === -1);
64+
65+
assert("hello 0 world!".lastIndexOf(-0) === 6);
66+
67+
// check undefined
68+
assert("hello world!".lastIndexOf(undefined) === -1);
69+
70+
var undefined_var;
71+
assert("Hello world, welcome to the universe.".lastIndexOf("welcome", undefined_var) === 13);
72+
73+
// check booleans
74+
assert("true".lastIndexOf(true, false) === 0);
75+
76+
// check coercible - undefined
77+
try {
78+
assert(String.prototype.lastIndexOf.call(undefined) === -1);
79+
assert(false);
80+
} catch(e) {
81+
assert(e instanceof TypeError);
82+
}
83+
84+
// check coercible - null
85+
try {
86+
assert(String.prototype.lastIndexOf.call(null, 0) === -1);
87+
assert(false);
88+
} catch (e) {
89+
assert(e instanceof TypeError);
90+
}
91+
92+
// check coercible - Boolean
93+
assert(String.prototype.lastIndexOf.call(true, "e") === 3);
94+
assert(String.prototype.lastIndexOf.call(false, "e") === 4);
95+
96+
// check coercible - Object
97+
var test_object = {firstName:"John", lastName:"Doe"};
98+
assert(String.prototype.lastIndexOf.call(test_object, "Obj") === 8);
99+
100+
// check coercible - Number
101+
assert(String.prototype.lastIndexOf.call(123, "2") === 1);

0 commit comments

Comments
 (0)