Skip to content

Commit d4ce20d

Browse files
Yuyupoakosthekiss
authored andcommitted
Implement String startsWith/includes/endsWith (#2926)
Co-authored-by: Tamas Keri tkeri@inf.u-szeged.hu JerryScript-DCO-1.0-Signed-off-by: Daniella Barsony bella@inf.u-szeged.hu
1 parent 04d5d56 commit d4ce20d

9 files changed

Lines changed: 394 additions & 38 deletions

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

Lines changed: 129 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -532,73 +532,167 @@ ecma_builtin_helper_string_index_normalize (ecma_number_t index, /**< index */
532532
} /* ecma_builtin_helper_string_index_normalize */
533533

534534
/**
535-
* Helper function for string indexOf and lastIndexOf functions
536-
*
537-
* This function implements string indexOf and lastIndexOf with required checks and conversions.
535+
* Helper function for string indexOf, lastIndexOf, startsWith, includes, endsWith functions
538536
*
539537
* See also:
540538
* ECMA-262 v5, 15.5.4.7
541539
* ECMA-262 v5, 15.5.4.8
540+
* ECMA-262 v6, 21.1.3.6
541+
* ECMA-262 v6, 21.1.3.7
542+
* ECMA-262 v6, 21.1.3.18
542543
*
543544
* Used by:
544545
* - The String.prototype.indexOf routine.
545546
* - The String.prototype.lastIndexOf routine.
547+
* - The String.prototype.startsWith routine.
548+
* - The String.prototype.includes routine.
549+
* - The String.prototype.endsWith routine.
546550
*
547-
* @return ecma_value_t - (last) index of search string as an ecma-value
551+
* @return ecma_value_t - Returns index (last index) or a
552+
* boolean value
548553
*/
549554
ecma_value_t
550555
ecma_builtin_helper_string_prototype_object_index_of (ecma_value_t this_arg, /**< this argument */
551556
ecma_value_t arg1, /**< routine's first argument */
552557
ecma_value_t arg2, /**< routine's second argument */
553-
bool first_index) /**< routine's third argument */
558+
ecma_string_index_of_mode_t mode) /**< routine's mode */
554559
{
555-
ecma_value_t ret_value = ECMA_VALUE_EMPTY;
556-
557560
/* 1 */
558-
ECMA_TRY_CATCH (check_coercible_val,
559-
ecma_op_check_object_coercible (this_arg),
560-
ret_value);
561+
if (ECMA_IS_VALUE_ERROR (ecma_op_check_object_coercible (this_arg)))
562+
{
563+
return ECMA_VALUE_ERROR;
564+
}
561565

562566
/* 2 */
563-
ECMA_TRY_CATCH (to_str_val,
564-
ecma_op_to_string (this_arg),
565-
ret_value);
567+
ecma_value_t to_str_val = ecma_op_to_string (this_arg);
566568

567-
/* 3 */
568-
ECMA_TRY_CATCH (search_str_val,
569-
ecma_op_to_string (arg1),
570-
ret_value);
571-
572-
/* 4 */
573-
ECMA_OP_TO_NUMBER_TRY_CATCH (pos_num,
574-
arg2,
575-
ret_value);
569+
if (ECMA_IS_VALUE_ERROR (to_str_val))
570+
{
571+
return to_str_val;
572+
}
576573

577-
/* 5 (indexOf) -- 6 (lastIndexOf) */
574+
/* 5 (indexOf), 6 (lastIndexOf), 11 (startsWith, includes) */
578575
ecma_string_t *original_str_p = ecma_get_string_from_value (to_str_val);
579576
const ecma_length_t original_len = ecma_string_get_length (original_str_p);
580577

581-
/* 4b, 6 (indexOf) - 4b, 5, 7 (lastIndexOf) */
582-
ecma_length_t start = ecma_builtin_helper_string_index_normalize (pos_num, original_len, first_index);
578+
#if ENABLED (JERRY_ES2015_BUILTIN)
579+
/* 4, 6 (startsWith, includes, endsWith) */
580+
if (mode >= ECMA_STRING_STARTS_WITH
581+
&& (ecma_is_value_object (arg1)
582+
&& ecma_object_class_is (ecma_get_object_from_value (arg1), LIT_MAGIC_STRING_REGEXP_UL)))
583+
{
584+
JERRY_ASSERT (ECMA_STRING_LAST_INDEX_OF < mode && mode <= ECMA_STRING_ENDS_WITH);
585+
ecma_deref_ecma_string (original_str_p);
586+
return ecma_raise_type_error (ECMA_ERR_MSG ("Search string can't be of type: RegExp"));
587+
}
588+
#endif /* ENABLED (JERRY_ES2015_BUILTIN) */
583589

584-
/* 7 (indexOf) -- 8 (lastIndexOf) */
590+
/* 3 */
591+
ecma_value_t search_str_val = ecma_op_to_string (arg1);
592+
593+
if (ECMA_IS_VALUE_ERROR (search_str_val))
594+
{
595+
ecma_deref_ecma_string (original_str_p);
596+
return search_str_val;
597+
}
598+
599+
/* 7, 8 */
585600
ecma_string_t *search_str_p = ecma_get_string_from_value (search_str_val);
586601

602+
/* 4 (indexOf, lastIndexOf), 9 (startsWith, includes), 10 (endsWith) */
603+
ecma_number_t pos_num;
604+
ecma_value_t ret_value = ecma_get_number (arg2, &pos_num);
605+
606+
/* 10 (startsWith, includes), 11 (endsWith) */
607+
if (ECMA_IS_VALUE_ERROR (ret_value))
608+
{
609+
ecma_deref_ecma_string (original_str_p);
610+
ecma_deref_ecma_string (search_str_p);
611+
return ret_value;
612+
}
613+
614+
bool use_first_index = mode != ECMA_STRING_LAST_INDEX_OF;
615+
616+
/* 4b, 6 (indexOf) - 4b, 5, 7 (lastIndexOf) */
617+
ecma_length_t start = ecma_builtin_helper_string_index_normalize (pos_num, original_len, use_first_index);
618+
587619
ecma_number_t ret_num = ECMA_NUMBER_MINUS_ONE;
588620

589-
/* 8 (indexOf) -- 9 (lastIndexOf) */
590621
ecma_length_t index_of = 0;
591-
if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, first_index, start, &index_of))
622+
623+
ret_value = ECMA_VALUE_FALSE;
624+
625+
switch (mode)
592626
{
593-
ret_num = ((ecma_number_t) index_of);
594-
}
627+
#if ENABLED (JERRY_ES2015_BUILTIN)
628+
case ECMA_STRING_STARTS_WITH:
629+
{
630+
if (pos_num + start > original_len)
631+
{
632+
break;
633+
}
634+
635+
if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, true, start, &index_of))
636+
{
637+
/* 15, 16 (startsWith) */
638+
ret_value = ecma_make_boolean_value (index_of == start);
639+
}
640+
break;
641+
}
642+
case ECMA_STRING_INCLUDES:
643+
{
644+
if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, true, start, &index_of))
645+
{
646+
ret_value = ECMA_VALUE_TRUE;
647+
}
648+
break;
649+
}
650+
case ECMA_STRING_ENDS_WITH:
651+
{
652+
if (start == 0)
653+
{
654+
start = original_len;
655+
}
656+
657+
ecma_length_t search_str_len = ecma_string_get_length (search_str_p);
595658

596-
ret_value = ecma_make_number_value (ret_num);
659+
if (search_str_len == 0)
660+
{
661+
ret_value = ECMA_VALUE_TRUE;
662+
break;
663+
}
664+
665+
int32_t start_ends_with = (int32_t) (start - search_str_len);
666+
667+
if (start_ends_with < 0)
668+
{
669+
break;
670+
}
671+
if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, true,
672+
(ecma_length_t) start_ends_with, &index_of))
673+
{
674+
ret_value = ecma_make_boolean_value (index_of == (ecma_length_t) start_ends_with);
675+
}
676+
break;
677+
}
678+
#endif /* ENABLED (JERRY_ES2015_BUILTIN) */
679+
680+
case ECMA_STRING_INDEX_OF:
681+
case ECMA_STRING_LAST_INDEX_OF:
682+
default:
683+
{
684+
/* 8 (indexOf) -- 9 (lastIndexOf) */
685+
if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, use_first_index, start, &index_of))
686+
{
687+
ret_num = ((ecma_number_t) index_of);
688+
}
689+
ret_value = ecma_make_number_value (ret_num);
690+
break;
691+
}
692+
}
597693

