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-alloc.h"
17 #include "ecma-literal-storage.h"
18 #include "ecma-helpers.h"
19 #include "jcontext.h"
20
21 /** \addtogroup ecma ECMA
22 * @{
23 *
24 * \addtogroup ecmalitstorage Literal storage
25 * @{
26 */
27
28 #if ENABLED (JERRY_ES2015)
29 /**
30 * Free symbol list
31 */
32 static void
ecma_free_symbol_list(jmem_cpointer_t symbol_list_cp)33 ecma_free_symbol_list (jmem_cpointer_t symbol_list_cp) /**< symbol list */
34 {
35 while (symbol_list_cp != JMEM_CP_NULL)
36 {
37 ecma_lit_storage_item_t *symbol_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, symbol_list_cp);
38
39 for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
40 {
41 if (symbol_list_p->values[i] != JMEM_CP_NULL)
42 {
43 ecma_string_t *string_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t,
44 symbol_list_p->values[i]);
45
46 JERRY_ASSERT (ECMA_STRING_IS_REF_EQUALS_TO_ONE (string_p));
47 ecma_deref_ecma_string (string_p);
48 }
49 }
50
51 jmem_cpointer_t next_item_cp = symbol_list_p->next_cp;
52 jmem_pools_free (symbol_list_p, sizeof (ecma_lit_storage_item_t));
53 symbol_list_cp = next_item_cp;
54 }
55 } /* ecma_free_symbol_list */
56 #endif /* ENABLED (JERRY_ES2015) */
57
58 /**
59 * Free string list
60 */
61 static void
ecma_free_string_list(jmem_cpointer_t string_list_cp)62 ecma_free_string_list (jmem_cpointer_t string_list_cp) /**< string list */
63 {
64 while (string_list_cp != JMEM_CP_NULL)
65 {
66 ecma_lit_storage_item_t *string_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, string_list_cp);
67
68 for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
69 {
70 if (string_list_p->values[i] != JMEM_CP_NULL)
71 {
72 ecma_string_t *string_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t,
73 string_list_p->values[i]);
74
75 JERRY_ASSERT (ECMA_STRING_IS_REF_EQUALS_TO_ONE (string_p));
76 ecma_destroy_ecma_string (string_p);
77 }
78 }
79
80 jmem_cpointer_t next_item_cp = string_list_p->next_cp;
81 jmem_pools_free (string_list_p, sizeof (ecma_lit_storage_item_t));
82 string_list_cp = next_item_cp;
83 }
84 } /* ecma_free_string_list */
85
86 /**
87 * Free number list
88 */
89 static void
ecma_free_number_list(jmem_cpointer_t number_list_cp)90 ecma_free_number_list (jmem_cpointer_t number_list_cp) /**< string list */
91 {
92 while (number_list_cp != JMEM_CP_NULL)
93 {
94 ecma_number_storage_item_t *number_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_number_storage_item_t,
95 number_list_cp);
96
97 for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
98 {
99 if (number_list_p->values[i] != JMEM_CP_NULL)
100 {
101 ecma_number_t *num_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_number_t, number_list_p->values[i]);
102 ecma_dealloc_number (num_p);
103 }
104 }
105
106 jmem_cpointer_t next_item_cp = number_list_p->next_cp;
107 jmem_pools_free (number_list_p, sizeof (ecma_number_storage_item_t));
108 number_list_cp = next_item_cp;
109 }
110 } /* ecma_free_number_list */
111
112 /**
113 * Finalize literal storage
114 */
115 void
ecma_finalize_lit_storage(void)116 ecma_finalize_lit_storage (void)
117 {
118 #if ENABLED (JERRY_ES2015)
119 ecma_free_symbol_list (JERRY_CONTEXT (symbol_list_first_cp));
120 #endif /* ENABLED (JERRY_ES2015) */
121 ecma_free_string_list (JERRY_CONTEXT (string_list_first_cp));
122 ecma_free_number_list (JERRY_CONTEXT (number_list_first_cp));
123 } /* ecma_finalize_lit_storage */
124
125 /**
126 * Find or create a literal string.
127 *
128 * @return ecma_string_t compressed pointer
129 */
130 ecma_value_t
ecma_find_or_create_literal_string(const lit_utf8_byte_t * chars_p,lit_utf8_size_t size)131 ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string to be searched */
132 lit_utf8_size_t size) /**< size of the string */
133 {
134 ecma_string_t *string_p = ecma_new_ecma_string_from_utf8 (chars_p, size);
135
136 if (ECMA_IS_DIRECT_STRING (string_p))
137 {
138 return ecma_make_string_value (string_p);
139 }
140
141 jmem_cpointer_t string_list_cp = JERRY_CONTEXT (string_list_first_cp);
142 jmem_cpointer_t *empty_cpointer_p = NULL;
143
144 while (string_list_cp != JMEM_CP_NULL)
145 {
146 ecma_lit_storage_item_t *string_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, string_list_cp);
147
148 for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
149 {
150 if (string_list_p->values[i] == JMEM_CP_NULL)
151 {
152 if (empty_cpointer_p == NULL)
153 {
154 empty_cpointer_p = string_list_p->values + i;
155 }
156 }
157 else
158 {
159 ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t,
160 string_list_p->values[i]);
161
162 if (ecma_compare_ecma_strings (string_p, value_p))
163 {
164 /* Return with string if found in the list. */
165 ecma_deref_ecma_string (string_p);
166 return ecma_make_string_value (value_p);
167 }
168 }
169 }
170
171 string_list_cp = string_list_p->next_cp;
172 }
173
174 ECMA_SET_STRING_AS_STATIC (string_p);
175 jmem_cpointer_t result;
176 JMEM_CP_SET_NON_NULL_POINTER (result, string_p);
177
178 if (empty_cpointer_p != NULL)
179 {
180 *empty_cpointer_p = result;
181 return ecma_make_string_value (string_p);
182 }
183
184 ecma_lit_storage_item_t *new_item_p;
185 new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t));
186
187 new_item_p->values[0] = result;
188 for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
189 {
190 new_item_p->values[i] = JMEM_CP_NULL;
191 }
192
193 new_item_p->next_cp = JERRY_CONTEXT (string_list_first_cp);
194 JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (string_list_first_cp), new_item_p);
195
196 return ecma_make_string_value (string_p);
197 } /* ecma_find_or_create_literal_string */
198
199 /**
200 * Find or create a literal number.
201 *
202 * @return ecma_string_t compressed pointer
203 */
204 ecma_value_t
ecma_find_or_create_literal_number(ecma_number_t number_arg)205 ecma_find_or_create_literal_number (ecma_number_t number_arg) /**< number to be searched */
206 {
207 ecma_value_t num = ecma_make_number_value (number_arg);
208
209 if (ecma_is_value_integer_number (num))
210 {
211 return num;
212 }
213
214 JERRY_ASSERT (ecma_is_value_float_number (num));
215
216 jmem_cpointer_t number_list_cp = JERRY_CONTEXT (number_list_first_cp);
217 jmem_cpointer_t *empty_cpointer_p = NULL;
218
219 while (number_list_cp != JMEM_CP_NULL)
220 {
221 ecma_number_storage_item_t *number_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_number_storage_item_t,
222 number_list_cp);
223
224 for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
225 {
226 if (number_list_p->values[i] == JMEM_CP_NULL)
227 {
228 if (empty_cpointer_p == NULL)
229 {
230 empty_cpointer_p = number_list_p->values + i;
231 }
232 }
233 else
234 {
235 ecma_number_t *number_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_number_t,
236 number_list_p->values[i]);
237
238 if (*number_p == number_arg)
239 {
240 ecma_free_value (num);
241 return ecma_make_float_value (number_p);
242 }
243 }
244 }
245
246 number_list_cp = number_list_p->next_cp;
247 }
248
249 ecma_number_t *num_p = ecma_get_pointer_from_float_value (num);
250
251 jmem_cpointer_t result;
252 JMEM_CP_SET_NON_NULL_POINTER (result, num_p);
253
254 if (empty_cpointer_p != NULL)
255 {
256 *empty_cpointer_p = result;
257 return num;
258 }
259
260 ecma_number_storage_item_t *new_item_p;
261 new_item_p = (ecma_number_storage_item_t *) jmem_pools_alloc (sizeof (ecma_number_storage_item_t));
262
263 new_item_p->values[0] = result;
264 for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
265 {
266 new_item_p->values[i] = JMEM_CP_NULL;
267 }
268
269 new_item_p->next_cp = JERRY_CONTEXT (number_list_first_cp);
270 JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (number_list_first_cp), new_item_p);
271
272 return num;
273 } /* ecma_find_or_create_literal_number */
274
275 /**
276 * Log2 of snapshot literal alignment.
277 */
278 #define JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG 1
279
280 /**
281 * Snapshot literal alignment.
282 */
283 #define JERRY_SNAPSHOT_LITERAL_ALIGNMENT (1u << JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG)
284
285 /**
286 * Literal offset shift.
287 */
288 #define JERRY_SNAPSHOT_LITERAL_SHIFT (ECMA_VALUE_SHIFT + 1)
289
290 /**
291 * Literal value is number.
292 */
293 #define JERRY_SNAPSHOT_LITERAL_IS_NUMBER (1u << ECMA_VALUE_SHIFT)
294
295 #if ENABLED (JERRY_SNAPSHOT_SAVE)
296
297 /**
298 * Append the value at the end of the appropriate list if it is not present there.
299 */
ecma_save_literals_append_value(ecma_value_t value,ecma_collection_t * lit_pool_p)300 void ecma_save_literals_append_value (ecma_value_t value, /**< value to be appended */
301 ecma_collection_t *lit_pool_p) /**< list of known values */
302 {
303 /* Unlike direct numbers, direct strings are converted to character literals. */
304 if (!ecma_is_value_string (value) && !ecma_is_value_float_number (value))
305 {
306 return;
307 }
308
309 ecma_value_t *buffer_p = lit_pool_p->buffer_p;
310
311 for (uint32_t i = 0; i < lit_pool_p->item_count; i++)
312 {
313 /* Strings / numbers are direct strings or stored in the literal storage.
314 * Therefore direct comparison is enough to find the same strings / numbers. */
315 if (buffer_p[i] == value)
316 {
317 return;
318 }
319 }
320
321 ecma_collection_push_back (lit_pool_p, value);
322 } /* ecma_save_literals_append_value */
323
324 /**
325 * Add names from a byte-code data to a list.
326 */
327 void
ecma_save_literals_add_compiled_code(const ecma_compiled_code_t * compiled_code_p,ecma_collection_t * lit_pool_p)328 ecma_save_literals_add_compiled_code (const ecma_compiled_code_t *compiled_code_p, /**< byte-code data */
329 ecma_collection_t *lit_pool_p) /**< list of known values */
330 {
331 ecma_value_t *literal_p;
332 uint32_t argument_end = 0;
333 uint32_t register_end;
334 uint32_t const_literal_end;
335 uint32_t literal_end;
336
337 JERRY_ASSERT (compiled_code_p->status_flags & CBC_CODE_FLAGS_FUNCTION);
338
339 if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
340 {
341 cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p;
342 uint8_t *byte_p = (uint8_t *) compiled_code_p;
343
344 literal_p = (ecma_value_t *) (byte_p + sizeof (cbc_uint16_arguments_t));
345 register_end = args_p->register_end;
346 const_literal_end = args_p->const_literal_end - register_end;
347 literal_end = args_p->literal_end - register_end;
348
349 if (compiled_code_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED)
350 {
351 argument_end = args_p->argument_end;
352 }
353 }
354 else
355 {
356 cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p;
357 uint8_t *byte_p = (uint8_t *) compiled_code_p;
358
359 literal_p = (ecma_value_t *) (byte_p + sizeof (cbc_uint8_arguments_t));
360 register_end = args_p->register_end;
361 const_literal_end = args_p->const_literal_end - register_end;
362 literal_end = args_p->literal_end - register_end;
363
364 if (compiled_code_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED)
365 {
366 argument_end = args_p->argument_end;
367 }
368 }
369
370 for (uint32_t i = 0; i < argument_end; i++)
371 {
372 ecma_save_literals_append_value (literal_p[i], lit_pool_p);
373 }
374
375 for (uint32_t i = 0; i < const_literal_end; i++)
376 {
377 ecma_save_literals_append_value (literal_p[i], lit_pool_p);
378 }
379
380 for (uint32_t i = const_literal_end; i < literal_end; i++)
381 {
382 ecma_compiled_code_t *bytecode_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t,
383 literal_p[i]);
384
385 if ((bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION)
386 && bytecode_p != compiled_code_p)
387 {
388 ecma_save_literals_add_compiled_code (bytecode_p, lit_pool_p);
389 }
390 }
391
392 if (argument_end != 0)
393 {
394 uint8_t *byte_p = (uint8_t *) compiled_code_p;
395 byte_p += ((size_t) compiled_code_p->size) << JMEM_ALIGNMENT_LOG;
396 literal_p = ((ecma_value_t *) byte_p) - argument_end;
397
398 for (uint32_t i = 0; i < argument_end; i++)
399 {
400 ecma_save_literals_append_value (literal_p[i], lit_pool_p);
401 }
402 }
403 } /* ecma_save_literals_add_compiled_code */
404
405 /**
406 * Save literals to specified snapshot buffer.
407 *
408 * Note:
409 * Frees 'lit_pool_p' regardless of success.
410 *
411 * @return true - if save was performed successfully (i.e. buffer size is sufficient),
412 * false - otherwise
413 */
414 bool
ecma_save_literals_for_snapshot(ecma_collection_t * lit_pool_p,uint32_t * buffer_p,size_t buffer_size,size_t * in_out_buffer_offset_p,lit_mem_to_snapshot_id_map_entry_t ** out_map_p,uint32_t * out_map_len_p)415 ecma_save_literals_for_snapshot (ecma_collection_t *lit_pool_p, /**< list of known values */
416 uint32_t *buffer_p, /**< [out] output snapshot buffer */
417 size_t buffer_size, /**< size of the buffer */
418 size_t *in_out_buffer_offset_p, /**< [in,out] write position in the buffer */
419 lit_mem_to_snapshot_id_map_entry_t **out_map_p, /**< [out] map from literal identifiers
420 * to the literal offsets
421 * in snapshot */
422 uint32_t *out_map_len_p) /**< [out] number of literals */
423 {
424 if (lit_pool_p->item_count == 0)
425 {
426 *out_map_p = NULL;
427 *out_map_len_p = 0;
428 }
429
430 uint32_t lit_table_size = 0;
431 size_t max_lit_table_size = buffer_size - *in_out_buffer_offset_p;
432
433 if (max_lit_table_size > (UINT32_MAX >> JERRY_SNAPSHOT_LITERAL_SHIFT))
434 {
435 max_lit_table_size = (UINT32_MAX >> JERRY_SNAPSHOT_LITERAL_SHIFT);
436 }
437
438 ecma_value_t *lit_buffer_p = lit_pool_p->buffer_p;
439
440 /* Compute the size of the literal pool. */
441 for (uint32_t i = 0; i < lit_pool_p->item_count; i++)
442 {
443 if (ecma_is_value_float_number (lit_buffer_p[i]))
444 {
445 lit_table_size += (uint32_t) sizeof (ecma_number_t);
446 }
447 else
448 {
449 ecma_string_t *string_p = ecma_get_string_from_value (lit_buffer_p[i]);
450
451 lit_table_size += (uint32_t) JERRY_ALIGNUP (sizeof (uint16_t) + ecma_string_get_size (string_p),
452 JERRY_SNAPSHOT_LITERAL_ALIGNMENT);
453 }
454
455 /* Check whether enough space is available and the maximum size is not reached. */
456 if (lit_table_size > max_lit_table_size)
457 {
458 ecma_collection_destroy (lit_pool_p);
459 return false;
460 }
461 }
462
463 lit_mem_to_snapshot_id_map_entry_t *map_p;
464 ecma_length_t total_count = lit_pool_p->item_count;
465
466 map_p = jmem_heap_alloc_block (total_count * sizeof (lit_mem_to_snapshot_id_map_entry_t));
467
468 /* Set return values (no error is possible from here). */
469 JERRY_ASSERT ((*in_out_buffer_offset_p % sizeof (uint32_t)) == 0);
470
471 uint8_t *destination_p = (uint8_t *) (buffer_p + (*in_out_buffer_offset_p / sizeof (uint32_t)));
472 uint32_t literal_offset = 0;
473
474 *in_out_buffer_offset_p += lit_table_size;
475 *out_map_p = map_p;
476 *out_map_len_p = total_count;
477
478 lit_buffer_p = lit_pool_p->buffer_p;
479
480 /* Generate literal pool data. */
481 for (uint32_t i = 0; i < lit_pool_p->item_count; i++)
482 {
483 map_p->literal_id = lit_buffer_p[i];
484 map_p->literal_offset = (literal_offset << JERRY_SNAPSHOT_LITERAL_SHIFT) | ECMA_TYPE_SNAPSHOT_OFFSET;
485
486 ecma_length_t length;
487
488 if (ecma_is_value_float_number (lit_buffer_p[i]))
489 {
490 map_p->literal_offset |= JERRY_SNAPSHOT_LITERAL_IS_NUMBER;
491
492 ecma_number_t num = ecma_get_float_from_value (lit_buffer_p[i]);
493 memcpy (destination_p, &num, sizeof (ecma_number_t));
494
495 length = JERRY_ALIGNUP (sizeof (ecma_number_t), JERRY_SNAPSHOT_LITERAL_ALIGNMENT);
496 }
497 else
498 {
499 ecma_string_t *string_p = ecma_get_string_from_value (lit_buffer_p[i]);
500 length = ecma_string_get_size (string_p);
501
502 *(uint16_t *) destination_p = (uint16_t) length;
503
504 ecma_string_to_utf8_bytes (string_p, destination_p + sizeof (uint16_t), length);
505
506 length = JERRY_ALIGNUP (sizeof (uint16_t) + length, JERRY_SNAPSHOT_LITERAL_ALIGNMENT);
507 }
508
509 JERRY_ASSERT ((length % sizeof (uint16_t)) == 0);
510 destination_p += length;
511 literal_offset += length;
512
513 map_p++;
514 }
515
516 ecma_collection_destroy (lit_pool_p);
517 return true;
518 } /* ecma_save_literals_for_snapshot */
519
520 #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */
521
522 #if ENABLED (JERRY_SNAPSHOT_EXEC) || ENABLED (JERRY_SNAPSHOT_SAVE)
523
524 /**
525 * Get the compressed pointer of a given literal.
526 *
527 * @return literal compressed pointer
528 */
529 ecma_value_t
ecma_snapshot_get_literal(const uint8_t * literal_base_p,ecma_value_t literal_value)530 ecma_snapshot_get_literal (const uint8_t *literal_base_p, /**< literal start */
531 ecma_value_t literal_value) /**< string / number offset */
532 {
533 JERRY_ASSERT ((literal_value & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_SNAPSHOT_OFFSET);
534
535 const uint8_t *literal_p = literal_base_p + (literal_value >> JERRY_SNAPSHOT_LITERAL_SHIFT);
536
537 if (literal_value & JERRY_SNAPSHOT_LITERAL_IS_NUMBER)
538 {
539 ecma_number_t num;
540 memcpy (&num, literal_p, sizeof (ecma_number_t));
541 return ecma_find_or_create_literal_number (num);
542 }
543
544 uint16_t length = *(const uint16_t *) literal_p;
545
546 return ecma_find_or_create_literal_string (literal_p + sizeof (uint16_t), length);
547 } /* ecma_snapshot_get_literal */
548
549 #endif /* ENABLED (JERRY_SNAPSHOT_EXEC) || ENABLED (JERRY_SNAPSHOT_SAVE) */
550
551 /**
552 * @}
553 * @}
554 */
555