1/* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecma-builtin-helpers.h"
17
18#include "ecma-alloc.h"
19#include "ecma-array-object.h"
20#include "ecma-builtins.h"
21#include "ecma-builtin-object.h"
22#include "ecma-conversion.h"
23#include "ecma-function-object.h"
24#include "ecma-exceptions.h"
25#include "ecma-gc.h"
26#include "ecma-helpers.h"
27#include "jmem.h"
28#include "ecma-objects.h"
29#include "ecma-try-catch-macro.h"
30#include "lit-magic-strings.h"
31#include "lit-char-helpers.h"
32
33/** \addtogroup ecma ECMA
34 * @{
35 *
36 * \addtogroup ecmabuiltinhelpers ECMA builtin helper operations
37 * @{
38 */
39
40#if ENABLED (JERRY_ES2015)
41/**
42 * Helper function for Object.prototype.toString routine when
43 * the @@toStringTag property is present
44 *
45 * See also:
46 *          ECMA-262 v6, 19.1.3.6
47 *
48 * @return ecma value
49 *         Returned value must be freed with ecma_free_value.
50 */
51static ecma_value_t
52ecma_builtin_helper_object_to_string_tag_helper (ecma_value_t tag_value) /**< string tag */
53{
54  JERRY_ASSERT (ecma_is_value_string (tag_value));
55
56  ecma_string_t *tag_str_p = ecma_get_string_from_value (tag_value);
57  ecma_string_t *ret_string_p;
58
59  /* Building string "[object #@@toStringTag#]"
60     The string size will be size("[object ") + size(#@@toStringTag#) + size ("]"). */
61  const lit_utf8_size_t buffer_size = 9 + ecma_string_get_size (tag_str_p);
62  JMEM_DEFINE_LOCAL_ARRAY (str_buffer, buffer_size, lit_utf8_byte_t);
63
64  lit_utf8_byte_t *buffer_ptr = str_buffer;
65
66  const lit_magic_string_id_t magic_string_ids[] =
67  {
68    LIT_MAGIC_STRING_LEFT_SQUARE_CHAR,
69    LIT_MAGIC_STRING_OBJECT,
70    LIT_MAGIC_STRING_SPACE_CHAR,
71  };
72
73  /* Copy to buffer the "[object " string */
74  for (uint32_t i = 0; i < sizeof (magic_string_ids) / sizeof (lit_magic_string_id_t); ++i)
75  {
76    buffer_ptr = lit_copy_magic_string_to_buffer (magic_string_ids[i], buffer_ptr,
77                                                  (lit_utf8_size_t) ((str_buffer + buffer_size) - buffer_ptr));
78
79    JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size);
80  }
81
82  /* Copy to buffer the #@@toStringTag# string */
83  buffer_ptr += ecma_string_copy_to_cesu8_buffer (tag_str_p, buffer_ptr,
84                                                  (lit_utf8_size_t) ((str_buffer + buffer_size) - buffer_ptr));
85
86  JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size);
87
88  /* Copy to buffer the "]" string */
89  buffer_ptr = lit_copy_magic_string_to_buffer (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR, buffer_ptr,
90                                                (lit_utf8_size_t) ((str_buffer + buffer_size) - buffer_ptr));
91
92  JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size);
93
94  ret_string_p = ecma_new_ecma_string_from_utf8 (str_buffer, (lit_utf8_size_t) (buffer_ptr - str_buffer));
95
96  JMEM_FINALIZE_LOCAL_ARRAY (str_buffer);
97  ecma_deref_ecma_string (tag_str_p);
98
99  return ecma_make_string_value (ret_string_p);
100} /* ecma_builtin_helper_object_to_string_tag_helper */
101#endif /* ENABLED (JERRY_ES2015) */
102
103/**
104 * Common implementation of the Object.prototype.toString routine
105 *
106 * See also:
107 *          ECMA-262 v5, 15.2.4.2
108 *
109 * Used by:
110 *         - The Object.prototype.toString routine.
111 *         - The Array.prototype.toString routine as fallback.
112 *
113 * @return ecma value
114 *         Returned value must be freed with ecma_free_value.
115 */
116
117ecma_value_t
118ecma_builtin_helper_object_to_string (const ecma_value_t this_arg) /**< this argument */
119{
120  lit_magic_string_id_t type_string;
121
122  if (ecma_is_value_undefined (this_arg))
123  {
124    type_string = LIT_MAGIC_STRING_UNDEFINED_UL;
125  }
126  else if (ecma_is_value_null (this_arg))
127  {
128    type_string = LIT_MAGIC_STRING_NULL_UL;
129  }
130  else
131  {
132    ecma_value_t obj_this = ecma_op_to_object (this_arg);
133
134    if (ECMA_IS_VALUE_ERROR (obj_this))
135    {
136      return obj_this;
137    }
138
139    JERRY_ASSERT (ecma_is_value_object (obj_this));
140
141    ecma_object_t *obj_p = ecma_get_object_from_value (obj_this);
142
143    type_string = ecma_object_get_class_name (obj_p);
144
145#if ENABLED (JERRY_ES2015)
146    ecma_value_t tag_value = ecma_op_object_get_by_symbol_id (obj_p, LIT_GLOBAL_SYMBOL_TO_STRING_TAG);
147
148    if (ECMA_IS_VALUE_ERROR (tag_value))
149    {
150      ecma_deref_object (obj_p);
151      return tag_value;
152    }
153
154    if (ecma_is_value_string (tag_value))
155    {
156      ecma_deref_object (obj_p);
157      return ecma_builtin_helper_object_to_string_tag_helper (tag_value);
158    }
159
160    ecma_free_value (tag_value);
161#endif /* ENABLED (JERRY_ES2015) */
162
163    ecma_deref_object (obj_p);
164  }
165
166  ecma_string_t *ret_string_p;
167
168  /* Building string "[object #type#]" where type is 'Undefined',
169     'Null' or one of possible object's classes.
170     The string with null character is maximum 27 characters long. */
171  const lit_utf8_size_t buffer_size = 27;
172  JERRY_VLA (lit_utf8_byte_t, str_buffer, buffer_size);
173
174  lit_utf8_byte_t *buffer_ptr = str_buffer;
175
176  const lit_magic_string_id_t magic_string_ids[] =
177  {
178    LIT_MAGIC_STRING_LEFT_SQUARE_CHAR,
179    LIT_MAGIC_STRING_OBJECT,
180    LIT_MAGIC_STRING_SPACE_CHAR,
181    type_string,
182    LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR
183  };
184
185  for (uint32_t i = 0; i < sizeof (magic_string_ids) / sizeof (lit_magic_string_id_t); ++i)
186  {
187    buffer_ptr = lit_copy_magic_string_to_buffer (magic_string_ids[i], buffer_ptr,
188                                                  (lit_utf8_size_t) ((str_buffer + buffer_size) - buffer_ptr));
189    JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size);
190  }
191
192  ret_string_p = ecma_new_ecma_string_from_utf8 (str_buffer, (lit_utf8_size_t) (buffer_ptr - str_buffer));
193
194  return ecma_make_string_value (ret_string_p);
195} /* ecma_builtin_helper_object_to_string */
196
197/**
198 * The Array.prototype's 'toLocaleString' single element operation routine
199 *
200 * See also:
201 *          ECMA-262 v5, 15.4.4.3 steps 6-8 and 10.b-d
202 *
203 * @return ecma value
204 *         Returned value must be freed with ecma_free_value.
205 */
206ecma_string_t *
207ecma_builtin_helper_get_to_locale_string_at_index (ecma_object_t *obj_p, /**< this object */
208                                                   uint32_t index) /**< array index */
209{
210  ecma_value_t index_value = ecma_op_object_get_by_uint32_index (obj_p, index);
211
212  if (ECMA_IS_VALUE_ERROR (index_value))
213  {
214    return NULL;
215  }
216
217  if (ecma_is_value_undefined (index_value) || ecma_is_value_null (index_value))
218  {
219    return ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);
220  }
221
222  ecma_value_t index_obj_value = ecma_op_to_object (index_value);
223
224  if (ECMA_IS_VALUE_ERROR (index_obj_value))
225  {
226    ecma_free_value (index_value);
227    return NULL;
228  }
229
230  ecma_string_t *ret_string_p = NULL;
231  ecma_object_t *index_obj_p = ecma_get_object_from_value (index_obj_value);
232  ecma_value_t to_locale_value = ecma_op_object_get_by_magic_id (index_obj_p, LIT_MAGIC_STRING_TO_LOCALE_STRING_UL);
233
234  if (ECMA_IS_VALUE_ERROR (to_locale_value))
235  {
236    goto cleanup;
237  }
238
239  if (!ecma_op_is_callable (to_locale_value))
240  {
241    ecma_free_value (to_locale_value);
242    ecma_raise_type_error (ECMA_ERR_MSG ("'toLocaleString' is missing or not a function."));
243    goto cleanup;
244  }
245
246  ecma_object_t *locale_func_obj_p = ecma_get_object_from_value (to_locale_value);
247  ecma_value_t call_value = ecma_op_function_call (locale_func_obj_p,
248                                                   index_obj_value,
249                                                   NULL,
250                                                   0);
251  ecma_deref_object (locale_func_obj_p);
252
253  if (ECMA_IS_VALUE_ERROR (call_value))
254  {
255    goto cleanup;
256  }
257
258  ret_string_p = ecma_op_to_string (call_value);
259  ecma_free_value (call_value);
260
261cleanup:
262  ecma_deref_object (index_obj_p);
263  ecma_free_value (index_value);
264
265  return ret_string_p;
266} /* ecma_builtin_helper_get_to_locale_string_at_index */
267
268/**
269 * The Object.keys and Object.getOwnPropertyNames routine's common part.
270 *
271 * See also:
272 *          ECMA-262 v5, 15.2.3.4 steps 2-5
273 *          ECMA-262 v5, 15.2.3.14 steps 3-6
274 *
275 * @return ecma value - Array of property names.
276 *         Returned value must be freed with ecma_free_value.
277 */
278ecma_value_t
279ecma_builtin_helper_object_get_properties (ecma_object_t *obj_p, /**< object */
280                                           uint32_t opts) /**< any combination of ecma_list_properties_options_t */
281{
282  JERRY_ASSERT (obj_p != NULL);
283
284  ecma_collection_t *props_p = ecma_op_object_get_property_names (obj_p, opts);
285
286#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
287  if (props_p == NULL)
288  {
289    return ECMA_VALUE_ERROR;
290  }
291#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
292
293  if (props_p->item_count == 0)
294  {
295    ecma_collection_destroy (props_p);
296    return ecma_op_create_array_object (NULL, 0, false);
297  }
298
299  ecma_value_t new_array = ecma_op_create_array_object (props_p->buffer_p, props_p->item_count, false);
300  ecma_collection_free (props_p);
301
302  return new_array;
303} /* ecma_builtin_helper_object_get_properties */
304
305/**
306 * Helper function to normalizing an array index
307 *
308 * See also:
309 *          ECMA-262 v5, 15.4.4.10 steps 5, 6, 7 part 2, 8
310 *          ECMA-262 v5, 15.4.4.12 steps 5, 6
311 *          ECMA-262 v5, 15.5.4.13 steps 4 - 7
312 *          ECMA-262 v6, 22.1.3.6 steps 5 - 7, 8 part 2, 9, 10
313 *          ECMA-262 v6, 22.1.3.3 steps 5 - 10, 11 part 2, 12, 13
314 *          ECMA-262 v6, 22.2.3.5 steps 5 - 10, 11 part 2, 12, 13
315 *          ECMA-262 v6, 22.2.3.23 steps 5 - 10
316 *          ECMA-262 v6, 24.1.4.3 steps 6 - 8, 9 part 2, 10, 11
317 *          ECMA-262 v6, 22.2.3.26 steps 7 - 9, 10 part 2, 11, 12
318 *          ECMA-262 v6, 22.2.3.8 steps 5 - 7, 8 part 2, 9, 10
319 *
320 * Used by:
321 *         - The Array.prototype.slice routine.
322 *         - The Array.prototype.splice routine.
323 *         - The String.prototype.slice routine.
324 *         - The Array.prototype.fill routine.
325 *         - The Array.prototype.copyWithin routine.
326 *         - The TypedArray.prototype.copyWithin routine.
327 *         - The TypedArray.prototype.slice routine.
328 *         - The ArrayBuffer.prototype.slice routine.
329 *         - The TypedArray.prototype.subarray routine.
330 *         - The TypedArray.prototype.fill routine.
331 *
332 * @return ECMA_VALUE_EMPTY if successful
333 *         conversion error otherwise
334 */
335uint32_t
336ecma_builtin_helper_array_index_normalize (ecma_value_t arg, /**< index */
337                                           uint32_t length, /**< array's length */
338                                           uint32_t *number_p) /**< [out] uint32_t */
339{
340  ecma_number_t to_int;
341
342  if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (arg, &to_int)))
343  {
344    return ECMA_VALUE_ERROR;
345  }
346
347  *number_p = ((to_int < 0) ? (uint32_t) JERRY_MAX ((length + to_int), 0)
348                            : (uint32_t) JERRY_MIN (to_int, length));
349
350  return ECMA_VALUE_EMPTY;
351} /* ecma_builtin_helper_array_index_normalize */
352
353/**
354 * Helper function for concatenating an ecma_value_t to an Array.
355 *
356 * See also:
357 *          ECMA-262 v5, 15.4.4.4 steps 5.b - 5.c
358 *
359 * Used by:
360 *         - The Array.prototype.concat routine.
361 *
362 * @return ecma value
363 *         Returned value must be freed with ecma_free_value.
364 */
365ecma_value_t
366ecma_builtin_helper_array_concat_value (ecma_object_t *array_obj_p, /**< array */
367                                        uint32_t *length_p, /**< [in,out] array's length */
368                                        ecma_value_t value) /**< value to concat */
369{
370  /* 5.b */
371#if ENABLED (JERRY_ES2015)
372  ecma_value_t is_spreadable = ecma_op_is_concat_spreadable (value);
373
374  if (ECMA_IS_VALUE_ERROR (is_spreadable))
375  {
376    return is_spreadable;
377  }
378
379  bool spread_object = is_spreadable == ECMA_VALUE_TRUE;
380#else /* !ENABLED (JERRY_ES2015) */
381  bool spread_object = ecma_is_value_true (ecma_is_value_array (value));
382#endif /* ENABLED (JERRY_ES2015) */
383
384  if (spread_object)
385  {
386    ecma_object_t *obj_p = ecma_get_object_from_value (value);
387
388#if ENABLED (JERRY_ES2015)
389    uint32_t arg_len;
390    ecma_value_t error = ecma_op_object_get_length (obj_p, &arg_len);
391
392    if (ECMA_IS_VALUE_ERROR (error))
393    {
394      return error;
395    }
396#else /* !ENABLED (JERRY_ES2015) */
397    /* 5.b.ii */
398    uint32_t arg_len = ecma_array_get_length (obj_p);
399#endif /* ENABLED (JERRY_ES2015) */
400    /* 5.b.iii */
401    for (uint32_t array_index = 0; array_index < arg_len; array_index++)
402    {
403      /* 5.b.iii.2 */
404      ecma_value_t get_value = ecma_op_object_find_by_uint32_index (obj_p, array_index);
405
406      if (ECMA_IS_VALUE_ERROR (get_value))
407      {
408        return get_value;
409      }
410
411      if (!ecma_is_value_found (get_value))
412      {
413        continue;
414      }
415
416      /* 5.b.iii.3.b */
417      /* This will always be a simple value since 'is_throw' is false, so no need to free. */
418      ecma_value_t put_comp = ecma_builtin_helper_def_prop_by_index (array_obj_p,
419                                                                     *length_p + array_index,
420                                                                     get_value,
421                                                                     ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
422
423      JERRY_ASSERT (ecma_is_value_true (put_comp));
424      ecma_free_value (get_value);
425    }
426
427    *length_p += arg_len;
428    return ECMA_VALUE_EMPTY;
429  }
430
431  /* 5.c.i */
432  /* This will always be a simple value since 'is_throw' is false, so no need to free. */
433  ecma_value_t put_comp = ecma_builtin_helper_def_prop_by_index (array_obj_p,
434                                                                 (*length_p)++,
435                                                                 value,
436                                                                 ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
437  JERRY_ASSERT (ecma_is_value_true (put_comp));
438
439  return ECMA_VALUE_EMPTY;
440} /* ecma_builtin_helper_array_concat_value */
441
442/**
443 * Helper function to normalizing a string index
444 *
445 * This function clamps the given index to the [0, length] range.
446 * If the index is negative, 0 value is used.
447 * If the index is greater than the length of the string, the normalized index will be the length of the string.
448 * NaN is mapped to zero or length depending on the nan_to_zero parameter.
449 *
450 * See also:
451 *          ECMA-262 v5, 15.5.4.15
452 *
453 * Used by:
454 *         - The String.prototype.substring routine.
455 *         - The ecma_builtin_helper_string_prototype_object_index_of helper routine.
456 *
457 * @return uint32_t - the normalized value of the index
458 */
459uint32_t
460ecma_builtin_helper_string_index_normalize (ecma_number_t index, /**< index */
461                                            uint32_t length, /**< string's length */
462                                            bool nan_to_zero) /**< whether NaN is mapped to zero (t) or length (f) */
463{
464  uint32_t norm_index = 0;
465
466  if (ecma_number_is_nan (index))
467  {
468    if (!nan_to_zero)
469    {
470      norm_index = length;
471    }
472  }
473  else if (!ecma_number_is_negative (index))
474  {
475    if (ecma_number_is_infinity (index))
476    {
477      norm_index = length;
478    }
479    else
480    {
481      norm_index = ecma_number_to_uint32 (index);
482
483      if (norm_index > length)
484      {
485        norm_index = length;
486      }
487    }
488  }
489
490  return norm_index;
491} /* ecma_builtin_helper_string_index_normalize */
492
493/**
494 * Helper function for string indexOf, lastIndexOf, startsWith, includes, endsWith functions
495 *
496 * See also:
497 *          ECMA-262 v5, 15.5.4.7
498 *          ECMA-262 v5, 15.5.4.8
499 *          ECMA-262 v6, 21.1.3.6
500 *          ECMA-262 v6, 21.1.3.7
501 *          ECMA-262 v6, 21.1.3.18
502 *
503 * Used by:
504 *         - The String.prototype.indexOf routine.
505 *         - The String.prototype.lastIndexOf routine.
506 *         - The String.prototype.startsWith routine.
507 *         - The String.prototype.includes routine.
508 *         - The String.prototype.endsWith routine.
509 *
510 * @return ecma_value_t - Returns index (last index) or a
511 *                        boolean value
512 */
513ecma_value_t
514ecma_builtin_helper_string_prototype_object_index_of (ecma_string_t *original_str_p, /**< this argument */
515                                                      ecma_value_t arg1, /**< routine's first argument */
516                                                      ecma_value_t arg2, /**< routine's second argument */
517                                                      ecma_string_index_of_mode_t mode) /**< routine's mode */
518{
519  /* 5 (indexOf) -- 6 (lastIndexOf) */
520  const ecma_length_t original_len = ecma_string_get_length (original_str_p);
521
522#if ENABLED (JERRY_ES2015)
523  /* 4, 6 (startsWith, includes, endsWith) */
524  if (mode >= ECMA_STRING_STARTS_WITH)
525  {
526    ecma_value_t regexp = ecma_op_is_regexp (arg1);
527
528    if (ECMA_IS_VALUE_ERROR (regexp))
529    {
530      return regexp;
531    }
532
533    if (regexp == ECMA_VALUE_TRUE)
534    {
535      JERRY_ASSERT (ECMA_STRING_LAST_INDEX_OF < mode && mode <= ECMA_STRING_ENDS_WITH);
536      return ecma_raise_type_error (ECMA_ERR_MSG ("Search string can't be of type: RegExp"));
537    }
538  }
539#endif /* ENABLED (JERRY_ES2015) */
540
541  /* 7, 8 */
542  ecma_string_t *search_str_p = ecma_op_to_string (arg1);
543
544  if (JERRY_UNLIKELY (search_str_p == NULL))
545  {
546    return ECMA_VALUE_ERROR;
547  }
548
549  /* 4 (indexOf, lastIndexOf), 9 (startsWith, includes), 10 (endsWith) */
550  ecma_number_t pos_num;
551  ecma_value_t ret_value;
552#if ENABLED (JERRY_ES2015)
553  if (mode > ECMA_STRING_LAST_INDEX_OF)
554  {
555    ret_value = ecma_op_to_integer (arg2, &pos_num);
556  }
557  else
558  {
559#endif /* ENABLED (JERRY_ES2015) */
560    ret_value = ecma_get_number (arg2, &pos_num);
561#if ENABLED (JERRY_ES2015)
562  }
563#endif /* ENABLED (JERRY_ES2015) */
564
565  /* 10 (startsWith, includes), 11 (endsWith) */
566  if (ECMA_IS_VALUE_ERROR (ret_value))
567  {
568    ecma_deref_ecma_string (search_str_p);
569    return ret_value;
570  }
571
572  bool use_first_index = mode != ECMA_STRING_LAST_INDEX_OF;
573
574  /* 4b, 6 (indexOf) - 4b, 5, 7 (lastIndexOf) */
575  ecma_length_t start = ecma_builtin_helper_string_index_normalize (pos_num, original_len, use_first_index);
576
577  ecma_number_t ret_num = ECMA_NUMBER_MINUS_ONE;
578
579  ecma_length_t index_of = 0;
580
581  ret_value = ECMA_VALUE_FALSE;
582
583  switch (mode)
584  {
585#if ENABLED (JERRY_ES2015)
586    case ECMA_STRING_STARTS_WITH:
587    {
588      const ecma_length_t search_len = ecma_string_get_length (search_str_p);
589
590      if (search_len + start > original_len)
591      {
592        break;
593      }
594
595      if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, true, start, &index_of))
596      {
597        /* 15, 16 (startsWith) */
598        ret_value = ecma_make_boolean_value (index_of == start);
599      }
600      break;
601    }
602    case ECMA_STRING_INCLUDES:
603    {
604      if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, true, start, &index_of))
605      {
606        ret_value = ECMA_VALUE_TRUE;
607      }
608      break;
609    }
610    case ECMA_STRING_ENDS_WITH:
611    {
612      if (start == 0)
613      {
614        start = original_len;
615      }
616
617      ecma_length_t search_str_len = ecma_string_get_length (search_str_p);
618
619      if (search_str_len == 0)
620      {
621        ret_value = ECMA_VALUE_TRUE;
622        break;
623      }
624
625      int32_t start_ends_with = (int32_t) (start - search_str_len);
626
627      if (start_ends_with < 0)
628      {
629        break;
630      }
631      if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, true,
632                                                 (ecma_length_t) start_ends_with, &index_of))
633      {
634        ret_value = ecma_make_boolean_value (index_of == (ecma_length_t) start_ends_with);
635      }
636      break;
637    }
638#endif /* ENABLED (JERRY_ES2015) */
639
640    case ECMA_STRING_INDEX_OF:
641    case ECMA_STRING_LAST_INDEX_OF:
642    default:
643    {
644      /* 8 (indexOf) -- 9 (lastIndexOf) */
645      if (ecma_builtin_helper_string_find_index (original_str_p, search_str_p, use_first_index, start, &index_of))
646      {
647        ret_num = ((ecma_number_t) index_of);
648      }
649      ret_value = ecma_make_number_value (ret_num);
650      break;
651    }
652  }
653
654  ecma_deref_ecma_string (search_str_p);
655
656  return ret_value;
657} /* ecma_builtin_helper_string_prototype_object_index_of */
658
659/**
660 * Helper function for finding index of a search string
661 *
662 * This function clamps the given index to the [0, length] range.
663 * If the index is negative, 0 value is used.
664 * If the index is greater than the length of the string, the normalized index will be the length of the string.
665 * NaN is mapped to zero or length depending on the nan_to_zero parameter.
666 *
667 * See also:
668 *          ECMA-262 v5, 15.5.4.7,8,11
669 *
670 * Used by:
671 *         - The ecma_builtin_helper_string_prototype_object_index_of helper routine.
672 *         - The ecma_builtin_string_prototype_object_replace_match helper routine.
673 *
674 * @return bool - whether there is a match for the search string
675 */
676bool
677ecma_builtin_helper_string_find_index (ecma_string_t *original_str_p, /**< index */
678                                       ecma_string_t *search_str_p, /**< string's length */
679                                       bool first_index, /**< whether search for first (t) or last (f) index */
680                                       ecma_length_t start_pos, /**< start position */
681                                       ecma_length_t *ret_index_p) /**< [out] position found in original string */
682{
683  bool match_found = false;
684  const ecma_length_t original_len = ecma_string_get_length (original_str_p);
685  const ecma_length_t search_len = ecma_string_get_length (search_str_p);
686
687  if (search_len <= original_len)
688  {
689    if (!search_len)
690    {
691      match_found = true;
692      *ret_index_p = first_index ? start_pos : original_len;
693    }
694    else
695    {
696      /* create utf8 string from original string and advance to position */
697      ECMA_STRING_TO_UTF8_STRING (original_str_p, original_str_utf8_p, original_str_size);
698
699      ecma_length_t index = start_pos;
700
701      const lit_utf8_byte_t *original_str_curr_p = original_str_utf8_p;
702      for (ecma_length_t idx = 0; idx < index; idx++)
703      {
704        lit_utf8_incr (&original_str_curr_p);
705      }
706
707      /* create utf8 string from search string */
708      ECMA_STRING_TO_UTF8_STRING (search_str_p, search_str_utf8_p, search_str_size);
709
710      const lit_utf8_byte_t *search_str_curr_p = search_str_utf8_p;
711
712      /* iterate original string and try to match at each position */
713      bool searching = true;
714      ecma_char_t first_char = lit_cesu8_read_next (&search_str_curr_p);
715      while (searching)
716      {
717        /* match as long as possible */
718        ecma_length_t match_len = 0;
719        const lit_utf8_byte_t *stored_original_str_curr_p = original_str_curr_p;
720
721        if (match_len < search_len &&
722            index + match_len < original_len &&
723            lit_cesu8_read_next (&original_str_curr_p) == first_char)
724        {
725          const lit_utf8_byte_t *nested_search_str_curr_p = search_str_curr_p;
726          match_len++;
727
728          while (match_len < search_len &&
729                 index + match_len < original_len &&
730                 lit_cesu8_read_next (&original_str_curr_p) == lit_cesu8_read_next (&nested_search_str_curr_p))
731          {
732            match_len++;
733          }
734        }
735
736        /* check for match */
737        if (match_len == search_len)
738        {
739          match_found = true;
740          *ret_index_p = index;
741
742          break;
743        }
744        else
745        {
746          /* inc/dec index and update iterators and search condition */
747          original_str_curr_p = stored_original_str_curr_p;
748
749          if (first_index)
750          {
751            if ((searching = (index <= original_len - search_len)))
752            {
753              lit_utf8_incr (&original_str_curr_p);
754              index++;
755            }
756          }
757          else
758          {
759            if ((searching = (index > 0)))
760            {
761              lit_utf8_decr (&original_str_curr_p);
762              index--;
763            }
764          }
765        }
766      }
767
768      ECMA_FINALIZE_UTF8_STRING (search_str_utf8_p, search_str_size);
769      ECMA_FINALIZE_UTF8_STRING (original_str_utf8_p, original_str_size);
770    }
771  }
772
773  return match_found;
774} /* ecma_builtin_helper_string_find_index */
775
776/**
777 * Helper function for using [[DefineOwnProperty]] specialized for indexed property names
778 *
779 * Note: this method falls back to the general ecma_builtin_helper_def_prop
780 *
781 * @return ecma value
782 *         Returned value must be freed with ecma_free_value.
783 */
784ecma_value_t
785ecma_builtin_helper_def_prop_by_index (ecma_object_t *obj_p, /**< object */
786                                       uint32_t index, /**< property index */
787                                       ecma_value_t value, /**< value */
788                                       uint32_t opts) /**< any combination of ecma_property_flag_t bits */
789{
790  if (JERRY_LIKELY (index <= ECMA_DIRECT_STRING_MAX_IMM))
791  {
792    return ecma_builtin_helper_def_prop (obj_p,
793                                         ECMA_CREATE_DIRECT_UINT32_STRING (index),
794                                         value,
795                                         opts);
796  }
797
798  ecma_string_t *index_str_p = ecma_new_non_direct_string_from_uint32 (index);
799  ecma_value_t ret_value = ecma_builtin_helper_def_prop (obj_p,
800                                                         index_str_p,
801                                                         value,
802                                                         opts);
803  ecma_deref_ecma_string (index_str_p);
804
805  return ret_value;
806} /* ecma_builtin_helper_def_prop_by_index */
807
808/**
809 * Helper function for using [[DefineOwnProperty]].
810 *
811 * See also:
812 *          ECMA-262 v5, 8.12.9
813 *          ECMA-262 v5, 15.4.5.1
814 *
815 * @return ecma value
816 *         Returned value must be freed with ecma_free_value.
817 */
818ecma_value_t
819ecma_builtin_helper_def_prop (ecma_object_t *obj_p, /**< object */
820                              ecma_string_t *name_p, /**< name string */
821                              ecma_value_t value, /**< value */
822                              uint32_t opts) /**< any combination of ecma_property_flag_t bits
823                                              *   with the optional ECMA_IS_THROW flag */
824{
825  ecma_property_descriptor_t prop_desc;
826
827  prop_desc.flags = (uint16_t) (ECMA_NAME_DATA_PROPERTY_DESCRIPTOR_BITS | opts);
828
829  prop_desc.value = value;
830
831  return ecma_op_object_define_own_property (obj_p,
832                                             name_p,
833                                             &prop_desc);
834} /* ecma_builtin_helper_def_prop */
835
836/**
837 * GetSubstitution abstract operation
838 *
839 * See:
840 *     ECMA-262 v6.0 21.1.3.14.1
841 */
842void
843ecma_builtin_replace_substitute (ecma_replace_context_t *ctx_p) /**< replace context */
844{
845  JERRY_ASSERT (ctx_p->string_p != NULL);
846  JERRY_ASSERT (ctx_p->matched_p == NULL
847                || (ctx_p->matched_p >= ctx_p->string_p
848                    && ctx_p->matched_p <= ctx_p->string_p + ctx_p->string_size));
849
850  lit_utf8_size_t replace_size;
851  uint8_t replace_flags = ECMA_STRING_FLAG_IS_ASCII;
852  const lit_utf8_byte_t *replace_buf_p = ecma_string_get_chars (ctx_p->replace_str_p,
853                                                                &replace_size,
854                                                                NULL,
855                                                                NULL,
856                                                                &replace_flags);
857
858  const lit_utf8_byte_t *const replace_end_p = replace_buf_p + replace_size;
859  const lit_utf8_byte_t *curr_p = replace_buf_p;
860  const lit_utf8_byte_t *last_inserted_end_p = replace_buf_p;
861
862  while (curr_p < replace_end_p)
863  {
864    if (*curr_p++ == LIT_CHAR_DOLLAR_SIGN)
865    {
866      ecma_stringbuilder_append_raw (&(ctx_p->builder),
867                                     last_inserted_end_p,
868                                     (lit_utf8_size_t) (curr_p - last_inserted_end_p - 1));
869      if (curr_p >= replace_end_p)
870      {
871        last_inserted_end_p = curr_p - 1;
872        break;
873      }
874
875      const lit_utf8_byte_t c = *curr_p++;
876
877      switch (c)
878      {
879        case LIT_CHAR_DOLLAR_SIGN:
880        {
881          ecma_stringbuilder_append_byte (&(ctx_p->builder), LIT_CHAR_DOLLAR_SIGN);
882          break;
883        }
884        case LIT_CHAR_AMPERSAND:
885        {
886#if ENABLED (JERRY_ES2015)
887          if (JERRY_UNLIKELY (ctx_p->matched_p == NULL))
888          {
889            JERRY_ASSERT (ctx_p->capture_count == 0);
890            JERRY_ASSERT (ctx_p->u.collection_p != NULL);
891            JERRY_ASSERT (ctx_p->u.collection_p->item_count > 0);
892            const ecma_value_t match_value = ctx_p->u.collection_p->buffer_p[0];
893
894            JERRY_ASSERT (ecma_is_value_string (match_value));
895            ecma_stringbuilder_append (&(ctx_p->builder), ecma_get_string_from_value (match_value));
896            break;
897          }
898#endif /* ENABLED (JERRY_ES2015) */
899
900          JERRY_ASSERT (ctx_p->matched_p != NULL);
901          ecma_stringbuilder_append_raw (&(ctx_p->builder), ctx_p->matched_p, ctx_p->matched_size);
902          break;
903        }
904        case LIT_CHAR_GRAVE_ACCENT:
905        {
906          ecma_stringbuilder_append_raw (&(ctx_p->builder), ctx_p->string_p, ctx_p->match_byte_pos);
907          break;
908        }
909        case LIT_CHAR_SINGLE_QUOTE:
910        {
911#if ENABLED (JERRY_ES2015)
912          if (JERRY_UNLIKELY (ctx_p->matched_p == NULL))
913          {
914            JERRY_ASSERT (ctx_p->capture_count == 0);
915            JERRY_ASSERT (ctx_p->u.collection_p != NULL);
916            JERRY_ASSERT (ctx_p->u.collection_p->item_count > 0);
917            const ecma_value_t match_value = ctx_p->u.collection_p->buffer_p[0];
918
919            JERRY_ASSERT (ecma_is_value_string (match_value));
920            const ecma_string_t *const matched_p = ecma_get_string_from_value (match_value);
921            const lit_utf8_size_t match_size = ecma_string_get_size (matched_p);
922            const lit_utf8_byte_t *const begin_p = ctx_p->string_p + ctx_p->match_byte_pos + match_size;
923
924            ecma_stringbuilder_append_raw (&(ctx_p->builder),
925                                           begin_p,
926                                           (lit_utf8_size_t) (ctx_p->string_p + ctx_p->string_size - begin_p));
927            break;
928          }
929#endif /* ENABLED (JERRY_ES2015) */
930
931          JERRY_ASSERT (ctx_p->matched_p != NULL);
932          ecma_stringbuilder_append_raw (&(ctx_p->builder),
933                                         ctx_p->matched_p + ctx_p->matched_size,
934                                         ctx_p->string_size - ctx_p->match_byte_pos - ctx_p->matched_size);
935          break;
936        }
937        default:
938        {
939          const lit_utf8_byte_t *const number_begin_p = curr_p - 1;
940
941          if (lit_char_is_decimal_digit (c))
942          {
943            uint32_t capture_count = ctx_p->capture_count;
944#if ENABLED (JERRY_ES2015)
945            if (capture_count == 0 && ctx_p->u.collection_p != NULL)
946            {
947              capture_count = ctx_p->u.collection_p->item_count;
948            }
949#endif /* ENABLED (JERRY_ES2015) */
950
951            uint8_t idx = (uint8_t) (c - LIT_CHAR_0);
952            if (curr_p < replace_end_p && lit_char_is_decimal_digit (*(curr_p)))
953            {
954              uint8_t two_digit_index = (uint8_t) (idx * 10 + (uint8_t) (*(curr_p) - LIT_CHAR_0));
955              if (two_digit_index < capture_count)
956              {
957                idx = two_digit_index;
958                curr_p++;
959              }
960            }
961
962            if (idx > 0 && idx < capture_count)
963            {
964              if (ctx_p->capture_count > 0)
965              {
966#if ENABLED (JERRY_BUILTIN_REGEXP)
967                JERRY_ASSERT (ctx_p->u.captures_p != NULL);
968                const ecma_regexp_capture_t *const capture_p = ctx_p->u.captures_p + idx;
969
970                if (ECMA_RE_IS_CAPTURE_DEFINED (capture_p))
971                {
972                  ecma_stringbuilder_append_raw (&(ctx_p->builder),
973                                                 capture_p->begin_p,
974                                                 (lit_utf8_size_t) (capture_p->end_p - capture_p->begin_p));
975                }
976
977                break;
978#endif /* ENABLED (JERRY_BUILTIN_REGEXP) */
979              }
980#if ENABLED (JERRY_ES2015)
981              else if (ctx_p->u.collection_p != NULL)
982              {
983                const ecma_value_t capture_value = ctx_p->u.collection_p->buffer_p[idx];
984                if (!ecma_is_value_undefined (capture_value))
985                {
986                  ecma_stringbuilder_append (&(ctx_p->builder), ecma_get_string_from_value (capture_value));
987                }
988
989                break;
990              }
991#endif /* ENABLED (JERRY_ES2015) */
992            }
993          }
994
995          ecma_stringbuilder_append_byte (&(ctx_p->builder), LIT_CHAR_DOLLAR_SIGN);
996          curr_p = number_begin_p;
997          break;
998        }
999      }
1000
1001      last_inserted_end_p = curr_p;
1002    }
1003  }
1004
1005  ecma_stringbuilder_append_raw (&(ctx_p->builder),
1006                                 last_inserted_end_p,
1007                                 (lit_utf8_size_t) (replace_end_p - last_inserted_end_p));
1008
1009  if (replace_flags & ECMA_STRING_FLAG_MUST_BE_FREED)
1010  {
1011    jmem_heap_free_block ((void *) replace_buf_p, replace_size);
1012  }
1013} /* ecma_builtin_replace_substitute */
1014
1015/**
1016 * @}
1017 * @}
1018 */
1019