598-
ECMA_OP_TO_NUMBER_FINALIZE (pos_num);
599-
ECMA_FINALIZE (search_str_val);
600-
ECMA_FINALIZE (to_str_val);
601-
ECMA_FINALIZE (check_coercible_val);
694+
ecma_deref_ecma_string (search_str_p);
695+
ecma_deref_ecma_string (original_str_p);
602696

603697
return ret_value;
604698
} /* ecma_builtin_helper_string_prototype_object_index_of */

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@
2626
* @{
2727
*/
2828

29+
/**
30+
* Mode of string index routine.
31+
*/
32+
typedef enum
33+
{
34+
/** These routines must be in this order */
35+
ECMA_STRING_INDEX_OF, /**< String.indexOf: ECMA-262 v5, 15.5.4.7 */
36+
ECMA_STRING_LAST_INDEX_OF, /**< String.lastIndexOf: ECMA-262 v5, 15.5.4.8 */
37+
ECMA_STRING_STARTS_WITH, /**< String.startsWith: ECMA-262 v6, 21.1.3.18 */
38+
ECMA_STRING_INCLUDES, /**< String.includes: ECMA-262 v6, 21.1.3.7 */
39+
ECMA_STRING_ENDS_WITH /**< String.includes: ECMA-262 v6, 21.1.3.6 */
40+
} ecma_string_index_of_mode_t;
41+
2942
ecma_value_t
3043
ecma_builtin_helper_object_to_string (const ecma_value_t this_arg);
3144
ecma_value_t
@@ -40,7 +53,7 @@ uint32_t
4053
ecma_builtin_helper_string_index_normalize (ecma_number_t index, uint32_t length, bool nan_to_zero);
4154
ecma_value_t
4255
ecma_builtin_helper_string_prototype_object_index_of (ecma_value_t this_arg, ecma_value_t arg1,
43-
ecma_value_t arg2, bool first_index);
56+
ecma_value_t arg2, ecma_string_index_of_mode_t mode);
4457
bool
4558
ecma_builtin_helper_string_find_index (ecma_string_t *original_str_p, ecma_string_t *search_str_p, bool first_index,
4659
ecma_length_t start_pos, ecma_length_t *ret_index_p);

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

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,59 @@ ecma_builtin_string_prototype_object_concat (ecma_value_t this_arg, /**< this ar
270270
return ret_value;
271271
} /* ecma_builtin_string_prototype_object_concat */
272272

273+
#if ENABLED (JERRY_ES2015_BUILTIN)
274+
/**
275+
* The String.prototype object's 'startsWith' routine
276+
*
277+
* See also:
278+
* ECMA-262 v6, 21.1.3.18
279+
*
280+
* @return ecma value
281+
* Returned value must be freed with ecma_free_value.
282+
*/
283+
static ecma_value_t
284+
ecma_builtin_string_prototype_object_starts_with (ecma_value_t this_arg, /**< this argument */
285+
ecma_value_t arg1, /**< routine's first argument */
286+
ecma_value_t arg2) /**< routine's second argument */
287+
{
288+
return ecma_builtin_helper_string_prototype_object_index_of (this_arg, arg1, arg2, ECMA_STRING_STARTS_WITH);
289+
} /* ecma_builtin_string_prototype_object_starts_with */
290+
291+
/**
292+
* The String.prototype object's 'includes' routine
293+
*
294+
* See also:
295+
* ECMA-262 v6, 21.1.3.7
296+
*
297+
* @return ecma value
298+
* Returned value must be freed with ecma_free_value.
299+
*/
300+
static ecma_value_t
301+
ecma_builtin_string_prototype_object_includes (ecma_value_t this_arg, /**< this argument */
302+
ecma_value_t arg1, /**< routine's first argument */
303+
ecma_value_t arg2) /**< routine's second argument */
304+
{
305+
return ecma_builtin_helper_string_prototype_object_index_of (this_arg, arg1, arg2, ECMA_STRING_INCLUDES);
306+
} /* ecma_builtin_string_prototype_object_includes */
307+
308+
/**
309+
* The String.prototype object's 'endsWith' routine
310+
*
311+
* See also:
312+
* ECMA-262 v6, 21.1.3.6
313+
*
314+
* @return ecma value
315+
* Returned value must be freed with ecma_free_value.
316+
*/
317+
static ecma_value_t
318+
ecma_builtin_string_prototype_object_ends_with (ecma_value_t this_arg, /**< this argument */
319+
ecma_value_t arg1, /**< routine's first argument */
320+
ecma_value_t arg2) /**< routine's second argument */
321+
{
322+
return ecma_builtin_helper_string_prototype_object_index_of (this_arg, arg1, arg2, ECMA_STRING_ENDS_WITH);
323+
} /* ecma_builtin_string_prototype_object_ends_with */
324+
#endif /* ENABLED (JERRY_ES2015_BUILTIN) */
325+
273326
/**
274327
* The String.prototype object's 'indexOf' routine
275328
*
@@ -284,7 +337,7 @@ ecma_builtin_string_prototype_object_index_of (ecma_value_t this_arg, /**< this
284337
ecma_value_t arg1, /**< routine's first argument */
285338
ecma_value_t arg2) /**< routine's second argument */
286339
{
287-
return ecma_builtin_helper_string_prototype_object_index_of (this_arg, arg1, arg2, true);
340+
return ecma_builtin_helper_string_prototype_object_index_of (this_arg, arg1, arg2, ECMA_STRING_INDEX_OF);
288341
} /* ecma_builtin_string_prototype_object_index_of */
289342

