• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-globals.h"
17 #include "ecma-helpers.h"
18 #include "ecma-property-hashmap.h"
19 #include "jrt-libc-includes.h"
20 #include "jcontext.h"
21 
22 /** \addtogroup ecma ECMA
23  * @{
24  *
25  * \addtogroup ecmapropertyhashmap Property hashmap
26  * @{
27  */
28 
29 #if ENABLED (JERRY_PROPRETY_HASHMAP)
30 
31 /**
32  * Compute the total size of the property hashmap.
33  */
34 #define ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE(max_property_count) \
35   (sizeof (ecma_property_hashmap_t) + (max_property_count * sizeof (jmem_cpointer_t)) + (max_property_count >> 3))
36 
37 /**
38  * Number of items in the stepping table.
39  */
40 #define ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS 8
41 
42 /**
43  * Stepping values for searching items in the hashmap.
44  */
45 static const uint8_t ecma_property_hashmap_steps[ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS] JERRY_ATTR_CONST_DATA =
46 {
47   3, 5, 7, 11, 13, 17, 19, 23
48 };
49 
50 /**
51  * Get the value of a bit in a bitmap.
52  */
53 #define ECMA_PROPERTY_HASHMAP_GET_BIT(byte_p, index) \
54   ((byte_p)[(index) >> 3] & (1 << ((index) & 0x7)))
55 
56 /**
57  * Clear the value of a bit in a bitmap.
58  */
59 #define ECMA_PROPERTY_HASHMAP_CLEAR_BIT(byte_p, index) \
60   ((byte_p)[(index) >> 3] = (uint8_t) ((byte_p)[(index) >> 3] & ~(1 << ((index) & 0x7))))
61 
62 /**
63  * Set the value of a bit in a bitmap.
64  */
65 #define ECMA_PROPERTY_HASHMAP_SET_BIT(byte_p, index) \
66   ((byte_p)[(index) >> 3] = (uint8_t) ((byte_p)[(index) >> 3] | (1 << ((index) & 0x7))))
67 
68 /**
69  * Create a new property hashmap for the object.
70  * The object must not have a property hashmap.
71  */
72 void
ecma_property_hashmap_create(ecma_object_t * object_p)73 ecma_property_hashmap_create (ecma_object_t *object_p) /**< object */
74 {
75   if (JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) != ECMA_PROP_HASHMAP_ALLOC_ON)
76   {
77     return;
78   }
79 
80   jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp;
81 
82   if (prop_iter_cp == JMEM_CP_NULL)
83   {
84     return;
85   }
86 
87   uint32_t named_property_count = 0;
88 
89   while (prop_iter_cp != JMEM_CP_NULL)
90   {
91     ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
92     JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
93 
94     for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
95     {
96       ecma_property_types_t type = ECMA_PROPERTY_GET_TYPE (prop_iter_p->types[i]);
97 
98       if (type != ECMA_PROPERTY_TYPE_SPECIAL)
99       {
100         named_property_count++;
101       }
102     }
103     prop_iter_cp = prop_iter_p->next_property_cp;
104   }
105 
106   if (named_property_count < (ECMA_PROPERTY_HASMAP_MINIMUM_SIZE / 2))
107   {
108     return;
109   }
110 
111   /* The max_property_count must be power of 2. */
112   uint32_t max_property_count = ECMA_PROPERTY_HASMAP_MINIMUM_SIZE;
113 
114   /* At least 1/3 items must be NULL. */
115   while (max_property_count < (named_property_count + (named_property_count >> 1)))
116   {
117     max_property_count <<= 1;
118   }
119 
120   size_t total_size = ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE (max_property_count);
121 
122   ecma_property_hashmap_t *hashmap_p = (ecma_property_hashmap_t *) jmem_heap_alloc_block_null_on_error (total_size);
123 
124   if (hashmap_p == NULL)
125   {
126     return;
127   }
128 
129   memset (hashmap_p, 0, total_size);
130 
131   hashmap_p->header.types[0] = ECMA_PROPERTY_TYPE_HASHMAP;
132   hashmap_p->header.next_property_cp = object_p->u1.property_list_cp;
133   hashmap_p->max_property_count = max_property_count;
134   hashmap_p->null_count = max_property_count - named_property_count;
135   hashmap_p->unused_count = max_property_count - named_property_count;
136 
137   jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1);
138   uint8_t *bits_p = (uint8_t *) (pair_list_p + max_property_count);
139   uint32_t mask = max_property_count - 1;
140 
141   prop_iter_cp = object_p->u1.property_list_cp;
142   ECMA_SET_NON_NULL_POINTER (object_p->u1.property_list_cp, hashmap_p);
143 
144   while (prop_iter_cp != JMEM_CP_NULL)
145   {
146     ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
147     JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
148 
149     for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
150     {
151       if (!ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[i]))
152       {
153         continue;
154       }
155 
156       ecma_property_pair_t *property_pair_p = (ecma_property_pair_t *) prop_iter_p;
157 
158       uint32_t entry_index = ecma_string_get_property_name_hash (prop_iter_p->types[i],
159                                                                  property_pair_p->names_cp[i]);
160       uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)];
161 
162       entry_index &= mask;
163 #ifndef JERRY_NDEBUG
164       /* Because max_property_count (power of 2) and step (a prime
165        * number) are relative primes, all entries of the hasmap are
166        * visited exactly once before the start entry index is reached
167        * again. Furthermore because at least one NULL is present in
168        * the hashmap, the while loop must be terminated before the
169        * the starting index is reached again. */
170       uint32_t start_entry_index = entry_index;
171 #endif /* !JERRY_NDEBUG */
172 
173       while (pair_list_p[entry_index] != ECMA_NULL_POINTER)
174       {
175         entry_index = (entry_index + step) & mask;
176 
177 #ifndef JERRY_NDEBUG
178         JERRY_ASSERT (entry_index != start_entry_index);
179 #endif /* !JERRY_NDEBUG */
180       }
181 
182       ECMA_SET_NON_NULL_POINTER (pair_list_p[entry_index], property_pair_p);
183 
184       if (i != 0)
185       {
186         ECMA_PROPERTY_HASHMAP_SET_BIT (bits_p, entry_index);
187       }
188     }
189 
190     prop_iter_cp = prop_iter_p->next_property_cp;
191   }
192 } /* ecma_property_hashmap_create */
193 
194 /**
195  * Free the hashmap of the object.
196  * The object must have a property hashmap.
197  */
198 void
ecma_property_hashmap_free(ecma_object_t * object_p)199 ecma_property_hashmap_free (ecma_object_t *object_p) /**< object */
200 {
201   /* Property hash must be exists and must be the first property. */
202   JERRY_ASSERT (object_p->u1.property_list_cp != JMEM_CP_NULL);
203 
204   ecma_property_header_t *property_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t,
205                                                                   object_p->u1.property_list_cp);
206 
207   JERRY_ASSERT (property_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP);
208 
209   ecma_property_hashmap_t *hashmap_p = (ecma_property_hashmap_t *) property_p;
210 
211   object_p->u1.property_list_cp = property_p->next_property_cp;
212 
213   jmem_heap_free_block (hashmap_p,
214                         ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE (hashmap_p->max_property_count));
215 } /* ecma_property_hashmap_free */
216 
217 /**
218  * Insert named property into the hashmap.
219  */
220 void
ecma_property_hashmap_insert(ecma_object_t * object_p,ecma_string_t * name_p,ecma_property_pair_t * property_pair_p,int property_index)221 ecma_property_hashmap_insert (ecma_object_t *object_p, /**< object */
222                               ecma_string_t *name_p, /**< name of the property */
223                               ecma_property_pair_t *property_pair_p, /**< property pair */
224                               int property_index) /**< property index in the pair (0 or 1) */
225 {
226   JERRY_ASSERT (property_pair_p != NULL);
227 
228   ecma_property_hashmap_t *hashmap_p = ECMA_GET_NON_NULL_POINTER (ecma_property_hashmap_t,
229                                                                   object_p->u1.property_list_cp);
230 
231   JERRY_ASSERT (hashmap_p->header.types[0] == ECMA_PROPERTY_TYPE_HASHMAP);
232 
233   /* The NULLs are reduced below 1/8 of the hashmap. */
234   if (hashmap_p->null_count < (hashmap_p->max_property_count >> 3))
235   {
236     ecma_property_hashmap_free (object_p);
237     ecma_property_hashmap_create (object_p);
238     return;
239   }
240 
241   JERRY_ASSERT (property_index < ECMA_PROPERTY_PAIR_ITEM_COUNT);
242 
243   uint32_t entry_index = ecma_string_hash (name_p);
244   uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)];
245   uint32_t mask = hashmap_p->max_property_count - 1;
246   entry_index &= mask;
247 
248 #ifndef JERRY_NDEBUG
249   /* See the comment for this variable in ecma_property_hashmap_create. */
250   uint32_t start_entry_index = entry_index;
251 #endif /* !JERRY_NDEBUG */
252 
253   jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1);
254 
255   while (pair_list_p[entry_index] != ECMA_NULL_POINTER)
256   {
257     entry_index = (entry_index + step) & mask;
258 
259 #ifndef JERRY_NDEBUG
260     JERRY_ASSERT (entry_index != start_entry_index);
261 #endif /* !JERRY_NDEBUG */
262   }
263 
264   ECMA_SET_NON_NULL_POINTER (pair_list_p[entry_index], property_pair_p);
265 
266   uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count);
267   bits_p += (entry_index >> 3);
268   mask = (uint32_t) (1 << (entry_index & 0x7));
269 
270   if (!(*bits_p & mask))
271   {
272     /* Deleted entries also has ECMA_NULL_POINTER
273      * value, but they are not NULL values. */
274     hashmap_p->null_count--;
275     JERRY_ASSERT (hashmap_p->null_count > 0);
276   }
277 
278   hashmap_p->unused_count--;
279   JERRY_ASSERT (hashmap_p->unused_count > 0);
280 
281   if (property_index == 0)
282   {
283     *bits_p = (uint8_t) ((*bits_p) & ~mask);
284   }
285   else
286   {
287     *bits_p = (uint8_t) ((*bits_p) | mask);
288   }
289 } /* ecma_property_hashmap_insert */
290 
291 /**
292  * Delete named property from the hashmap.
293  *
294  * @return ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP if hashmap should be recreated
295  *         ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP otherwise
296  */
297 ecma_property_hashmap_delete_status
ecma_property_hashmap_delete(ecma_object_t * object_p,jmem_cpointer_t name_cp,ecma_property_t * property_p)298 ecma_property_hashmap_delete (ecma_object_t *object_p, /**< object */
299                               jmem_cpointer_t name_cp, /**< property name */
300                               ecma_property_t *property_p) /**< property */
301 {
302   ecma_property_hashmap_t *hashmap_p = ECMA_GET_NON_NULL_POINTER (ecma_property_hashmap_t,
303                                                                   object_p->u1.property_list_cp);
304 
305   JERRY_ASSERT (hashmap_p->header.types[0] == ECMA_PROPERTY_TYPE_HASHMAP);
306 
307   hashmap_p->unused_count++;
308 
309   /* The NULLs are above 3/4 of the hashmap. */
310   if (hashmap_p->unused_count > ((hashmap_p->max_property_count * 3) >> 2))
311   {
312     return ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP;
313   }
314 
315   uint32_t entry_index = ecma_string_get_property_name_hash (*property_p, name_cp);
316   uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)];
317   uint32_t mask = hashmap_p->max_property_count - 1;
318   jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1);
319   uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count);
320 
321   entry_index &= mask;
322 
323 #ifndef JERRY_NDEBUG
324   /* See the comment for this variable in ecma_property_hashmap_create. */
325   uint32_t start_entry_index = entry_index;
326 #endif /* !JERRY_NDEBUG */
327 
328   while (true)
329   {
330     if (pair_list_p[entry_index] != ECMA_NULL_POINTER)
331     {
332       size_t offset = 0;
333 
334       if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index))
335       {
336         offset = 1;
337       }
338 
339       ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t,
340                                                                          pair_list_p[entry_index]);
341 
342       if ((property_pair_p->header.types + offset) == property_p)
343       {
344         JERRY_ASSERT (property_pair_p->names_cp[offset] == name_cp);
345 
346         pair_list_p[entry_index] = ECMA_NULL_POINTER;
347         ECMA_PROPERTY_HASHMAP_SET_BIT (bits_p, entry_index);
348         return ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP;
349       }
350     }
351     else
352     {
353       /* Must be a deleted entry. */
354       JERRY_ASSERT (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index));
355     }
356 
357     entry_index = (entry_index + step) & mask;
358 
359 #ifndef JERRY_NDEBUG
360     JERRY_ASSERT (entry_index != start_entry_index);
361 #endif /* !JERRY_NDEBUG */
362   }
363 } /* ecma_property_hashmap_delete */
364 
365 /**
366  * Find a named property.
367  *
368  * @return pointer to the property if found or NULL otherwise
369  */
370 ecma_property_t *
ecma_property_hashmap_find(ecma_property_hashmap_t * hashmap_p,ecma_string_t * name_p,jmem_cpointer_t * property_real_name_cp)371 ecma_property_hashmap_find (ecma_property_hashmap_t *hashmap_p, /**< hashmap */
372                             ecma_string_t *name_p, /**< property name */
373                             jmem_cpointer_t *property_real_name_cp) /**< [out] property real name */
374 {
375 #ifndef JERRY_NDEBUG
376   /* A sanity check in debug mode: a named property must be present
377    * in both the property hashmap and in the property chain, or missing
378    * from both data collection. The following code checks the property
379    * chain, and sets the property_found variable. */
380   bool property_found = false;
381 
382   jmem_cpointer_t prop_iter_cp = hashmap_p->header.next_property_cp;
383 
384   while (prop_iter_cp != JMEM_CP_NULL && !property_found)
385   {
386     ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
387     JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
388 
389     ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p;
390 
391     for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
392     {
393       if (ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[i]))
394       {
395         if (ecma_string_compare_to_property_name (prop_iter_p->types[i],
396                                                   prop_pair_p->names_cp[i],
397                                                   name_p))
398         {
399           /* Property is found */
400           property_found = true;
401           break;
402         }
403       }
404     }
405 
406     prop_iter_cp = prop_iter_p->next_property_cp;
407   }
408 #endif /* !JERRY_NDEBUG */
409 
410   uint32_t entry_index = ecma_string_hash (name_p);
411   uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)];
412   uint32_t mask = hashmap_p->max_property_count - 1;
413   jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1);
414   uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count);
415   entry_index &= mask;
416 
417 #ifndef JERRY_NDEBUG
418   /* See the comment for this variable in ecma_property_hashmap_create. */
419   uint32_t start_entry_index = entry_index;
420 #endif /* !JERRY_NDEBUG */
421 
422   if (ECMA_IS_DIRECT_STRING (name_p))
423   {
424     ecma_property_t prop_name_type = (ecma_property_t) ECMA_GET_DIRECT_STRING_TYPE (name_p);
425     jmem_cpointer_t property_name_cp = (jmem_cpointer_t) ECMA_GET_DIRECT_STRING_VALUE (name_p);
426 
427     JERRY_ASSERT (prop_name_type > 0);
428 
429     while (true)
430     {
431       if (pair_list_p[entry_index] != ECMA_NULL_POINTER)
432       {
433         size_t offset = 0;
434         if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index))
435         {
436           offset = 1;
437         }
438 
439         ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t,
440                                                                            pair_list_p[entry_index]);
441 
442         ecma_property_t *property_p = property_pair_p->header.types + offset;
443 
444         JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*property_p));
445 
446         if (property_pair_p->names_cp[offset] == property_name_cp
447             && ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == prop_name_type)
448         {
449 #ifndef JERRY_NDEBUG
450           JERRY_ASSERT (property_found);
451 #endif /* !JERRY_NDEBUG */
452 
453           *property_real_name_cp = property_name_cp;
454           return property_p;
455         }
456       }
457       else
458       {
459         if (!ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index))
460         {
461 #ifndef JERRY_NDEBUG
462           JERRY_ASSERT (!property_found);
463 #endif /* !JERRY_NDEBUG */
464 
465           return NULL;
466         }
467         /* Otherwise it is a deleted entry. */
468       }
469 
470       entry_index = (entry_index + step) & mask;
471 
472 #ifndef JERRY_NDEBUG
473       JERRY_ASSERT (entry_index != start_entry_index);
474 #endif /* !JERRY_NDEBUG */
475     }
476   }
477 
478   while (true)
479   {
480     if (pair_list_p[entry_index] != ECMA_NULL_POINTER)
481     {
482       size_t offset = 0;
483       if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index))
484       {
485         offset = 1;
486       }
487 
488       ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t,
489                                                                          pair_list_p[entry_index]);
490 
491       ecma_property_t *property_p = property_pair_p->header.types + offset;
492 
493       JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*property_p));
494 
495       if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_PTR)
496       {
497         ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, property_pair_p->names_cp[offset]);
498 
499         if (ecma_compare_ecma_non_direct_strings (prop_name_p, name_p))
500         {
501 #ifndef JERRY_NDEBUG
502           JERRY_ASSERT (property_found);
503 #endif /* !JERRY_NDEBUG */
504 
505           *property_real_name_cp = property_pair_p->names_cp[offset];
506           return property_p;
507         }
508       }
509     }
510     else
511     {
512       if (!ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index))
513       {
514 #ifndef JERRY_NDEBUG
515         JERRY_ASSERT (!property_found);
516 #endif /* !JERRY_NDEBUG */
517 
518         return NULL;
519       }
520       /* Otherwise it is a deleted entry. */
521     }
522 
523     entry_index = (entry_index + step) & mask;
524 
525 #ifndef JERRY_NDEBUG
526     JERRY_ASSERT (entry_index != start_entry_index);
527 #endif /* !JERRY_NDEBUG */
528   }
529 } /* ecma_property_hashmap_find */
530 #endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */
531 
532 /**
533  * @}
534  * @}
535  */
536