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-conversion.h" |
17 | #include "ecma-exceptions.h" |
18 | #include "ecma-function-object.h" |
19 | #include "ecma-helpers.h" |
20 | #include "ecma-lex-env.h" |
21 | #include "ecma-literal-storage.h" |
22 | #include "jcontext.h" |
23 | #include "jerryscript.h" |
24 | #include "jerry-snapshot.h" |
25 | #include "js-parser.h" |
26 | #include "lit-char-helpers.h" |
27 | #include "js-parser-internal.h" |
28 | #include "re-compiler.h" |
29 | |
30 | #if ENABLED (JERRY_SNAPSHOT_SAVE) || ENABLED (JERRY_SNAPSHOT_EXEC) |
31 | |
32 | /** |
33 | * Get snapshot configuration flags. |
34 | * |
35 | * @return configuration flags |
36 | */ |
37 | static inline uint32_t JERRY_ATTR_ALWAYS_INLINE |
38 | snapshot_get_global_flags (bool has_regex, /**< regex literal is present */ |
39 | bool has_class) /**< class literal is present */ |
40 | { |
41 | JERRY_UNUSED (has_regex); |
42 | JERRY_UNUSED (has_class); |
43 | |
44 | uint32_t flags = 0; |
45 | |
46 | #if ENABLED (JERRY_BUILTIN_REGEXP) |
47 | flags |= (has_regex ? JERRY_SNAPSHOT_HAS_REGEX_LITERAL : 0); |
48 | #endif /* ENABLED (JERRY_BUILTIN_REGEXP) */ |
49 | #if ENABLED (JERRY_ESNEXT) |
50 | flags |= (has_class ? JERRY_SNAPSHOT_HAS_CLASS_LITERAL : 0); |
51 | #endif /* ENABLED (JERRY_ESNEXT) */ |
52 | #if ENABLED (JERRY_BUILTIN_REALMS) |
53 | flags |= JERRY_SNAPSHOT_HAS_REALM_VALUE; |
54 | #endif /* ENABLED (JERRY_BUILTIN_REALMS) */ |
55 | |
56 | return flags; |
57 | } /* snapshot_get_global_flags */ |
58 | |
59 | /** |
60 | * Checks whether the global_flags argument matches to the current feature set. |
61 | * |
62 | * @return true if global_flags accepted, false otherwise |
63 | */ |
64 | static inline bool JERRY_ATTR_ALWAYS_INLINE |
65 | snapshot_check_global_flags (uint32_t global_flags) /**< global flags */ |
66 | { |
67 | #if ENABLED (JERRY_BUILTIN_REGEXP) |
68 | global_flags &= (uint32_t) ~JERRY_SNAPSHOT_HAS_REGEX_LITERAL; |
69 | #endif /* ENABLED (JERRY_BUILTIN_REGEXP) */ |
70 | #if ENABLED (JERRY_ESNEXT) |
71 | global_flags &= (uint32_t) ~JERRY_SNAPSHOT_HAS_CLASS_LITERAL; |
72 | #endif /* ENABLED (JERRY_ESNEXT) */ |
73 | #if ENABLED (JERRY_BUILTIN_REALMS) |
74 | global_flags |= JERRY_SNAPSHOT_HAS_REALM_VALUE; |
75 | #endif /* ENABLED (JERRY_BUILTIN_REALMS) */ |
76 | |
77 | return global_flags == snapshot_get_global_flags (false, false); |
78 | } /* snapshot_check_global_flags */ |
79 | |
80 | #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) || ENABLED (JERRY_SNAPSHOT_EXEC) */ |
81 | |
82 | #if ENABLED (JERRY_SNAPSHOT_SAVE) |
83 | |
84 | /** |
85 | * Variables required to take a snapshot. |
86 | */ |
87 | typedef struct |
88 | { |
89 | size_t snapshot_buffer_write_offset; |
90 | ecma_value_t snapshot_error; |
91 | bool regex_found; |
92 | bool class_found; |
93 | } snapshot_globals_t; |
94 | |
95 | /** \addtogroup jerrysnapshot Jerry snapshot operations |
96 | * @{ |
97 | */ |
98 | |
99 | /** |
100 | * Write data into the specified buffer. |
101 | * |
102 | * Note: |
103 | * Offset is in-out and is incremented if the write operation completes successfully. |
104 | * |
105 | * @return true - if write was successful, i.e. offset + data_size doesn't exceed buffer size, |
106 | * false - otherwise |
107 | */ |
108 | static inline bool JERRY_ATTR_ALWAYS_INLINE |
109 | snapshot_write_to_buffer_by_offset (uint8_t *buffer_p, /**< buffer */ |
110 | size_t buffer_size, /**< size of buffer */ |
111 | size_t *in_out_buffer_offset_p, /**< [in,out] offset to write to |
112 | * incremented with data_size */ |
113 | const void *data_p, /**< data */ |
114 | size_t data_size) /**< size of the writable data */ |
115 | { |
116 | if (*in_out_buffer_offset_p + data_size > buffer_size) |
117 | { |
118 | return false; |
119 | } |
120 | |
121 | memcpy (buffer_p + *in_out_buffer_offset_p, data_p, data_size); |
122 | *in_out_buffer_offset_p += data_size; |
123 | |
124 | return true; |
125 | } /* snapshot_write_to_buffer_by_offset */ |
126 | |
127 | /** |
128 | * Maximum snapshot write buffer offset. |
129 | */ |
130 | #if !ENABLED (JERRY_NUMBER_TYPE_FLOAT64) |
131 | #define JERRY_SNAPSHOT_MAXIMUM_WRITE_OFFSET (0x7fffff >> 1) |
132 | #else /* ENABLED (JERRY_NUMBER_TYPE_FLOAT64) */ |
133 | #define JERRY_SNAPSHOT_MAXIMUM_WRITE_OFFSET (UINT32_MAX >> 1) |
134 | #endif /* !ENABLED (JERRY_NUMBER_TYPE_FLOAT64) */ |
135 | |
136 | /** |
137 | * Save snapshot helper. |
138 | * |
139 | * @return start offset |
140 | */ |
141 | static uint32_t |
142 | snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ |
143 | uint8_t *snapshot_buffer_p, /**< snapshot buffer */ |
144 | size_t snapshot_buffer_size, /**< snapshot buffer size */ |
145 | snapshot_globals_t *globals_p) /**< snapshot globals */ |
146 | { |
147 | const jerry_char_t *error_buffer_too_small_p = (const jerry_char_t *) "Snapshot buffer too small." ; |
148 | |
149 | if (!ecma_is_value_empty (globals_p->snapshot_error)) |
150 | { |
151 | return 0; |
152 | } |
153 | |
154 | JERRY_ASSERT ((globals_p->snapshot_buffer_write_offset & (JMEM_ALIGNMENT - 1)) == 0); |
155 | |
156 | if (globals_p->snapshot_buffer_write_offset > JERRY_SNAPSHOT_MAXIMUM_WRITE_OFFSET) |
157 | { |
158 | const char * const error_message_p = "Maximum snapshot size reached." ; |
159 | globals_p->snapshot_error = jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) error_message_p); |
160 | return 0; |
161 | } |
162 | |
163 | /* The snapshot generator always parses a single file, |
164 | * so the base always starts right after the snapshot header. */ |
165 | uint32_t start_offset = (uint32_t) (globals_p->snapshot_buffer_write_offset - sizeof (jerry_snapshot_header_t)); |
166 | |
167 | uint8_t *copied_code_start_p = snapshot_buffer_p + globals_p->snapshot_buffer_write_offset; |
168 | ecma_compiled_code_t *copied_code_p = (ecma_compiled_code_t *) copied_code_start_p; |
169 | |
170 | #if ENABLED (JERRY_ESNEXT) |
171 | if (compiled_code_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS) |
172 | { |
173 | const char * const error_message_p = "Unsupported feature: tagged template literals." ; |
174 | globals_p->snapshot_error = jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) error_message_p); |
175 | return 0; |
176 | } |
177 | |
178 | if (CBC_FUNCTION_GET_TYPE (compiled_code_p->status_flags) == CBC_FUNCTION_CONSTRUCTOR) |
179 | { |
180 | globals_p->class_found = true; |
181 | } |
182 | #endif /* ENABLED (JERRY_ESNEXT) */ |
183 | |
184 | #if ENABLED (JERRY_BUILTIN_REGEXP) |
185 | if (!CBC_IS_FUNCTION (compiled_code_p->status_flags)) |
186 | { |
187 | /* Regular expression. */ |
188 | if (globals_p->snapshot_buffer_write_offset + sizeof (ecma_compiled_code_t) > snapshot_buffer_size) |
189 | { |
190 | globals_p->snapshot_error = jerry_create_error (JERRY_ERROR_RANGE, error_buffer_too_small_p); |
191 | return 0; |
192 | } |
193 | |
194 | globals_p->snapshot_buffer_write_offset += sizeof (ecma_compiled_code_t); |
195 | |
196 | ecma_value_t pattern = ((re_compiled_code_t *) compiled_code_p)->source; |
197 | ecma_string_t *pattern_string_p = ecma_get_string_from_value (pattern); |
198 | |
199 | lit_utf8_size_t pattern_size = 0; |
200 | |
201 | ECMA_STRING_TO_UTF8_STRING (pattern_string_p, buffer_p, buffer_size); |
202 | |
203 | pattern_size = buffer_size; |
204 | |
205 | if (!snapshot_write_to_buffer_by_offset (snapshot_buffer_p, |
206 | snapshot_buffer_size, |
207 | &globals_p->snapshot_buffer_write_offset, |
208 | buffer_p, |
209 | buffer_size)) |
210 | { |
211 | globals_p->snapshot_error = jerry_create_error (JERRY_ERROR_RANGE, error_buffer_too_small_p); |
212 | /* cannot return inside ECMA_FINALIZE_UTF8_STRING */ |
213 | } |
214 | |
215 | ECMA_FINALIZE_UTF8_STRING (buffer_p, buffer_size); |
216 | |
217 | if (!ecma_is_value_empty (globals_p->snapshot_error)) |
218 | { |
219 | return 0; |
220 | } |
221 | |
222 | globals_p->regex_found = true; |
223 | globals_p->snapshot_buffer_write_offset = JERRY_ALIGNUP (globals_p->snapshot_buffer_write_offset, |
224 | JMEM_ALIGNMENT); |
225 | |
226 | /* Regexp character size is stored in refs. */ |
227 | copied_code_p->refs = (uint16_t) pattern_size; |
228 | |
229 | pattern_size += (lit_utf8_size_t) sizeof (ecma_compiled_code_t); |
230 | copied_code_p->size = (uint16_t) ((pattern_size + JMEM_ALIGNMENT - 1) >> JMEM_ALIGNMENT_LOG); |
231 | |
232 | copied_code_p->status_flags = compiled_code_p->status_flags; |
233 | |
234 | return start_offset; |
235 | } |
236 | #endif /* ENABLED (JERRY_BUILTIN_REGEXP) */ |
237 | |
238 | JERRY_ASSERT (CBC_IS_FUNCTION (compiled_code_p->status_flags)); |
239 | |
240 | if (!snapshot_write_to_buffer_by_offset (snapshot_buffer_p, |
241 | snapshot_buffer_size, |
242 | &globals_p->snapshot_buffer_write_offset, |
243 | compiled_code_p, |
244 | ((size_t) compiled_code_p->size) << JMEM_ALIGNMENT_LOG)) |
245 | { |
246 | globals_p->snapshot_error = jerry_create_error (JERRY_ERROR_RANGE, error_buffer_too_small_p); |
247 | return 0; |
248 | } |
249 | |
250 | /* Sub-functions and regular expressions are stored recursively. */ |
251 | uint8_t *buffer_p = (uint8_t *) copied_code_p; |
252 | ecma_value_t *literal_start_p; |
253 | uint32_t const_literal_end; |
254 | uint32_t literal_end; |
255 | |
256 | if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) |
257 | { |
258 | literal_start_p = (ecma_value_t *) (buffer_p + sizeof (cbc_uint16_arguments_t)); |
259 | |
260 | cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p; |
261 | literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); |
262 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
263 | } |
264 | else |
265 | { |
266 | literal_start_p = (ecma_value_t *) (buffer_p + sizeof (cbc_uint8_arguments_t)); |
267 | |
268 | cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p; |
269 | literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); |
270 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
271 | } |
272 | |
273 | for (uint32_t i = const_literal_end; i < literal_end; i++) |
274 | { |
275 | ecma_compiled_code_t *bytecode_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, |
276 | literal_start_p[i]); |
277 | |
278 | if (bytecode_p == compiled_code_p) |
279 | { |
280 | literal_start_p[i] = 0; |
281 | } |
282 | else |
283 | { |
284 | uint32_t offset = snapshot_add_compiled_code (bytecode_p, |
285 | snapshot_buffer_p, |
286 | snapshot_buffer_size, |
287 | globals_p); |
288 | |
289 | JERRY_ASSERT (!ecma_is_value_empty (globals_p->snapshot_error) || offset > start_offset); |
290 | |
291 | literal_start_p[i] = offset - start_offset; |
292 | } |
293 | } |
294 | |
295 | return start_offset; |
296 | } /* snapshot_add_compiled_code */ |
297 | |
298 | /** |
299 | * Create unsupported literal error. |
300 | */ |
301 | static void |
302 | static_snapshot_error_unsupported_literal (snapshot_globals_t *globals_p, /**< snapshot globals */ |
303 | ecma_value_t literal) /**< literal form the literal pool */ |
304 | { |
305 | lit_utf8_byte_t *str_p = (lit_utf8_byte_t *) "Unsupported static snapshot literal: " ; |
306 | ecma_stringbuilder_t builder = ecma_stringbuilder_create_raw (str_p, 37); |
307 | |
308 | JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (literal)); |
309 | |
310 | ecma_string_t *literal_string_p = ecma_op_to_string (literal); |
311 | JERRY_ASSERT (literal_string_p != NULL); |
312 | |
313 | ecma_stringbuilder_append (&builder, literal_string_p); |
314 | |
315 | ecma_deref_ecma_string (literal_string_p); |
316 | |
317 | ecma_object_t *error_object_p = ecma_new_standard_error (ECMA_ERROR_RANGE, |
318 | ecma_stringbuilder_finalize (&builder)); |
319 | |
320 | globals_p->snapshot_error = ecma_create_error_object_reference (error_object_p); |
321 | } /* static_snapshot_error_unsupported_literal */ |
322 | |
323 | /** |
324 | * Save static snapshot helper. |
325 | * |
326 | * @return start offset |
327 | */ |
328 | static uint32_t |
329 | static_snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ |
330 | uint8_t *snapshot_buffer_p, /**< snapshot buffer */ |
331 | size_t snapshot_buffer_size, /**< snapshot buffer size */ |
332 | snapshot_globals_t *globals_p) /**< snapshot globals */ |
333 | { |
334 | if (!ecma_is_value_empty (globals_p->snapshot_error)) |
335 | { |
336 | return 0; |
337 | } |
338 | |
339 | JERRY_ASSERT ((globals_p->snapshot_buffer_write_offset & (JMEM_ALIGNMENT - 1)) == 0); |
340 | |
341 | if (globals_p->snapshot_buffer_write_offset >= JERRY_SNAPSHOT_MAXIMUM_WRITE_OFFSET) |
342 | { |
343 | const char * const error_message_p = "Maximum snapshot size reached." ; |
344 | globals_p->snapshot_error = jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) error_message_p); |
345 | return 0; |
346 | } |
347 | |
348 | /* The snapshot generator always parses a single file, |
349 | * so the base always starts right after the snapshot header. */ |
350 | uint32_t start_offset = (uint32_t) (globals_p->snapshot_buffer_write_offset - sizeof (jerry_snapshot_header_t)); |
351 | |
352 | uint8_t *copied_code_start_p = snapshot_buffer_p + globals_p->snapshot_buffer_write_offset; |
353 | ecma_compiled_code_t *copied_code_p = (ecma_compiled_code_t *) copied_code_start_p; |
354 | |
355 | if (!CBC_IS_FUNCTION (compiled_code_p->status_flags)) |
356 | { |
357 | /* Regular expression literals are not supported. */ |
358 | const char * const error_message_p = "Regular expression literals are not supported." ; |
359 | globals_p->snapshot_error = jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) error_message_p); |
360 | return 0; |
361 | } |
362 | |
363 | if (!snapshot_write_to_buffer_by_offset (snapshot_buffer_p, |
364 | snapshot_buffer_size, |
365 | &globals_p->snapshot_buffer_write_offset, |
366 | compiled_code_p, |
367 | ((size_t) compiled_code_p->size) << JMEM_ALIGNMENT_LOG)) |
368 | { |
369 | const char * const error_message_p = "Snapshot buffer too small." ; |
370 | globals_p->snapshot_error = jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) error_message_p); |
371 | return 0; |
372 | } |
373 | |
374 | /* Sub-functions and regular expressions are stored recursively. */ |
375 | uint8_t *buffer_p = (uint8_t *) copied_code_p; |
376 | ecma_value_t *literal_start_p; |
377 | uint32_t const_literal_end; |
378 | uint32_t literal_end; |
379 | |
380 | ((ecma_compiled_code_t *) copied_code_p)->status_flags |= CBC_CODE_FLAGS_STATIC_FUNCTION; |
381 | |
382 | if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) |
383 | { |
384 | literal_start_p = (ecma_value_t *) (buffer_p + sizeof (cbc_uint16_arguments_t)); |
385 | |
386 | cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p; |
387 | literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); |
388 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
389 | |
390 | #if ENABLED (JERRY_BUILTIN_REALMS) |
391 | args_p->realm_value = JMEM_CP_NULL; |
392 | #endif /* ENABLED (JERRY_BUILTIN_REALMS) */ |
393 | } |
394 | else |
395 | { |
396 | literal_start_p = (ecma_value_t *) (buffer_p + sizeof (cbc_uint8_arguments_t)); |
397 | |
398 | cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p; |
399 | literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); |
400 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
401 | |
402 | #if ENABLED (JERRY_BUILTIN_REALMS) |
403 | args_p->realm_value = JMEM_CP_NULL; |
404 | #endif /* ENABLED (JERRY_BUILTIN_REALMS) */ |
405 | } |
406 | |
407 | for (uint32_t i = 0; i < const_literal_end; i++) |
408 | { |
409 | if (!ecma_is_value_direct (literal_start_p[i]) |
410 | && !ecma_is_value_direct_string (literal_start_p[i])) |
411 | { |
412 | static_snapshot_error_unsupported_literal (globals_p, literal_start_p[i]); |
413 | return 0; |
414 | } |
415 | } |
416 | |
417 | for (uint32_t i = const_literal_end; i < literal_end; i++) |
418 | { |
419 | ecma_compiled_code_t *bytecode_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, |
420 | literal_start_p[i]); |
421 | |
422 | if (bytecode_p == compiled_code_p) |
423 | { |
424 | literal_start_p[i] = 0; |
425 | } |
426 | else |
427 | { |
428 | uint32_t offset = static_snapshot_add_compiled_code (bytecode_p, |
429 | snapshot_buffer_p, |
430 | snapshot_buffer_size, |
431 | globals_p); |
432 | |
433 | JERRY_ASSERT (!ecma_is_value_empty (globals_p->snapshot_error) || offset > start_offset); |
434 | |
435 | literal_start_p[i] = offset - start_offset; |
436 | } |
437 | } |
438 | |
439 | buffer_p += ((size_t) compiled_code_p->size) << JMEM_ALIGNMENT_LOG; |
440 | literal_start_p = ecma_snapshot_resolve_serializable_values (compiled_code_p, buffer_p); |
441 | |
442 | while (literal_start_p < (ecma_value_t *) buffer_p) |
443 | { |
444 | if (!ecma_is_value_direct_string (*literal_start_p) |
445 | && !ecma_is_value_empty (*literal_start_p)) |
446 | { |
447 | static_snapshot_error_unsupported_literal (globals_p, *literal_start_p); |
448 | return 0; |
449 | } |
450 | |
451 | literal_start_p++; |
452 | } |
453 | |
454 | return start_offset; |
455 | } /* static_snapshot_add_compiled_code */ |
456 | |
457 | /** |
458 | * Set the uint16_t offsets in the code area. |
459 | */ |
460 | static void |
461 | jerry_snapshot_set_offsets (uint32_t *buffer_p, /**< buffer */ |
462 | uint32_t size, /**< buffer size */ |
463 | lit_mem_to_snapshot_id_map_entry_t *lit_map_p) /**< literal map */ |
464 | { |
465 | JERRY_ASSERT (size > 0); |
466 | |
467 | do |
468 | { |
469 | ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) buffer_p; |
470 | uint32_t code_size = ((uint32_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG; |
471 | |
472 | if (CBC_IS_FUNCTION (bytecode_p->status_flags)) |
473 | { |
474 | ecma_value_t *literal_start_p; |
475 | uint32_t const_literal_end; |
476 | |
477 | if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) |
478 | { |
479 | literal_start_p = (ecma_value_t *) (((uint8_t *) buffer_p) + sizeof (cbc_uint16_arguments_t)); |
480 | |
481 | cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p; |
482 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
483 | } |
484 | else |
485 | { |
486 | literal_start_p = (ecma_value_t *) (((uint8_t *) buffer_p) + sizeof (cbc_uint8_arguments_t)); |
487 | |
488 | cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p; |
489 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
490 | } |
491 | |
492 | for (uint32_t i = 0; i < const_literal_end; i++) |
493 | { |
494 | if (ecma_is_value_string (literal_start_p[i]) |
495 | #if ENABLED (JERRY_BUILTIN_BIGINT) |
496 | || ecma_is_value_bigint (literal_start_p[i]) |
497 | #endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ |
498 | || ecma_is_value_float_number (literal_start_p[i])) |
499 | { |
500 | lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; |
501 | |
502 | while (current_p->literal_id != literal_start_p[i]) |
503 | { |
504 | current_p++; |
505 | } |
506 | |
507 | literal_start_p[i] = current_p->literal_offset; |
508 | } |
509 | } |
510 | |
511 | uint8_t *byte_p = (uint8_t *) bytecode_p + (((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG); |
512 | literal_start_p = ecma_snapshot_resolve_serializable_values (bytecode_p, byte_p); |
513 | |
514 | while (literal_start_p < (ecma_value_t *) byte_p) |
515 | { |
516 | if (*literal_start_p != ECMA_VALUE_EMPTY) |
517 | { |
518 | JERRY_ASSERT (ecma_is_value_string (*literal_start_p)); |
519 | |
520 | lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; |
521 | |
522 | while (current_p->literal_id != *literal_start_p) |
523 | { |
524 | current_p++; |
525 | } |
526 | |
527 | *literal_start_p = current_p->literal_offset; |
528 | } |
529 | |
530 | literal_start_p++; |
531 | } |
532 | |
533 | /* Set reference counter to 1. */ |
534 | bytecode_p->refs = 1; |
535 | } |
536 | |
537 | JERRY_ASSERT ((code_size % sizeof (uint32_t)) == 0); |
538 | buffer_p += code_size / sizeof (uint32_t); |
539 | size -= code_size; |
540 | } |
541 | while (size > 0); |
542 | } /* jerry_snapshot_set_offsets */ |
543 | |
544 | #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */ |
545 | |
546 | #if ENABLED (JERRY_SNAPSHOT_EXEC) |
547 | |
548 | /** |
549 | * Byte code blocks shorter than this threshold are always copied into the memory. |
550 | * The memory / performance trade-of of byte code redirection does not worth |
551 | * in such cases. |
552 | */ |
553 | #define BYTECODE_NO_COPY_THRESHOLD 8 |
554 | |
555 | /** |
556 | * Load byte code from snapshot. |
557 | * |
558 | * @return byte code |
559 | */ |
560 | static ecma_compiled_code_t * |
561 | snapshot_load_compiled_code (const uint8_t *base_addr_p, /**< base address of the |
562 | * current primary function */ |
563 | const uint8_t *literal_base_p, /**< literal start */ |
564 | bool copy_bytecode) /**< byte code should be copied to memory */ |
565 | { |
566 | ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) base_addr_p; |
567 | uint32_t code_size = ((uint32_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG; |
568 | |
569 | #if ENABLED (JERRY_BUILTIN_REGEXP) |
570 | if (!CBC_IS_FUNCTION (bytecode_p->status_flags)) |
571 | { |
572 | const uint8_t *regex_start_p = ((const uint8_t *) bytecode_p) + sizeof (ecma_compiled_code_t); |
573 | |
574 | /* Real size is stored in refs. */ |
575 | ecma_string_t *pattern_str_p = ecma_new_ecma_string_from_utf8 (regex_start_p, |
576 | bytecode_p->refs); |
577 | |
578 | const re_compiled_code_t *re_bytecode_p = re_compile_bytecode (pattern_str_p, |
579 | bytecode_p->status_flags); |
580 | ecma_deref_ecma_string (pattern_str_p); |
581 | |
582 | return (ecma_compiled_code_t *) re_bytecode_p; |
583 | } |
584 | #else /* !ENABLED (JERRY_BUILTIN_REGEXP) */ |
585 | JERRY_ASSERT (CBC_IS_FUNCTION (bytecode_p->status_flags)); |
586 | #endif /* ENABLED (JERRY_BUILTIN_REGEXP) */ |
587 | |
588 | size_t header_size; |
589 | uint32_t argument_end; |
590 | uint32_t const_literal_end; |
591 | uint32_t literal_end; |
592 | |
593 | if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) |
594 | { |
595 | uint8_t *byte_p = (uint8_t *) bytecode_p; |
596 | cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) byte_p; |
597 | |
598 | argument_end = args_p->argument_end; |
599 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
600 | literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); |
601 | header_size = sizeof (cbc_uint16_arguments_t); |
602 | |
603 | #if ENABLED (JERRY_BUILTIN_REALMS) |
604 | ECMA_SET_INTERNAL_VALUE_POINTER (args_p->realm_value, ecma_builtin_get_global ()); |
605 | #endif /* ENABLED (JERRY_BUILTIN_REALMS) */ |
606 | } |
607 | else |
608 | { |
609 | uint8_t *byte_p = (uint8_t *) bytecode_p; |
610 | cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) byte_p; |
611 | |
612 | argument_end = args_p->argument_end; |
613 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
614 | literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); |
615 | header_size = sizeof (cbc_uint8_arguments_t); |
616 | |
617 | #if ENABLED (JERRY_BUILTIN_REALMS) |
618 | ECMA_SET_INTERNAL_VALUE_POINTER (args_p->realm_value, ecma_builtin_get_global ()); |
619 | #endif /* ENABLED (JERRY_BUILTIN_REALMS) */ |
620 | } |
621 | |
622 | if (copy_bytecode |
623 | || (header_size + (literal_end * sizeof (uint16_t)) + BYTECODE_NO_COPY_THRESHOLD > code_size)) |
624 | { |
625 | bytecode_p = (ecma_compiled_code_t *) jmem_heap_alloc_block (code_size); |
626 | |
627 | #if ENABLED (JERRY_MEM_STATS) |
628 | jmem_stats_allocate_byte_code_bytes (code_size); |
629 | #endif /* ENABLED (JERRY_MEM_STATS) */ |
630 | |
631 | memcpy (bytecode_p, base_addr_p, code_size); |
632 | } |
633 | else |
634 | { |
635 | uint32_t start_offset = (uint32_t) (header_size + literal_end * sizeof (ecma_value_t)); |
636 | |
637 | uint8_t *real_bytecode_p = ((uint8_t *) bytecode_p) + start_offset; |
638 | uint32_t new_code_size = (uint32_t) (start_offset + 1 + sizeof (uint8_t *)); |
639 | uint32_t extra_bytes = 0; |
640 | |
641 | if (bytecode_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED) |
642 | { |
643 | extra_bytes += (uint32_t) (argument_end * sizeof (ecma_value_t)); |
644 | } |
645 | |
646 | #if ENABLED (JERRY_ESNEXT) |
647 | /* function name */ |
648 | if (CBC_FUNCTION_GET_TYPE (bytecode_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR) |
649 | { |
650 | extra_bytes += (uint32_t) sizeof (ecma_value_t); |
651 | } |
652 | |
653 | /* tagged template literals */ |
654 | if (bytecode_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS) |
655 | { |
656 | extra_bytes += (uint32_t) sizeof (ecma_value_t); |
657 | } |
658 | #endif /* ENABLED (JERRY_ESNEXT) */ |
659 | |
660 | #if ENABLED (JERRY_RESOURCE_NAME) |
661 | /* resource name */ |
662 | extra_bytes += (uint32_t) sizeof (ecma_value_t); |
663 | #endif /* ENABLED (JERRY_RESOURCE_NAME) */ |
664 | |
665 | new_code_size = JERRY_ALIGNUP (new_code_size + extra_bytes, JMEM_ALIGNMENT); |
666 | |
667 | bytecode_p = (ecma_compiled_code_t *) jmem_heap_alloc_block (new_code_size); |
668 | |
669 | #if ENABLED (JERRY_MEM_STATS) |
670 | jmem_stats_allocate_byte_code_bytes (new_code_size); |
671 | #endif /* ENABLED (JERRY_MEM_STATS) */ |
672 | |
673 | memcpy (bytecode_p, base_addr_p, start_offset); |
674 | |
675 | bytecode_p->size = (uint16_t) (new_code_size >> JMEM_ALIGNMENT_LOG); |
676 | |
677 | uint8_t *byte_p = (uint8_t *) bytecode_p; |
678 | |
679 | uint8_t *new_base_p = byte_p + new_code_size - extra_bytes; |
680 | const uint8_t *base_p = base_addr_p + code_size - extra_bytes; |
681 | |
682 | if (extra_bytes != 0) |
683 | { |
684 | memcpy (new_base_p, base_p, extra_bytes); |
685 | } |
686 | |
687 | byte_p[start_offset] = CBC_SET_BYTECODE_PTR; |
688 | memcpy (byte_p + start_offset + 1, &real_bytecode_p, sizeof (uint8_t *)); |
689 | |
690 | code_size = new_code_size; |
691 | } |
692 | |
693 | JERRY_ASSERT (bytecode_p->refs == 1); |
694 | |
695 | #if ENABLED (JERRY_DEBUGGER) |
696 | bytecode_p->status_flags = (uint16_t) (bytecode_p->status_flags | CBC_CODE_FLAGS_DEBUGGER_IGNORE); |
697 | #endif /* ENABLED (JERRY_DEBUGGER) */ |
698 | |
699 | ecma_value_t *literal_start_p = (ecma_value_t *) (((uint8_t *) bytecode_p) + header_size); |
700 | |
701 | for (uint32_t i = 0; i < const_literal_end; i++) |
702 | { |
703 | if ((literal_start_p[i] & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_SNAPSHOT_OFFSET) |
704 | { |
705 | literal_start_p[i] = ecma_snapshot_get_literal (literal_base_p, literal_start_p[i]); |
706 | } |
707 | } |
708 | |
709 | for (uint32_t i = const_literal_end; i < literal_end; i++) |
710 | { |
711 | size_t literal_offset = (size_t) literal_start_p[i]; |
712 | |
713 | if (literal_offset == 0) |
714 | { |
715 | /* Self reference */ |
716 | ECMA_SET_INTERNAL_VALUE_POINTER (literal_start_p[i], |
717 | bytecode_p); |
718 | } |
719 | else |
720 | { |
721 | ecma_compiled_code_t *literal_bytecode_p; |
722 | literal_bytecode_p = snapshot_load_compiled_code (base_addr_p + literal_offset, |
723 | literal_base_p, |
724 | copy_bytecode); |
725 | |
726 | ECMA_SET_INTERNAL_VALUE_POINTER (literal_start_p[i], |
727 | literal_bytecode_p); |
728 | } |
729 | } |
730 | |
731 | uint8_t *byte_p = ((uint8_t *) bytecode_p) + code_size; |
732 | literal_start_p = ecma_snapshot_resolve_serializable_values (bytecode_p, byte_p); |
733 | |
734 | while (literal_start_p < (ecma_value_t *) byte_p) |
735 | { |
736 | if ((*literal_start_p & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_SNAPSHOT_OFFSET) |
737 | { |
738 | *literal_start_p = ecma_snapshot_get_literal (literal_base_p, *literal_start_p); |
739 | } |
740 | |
741 | literal_start_p++; |
742 | } |
743 | |
744 | return bytecode_p; |
745 | } /* snapshot_load_compiled_code */ |
746 | |
747 | #endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ |
748 | |
749 | #if ENABLED (JERRY_SNAPSHOT_SAVE) |
750 | |
751 | /** |
752 | * Generate snapshot from specified source and arguments |
753 | * |
754 | * @return size of snapshot (a number value), if it was generated succesfully |
755 | * (i.e. there are no syntax errors in source code, buffer size is sufficient, |
756 | * and snapshot support is enabled in current configuration through JERRY_SNAPSHOT_SAVE), |
757 | * error object otherwise |
758 | */ |
759 | static jerry_value_t |
760 | jerry_generate_snapshot_with_args (const jerry_char_t *resource_name_p, /**< script resource name */ |
761 | size_t resource_name_length, /**< script resource name length */ |
762 | const jerry_char_t *source_p, /**< script source */ |
763 | size_t source_size, /**< script source size */ |
764 | const jerry_char_t *args_p, /**< arguments string */ |
765 | size_t args_size, /**< arguments string size */ |
766 | uint32_t generate_snapshot_opts, /**< jerry_generate_snapshot_opts_t option bits */ |
767 | uint32_t *buffer_p, /**< buffer to save snapshot to */ |
768 | size_t buffer_size) /**< the buffer's size */ |
769 | { |
770 | /* Currently unused arguments. */ |
771 | JERRY_UNUSED (resource_name_p); |
772 | JERRY_UNUSED (resource_name_length); |
773 | |
774 | ecma_value_t resource_name = ecma_make_magic_string_value (LIT_MAGIC_STRING_RESOURCE_ANON); |
775 | |
776 | #if ENABLED (JERRY_RESOURCE_NAME) |
777 | if (resource_name_length > 0) |
778 | { |
779 | resource_name = ecma_find_or_create_literal_string (resource_name_p, (lit_utf8_size_t) resource_name_length); |
780 | } |
781 | #endif /* ENABLED (JERRY_RESOURCE_NAME) */ |
782 | |
783 | snapshot_globals_t globals; |
784 | const uint32_t aligned_header_size = JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), |
785 | JMEM_ALIGNMENT); |
786 | |
787 | globals.snapshot_buffer_write_offset = aligned_header_size; |
788 | globals.snapshot_error = ECMA_VALUE_EMPTY; |
789 | globals.regex_found = false; |
790 | globals.class_found = false; |
791 | |
792 | uint32_t status_flags = ((generate_snapshot_opts & JERRY_SNAPSHOT_SAVE_STRICT) ? ECMA_PARSE_STRICT_MODE |
793 | : ECMA_PARSE_NO_OPTS); |
794 | |
795 | ecma_compiled_code_t *bytecode_data_p = parser_parse_script (args_p, |
796 | args_size, |
797 | source_p, |
798 | source_size, |
799 | resource_name, |
800 | status_flags); |
801 | |
802 | if (JERRY_UNLIKELY (bytecode_data_p == NULL)) |
803 | { |
804 | return ecma_create_error_reference_from_context (); |
805 | } |
806 | |
807 | if (generate_snapshot_opts & JERRY_SNAPSHOT_SAVE_STATIC) |
808 | { |
809 | static_snapshot_add_compiled_code (bytecode_data_p, (uint8_t *) buffer_p, buffer_size, &globals); |
810 | } |
811 | else |
812 | { |
813 | snapshot_add_compiled_code (bytecode_data_p, (uint8_t *) buffer_p, buffer_size, &globals); |
814 | } |
815 | |
816 | if (!ecma_is_value_empty (globals.snapshot_error)) |
817 | { |
818 | ecma_bytecode_deref (bytecode_data_p); |
819 | return globals.snapshot_error; |
820 | } |
821 | |
822 | jerry_snapshot_header_t header; |
823 | header.magic = JERRY_SNAPSHOT_MAGIC; |
824 | header.version = JERRY_SNAPSHOT_VERSION; |
825 | header.global_flags = snapshot_get_global_flags (globals.regex_found, globals.class_found); |
826 | header.lit_table_offset = (uint32_t) globals.snapshot_buffer_write_offset; |
827 | header.number_of_funcs = 1; |
828 | header.func_offsets[0] = aligned_header_size; |
829 | |
830 | lit_mem_to_snapshot_id_map_entry_t *lit_map_p = NULL; |
831 | uint32_t literals_num = 0; |
832 | |
833 | if (!(generate_snapshot_opts & JERRY_SNAPSHOT_SAVE_STATIC)) |
834 | { |
835 | ecma_collection_t *lit_pool_p = ecma_new_collection (); |
836 | |
837 | ecma_save_literals_add_compiled_code (bytecode_data_p, lit_pool_p); |
838 | |
839 | if (!ecma_save_literals_for_snapshot (lit_pool_p, |
840 | buffer_p, |
841 | buffer_size, |
842 | &globals.snapshot_buffer_write_offset, |
843 | &lit_map_p, |
844 | &literals_num)) |
845 | { |
846 | JERRY_ASSERT (lit_map_p == NULL); |
847 | const char * const error_message_p = "Cannot allocate memory for literals." ; |
848 | ecma_bytecode_deref (bytecode_data_p); |
849 | return jerry_create_error (JERRY_ERROR_COMMON, (const jerry_char_t *) error_message_p); |
850 | } |
851 | |
852 | jerry_snapshot_set_offsets (buffer_p + (aligned_header_size / sizeof (uint32_t)), |
853 | (uint32_t) (header.lit_table_offset - aligned_header_size), |
854 | lit_map_p); |
855 | } |
856 | |
857 | size_t header_offset = 0; |
858 | |
859 | snapshot_write_to_buffer_by_offset ((uint8_t *) buffer_p, |
860 | buffer_size, |
861 | &header_offset, |
862 | &header, |
863 | sizeof (header)); |
864 | |
865 | if (lit_map_p != NULL) |
866 | { |
867 | jmem_heap_free_block (lit_map_p, literals_num * sizeof (lit_mem_to_snapshot_id_map_entry_t)); |
868 | } |
869 | |
870 | ecma_bytecode_deref (bytecode_data_p); |
871 | |
872 | return ecma_make_number_value ((ecma_number_t) globals.snapshot_buffer_write_offset); |
873 | } /* jerry_generate_snapshot_with_args */ |
874 | |
875 | #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */ |
876 | |
877 | /** |
878 | * Generate snapshot from specified source and arguments |
879 | * |
880 | * @return size of snapshot (a number value), if it was generated succesfully |
881 | * (i.e. there are no syntax errors in source code, buffer size is sufficient, |
882 | * and snapshot support is enabled in current configuration through JERRY_SNAPSHOT_SAVE), |
883 | * error object otherwise |
884 | */ |
885 | jerry_value_t |
886 | jerry_generate_snapshot (const jerry_char_t *resource_name_p, /**< script resource name */ |
887 | size_t resource_name_length, /**< script resource name length */ |
888 | const jerry_char_t *source_p, /**< script source */ |
889 | size_t source_size, /**< script source size */ |
890 | uint32_t generate_snapshot_opts, /**< jerry_generate_snapshot_opts_t option bits */ |
891 | uint32_t *buffer_p, /**< buffer to save snapshot to */ |
892 | size_t buffer_size) /**< the buffer's size */ |
893 | { |
894 | #if ENABLED (JERRY_SNAPSHOT_SAVE) |
895 | uint32_t allowed_opts = (JERRY_SNAPSHOT_SAVE_STATIC | JERRY_SNAPSHOT_SAVE_STRICT); |
896 | |
897 | if ((generate_snapshot_opts & ~(allowed_opts)) != 0) |
898 | { |
899 | const char * const error_message_p = "Unsupported generate snapshot flags specified." ; |
900 | return jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) error_message_p); |
901 | } |
902 | |
903 | return jerry_generate_snapshot_with_args (resource_name_p, |
904 | resource_name_length, |
905 | source_p, |
906 | source_size, |
907 | NULL, |
908 | 0, |
909 | generate_snapshot_opts, |
910 | buffer_p, |
911 | buffer_size); |
912 | #else /* !ENABLED (JERRY_SNAPSHOT_SAVE) */ |
913 | JERRY_UNUSED (resource_name_p); |
914 | JERRY_UNUSED (resource_name_length); |
915 | JERRY_UNUSED (source_p); |
916 | JERRY_UNUSED (source_size); |
917 | JERRY_UNUSED (generate_snapshot_opts); |
918 | JERRY_UNUSED (buffer_p); |
919 | JERRY_UNUSED (buffer_size); |
920 | |
921 | return jerry_create_error (JERRY_ERROR_COMMON, (const jerry_char_t *) "Snapshot save is not supported." ); |
922 | #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */ |
923 | } /* jerry_generate_snapshot */ |
924 | |
925 | #if ENABLED (JERRY_SNAPSHOT_EXEC) |
926 | /** |
927 | * Execute/load snapshot from specified buffer |
928 | * |
929 | * Note: |
930 | * returned value must be freed with jerry_release_value, when it is no longer needed. |
931 | * |
932 | * @return result of bytecode - if run was successful |
933 | * thrown error - otherwise |
934 | */ |
935 | static jerry_value_t |
936 | jerry_snapshot_result (const uint32_t *snapshot_p, /**< snapshot */ |
937 | size_t snapshot_size, /**< size of snapshot */ |
938 | size_t func_index, /**< index of primary function */ |
939 | uint32_t exec_snapshot_opts, /**< jerry_exec_snapshot_opts_t option bits */ |
940 | bool as_function) /** < specify if the loaded snapshot should be returned as a function */ |
941 | { |
942 | JERRY_ASSERT (snapshot_p != NULL); |
943 | |
944 | uint32_t allowed_opts = (JERRY_SNAPSHOT_EXEC_COPY_DATA | JERRY_SNAPSHOT_EXEC_ALLOW_STATIC); |
945 | |
946 | if ((exec_snapshot_opts & ~(allowed_opts)) != 0) |
947 | { |
948 | ecma_raise_range_error (ECMA_ERR_MSG ("Unsupported exec snapshot flags specified." )); |
949 | return ecma_create_error_reference_from_context (); |
950 | } |
951 | |
952 | const char * const invalid_version_error_p = "Invalid snapshot version or unsupported features present" ; |
953 | const char * const invalid_format_error_p = "Invalid snapshot format" ; |
954 | const uint8_t *snapshot_data_p = (uint8_t *) snapshot_p; |
955 | |
956 | if (snapshot_size <= sizeof (jerry_snapshot_header_t)) |
957 | { |
958 | ecma_raise_type_error (invalid_format_error_p); |
959 | return ecma_create_error_reference_from_context (); |
960 | } |
961 | |
962 | const jerry_snapshot_header_t *header_p = (const jerry_snapshot_header_t *) snapshot_data_p; |
963 | |
964 | if (header_p->magic != JERRY_SNAPSHOT_MAGIC |
965 | || header_p->version != JERRY_SNAPSHOT_VERSION |
966 | || !snapshot_check_global_flags (header_p->global_flags)) |
967 | { |
968 | ecma_raise_type_error (invalid_version_error_p); |
969 | return ecma_create_error_reference_from_context (); |
970 | } |
971 | |
972 | if (header_p->lit_table_offset > snapshot_size) |
973 | { |
974 | ecma_raise_type_error (invalid_version_error_p); |
975 | return ecma_create_error_reference_from_context (); |
976 | } |
977 | |
978 | if (func_index >= header_p->number_of_funcs) |
979 | { |
980 | ecma_raise_range_error (ECMA_ERR_MSG ("Function index is higher than maximum" )); |
981 | return ecma_create_error_reference_from_context (); |
982 | } |
983 | |
984 | JERRY_ASSERT ((header_p->lit_table_offset % sizeof (uint32_t)) == 0); |
985 | |
986 | uint32_t func_offset = header_p->func_offsets[func_index]; |
987 | ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) (snapshot_data_p + func_offset); |
988 | |
989 | if (bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION) |
990 | { |
991 | if (!(exec_snapshot_opts & JERRY_SNAPSHOT_EXEC_ALLOW_STATIC)) |
992 | { |
993 | ecma_raise_common_error (ECMA_ERR_MSG ("Static snapshots not allowed" )); |
994 | return ecma_create_error_reference_from_context (); |
995 | } |
996 | |
997 | if (exec_snapshot_opts & JERRY_SNAPSHOT_EXEC_COPY_DATA) |
998 | { |
999 | ecma_raise_common_error (ECMA_ERR_MSG ("Static snapshots cannot be copied into memory" )); |
1000 | return ecma_create_error_reference_from_context (); |
1001 | } |
1002 | } |
1003 | else |
1004 | { |
1005 | const uint8_t *literal_base_p = snapshot_data_p + header_p->lit_table_offset; |
1006 | |
1007 | bytecode_p = snapshot_load_compiled_code ((const uint8_t *) bytecode_p, |
1008 | literal_base_p, |
1009 | (exec_snapshot_opts & JERRY_SNAPSHOT_EXEC_COPY_DATA) != 0); |
1010 | |
1011 | if (bytecode_p == NULL) |
1012 | { |
1013 | return ecma_raise_type_error (invalid_format_error_p); |
1014 | } |
1015 | } |
1016 | |
1017 | #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) |
1018 | if (JERRY_CONTEXT (jerry_init_flags) & ECMA_INIT_SHOW_OPCODES) |
1019 | { |
1020 | util_print_cbc (bytecode_p); |
1021 | } |
1022 | #endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ |
1023 | |
1024 | ecma_value_t ret_val; |
1025 | |
1026 | if (as_function) |
1027 | { |
1028 | ecma_object_t *global_object_p = ecma_builtin_get_global (); |
1029 | |
1030 | #if ENABLED (JERRY_BUILTIN_REALMS) |
1031 | JERRY_ASSERT (global_object_p == (ecma_object_t *) ecma_op_function_get_realm (bytecode_p)); |
1032 | #endif /* ENABLED (JERRY_BUILTIN_REALMS) */ |
1033 | |
1034 | #if ENABLED (JERRY_ESNEXT) |
1035 | if (bytecode_p->status_flags & CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED) |
1036 | { |
1037 | ecma_create_global_lexical_block (global_object_p); |
1038 | } |
1039 | #endif /* ENABLED (JERRY_ESNEXT) */ |
1040 | |
1041 | ecma_object_t *lex_env_p = ecma_get_global_scope (global_object_p); |
1042 | ecma_object_t *func_obj_p = ecma_op_create_simple_function_object (lex_env_p, bytecode_p); |
1043 | |
1044 | if (!(bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) |
1045 | { |
1046 | ecma_bytecode_deref (bytecode_p); |
1047 | } |
1048 | ret_val = ecma_make_object_value (func_obj_p); |
1049 | } |
1050 | else |
1051 | { |
1052 | ret_val = vm_run_global (bytecode_p); |
1053 | if (!(bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) |
1054 | { |
1055 | ecma_bytecode_deref (bytecode_p); |
1056 | } |
1057 | } |
1058 | |
1059 | if (ECMA_IS_VALUE_ERROR (ret_val)) |
1060 | { |
1061 | return ecma_create_error_reference_from_context (); |
1062 | } |
1063 | |
1064 | return ret_val; |
1065 | } /* jerry_snapshot_result */ |
1066 | #endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ |
1067 | |
1068 | /** |
1069 | * Execute snapshot from specified buffer |
1070 | * |
1071 | * Note: |
1072 | * returned value must be freed with jerry_release_value, when it is no longer needed. |
1073 | * |
1074 | * @return result of bytecode - if run was successful |
1075 | * thrown error - otherwise |
1076 | */ |
1077 | jerry_value_t |
1078 | jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */ |
1079 | size_t snapshot_size, /**< size of snapshot */ |
1080 | size_t func_index, /**< index of primary function */ |
1081 | uint32_t exec_snapshot_opts) /**< jerry_exec_snapshot_opts_t option bits */ |
1082 | { |
1083 | #if ENABLED (JERRY_SNAPSHOT_EXEC) |
1084 | return jerry_snapshot_result (snapshot_p, snapshot_size, func_index, exec_snapshot_opts, false); |
1085 | #else /* !ENABLED (JERRY_SNAPSHOT_EXEC) */ |
1086 | JERRY_UNUSED (snapshot_p); |
1087 | JERRY_UNUSED (snapshot_size); |
1088 | JERRY_UNUSED (func_index); |
1089 | JERRY_UNUSED (exec_snapshot_opts); |
1090 | |
1091 | return jerry_create_error (JERRY_ERROR_COMMON, (const jerry_char_t *) "Snapshot execution is not supported." ); |
1092 | #endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ |
1093 | } /* jerry_exec_snapshot */ |
1094 | |
1095 | /** |
1096 | * @} |
1097 | */ |
1098 | |
1099 | #if ENABLED (JERRY_SNAPSHOT_SAVE) |
1100 | |
1101 | /** |
1102 | * Collect all literals from a snapshot file. |
1103 | */ |
1104 | static void |
1105 | scan_snapshot_functions (const uint8_t *buffer_p, /**< snapshot buffer start */ |
1106 | const uint8_t *buffer_end_p, /**< snapshot buffer end */ |
1107 | ecma_collection_t *lit_pool_p, /**< list of known values */ |
1108 | const uint8_t *literal_base_p) /**< start of literal data */ |
1109 | { |
1110 | JERRY_ASSERT (buffer_end_p > buffer_p); |
1111 | |
1112 | do |
1113 | { |
1114 | const ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) buffer_p; |
1115 | uint32_t code_size = ((uint32_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG; |
1116 | |
1117 | if (CBC_IS_FUNCTION (bytecode_p->status_flags) |
1118 | && !(bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) |
1119 | { |
1120 | const ecma_value_t *literal_start_p; |
1121 | uint32_t const_literal_end; |
1122 | |
1123 | if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) |
1124 | { |
1125 | literal_start_p = (ecma_value_t *) (buffer_p + sizeof (cbc_uint16_arguments_t)); |
1126 | |
1127 | cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p; |
1128 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
1129 | } |
1130 | else |
1131 | { |
1132 | literal_start_p = (ecma_value_t *) (buffer_p + sizeof (cbc_uint8_arguments_t)); |
1133 | |
1134 | cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p; |
1135 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
1136 | } |
1137 | |
1138 | for (uint32_t i = 0; i < const_literal_end; i++) |
1139 | { |
1140 | if ((literal_start_p[i] & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_SNAPSHOT_OFFSET) |
1141 | { |
1142 | ecma_value_t lit_value = ecma_snapshot_get_literal (literal_base_p, literal_start_p[i]); |
1143 | ecma_save_literals_append_value (lit_value, lit_pool_p); |
1144 | } |
1145 | } |
1146 | |
1147 | uint8_t *byte_p = (uint8_t *) bytecode_p + (((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG); |
1148 | literal_start_p = ecma_snapshot_resolve_serializable_values ((ecma_compiled_code_t *) bytecode_p, byte_p); |
1149 | |
1150 | while (literal_start_p < (ecma_value_t *) byte_p) |
1151 | { |
1152 | if ((*literal_start_p & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_SNAPSHOT_OFFSET) |
1153 | { |
1154 | ecma_value_t lit_value = ecma_snapshot_get_literal (literal_base_p, *literal_start_p); |
1155 | ecma_save_literals_append_value (lit_value, lit_pool_p); |
1156 | } |
1157 | |
1158 | literal_start_p++; |
1159 | } |
1160 | } |
1161 | |
1162 | buffer_p += code_size; |
1163 | } |
1164 | while (buffer_p < buffer_end_p); |
1165 | } /* scan_snapshot_functions */ |
1166 | |
1167 | /** |
1168 | * Update all literal offsets in a snapshot data. |
1169 | */ |
1170 | static void |
1171 | update_literal_offsets (uint8_t *buffer_p, /**< [in,out] snapshot buffer start */ |
1172 | const uint8_t *buffer_end_p, /**< snapshot buffer end */ |
1173 | const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< literal map */ |
1174 | const uint8_t *literal_base_p) /**< start of literal data */ |
1175 | { |
1176 | JERRY_ASSERT (buffer_end_p > buffer_p); |
1177 | |
1178 | do |
1179 | { |
1180 | const ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) buffer_p; |
1181 | uint32_t code_size = ((uint32_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG; |
1182 | |
1183 | if (CBC_IS_FUNCTION (bytecode_p->status_flags) |
1184 | && !(bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) |
1185 | { |
1186 | ecma_value_t *literal_start_p; |
1187 | uint32_t const_literal_end; |
1188 | |
1189 | if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) |
1190 | { |
1191 | literal_start_p = (ecma_value_t *) (buffer_p + sizeof (cbc_uint16_arguments_t)); |
1192 | |
1193 | cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p; |
1194 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
1195 | } |
1196 | else |
1197 | { |
1198 | literal_start_p = (ecma_value_t *) (buffer_p + sizeof (cbc_uint8_arguments_t)); |
1199 | |
1200 | cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p; |
1201 | const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); |
1202 | } |
1203 | |
1204 | for (uint32_t i = 0; i < const_literal_end; i++) |
1205 | { |
1206 | if ((literal_start_p[i] & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_SNAPSHOT_OFFSET) |
1207 | { |
1208 | ecma_value_t lit_value = ecma_snapshot_get_literal (literal_base_p, literal_start_p[i]); |
1209 | const lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; |
1210 | |
1211 | while (current_p->literal_id != lit_value) |
1212 | { |
1213 | current_p++; |
1214 | } |
1215 | |
1216 | literal_start_p[i] = current_p->literal_offset; |
1217 | } |
1218 | } |
1219 | |
1220 | uint8_t *byte_p = (uint8_t *) bytecode_p + (((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG); |
1221 | literal_start_p = ecma_snapshot_resolve_serializable_values ((ecma_compiled_code_t *) bytecode_p, byte_p); |
1222 | |
1223 | while (literal_start_p < (ecma_value_t *) byte_p) |
1224 | { |
1225 | if ((*literal_start_p & ECMA_VALUE_TYPE_MASK) == ECMA_TYPE_SNAPSHOT_OFFSET) |
1226 | { |
1227 | ecma_value_t lit_value = ecma_snapshot_get_literal (literal_base_p, *literal_start_p); |
1228 | const lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; |
1229 | |
1230 | while (current_p->literal_id != lit_value) |
1231 | { |
1232 | current_p++; |
1233 | } |
1234 | |
1235 | *literal_start_p = current_p->literal_offset; |
1236 | } |
1237 | |
1238 | literal_start_p++; |
1239 | } |
1240 | } |
1241 | |
1242 | buffer_p += code_size; |
1243 | } |
1244 | while (buffer_p < buffer_end_p); |
1245 | } /* update_literal_offsets */ |
1246 | |
1247 | #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */ |
1248 | |
1249 | /** |
1250 | * Merge multiple snapshots into a single buffer |
1251 | * |
1252 | * @return length of merged snapshot file |
1253 | * 0 on error |
1254 | */ |
1255 | size_t |
1256 | jerry_merge_snapshots (const uint32_t **inp_buffers_p, /**< array of (pointers to start of) input buffers */ |
1257 | size_t *inp_buffer_sizes_p, /**< array of input buffer sizes */ |
1258 | size_t number_of_snapshots, /**< number of snapshots */ |
1259 | uint32_t *out_buffer_p, /**< output buffer */ |
1260 | size_t out_buffer_size, /**< output buffer size */ |
1261 | const char **error_p) /**< error description */ |
1262 | { |
1263 | #if ENABLED (JERRY_SNAPSHOT_SAVE) |
1264 | uint32_t number_of_funcs = 0; |
1265 | uint32_t merged_global_flags = 0; |
1266 | size_t functions_size = sizeof (jerry_snapshot_header_t); |
1267 | |
1268 | if (number_of_snapshots < 2) |
1269 | { |
1270 | *error_p = "at least two snapshots must be passed" ; |
1271 | return 0; |
1272 | } |
1273 | |
1274 | ecma_collection_t *lit_pool_p = ecma_new_collection (); |
1275 | |
1276 | for (uint32_t i = 0; i < number_of_snapshots; i++) |
1277 | { |
1278 | if (inp_buffer_sizes_p[i] < sizeof (jerry_snapshot_header_t)) |
1279 | { |
1280 | *error_p = "invalid snapshot file" ; |
1281 | ecma_collection_destroy (lit_pool_p); |
1282 | return 0; |
1283 | } |
1284 | |
1285 | const jerry_snapshot_header_t *header_p = (const jerry_snapshot_header_t *) inp_buffers_p[i]; |
1286 | |
1287 | if (header_p->magic != JERRY_SNAPSHOT_MAGIC |
1288 | || header_p->version != JERRY_SNAPSHOT_VERSION |
1289 | || !snapshot_check_global_flags (header_p->global_flags)) |
1290 | { |
1291 | *error_p = "invalid snapshot version or unsupported features present" ; |
1292 | ecma_collection_destroy (lit_pool_p); |
1293 | return 0; |
1294 | } |
1295 | |
1296 | merged_global_flags |= header_p->global_flags; |
1297 | |
1298 | uint32_t start_offset = header_p->func_offsets[0]; |
1299 | const uint8_t *data_p = (const uint8_t *) inp_buffers_p[i]; |
1300 | const uint8_t *literal_base_p = data_p + header_p->lit_table_offset; |
1301 | |
1302 | JERRY_ASSERT (header_p->number_of_funcs > 0); |
1303 | |
1304 | number_of_funcs += header_p->number_of_funcs; |
1305 | functions_size += header_p->lit_table_offset - start_offset; |
1306 | |
1307 | scan_snapshot_functions (data_p + start_offset, |
1308 | literal_base_p, |
1309 | lit_pool_p, |
1310 | literal_base_p); |
1311 | } |
1312 | |
1313 | JERRY_ASSERT (number_of_funcs > 0); |
1314 | |
1315 | functions_size += JERRY_ALIGNUP ((number_of_funcs - 1) * sizeof (uint32_t), JMEM_ALIGNMENT); |
1316 | |
1317 | if (functions_size >= out_buffer_size) |
1318 | { |
1319 | *error_p = "output buffer is too small" ; |
1320 | ecma_collection_destroy (lit_pool_p); |
1321 | return 0; |
1322 | } |
1323 | |
1324 | jerry_snapshot_header_t *header_p = (jerry_snapshot_header_t *) out_buffer_p; |
1325 | |
1326 | header_p->magic = JERRY_SNAPSHOT_MAGIC; |
1327 | header_p->version = JERRY_SNAPSHOT_VERSION; |
1328 | header_p->global_flags = merged_global_flags; |
1329 | header_p->lit_table_offset = (uint32_t) functions_size; |
1330 | header_p->number_of_funcs = number_of_funcs; |
1331 | |
1332 | lit_mem_to_snapshot_id_map_entry_t *lit_map_p; |
1333 | uint32_t literals_num; |
1334 | |
1335 | if (!ecma_save_literals_for_snapshot (lit_pool_p, |
1336 | out_buffer_p, |
1337 | out_buffer_size, |
1338 | &functions_size, |
1339 | &lit_map_p, |
1340 | &literals_num)) |
1341 | { |
1342 | *error_p = "buffer is too small" ; |
1343 | return 0; |
1344 | } |
1345 | |
1346 | uint32_t *func_offset_p = header_p->func_offsets; |
1347 | uint8_t *dst_p = ((uint8_t *) out_buffer_p) + sizeof (jerry_snapshot_header_t); |
1348 | dst_p += JERRY_ALIGNUP ((number_of_funcs - 1) * sizeof (uint32_t), JMEM_ALIGNMENT); |
1349 | |
1350 | for (uint32_t i = 0; i < number_of_snapshots; i++) |
1351 | { |
1352 | const jerry_snapshot_header_t *current_header_p = (const jerry_snapshot_header_t *) inp_buffers_p[i]; |
1353 | |
1354 | uint32_t start_offset = current_header_p->func_offsets[0]; |
1355 | |
1356 | memcpy (dst_p, |
1357 | ((const uint8_t *) inp_buffers_p[i]) + start_offset, |
1358 | current_header_p->lit_table_offset - start_offset); |
1359 | |
1360 | const uint8_t *literal_base_p = ((const uint8_t *) inp_buffers_p[i]) + current_header_p->lit_table_offset; |
1361 | update_literal_offsets (dst_p, |
1362 | dst_p + current_header_p->lit_table_offset - start_offset, |
1363 | lit_map_p, |
1364 | literal_base_p); |
1365 | |
1366 | uint32_t current_offset = (uint32_t) (dst_p - (uint8_t *) out_buffer_p) - start_offset; |
1367 | |
1368 | for (uint32_t j = 0; j < current_header_p->number_of_funcs; j++) |
1369 | { |
1370 | /* Updating offset without changing any flags. */ |
1371 | *func_offset_p++ = current_header_p->func_offsets[j] + current_offset; |
1372 | } |
1373 | |
1374 | dst_p += current_header_p->lit_table_offset - start_offset; |
1375 | } |
1376 | |
1377 | JERRY_ASSERT ((uint32_t) (dst_p - (uint8_t *) out_buffer_p) == header_p->lit_table_offset); |
1378 | |
1379 | if (lit_map_p != NULL) |
1380 | { |
1381 | jmem_heap_free_block (lit_map_p, literals_num * sizeof (lit_mem_to_snapshot_id_map_entry_t)); |
1382 | } |
1383 | |
1384 | *error_p = NULL; |
1385 | return functions_size; |
1386 | #else /* !ENABLED (JERRY_SNAPSHOT_SAVE) */ |
1387 | JERRY_UNUSED (inp_buffers_p); |
1388 | JERRY_UNUSED (inp_buffer_sizes_p); |
1389 | JERRY_UNUSED (number_of_snapshots); |
1390 | JERRY_UNUSED (out_buffer_p); |
1391 | JERRY_UNUSED (out_buffer_size); |
1392 | JERRY_UNUSED (error_p); |
1393 | |
1394 | *error_p = "snapshot merge not supported" ; |
1395 | return 0; |
1396 | #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */ |
1397 | } /* jerry_merge_snapshots */ |
1398 | |
1399 | #if ENABLED (JERRY_SNAPSHOT_SAVE) |
1400 | |
1401 | /** |
1402 | * ====================== Functions for literal saving ========================== |
1403 | */ |
1404 | |
1405 | /** |
1406 | * Compare two ecma_strings by size, then lexicographically. |
1407 | * |
1408 | * @return true - if the first string is less than the second one, |
1409 | * false - otherwise |
1410 | */ |
1411 | static bool |
1412 | jerry_save_literals_compare (ecma_string_t *literal1, /**< first literal */ |
1413 | ecma_string_t *literal2) /**< second literal */ |
1414 | { |
1415 | const lit_utf8_size_t lit1_size = ecma_string_get_size (literal1); |
1416 | const lit_utf8_size_t lit2_size = ecma_string_get_size (literal2); |
1417 | |
1418 | if (lit1_size == lit2_size) |
1419 | { |
1420 | return ecma_compare_ecma_strings_relational (literal1, literal2); |
1421 | } |
1422 | |
1423 | return (lit1_size < lit2_size); |
1424 | } /* jerry_save_literals_compare */ |
1425 | |
1426 | /** |
1427 | * Helper function for the heapsort algorithm. |
1428 | * |
1429 | * @return index of the maximum value |
1430 | */ |
1431 | static lit_utf8_size_t |
1432 | jerry_save_literals_heap_max (ecma_string_t *literals[], /**< array of literals */ |
1433 | lit_utf8_size_t num_of_nodes, /**< number of nodes */ |
1434 | lit_utf8_size_t node_idx, /**< index of parent node */ |
1435 | lit_utf8_size_t child_idx1, /**< index of the first child */ |
1436 | lit_utf8_size_t child_idx2) /**< index of the second child */ |
1437 | { |
1438 | lit_utf8_size_t max_idx = node_idx; |
1439 | |
1440 | if (child_idx1 < num_of_nodes |
1441 | && jerry_save_literals_compare (literals[max_idx], literals[child_idx1])) |
1442 | { |
1443 | max_idx = child_idx1; |
1444 | } |
1445 | |
1446 | if (child_idx2 < num_of_nodes |
1447 | && jerry_save_literals_compare (literals[max_idx], literals[child_idx2])) |
1448 | { |
1449 | max_idx = child_idx2; |
1450 | } |
1451 | |
1452 | return max_idx; |
1453 | } /* jerry_save_literals_heap_max */ |
1454 | |
1455 | /** |
1456 | * Helper function for the heapsort algorithm. |
1457 | */ |
1458 | static void |
1459 | jerry_save_literals_down_heap (ecma_string_t *literals[], /**< array of literals */ |
1460 | lit_utf8_size_t num_of_nodes, /**< number of nodes */ |
1461 | lit_utf8_size_t node_idx) /**< index of parent node */ |
1462 | { |
1463 | while (true) |
1464 | { |
1465 | lit_utf8_size_t max_idx = jerry_save_literals_heap_max (literals, |
1466 | num_of_nodes, |
1467 | node_idx, |
1468 | 2 * node_idx + 1, |
1469 | 2 * node_idx + 2); |
1470 | if (max_idx == node_idx) |
1471 | { |
1472 | break; |
1473 | } |
1474 | |
1475 | ecma_string_t *tmp_str_p = literals[node_idx]; |
1476 | literals[node_idx] = literals[max_idx]; |
1477 | literals[max_idx] = tmp_str_p; |
1478 | |
1479 | node_idx = max_idx; |
1480 | } |
1481 | } /* jerry_save_literals_down_heap */ |
1482 | |
1483 | /** |
1484 | * Helper function for a heapsort algorithm. |
1485 | */ |
1486 | static void |
1487 | jerry_save_literals_sort (ecma_string_t *literals[], /**< array of literals */ |
1488 | lit_utf8_size_t num_of_literals) /**< number of literals */ |
1489 | { |
1490 | if (num_of_literals < 2) |
1491 | { |
1492 | return; |
1493 | } |
1494 | |
1495 | lit_utf8_size_t lit_idx = (num_of_literals - 2) / 2; |
1496 | |
1497 | while (lit_idx <= (num_of_literals - 2) / 2) |
1498 | { |
1499 | jerry_save_literals_down_heap (literals, num_of_literals, lit_idx--); |
1500 | } |
1501 | |
1502 | for (lit_idx = 0; lit_idx < num_of_literals; lit_idx++) |
1503 | { |
1504 | const lit_utf8_size_t last_idx = num_of_literals - lit_idx - 1; |
1505 | |
1506 | ecma_string_t *tmp_str_p = literals[last_idx]; |
1507 | literals[last_idx] = literals[0]; |
1508 | literals[0] = tmp_str_p; |
1509 | |
1510 | jerry_save_literals_down_heap (literals, last_idx, 0); |
1511 | } |
1512 | } /* jerry_save_literals_sort */ |
1513 | |
1514 | /** |
1515 | * Append characters to the specified buffer. |
1516 | * |
1517 | * @return the position of the buffer pointer after copy. |
1518 | */ |
1519 | static uint8_t * |
1520 | jerry_append_chars_to_buffer (uint8_t *buffer_p, /**< buffer */ |
1521 | uint8_t *buffer_end_p, /**< the end of the buffer */ |
1522 | const char *chars, /**< string */ |
1523 | lit_utf8_size_t string_size) /**< string size */ |
1524 | { |
1525 | if (buffer_p > buffer_end_p) |
1526 | { |
1527 | return buffer_p; |
1528 | } |
1529 | |
1530 | if (string_size == 0) |
1531 | { |
1532 | string_size = (lit_utf8_size_t) strlen (chars); |
1533 | } |
1534 | |
1535 | if (buffer_p + string_size <= buffer_end_p) |
1536 | { |
1537 | memcpy ((char *) buffer_p, chars, string_size); |
1538 | |
1539 | return buffer_p + string_size; |
1540 | } |
1541 | |
1542 | /* Move the pointer behind the buffer to prevent further writes. */ |
1543 | return buffer_end_p + 1; |
1544 | } /* jerry_append_chars_to_buffer */ |
1545 | |
1546 | /** |
1547 | * Append an ecma-string to the specified buffer. |
1548 | * |
1549 | * @return the position of the buffer pointer after copy. |
1550 | */ |
1551 | static uint8_t * |
1552 | jerry_append_ecma_string_to_buffer (uint8_t *buffer_p, /**< buffer */ |
1553 | uint8_t *buffer_end_p, /**< the end of the buffer */ |
1554 | ecma_string_t *string_p) /**< ecma-string */ |
1555 | { |
1556 | ECMA_STRING_TO_UTF8_STRING (string_p, str_buffer_p, str_buffer_size); |
1557 | |
1558 | /* Append the string to the buffer. */ |
1559 | uint8_t *new_buffer_p = jerry_append_chars_to_buffer (buffer_p, |
1560 | buffer_end_p, |
1561 | (const char *) str_buffer_p, |
1562 | str_buffer_size); |
1563 | |
1564 | ECMA_FINALIZE_UTF8_STRING (str_buffer_p, str_buffer_size); |
1565 | |
1566 | return new_buffer_p; |
1567 | } /* jerry_append_ecma_string_to_buffer */ |
1568 | |
1569 | /** |
1570 | * Append an unsigned number to the specified buffer. |
1571 | * |
1572 | * @return the position of the buffer pointer after copy. |
1573 | */ |
1574 | static uint8_t * |
1575 | jerry_append_number_to_buffer (uint8_t *buffer_p, /**< buffer */ |
1576 | uint8_t *buffer_end_p, /**< the end of the buffer */ |
1577 | lit_utf8_size_t number) /**< number */ |
1578 | { |
1579 | lit_utf8_byte_t uint32_to_str_buffer[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; |
1580 | lit_utf8_size_t utf8_str_size = ecma_uint32_to_utf8_string (number, |
1581 | uint32_to_str_buffer, |
1582 | ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); |
1583 | |
1584 | JERRY_ASSERT (utf8_str_size <= ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); |
1585 | |
1586 | return jerry_append_chars_to_buffer (buffer_p, |
1587 | buffer_end_p, |
1588 | (const char *) uint32_to_str_buffer, |
1589 | utf8_str_size); |
1590 | } /* jerry_append_number_to_buffer */ |
1591 | |
1592 | #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */ |
1593 | |
1594 | /** |
1595 | * Get the literals from a snapshot. Copies certain string literals into the given |
1596 | * buffer in a specified format. |
1597 | * |
1598 | * Note: |
1599 | * Only valid identifiers are saved in C format. |
1600 | * |
1601 | * @return size of the literal-list in bytes, at most equal to the buffer size, |
1602 | * if the list of the literals isn't empty, |
1603 | * 0 - otherwise. |
1604 | */ |
1605 | size_t |
1606 | jerry_get_literals_from_snapshot (const uint32_t *snapshot_p, /**< input snapshot buffer */ |
1607 | size_t snapshot_size, /**< size of the input snapshot buffer */ |
1608 | jerry_char_t *lit_buf_p, /**< [out] buffer to save literals to */ |
1609 | size_t lit_buf_size, /**< the buffer's size */ |
1610 | bool is_c_format) /**< format-flag */ |
1611 | { |
1612 | #if ENABLED (JERRY_SNAPSHOT_SAVE) |
1613 | const uint8_t *snapshot_data_p = (uint8_t *) snapshot_p; |
1614 | const jerry_snapshot_header_t *header_p = (const jerry_snapshot_header_t *) snapshot_data_p; |
1615 | |
1616 | if (snapshot_size <= sizeof (jerry_snapshot_header_t) |
1617 | || header_p->magic != JERRY_SNAPSHOT_MAGIC |
1618 | || header_p->version != JERRY_SNAPSHOT_VERSION |
1619 | || !snapshot_check_global_flags (header_p->global_flags)) |
1620 | { |
1621 | /* Invalid snapshot format */ |
1622 | return 0; |
1623 | } |
1624 | |
1625 | JERRY_ASSERT ((header_p->lit_table_offset % sizeof (uint32_t)) == 0); |
1626 | const uint8_t *literal_base_p = snapshot_data_p + header_p->lit_table_offset; |
1627 | |
1628 | ecma_collection_t *lit_pool_p = ecma_new_collection (); |
1629 | scan_snapshot_functions (snapshot_data_p + header_p->func_offsets[0], |
1630 | literal_base_p, |
1631 | lit_pool_p, |
1632 | literal_base_p); |
1633 | |
1634 | lit_utf8_size_t literal_count = 0; |
1635 | ecma_value_t *buffer_p = lit_pool_p->buffer_p; |
1636 | |
1637 | /* Count the valid and non-magic identifiers in the list. */ |
1638 | for (uint32_t i = 0; i < lit_pool_p->item_count; i++) |
1639 | { |
1640 | if (ecma_is_value_string (buffer_p[i])) |
1641 | { |
1642 | ecma_string_t *literal_p = ecma_get_string_from_value (buffer_p[i]); |
1643 | |
1644 | if (ecma_get_string_magic (literal_p) == LIT_MAGIC_STRING__COUNT) |
1645 | { |
1646 | literal_count++; |
1647 | } |
1648 | } |
1649 | } |
1650 | |
1651 | if (literal_count == 0) |
1652 | { |
1653 | ecma_collection_destroy (lit_pool_p); |
1654 | return 0; |
1655 | } |
1656 | |
1657 | jerry_char_t *const buffer_start_p = lit_buf_p; |
1658 | jerry_char_t *const buffer_end_p = lit_buf_p + lit_buf_size; |
1659 | |
1660 | JMEM_DEFINE_LOCAL_ARRAY (literal_array, literal_count, ecma_string_t *); |
1661 | lit_utf8_size_t literal_idx = 0; |
1662 | |
1663 | buffer_p = lit_pool_p->buffer_p; |
1664 | |
1665 | /* Count the valid and non-magic identifiers in the list. */ |
1666 | for (uint32_t i = 0; i < lit_pool_p->item_count; i++) |
1667 | { |
1668 | if (ecma_is_value_string (buffer_p[i])) |
1669 | { |
1670 | ecma_string_t *literal_p = ecma_get_string_from_value (buffer_p[i]); |
1671 | |
1672 | if (ecma_get_string_magic (literal_p) == LIT_MAGIC_STRING__COUNT) |
1673 | { |
1674 | literal_array[literal_idx++] = literal_p; |
1675 | } |
1676 | } |
1677 | } |
1678 | |
1679 | ecma_collection_destroy (lit_pool_p); |
1680 | |
1681 | /* Sort the strings by size at first, then lexicographically. */ |
1682 | jerry_save_literals_sort (literal_array, literal_count); |
1683 | |
1684 | if (is_c_format) |
1685 | { |
1686 | /* Save literal count. */ |
1687 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, |
1688 | buffer_end_p, |
1689 | "jerry_length_t literal_count = " , |
1690 | 0); |
1691 | |
1692 | lit_buf_p = jerry_append_number_to_buffer (lit_buf_p, buffer_end_p, literal_count); |
1693 | |
1694 | /* Save the array of literals. */ |
1695 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, |
1696 | buffer_end_p, |
1697 | ";\n\njerry_char_t *literals[" , |
1698 | 0); |
1699 | |
1700 | lit_buf_p = jerry_append_number_to_buffer (lit_buf_p, buffer_end_p, literal_count); |
1701 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "] =\n{\n" , 0); |
1702 | |
1703 | for (lit_utf8_size_t i = 0; i < literal_count; i++) |
1704 | { |
1705 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, " \"" , 0); |
1706 | ECMA_STRING_TO_UTF8_STRING (literal_array[i], str_buffer_p, str_buffer_size); |
1707 | for (lit_utf8_size_t j = 0; j < str_buffer_size; j++) |
1708 | { |
1709 | uint8_t byte = str_buffer_p[j]; |
1710 | if (byte < 32 || byte > 127) |
1711 | { |
1712 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "\\x" , 0); |
1713 | ecma_char_t hex_digit = (ecma_char_t) (byte >> 4); |
1714 | *lit_buf_p++ = (lit_utf8_byte_t) ((hex_digit > 9) ? (hex_digit + ('A' - 10)) : (hex_digit + '0')); |
1715 | hex_digit = (lit_utf8_byte_t) (byte & 0xf); |
1716 | *lit_buf_p++ = (lit_utf8_byte_t) ((hex_digit > 9) ? (hex_digit + ('A' - 10)) : (hex_digit + '0')); |
1717 | } |
1718 | else |
1719 | { |
1720 | if (byte == '\\' || byte == '"') |
1721 | { |
1722 | *lit_buf_p++ = '\\'; |
1723 | } |
1724 | *lit_buf_p++ = byte; |
1725 | } |
1726 | } |
1727 | |
1728 | ECMA_FINALIZE_UTF8_STRING (str_buffer_p, str_buffer_size); |
1729 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "\"" , 0); |
1730 | |
1731 | if (i < literal_count - 1) |
1732 | { |
1733 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "," , 0); |
1734 | } |
1735 | |
1736 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "\n" , 0); |
1737 | } |
1738 | |
1739 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, |
1740 | buffer_end_p, |
1741 | "};\n\njerry_length_t literal_sizes[" , |
1742 | 0); |
1743 | |
1744 | lit_buf_p = jerry_append_number_to_buffer (lit_buf_p, buffer_end_p, literal_count); |
1745 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "] =\n{\n" , 0); |
1746 | } |
1747 | |
1748 | /* Save the literal sizes respectively. */ |
1749 | for (lit_utf8_size_t i = 0; i < literal_count; i++) |
1750 | { |
1751 | lit_utf8_size_t str_size = ecma_string_get_size (literal_array[i]); |
1752 | |
1753 | if (is_c_format) |
1754 | { |
1755 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, " " , 0); |
1756 | } |
1757 | |
1758 | lit_buf_p = jerry_append_number_to_buffer (lit_buf_p, buffer_end_p, str_size); |
1759 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, " " , 0); |
1760 | |
1761 | if (is_c_format) |
1762 | { |
1763 | /* Show the given string as a comment. */ |
1764 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "/* " , 0); |
1765 | lit_buf_p = jerry_append_ecma_string_to_buffer (lit_buf_p, buffer_end_p, literal_array[i]); |
1766 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, " */" , 0); |
1767 | |
1768 | if (i < literal_count - 1) |
1769 | { |
1770 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "," , 0); |
1771 | } |
1772 | } |
1773 | else |
1774 | { |
1775 | lit_buf_p = jerry_append_ecma_string_to_buffer (lit_buf_p, buffer_end_p, literal_array[i]); |
1776 | } |
1777 | |
1778 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "\n" , 0); |
1779 | } |
1780 | |
1781 | if (is_c_format) |
1782 | { |
1783 | lit_buf_p = jerry_append_chars_to_buffer (lit_buf_p, buffer_end_p, "};\n" , 0); |
1784 | } |
1785 | |
1786 | JMEM_FINALIZE_LOCAL_ARRAY (literal_array); |
1787 | |
1788 | return lit_buf_p <= buffer_end_p ? (size_t) (lit_buf_p - buffer_start_p) : 0; |
1789 | #else /* !ENABLED (JERRY_SNAPSHOT_SAVE) */ |
1790 | JERRY_UNUSED (snapshot_p); |
1791 | JERRY_UNUSED (snapshot_size); |
1792 | JERRY_UNUSED (lit_buf_p); |
1793 | JERRY_UNUSED (lit_buf_size); |
1794 | JERRY_UNUSED (is_c_format); |
1795 | |
1796 | return 0; |
1797 | #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */ |
1798 | } /* jerry_get_literals_from_snapshot */ |
1799 | |
1800 | /** |
1801 | * Generate snapshot function from specified source and arguments |
1802 | * |
1803 | * @return size of snapshot (a number value), if it was generated succesfully |
1804 | * (i.e. there are no syntax errors in source code, buffer size is sufficient, |
1805 | * and snapshot support is enabled in current configuration through JERRY_SNAPSHOT_SAVE), |
1806 | * error object otherwise |
1807 | */ |
1808 | jerry_value_t |
1809 | jerry_generate_function_snapshot (const jerry_char_t *resource_name_p, /**< script resource name */ |
1810 | size_t resource_name_length, /**< script resource name length */ |
1811 | const jerry_char_t *source_p, /**< script source */ |
1812 | size_t source_size, /**< script source size */ |
1813 | const jerry_char_t *args_p, /**< arguments string */ |
1814 | size_t args_size, /**< arguments string size */ |
1815 | uint32_t generate_snapshot_opts, /**< jerry_generate_snapshot_opts_t option bits */ |
1816 | uint32_t *buffer_p, /**< buffer to save snapshot to */ |
1817 | size_t buffer_size) /**< the buffer's size */ |
1818 | { |
1819 | #if ENABLED (JERRY_SNAPSHOT_SAVE) |
1820 | uint32_t allowed_opts = (JERRY_SNAPSHOT_SAVE_STATIC | JERRY_SNAPSHOT_SAVE_STRICT); |
1821 | |
1822 | if ((generate_snapshot_opts & ~(allowed_opts)) != 0) |
1823 | { |
1824 | const char * const error_message_p = "Unsupported generate snapshot flags specified." ; |
1825 | return jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) error_message_p); |
1826 | } |
1827 | |
1828 | return jerry_generate_snapshot_with_args (resource_name_p, |
1829 | resource_name_length, |
1830 | source_p, |
1831 | source_size, |
1832 | args_p, |
1833 | args_size, |
1834 | generate_snapshot_opts, |
1835 | buffer_p, |
1836 | buffer_size); |
1837 | #else /* !ENABLED (JERRY_SNAPSHOT_SAVE) */ |
1838 | JERRY_UNUSED (resource_name_p); |
1839 | JERRY_UNUSED (resource_name_length); |
1840 | JERRY_UNUSED (source_p); |
1841 | JERRY_UNUSED (source_size); |
1842 | JERRY_UNUSED (args_p); |
1843 | JERRY_UNUSED (args_size); |
1844 | JERRY_UNUSED (generate_snapshot_opts); |
1845 | JERRY_UNUSED (buffer_p); |
1846 | JERRY_UNUSED (buffer_size); |
1847 | |
1848 | return jerry_create_error (JERRY_ERROR_COMMON, (const jerry_char_t *) "Snapshot save is not supported." ); |
1849 | #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */ |
1850 | } /* jerry_generate_function_snapshot */ |
1851 | |
1852 | /** |
1853 | * Load function from specified snapshot buffer |
1854 | * |
1855 | * Note: |
1856 | * returned value must be freed with jerry_release_value, when it is no longer needed. |
1857 | * |
1858 | * @return result of bytecode - if run was successful |
1859 | * thrown error - otherwise |
1860 | */ |
1861 | jerry_value_t |
1862 | jerry_load_function_snapshot (const uint32_t *function_snapshot_p, /**< snapshot of the function(s) */ |
1863 | const size_t function_snapshot_size, /**< size of the snapshot */ |
1864 | size_t func_index, /**< index of the function to load */ |
1865 | uint32_t exec_snapshot_opts) /**< jerry_exec_snapshot_opts_t option bits */ |
1866 | { |
1867 | #if ENABLED (JERRY_SNAPSHOT_EXEC) |
1868 | return jerry_snapshot_result (function_snapshot_p, function_snapshot_size, func_index, exec_snapshot_opts, true); |
1869 | #else /* !ENABLED (JERRY_SNAPSHOT_EXEC) */ |
1870 | JERRY_UNUSED (function_snapshot_p); |
1871 | JERRY_UNUSED (function_snapshot_size); |
1872 | JERRY_UNUSED (func_index); |
1873 | JERRY_UNUSED (exec_snapshot_opts); |
1874 | |
1875 | return jerry_create_error (JERRY_ERROR_COMMON, (const jerry_char_t *) "Snapshot execution is not supported." ); |
1876 | #endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ |
1877 | } /* jerry_load_function_snapshot */ |
1878 | |