290343
/**
@@ -301,7 +354,7 @@ ecma_builtin_string_prototype_object_last_index_of (ecma_value_t this_arg, /**<
301354
ecma_value_t arg1, /**< routine's first argument */
302355
ecma_value_t arg2) /**< routine's second argument */
303356
{
304-
return ecma_builtin_helper_string_prototype_object_index_of (this_arg, arg1, arg2, false);
357+
return ecma_builtin_helper_string_prototype_object_index_of (this_arg, arg1, arg2, ECMA_STRING_LAST_INDEX_OF);
305358
} /* ecma_builtin_string_prototype_object_last_index_of */
306359

307360
/**

jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.inc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ ROUTINE (LIT_MAGIC_STRING_CHAR_AT_UL, ecma_builtin_string_prototype_object_char_
4949
ROUTINE (LIT_MAGIC_STRING_CHAR_CODE_AT_UL, ecma_builtin_string_prototype_object_char_code_at, 1, 1)
5050
ROUTINE (LIT_MAGIC_STRING_LOCALE_COMPARE_UL, ecma_builtin_string_prototype_object_locale_compare, 1, 1)
5151

52+
#if ENABLED (JERRY_ES2015_BUILTIN)
53+
ROUTINE (LIT_MAGIC_STRING_STARTS_WITH, ecma_builtin_string_prototype_object_starts_with, 2, 1)
54+
ROUTINE (LIT_MAGIC_STRING_INCLUDES, ecma_builtin_string_prototype_object_includes, 2, 1)
55+
ROUTINE (LIT_MAGIC_STRING_ENDS_WITH, ecma_builtin_string_prototype_object_ends_with, 2, 1)
56+
#endif /* ENABLED (JERRY_ES2015_BUILTIN) */
57+
5258
#if ENABLED (JERRY_BUILTIN_REGEXP)
5359
ROUTINE (LIT_MAGIC_STRING_MATCH, ecma_builtin_string_prototype_object_match, 1, 1)
5460
ROUTINE (LIT_MAGIC_STRING_REPLACE, ecma_builtin_string_prototype_object_replace, 2, 2)

