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#include "jcontext.h"
16#include "ecma-alloc.h"
17#include "ecma-array-object.h"
18#include "ecma-builtins.h"
19#include "ecma-builtin-helpers.h"
20#include "ecma-exceptions.h"
21#include "ecma-function-object.h"
22#include "ecma-gc.h"
23#include "ecma-helpers.h"
24#include "ecma-iterator-object.h"
25#include "ecma-container-object.h"
26#include "ecma-property-hashmap.h"
27#include "ecma-objects.h"
28
29#if ENABLED (JERRY_ES2015_BUILTIN_CONTAINER)
30
31/** \addtogroup ecma ECMA
32 * @{
33 *
34 * \addtogroup \addtogroup ecmamaphelpers ECMA builtin Map/Set helper functions
35 * @{
36 */
37
38/**
39 * Create a new internal buffer.
40 *
41 * Note:
42 *   The first element of the collection tracks the size of the buffer.
43 *   ECMA_VALUE_EMPTY values are not calculated into the size.
44 *
45 * @return pointer to the internal buffer
46 */
47static inline ecma_collection_t *
48ecma_op_create_internal_buffer (void)
49{
50  ecma_collection_t *collection_p = ecma_new_collection ();
51  ecma_collection_push_back (collection_p, (ecma_value_t) 0);
52
53  return collection_p;
54} /* ecma_op_create_internal_buffer */
55
56/**
57 * Append values to the internal buffer.
58 */
59static void
60ecma_op_internal_buffer_append (ecma_collection_t *container_p, /**< internal container pointer */
61                                ecma_value_t key_arg, /**< key argument */
62                                ecma_value_t value_arg, /**< value argument */
63                                lit_magic_string_id_t lit_id) /**< class id */
64{
65  JERRY_ASSERT (container_p != NULL);
66
67  if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL)
68  {
69    ecma_value_t values[] = { ecma_copy_value_if_not_object (key_arg), ecma_copy_value_if_not_object (value_arg) };
70    ecma_collection_append (container_p, values, 2);
71  }
72  else
73  {
74    ecma_collection_push_back (container_p, ecma_copy_value_if_not_object (key_arg));
75  }
76
77  ECMA_CONTAINER_SET_SIZE (container_p, ECMA_CONTAINER_GET_SIZE (container_p) + 1);
78} /* ecma_op_internal_buffer_append */
79
80/**
81 * Update the value of a given entry.
82 */
83static inline void
84ecma_op_internal_buffer_update (ecma_value_t *entry_p, /**< entry pointer */
85                                ecma_value_t value_arg, /**< value argument */
86                                lit_magic_string_id_t lit_id) /**< class id */
87{
88  JERRY_ASSERT (entry_p != NULL);
89
90  if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL)
91  {
92    ecma_free_value_if_not_object (((ecma_container_pair_t *) entry_p)->value);
93
94    ((ecma_container_pair_t *) entry_p)->value = ecma_copy_value_if_not_object (value_arg);
95  }
96} /* ecma_op_internal_buffer_update */
97
98/**
99 * Delete element from the internal buffer.
100 */
101static void
102ecma_op_internal_buffer_delete (ecma_collection_t *container_p, /**< internal container pointer */
103                                ecma_container_pair_t *entry_p, /**< entry pointer */
104                                lit_magic_string_id_t lit_id) /**< class id */
105{
106  JERRY_ASSERT (container_p != NULL);
107  JERRY_ASSERT (entry_p != NULL);
108
109  ecma_free_value_if_not_object (entry_p->key);
110  entry_p->key = ECMA_VALUE_EMPTY;
111
112  if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL)
113  {
114    ecma_free_value_if_not_object (entry_p->value);
115    entry_p->value = ECMA_VALUE_EMPTY;
116  }
117
118  ECMA_CONTAINER_SET_SIZE (container_p, ECMA_CONTAINER_GET_SIZE (container_p) - 1);
119} /* ecma_op_internal_buffer_delete */
120
121/**
122 * Find an entry in the collection.
123 *
124 * @return pointer to the appropriate entry.
125 */
126static ecma_value_t *
127ecma_op_internal_buffer_find (ecma_collection_t *container_p, /**< internal container pointer */
128                              ecma_value_t key_arg, /**< key argument */
129                              lit_magic_string_id_t lit_id) /**< class id */
130{
131  JERRY_ASSERT (container_p != NULL);
132
133  uint8_t entry_size = ecma_op_container_entry_size (lit_id);
134  uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p);
135  ecma_value_t *start_p = ECMA_CONTAINER_START (container_p);
136
137  for (uint32_t i = 0; i < entry_count; i += entry_size)
138  {
139    ecma_value_t *entry_p = start_p + i;
140
141    if (ecma_op_same_value_zero (*entry_p, key_arg))
142    {
143      return entry_p;
144    }
145  }
146
147  return NULL;
148} /* ecma_op_internal_buffer_find */
149
150/**
151 * Get the value that belongs to the key.
152 *
153 * Note: in case of Set containers, the values are the same as the keys.
154 *
155 * @return ecma value
156 */
157static ecma_value_t
158ecma_op_container_get_value (ecma_value_t *entry_p, /**< entry (key) pointer */
159                             lit_magic_string_id_t lit_id) /**< class id */
160{
161  JERRY_ASSERT (entry_p != NULL);
162
163  if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL)
164  {
165    return ((ecma_container_pair_t *) entry_p)->value;
166  }
167
168  return *entry_p;
169} /* ecma_op_container_get_value */
170
171/**
172 * Get the size (in ecma_value_t) of the stored entries.
173 *
174 * @return size of the entries.
175 */
176uint8_t
177ecma_op_container_entry_size (lit_magic_string_id_t lit_id) /**< class id */
178{
179  if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL)
180  {
181    return ECMA_CONTAINER_PAIR_SIZE;
182  }
183
184  return ECMA_CONTAINER_VALUE_SIZE;
185} /* ecma_op_container_entry_size */
186
187#if ENABLED (JERRY_ES2015_BUILTIN_WEAKSET)
188/**
189 * Release the entries in the WeakSet container.
190 */
191static void
192ecma_op_container_free_weakset_entries (ecma_object_t *object_p, /**< object pointer */
193                                        ecma_collection_t *container_p) /** internal buffer pointer */
194{
195  JERRY_ASSERT (object_p != NULL);
196  JERRY_ASSERT (container_p != NULL);
197
198  uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p);
199  ecma_value_t *start_p = ECMA_CONTAINER_START (container_p);
200
201  for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_VALUE_SIZE)
202  {
203    ecma_value_t *entry_p = start_p + i;
204
205    if (ecma_is_value_empty (*entry_p))
206    {
207      continue;
208    }
209
210    ecma_op_container_unref_weak (ecma_get_object_from_value (*entry_p), ecma_make_object_value (object_p));
211    ecma_op_container_remove_weak_entry (object_p, *entry_p);
212
213    *entry_p = ECMA_VALUE_EMPTY;
214  }
215} /* ecma_op_container_free_weakset_entries */
216#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */
217
218#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP)
219/**
220 * Release the entries in the WeakMap container.
221 */
222static void
223ecma_op_container_free_weakmap_entries (ecma_object_t *object_p, /**< object pointer */
224                                        ecma_collection_t *container_p) /**< internal buffer pointer */
225{
226  JERRY_ASSERT (object_p != NULL);
227  JERRY_ASSERT (container_p != NULL);
228
229  uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p);
230  ecma_value_t *start_p = ECMA_CONTAINER_START (container_p);
231
232  for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE)
233  {
234    ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i);
235
236    if (ecma_is_value_empty (entry_p->key))
237    {
238      continue;
239    }
240
241    ecma_op_container_unref_weak (ecma_get_object_from_value (entry_p->key), ecma_make_object_value (object_p));
242    ecma_op_container_remove_weak_entry (object_p, entry_p->key);
243
244    ecma_free_value_if_not_object (entry_p->value);
245
246    entry_p->key = ECMA_VALUE_EMPTY;
247    entry_p->value = ECMA_VALUE_EMPTY;
248  }
249} /* ecma_op_container_free_weakmap_entries */
250#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */
251
252#if ENABLED (JERRY_ES2015_BUILTIN_SET)
253/**
254 * Release the entries in the Set container.
255 */
256static void
257ecma_op_container_free_set_entries (ecma_collection_t *container_p)
258{
259  JERRY_ASSERT (container_p != NULL);
260
261  uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p);
262  ecma_value_t *start_p = ECMA_CONTAINER_START (container_p);
263
264  for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_VALUE_SIZE)
265  {
266    ecma_value_t *entry_p = start_p + i;
267
268    if (ecma_is_value_empty (*entry_p))
269    {
270      continue;
271    }
272
273    ecma_free_value_if_not_object (*entry_p);
274    *entry_p = ECMA_VALUE_EMPTY;
275  }
276} /* ecma_op_container_free_set_entries */
277#endif /* ENABLED (JERRY_ES2015_BUILTIN_SET) */
278
279#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
280/**
281 * Release the entries in the Map container.
282 */
283static void
284ecma_op_container_free_map_entries (ecma_collection_t *container_p)
285{
286  JERRY_ASSERT (container_p != NULL);
287
288  uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p);
289  ecma_value_t *start_p = ECMA_CONTAINER_START (container_p);
290
291  for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE)
292  {
293    ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i);
294
295    if (ecma_is_value_empty (entry_p->key))
296    {
297      continue;
298    }
299
300    ecma_free_value_if_not_object (entry_p->key);
301    ecma_free_value_if_not_object (entry_p->value);
302
303    entry_p->key = ECMA_VALUE_EMPTY;
304    entry_p->value = ECMA_VALUE_EMPTY;
305  }
306} /* ecma_op_container_free_map_entries */
307#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */
308
309/**
310 * Release the internal buffer and the stored entries.
311 */
312void
313ecma_op_container_free_entries (ecma_object_t *object_p) /**< collection object pointer */
314{
315  JERRY_ASSERT (object_p != NULL);
316
317  ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p;
318  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
319                                                                    map_object_p->u.class_prop.u.value);
320
321  switch (map_object_p->u.class_prop.class_id)
322  {
323#if ENABLED (JERRY_ES2015_BUILTIN_WEAKSET)
324    case LIT_MAGIC_STRING_WEAKSET_UL:
325    {
326      ecma_op_container_free_weakset_entries (object_p, container_p);
327      break;
328    }
329#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */
330#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP)
331    case LIT_MAGIC_STRING_WEAKMAP_UL:
332    {
333      ecma_op_container_free_weakmap_entries (object_p, container_p);
334      break;
335    }
336#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */
337#if ENABLED (JERRY_ES2015_BUILTIN_SET)
338    case LIT_MAGIC_STRING_SET_UL:
339    {
340      ecma_op_container_free_set_entries (container_p);
341      break;
342    }
343#endif /* ENABLED (JERRY_ES2015_BUILTIN_SET) */
344#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
345    case LIT_MAGIC_STRING_MAP_UL:
346    {
347      ecma_op_container_free_map_entries (container_p);
348      break;
349    }
350#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */
351    default:
352    {
353      break;
354    }
355  }
356
357  ECMA_CONTAINER_SET_SIZE (container_p, 0);
358} /* ecma_op_container_free_entries */
359
360/**
361 * Handle calling [[Construct]] of built-in Map/Set like objects
362 *
363 * @return ecma value
364 */
365ecma_value_t
366ecma_op_container_create (const ecma_value_t *arguments_list_p, /**< arguments list */
367                          ecma_length_t arguments_list_len, /**< number of arguments */
368                          lit_magic_string_id_t lit_id, /**< internal class id */
369                          ecma_builtin_id_t proto_id) /**< prototype builtin id */
370{
371  JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL);
372  JERRY_ASSERT (lit_id == LIT_MAGIC_STRING_MAP_UL
373                || lit_id == LIT_MAGIC_STRING_SET_UL
374                || lit_id == LIT_MAGIC_STRING_WEAKMAP_UL
375                || lit_id == LIT_MAGIC_STRING_WEAKSET_UL);
376  JERRY_ASSERT (JERRY_CONTEXT (current_new_target) != NULL);
377
378  ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target), proto_id);
379
380  if (JERRY_UNLIKELY (proto_p == NULL))
381  {
382    return ECMA_VALUE_ERROR;
383  }
384
385  ecma_collection_t *container_p = ecma_op_create_internal_buffer ();
386  ecma_object_t *object_p  = ecma_create_object (proto_p,
387                                                 sizeof (ecma_extended_object_t),
388                                                 ECMA_OBJECT_TYPE_CLASS);
389  ecma_deref_object (proto_p);
390  ecma_extended_object_t *map_obj_p = (ecma_extended_object_t *) object_p;
391  map_obj_p->u.class_prop.extra_info = ECMA_CONTAINER_FLAGS_EMPTY;
392  map_obj_p->u.class_prop.class_id = (uint16_t) lit_id;
393
394  if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_WEAKSET_UL)
395  {
396    map_obj_p->u.class_prop.extra_info |= ECMA_CONTAINER_FLAGS_WEAK;
397  }
398
399  ECMA_SET_INTERNAL_VALUE_POINTER (map_obj_p->u.class_prop.u.value, container_p);
400
401  ecma_value_t set_value = ecma_make_object_value (object_p);
402  ecma_value_t result = set_value;
403
404#if ENABLED (JERRY_ES2015)
405  if (arguments_list_len == 0)
406  {
407    return result;
408  }
409
410  ecma_value_t iterable = arguments_list_p[0];
411
412  if (ecma_is_value_undefined (iterable) || ecma_is_value_null (iterable))
413  {
414    return result;
415  }
416
417  lit_magic_string_id_t adder_string_id;
418  if (lit_id == LIT_MAGIC_STRING_MAP_UL || lit_id == LIT_MAGIC_STRING_WEAKMAP_UL)
419  {
420    adder_string_id = LIT_MAGIC_STRING_SET;
421  }
422  else
423  {
424    adder_string_id = LIT_MAGIC_STRING_ADD;
425  }
426
427  result = ecma_op_object_get_by_magic_id (object_p, adder_string_id);
428  if (ECMA_IS_VALUE_ERROR (result))
429  {
430    goto cleanup_object;
431  }
432
433  if (!ecma_op_is_callable (result))
434  {
435    ecma_free_value (result);
436    result = ecma_raise_type_error (ECMA_ERR_MSG ("add/set function is not callable."));
437    goto cleanup_object;
438  }
439
440  ecma_object_t *adder_func_p = ecma_get_object_from_value (result);
441
442  result = ecma_op_get_iterator (iterable, ECMA_VALUE_EMPTY);
443
444  if (ECMA_IS_VALUE_ERROR (result))
445  {
446    goto cleanup_adder;
447  }
448
449  const ecma_value_t iter = result;
450
451  while (true)
452  {
453    result = ecma_op_iterator_step (iter);
454
455    if (ECMA_IS_VALUE_ERROR (result))
456    {
457      goto cleanup_iter;
458    }
459
460    if (ecma_is_value_false (result))
461    {
462      break;
463    }
464
465    const ecma_value_t next = result;
466    result = ecma_op_iterator_value (next);
467    ecma_free_value (next);
468
469    if (ECMA_IS_VALUE_ERROR (result))
470    {
471      goto cleanup_iter;
472    }
473
474    if (lit_id == LIT_MAGIC_STRING_SET_UL || lit_id == LIT_MAGIC_STRING_WEAKSET_UL)
475    {
476      const ecma_value_t value = result;
477
478      ecma_value_t arguments[] = { value };
479      result = ecma_op_function_call (adder_func_p, set_value, arguments, 1);
480
481      ecma_free_value (value);
482    }
483    else
484    {
485      if (!ecma_is_value_object (result))
486      {
487        ecma_free_value (result);
488        ecma_raise_type_error (ECMA_ERR_MSG ("Iterator value is not an object."));
489        result = ecma_op_iterator_close (iter);
490        JERRY_ASSERT (ECMA_IS_VALUE_ERROR (result));
491        goto cleanup_iter;
492      }
493
494      ecma_object_t *next_object_p = ecma_get_object_from_value (result);
495
496      result = ecma_op_object_get_by_uint32_index (next_object_p, 0);
497
498      if (ECMA_IS_VALUE_ERROR (result))
499      {
500        ecma_deref_object (next_object_p);
501        ecma_op_iterator_close (iter);
502        goto cleanup_iter;
503      }
504
505      const ecma_value_t key = result;
506
507      result = ecma_op_object_get_by_uint32_index (next_object_p, 1);
508
509      if (ECMA_IS_VALUE_ERROR (result))
510      {
511        ecma_deref_object (next_object_p);
512        ecma_free_value (key);
513        ecma_op_iterator_close (iter);
514        goto cleanup_iter;
515      }
516
517      const ecma_value_t value = result;
518      ecma_value_t arguments[] = { key, value };
519      result = ecma_op_function_call (adder_func_p, set_value, arguments, 2);
520
521      ecma_free_value (key);
522      ecma_free_value (value);
523      ecma_deref_object (next_object_p);
524    }
525
526    if (ECMA_IS_VALUE_ERROR (result))
527    {
528      ecma_op_iterator_close (iter);
529      goto cleanup_iter;
530    }
531
532    ecma_free_value (result);
533  }
534
535  ecma_free_value (iter);
536  ecma_deref_object (adder_func_p);
537  return ecma_make_object_value (object_p);
538
539cleanup_iter:
540  ecma_free_value (iter);
541cleanup_adder:
542  ecma_deref_object (adder_func_p);
543cleanup_object:
544  ecma_deref_object (object_p);
545#endif /* ENABLED (JERRY_ES2015) */
546
547  return result;
548} /* ecma_op_container_create */
549
550/**
551 * Get Map/Set object pointer
552 *
553 * Note:
554 *   If the function returns with NULL, the error object has
555 *   already set, and the caller must return with ECMA_VALUE_ERROR
556 *
557 * @return pointer to the Map/Set if this_arg is a valid Map/Set object
558 *         NULL otherwise
559 */
560static ecma_extended_object_t *
561ecma_op_container_get_object (ecma_value_t this_arg, /**< this argument */
562                              lit_magic_string_id_t lit_id) /**< internal class id */
563{
564  if (ecma_is_value_object (this_arg))
565  {
566    ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) ecma_get_object_from_value (this_arg);
567
568    if (ecma_get_object_type ((ecma_object_t *) map_object_p) == ECMA_OBJECT_TYPE_CLASS
569        && map_object_p->u.class_prop.class_id == lit_id)
570    {
571      return map_object_p;
572    }
573  }
574
575#if ENABLED (JERRY_ERROR_MESSAGES)
576  ecma_raise_standard_error_with_format (ECMA_ERROR_TYPE,
577                                         "Expected a % object.",
578                                         ecma_make_string_value (ecma_get_magic_string (lit_id)));
579#else /* !ENABLED (JERRY_ERROR_MESSAGES) */
580  ecma_raise_type_error (NULL);
581#endif /* ENABLED (JERRY_ERROR_MESSAGES) */
582
583  return NULL;
584} /* ecma_op_container_get_object */
585
586/**
587 * Returns with the size of the Map/Set object.
588 *
589 * @return size of the Map/Set object as ecma-value.
590 */
591ecma_value_t
592ecma_op_container_size (ecma_value_t this_arg, /**< this argument */
593                        lit_magic_string_id_t lit_id) /**< internal class id */
594{
595  ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id);
596
597  if (map_object_p == NULL)
598  {
599    return ECMA_VALUE_ERROR;
600  }
601
602  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
603                                                                    map_object_p->u.class_prop.u.value);
604
605  return ecma_make_uint32_value (ECMA_CONTAINER_GET_SIZE (container_p));
606} /* ecma_op_container_size */
607
608/**
609 * The generic Map/WeakMap prototype object's 'get' routine
610 *
611 * @return ecma value
612 *         Returned value must be freed with ecma_free_value.
613 */
614ecma_value_t
615ecma_op_container_get (ecma_value_t this_arg, /**< this argument */
616                       ecma_value_t key_arg, /**< key argument */
617                       lit_magic_string_id_t lit_id) /**< internal class id */
618{
619  ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id);
620
621  if (map_object_p == NULL)
622  {
623    return ECMA_VALUE_ERROR;
624  }
625
626#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP)
627  if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL && !ecma_is_value_object (key_arg))
628  {
629    return ECMA_VALUE_UNDEFINED;
630  }
631#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */
632
633  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
634                                                                    map_object_p->u.class_prop.u.value);
635
636  if (ECMA_CONTAINER_GET_SIZE (container_p) == 0)
637  {
638    return ECMA_VALUE_UNDEFINED;
639  }
640
641  ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id);
642
643  if (entry_p == NULL)
644  {
645    return ECMA_VALUE_UNDEFINED;
646  }
647
648  return ecma_copy_value (((ecma_container_pair_t *) entry_p)->value);
649} /* ecma_op_container_get */
650
651/**
652 * The generic Map/Set prototype object's 'has' routine
653 *
654 * @return ecma value
655 *         Returned value must be freed with ecma_free_value.
656 */
657ecma_value_t
658ecma_op_container_has (ecma_value_t this_arg, /**< this argument */
659                       ecma_value_t key_arg, /**< key argument */
660                       lit_magic_string_id_t lit_id) /**< internal class id */
661{
662  ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id);
663
664  if (map_object_p == NULL)
665  {
666    return ECMA_VALUE_ERROR;
667  }
668
669  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
670                                                                    map_object_p->u.class_prop.u.value);
671
672#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) || ENABLED (JERRY_ES2015_BUILTIN_WEAKSET)
673  if ((map_object_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) != 0
674      && !ecma_is_value_object (key_arg))
675  {
676    return ECMA_VALUE_FALSE;
677  }
678#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) ||  ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */
679
680  if (ECMA_CONTAINER_GET_SIZE (container_p) == 0)
681  {
682    return ECMA_VALUE_FALSE;
683  }
684
685  ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id);
686
687  return ecma_make_boolean_value (entry_p != NULL);
688} /* ecma_op_container_has */
689
690/**
691 * Set a weak reference from a container to a key object
692 */
693static void
694ecma_op_container_set_weak (ecma_object_t *const key_p, /**< key object */
695                            ecma_extended_object_t *const container_p) /**< container */
696{
697  if (JERRY_UNLIKELY (ecma_op_object_is_fast_array (key_p)))
698  {
699    ecma_fast_array_convert_to_normal (key_p);
700  }
701
702  ecma_string_t *weak_refs_string_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_WEAK_REFS);
703  ecma_property_t *property_p = ecma_find_named_property (key_p, weak_refs_string_p);
704  ecma_collection_t *refs_p;
705
706  if (property_p == NULL)
707  {
708    ecma_property_value_t *value_p = ecma_create_named_data_property (key_p,
709                                                                      weak_refs_string_p,
710                                                                      ECMA_PROPERTY_CONFIGURABLE_WRITABLE,
711                                                                      &property_p);
712    ECMA_CONVERT_DATA_PROPERTY_TO_INTERNAL_PROPERTY (property_p);
713    refs_p = ecma_new_collection ();
714    ECMA_SET_INTERNAL_VALUE_POINTER (value_p->value, refs_p);
715  }
716  else
717  {
718    refs_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, (ECMA_PROPERTY_VALUE_PTR (property_p)->value));
719  }
720
721  const ecma_value_t container_value = ecma_make_object_value ((ecma_object_t *) container_p);
722  for (uint32_t i = 0; i < refs_p->item_count; i++)
723  {
724    if (ecma_is_value_empty (refs_p->buffer_p[i]))
725    {
726      refs_p->buffer_p[i] = container_value;
727      return;
728    }
729  }
730
731  ecma_collection_push_back (refs_p, container_value);
732} /* ecma_op_container_set_weak */
733
734/**
735 * Helper method for the Map.prototype.set and Set.prototype.add methods to swap the sign of the given value if needed
736 *
737 * See also:
738 *          ECMA-262 v6, 23.2.3.1 step 6
739 *          ECMA-262 v6, 23.1.3.9 step 6
740 *
741 * @return ecma value
742 */
743static ecma_value_t
744ecma_op_container_set_noramlize_zero (ecma_value_t this_arg) /*< this arg */
745{
746  if (ecma_is_value_number (this_arg))
747  {
748    ecma_number_t number_value = ecma_get_number_from_value (this_arg);
749
750    if (JERRY_UNLIKELY (ecma_number_is_zero (number_value) && ecma_number_is_negative (number_value)))
751    {
752      return ecma_make_integer_value (0);
753    }
754  }
755
756  return this_arg;
757} /* ecma_op_container_set_noramlize_zero */
758
759/**
760 * The generic Map prototype object's 'set' and Set prototype object's 'add' routine
761 *
762 * @return ecma value
763 *         Returned value must be freed with ecma_free_value.
764 */
765ecma_value_t
766ecma_op_container_set (ecma_value_t this_arg, /**< this argument */
767                       ecma_value_t key_arg, /**< key argument */
768                       ecma_value_t value_arg, /**< value argument */
769                       lit_magic_string_id_t lit_id) /**< internal class id */
770{
771  ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id);
772
773  if (map_object_p == NULL)
774  {
775    return ECMA_VALUE_ERROR;
776  }
777
778  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
779                                                                    map_object_p->u.class_prop.u.value);
780
781#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) ||  ENABLED (JERRY_ES2015_BUILTIN_WEAKSET)
782  if ((map_object_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) != 0
783      && !ecma_is_value_object (key_arg))
784  {
785    return ecma_raise_type_error (ECMA_ERR_MSG ("Key must be an object"));
786  }
787#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) ||  ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */
788
789  ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id);
790
791  if (entry_p == NULL)
792  {
793    ecma_op_internal_buffer_append (container_p,
794                                    ecma_op_container_set_noramlize_zero (key_arg),
795                                    value_arg,
796                                    lit_id);
797
798#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) ||  ENABLED (JERRY_ES2015_BUILTIN_WEAKSET)
799    if ((map_object_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) != 0)
800    {
801      ecma_object_t *key_p = ecma_get_object_from_value (key_arg);
802      ecma_op_container_set_weak (key_p, map_object_p);
803    }
804#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) ||  ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */
805  }
806  else
807  {
808    ecma_op_internal_buffer_update (entry_p, ecma_op_container_set_noramlize_zero (value_arg), lit_id);
809  }
810
811  ecma_ref_object ((ecma_object_t *) map_object_p);
812  return this_arg;
813} /* ecma_op_container_set */
814
815/**
816 * The generic Map/Set prototype object's 'forEach' routine
817 *
818 * @return ecma value
819 *         Returned value must be freed with ecma_free_value.
820 */
821ecma_value_t
822ecma_op_container_foreach (ecma_value_t this_arg, /**< this argument */
823                           ecma_value_t predicate, /**< callback function */
824                           ecma_value_t predicate_this_arg, /**< this argument for
825                                                             *   invoke predicate */
826                           lit_magic_string_id_t lit_id) /**< internal class id */
827{
828  ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id);
829
830  if (map_object_p == NULL)
831  {
832    return ECMA_VALUE_ERROR;
833  }
834
835  if (!ecma_op_is_callable (predicate))
836  {
837    return ecma_raise_type_error (ECMA_ERR_MSG ("Callback function is not callable."));
838  }
839
840  JERRY_ASSERT (ecma_is_value_object (predicate));
841  ecma_object_t *func_object_p = ecma_get_object_from_value (predicate);
842  ecma_value_t ret_value = ECMA_VALUE_UNDEFINED;
843
844  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
845                                                                    map_object_p->u.class_prop.u.value);
846
847  uint8_t entry_size = ecma_op_container_entry_size (lit_id);
848
849  for (uint32_t i = 0; i < ECMA_CONTAINER_ENTRY_COUNT (container_p); i += entry_size)
850  {
851    ecma_value_t *entry_p = ECMA_CONTAINER_START (container_p) + i;
852
853    if (ecma_is_value_empty (*entry_p))
854    {
855      continue;
856    }
857
858    ecma_value_t key_arg = *entry_p;
859    ecma_value_t value_arg = ecma_op_container_get_value (entry_p, lit_id);
860
861    ecma_value_t call_args[] = { value_arg, key_arg, this_arg };
862    ecma_value_t call_value = ecma_op_function_call (func_object_p, predicate_this_arg, call_args, 3);
863
864    if (ECMA_IS_VALUE_ERROR (call_value))
865    {
866      ret_value = call_value;
867      break;
868    }
869
870    ecma_free_value (call_value);
871  }
872
873  return ret_value;
874} /* ecma_op_container_foreach */
875
876/**
877 * The Map/Set prototype object's 'clear' routine
878 *
879 * @return ecma value
880 *         Returned value must be freed with ecma_free_value.
881 */
882ecma_value_t
883ecma_op_container_clear (ecma_value_t this_arg, /**< this argument */
884                         lit_magic_string_id_t lit_id) /**< internal class id */
885{
886  ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id);
887
888  if (map_object_p == NULL)
889  {
890    return ECMA_VALUE_ERROR;
891  }
892
893  ecma_op_container_free_entries ((ecma_object_t *) map_object_p);
894
895  return ECMA_VALUE_UNDEFINED;
896} /* ecma_op_container_clear */
897
898/**
899 * The generic Map/Set prototype object's 'delete' routine
900 *
901 * @return ecma value
902 *         Returned value must be freed with ecma_free_value.
903 */
904ecma_value_t
905ecma_op_container_delete (ecma_value_t this_arg, /**< this argument */
906                          ecma_value_t key_arg, /**< key argument */
907                          lit_magic_string_id_t lit_id) /**< internal class id */
908{
909  ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id);
910
911  if (map_object_p == NULL)
912  {
913    return ECMA_VALUE_ERROR;
914  }
915
916  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
917                                                                    map_object_p->u.class_prop.u.value);
918
919  ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id);
920
921  if (entry_p == NULL)
922  {
923    return ECMA_VALUE_FALSE;
924  }
925
926  ecma_op_internal_buffer_delete (container_p, (ecma_container_pair_t *) entry_p, lit_id);
927  return ECMA_VALUE_TRUE;
928} /* ecma_op_container_delete */
929
930/**
931 * The generic WeakMap/WeakSet prototype object's 'delete' routine
932 *
933 * @return ecma value
934 *         Returned value must be freed with ecma_free_value.
935 */
936ecma_value_t
937ecma_op_container_delete_weak (ecma_value_t this_arg, /**< this argument */
938                               ecma_value_t key_arg, /**< key argument */
939                               lit_magic_string_id_t lit_id) /**< internal class id */
940{
941  ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id);
942
943  if (map_object_p == NULL)
944  {
945    return ECMA_VALUE_ERROR;
946  }
947
948  if (!ecma_is_value_object (key_arg))
949  {
950    return ECMA_VALUE_FALSE;
951  }
952
953  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
954                                                                    map_object_p->u.class_prop.u.value);
955
956  ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id);
957
958  if (entry_p == NULL)
959  {
960    return ECMA_VALUE_FALSE;
961  }
962
963  ecma_op_internal_buffer_delete (container_p, (ecma_container_pair_t *) entry_p, lit_id);
964
965  ecma_object_t *key_object_p = ecma_get_object_from_value (key_arg);
966  ecma_op_container_unref_weak (key_object_p, ecma_make_object_value ((ecma_object_t *) map_object_p));
967
968  return ECMA_VALUE_TRUE;
969} /* ecma_op_container_delete_weak */
970
971/**
972 * Helper function to remove a weak reference to an object.
973 *
974 * @return ecma value
975 *         Returned value must be freed with ecma_free_value.
976 */
977void
978ecma_op_container_unref_weak (ecma_object_t *object_p, /**< this argument */
979                              ecma_value_t ref_holder) /**< key argument */
980{
981  ecma_string_t *weak_refs_string_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_WEAK_REFS);
982
983  ecma_property_t *property_p = ecma_find_named_property (object_p, weak_refs_string_p);
984  JERRY_ASSERT (property_p != NULL);
985
986  ecma_collection_t *refs_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
987                                                               ECMA_PROPERTY_VALUE_PTR (property_p)->value);
988  for (uint32_t i = 0; i < refs_p->item_count; i++)
989  {
990    if (refs_p->buffer_p[i] == ref_holder)
991    {
992      refs_p->buffer_p[i] = ECMA_VALUE_EMPTY;
993      break;
994    }
995  }
996} /* ecma_op_container_unref_weak */
997
998/**
999 * Helper function to remove a key/value pair from a weak container object
1000 */
1001void
1002ecma_op_container_remove_weak_entry (ecma_object_t *object_p, /**< internal container object */
1003                                     ecma_value_t key_arg) /**< key */
1004{
1005  ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p;
1006
1007  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
1008                                                                    map_object_p->u.class_prop.u.value);
1009
1010  ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, map_object_p->u.class_prop.class_id);
1011
1012  JERRY_ASSERT (entry_p != NULL);
1013
1014  ecma_op_internal_buffer_delete (container_p, (ecma_container_pair_t *) entry_p, map_object_p->u.class_prop.class_id);
1015} /* ecma_op_container_remove_weak_entry */
1016
1017#if ENABLED (JERRY_ES2015)
1018
1019/**
1020 * The Create{Set, Map}Iterator Abstract operation
1021 *
1022 * See also:
1023 *          ECMA-262 v6, 23.1.5.1
1024 *          ECMA-262 v6, 23.2.5.1
1025 *
1026 * Note:
1027 *     Returned value must be freed with ecma_free_value.
1028 *
1029 * @return Map/Set iterator object, if success
1030 *         error - otherwise
1031 */
1032ecma_value_t
1033ecma_op_container_create_iterator (ecma_value_t this_arg, /**< this argument */
1034                                   uint8_t type, /**< any combination of
1035                                                  *   ecma_iterator_type_t bits */
1036                                   lit_magic_string_id_t lit_id, /**< internal class id */
1037                                   ecma_builtin_id_t proto_id, /**< prototype builtin id */
1038                                   ecma_pseudo_array_type_t iterator_type) /**< type of the iterator */
1039{
1040  ecma_extended_object_t *map_object_p = ecma_op_container_get_object (this_arg, lit_id);
1041
1042  if (map_object_p == NULL)
1043  {
1044    return ECMA_VALUE_ERROR;
1045  }
1046
1047  return ecma_op_create_iterator_object (this_arg,
1048                                         ecma_builtin_get (proto_id),
1049                                         (uint8_t) iterator_type,
1050                                         type);
1051} /* ecma_op_container_create_iterator */
1052
1053/**
1054 * Get the index of the iterator object.
1055 *
1056 * @return index of the iterator.
1057 */
1058static uint32_t
1059ecma_op_iterator_get_index (ecma_object_t *iter_obj_p)  /**< iterator object pointer */
1060{
1061  uint32_t index = ((ecma_extended_object_t *) iter_obj_p)->u.pseudo_array.u1.iterator_index;
1062
1063  if (JERRY_UNLIKELY (index == ECMA_ITERATOR_INDEX_LIMIT))
1064  {
1065    ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX);
1066    ecma_property_t *property_p = ecma_find_named_property (iter_obj_p, prop_name_p);
1067    ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p);
1068
1069    return (uint32_t) (ecma_get_number_from_value (value_p->value));
1070  }
1071
1072  return index;
1073} /* ecma_op_iterator_get_index */
1074
1075/**
1076 * Set the index of the iterator object.
1077 */
1078static void
1079ecma_op_iterator_set_index (ecma_object_t *iter_obj_p, /**< iterator object pointer */
1080                            uint32_t index) /* iterator index to set */
1081{
1082  if (JERRY_UNLIKELY (index >= ECMA_ITERATOR_INDEX_LIMIT))
1083  {
1084    /* After the ECMA_ITERATOR_INDEX_LIMIT limit is reached the [[%Iterator%NextIndex]]
1085       property is stored as an internal property */
1086    ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX);
1087    ecma_property_t *property_p = ecma_find_named_property (iter_obj_p, prop_name_p);
1088    ecma_property_value_t *value_p;
1089
1090    if (property_p == NULL)
1091    {
1092      value_p = ecma_create_named_data_property (iter_obj_p, prop_name_p, ECMA_PROPERTY_FLAG_WRITABLE, &property_p);
1093      value_p->value = ecma_make_uint32_value (index);
1094    }
1095    else
1096    {
1097      value_p = ECMA_PROPERTY_VALUE_PTR (property_p);
1098      value_p->value = ecma_make_uint32_value (index);
1099    }
1100  }
1101  else
1102  {
1103    ((ecma_extended_object_t *) iter_obj_p)->u.pseudo_array.u1.iterator_index = (uint16_t) index;
1104  }
1105} /* ecma_op_iterator_set_index */
1106
1107/**
1108 * The %{Set, Map}IteratorPrototype% object's 'next' routine
1109 *
1110 * See also:
1111 *          ECMA-262 v6, 23.1.5.2.1
1112 *          ECMA-262 v6, 23.2.5.2.1
1113 *
1114 * Note:
1115 *     Returned value must be freed with ecma_free_value.
1116 *
1117 * @return iterator result object, if success
1118 *         error - otherwise
1119 */
1120ecma_value_t
1121ecma_op_container_iterator_next (ecma_value_t this_val, /**< this argument */
1122                                 ecma_pseudo_array_type_t iterator_type) /**< type of the iterator */
1123{
1124  if (!ecma_is_value_object (this_val))
1125  {
1126    return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not an object."));
1127  }
1128
1129  ecma_object_t *obj_p = ecma_get_object_from_value (this_val);
1130  ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p;
1131
1132  if (ecma_get_object_type (obj_p) != ECMA_OBJECT_TYPE_PSEUDO_ARRAY
1133      || ext_obj_p->u.pseudo_array.type != iterator_type)
1134  {
1135    return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not an iterator."));
1136  }
1137
1138  ecma_value_t iterated_value = ext_obj_p->u.pseudo_array.u2.iterated_value;
1139
1140  if (ecma_is_value_empty (iterated_value))
1141  {
1142    return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE);
1143  }
1144
1145  ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) (ecma_get_object_from_value (iterated_value));
1146  lit_magic_string_id_t lit_id = map_object_p->u.class_prop.class_id;
1147
1148  ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
1149                                                                    map_object_p->u.class_prop.u.value);
1150  uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p);
1151  uint32_t index = ecma_op_iterator_get_index (obj_p);
1152
1153  if (index == entry_count)
1154  {
1155    ext_obj_p->u.pseudo_array.u2.iterated_value = ECMA_VALUE_EMPTY;
1156
1157    return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE);
1158  }
1159
1160  uint8_t entry_size = ecma_op_container_entry_size (lit_id);
1161  uint8_t iterator_kind = ext_obj_p->u.pseudo_array.extra_info;
1162  ecma_value_t *start_p = ECMA_CONTAINER_START (container_p);
1163  ecma_value_t ret_value = ECMA_VALUE_UNDEFINED;
1164
1165  for (uint32_t i = index; i < entry_count; i += entry_size)
1166  {
1167    ecma_value_t *entry_p = start_p + i;
1168
1169    if (ecma_is_value_empty (*entry_p))
1170    {
1171      if (i == (entry_count - entry_size))
1172      {
1173        ret_value = ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE);
1174        break;
1175      }
1176
1177      continue;
1178    }
1179
1180    ecma_op_iterator_set_index (obj_p, i + entry_size);
1181
1182    ecma_value_t key_arg = *entry_p;
1183    ecma_value_t value_arg = ecma_op_container_get_value (entry_p, lit_id);
1184
1185    if (iterator_kind == ECMA_ITERATOR_KEYS)
1186    {
1187      ret_value = ecma_create_iter_result_object (key_arg, ECMA_VALUE_FALSE);
1188    }
1189    else if (iterator_kind == ECMA_ITERATOR_VALUES)
1190    {
1191      ret_value = ecma_create_iter_result_object (value_arg, ECMA_VALUE_FALSE);
1192    }
1193    else
1194    {
1195      JERRY_ASSERT (iterator_kind == ECMA_ITERATOR_KEYS_VALUES);
1196
1197      ecma_value_t entry_array_value;
1198      entry_array_value = ecma_create_array_from_iter_element (value_arg, key_arg);
1199
1200      ret_value = ecma_create_iter_result_object (entry_array_value, ECMA_VALUE_FALSE);
1201      ecma_free_value (entry_array_value);
1202    }
1203
1204    break;
1205  }
1206
1207  return ret_value;
1208} /* ecma_op_container_iterator_next */
1209
1210#endif /* ENABLED (JERRY_ES2015) */
1211
1212/**
1213 * @}
1214 * @}
1215 */
1216
1217#endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */
1218