jerry-core/lit/lit-magic-strings.inc.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INFINITY_UL, "Infinity")
422422
#if ENABLED (JERRY_BUILTIN_ERRORS)
423423
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_URI_ERROR_UL, "URIError")
424424
#endif
425+
#if ENABLED (JERRY_BUILTIN_STRING) && ENABLED (JERRY_ES2015_BUILTIN)
426+
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ENDS_WITH, "endsWith")
427+
#endif
425428
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION, "function")
426429
#if ENABLED (JERRY_BUILTIN_DATE)
427430
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_HOURS_UL, "getHours")
@@ -436,6 +439,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_MONTH_UL, "getMonth")
436439
#if ENABLED (JERRY_ES2015_BUILTIN_DATAVIEW)
437440
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UINT8_UL, "getUint8")
438441
#endif
442+
#if ENABLED (JERRY_BUILTIN_STRING) && ENABLED (JERRY_ES2015_BUILTIN)
443+
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INCLUDES, "includes")
444+
#endif
439445
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_FINITE, "isFinite")
440446
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_FROZEN_UL, "isFrozen")
441447
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_SEALED_UL, "isSealed")
@@ -557,6 +563,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_MINUTES_UL, "setMinutes")
557563
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_SECONDS_UL, "setSeconds")
558564
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UTC_DATE_UL, "setUTCDate")
559565
#endif
566+
#if ENABLED (JERRY_BUILTIN_STRING) && ENABLED (JERRY_ES2015_BUILTIN)
567+
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STARTS_WITH, "startsWith")
568+
#endif
560569
#if ENABLED (JERRY_ES2015_BUILTIN_DATAVIEW) \
561570
|| ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY)
562571
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARRAY_BUFFER_UL, "ArrayBuffer")

jerry-core/lit/lit-magic-strings.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,14 @@ LIT_MAGIC_STRING_DATAVIEW_UL = "DataView"
180180
LIT_MAGIC_STRING_FUNCTION_UL = "Function"
181181
LIT_MAGIC_STRING_INFINITY_UL = "Infinity"
182182
LIT_MAGIC_STRING_URI_ERROR_UL = "URIError"
183+
LIT_MAGIC_STRING_ENDS_WITH = "endsWith"
183184
LIT_MAGIC_STRING_FUNCTION = "function"
184185
LIT_MAGIC_STRING_GET_HOURS_UL = "getHours"
185186
LIT_MAGIC_STRING_GET_INT16_UL = "getInt16"
186187
LIT_MAGIC_STRING_GET_INT32_UL = "getInt32"
187188
LIT_MAGIC_STRING_GET_MONTH_UL = "getMonth"
188189
LIT_MAGIC_STRING_GET_UINT8_UL = "getUint8"
190+
LIT_MAGIC_STRING_INCLUDES = "includes"
189191
LIT_MAGIC_STRING_IS_FINITE = "isFinite"
190192
LIT_MAGIC_STRING_IS_FROZEN_UL = "isFrozen"
191193
LIT_MAGIC_STRING_IS_SEALED_UL = "isSealed"
@@ -244,6 +246,7 @@ LIT_MAGIC_STRING_SET_FLOAT_64_UL = "setFloat64"
244246
LIT_MAGIC_STRING_SET_MINUTES_UL = "setMinutes"
245247
LIT_MAGIC_STRING_SET_SECONDS_UL = "setSeconds"
246248
LIT_MAGIC_STRING_SET_UTC_DATE_UL = "setUTCDate"
249+
LIT_MAGIC_STRING_STARTS_WITH = "startsWith"
247250
LIT_MAGIC_STRING_ARRAY_BUFFER_UL = "ArrayBuffer"
248251
LIT_MAGIC_STRING_SYNTAX_ERROR_UL = "SyntaxError"
249252
LIT_MAGIC_STRING_UINT16_ARRAY_UL = "Uint16Array"

0 commit comments

Comments
 (0)