mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Moved all public API headers under the `jerry-core/include` directory. This makes installing all the public headers easier. Also, should we have new public headers in the future, their installation will be automatic, there will be no need to update the build files. Moreover, this aligns better with the structure of other libraries in the project (in those cases, public headers always reside in `<library>/include`). Moved all public API implementations under the `jerry-core/api` directory. This cleans up the root directory of `jerry-core`, moving all implementation code under "modules", i.e., subdirectories. This also makes the future splitting of the big and monolithic `jerry.c` along features easier, if needed. (Debugger and snapshot-related functions are already in separate sources.) Notes: * `jerryscript.h` is split up to separate header files along feature boundaries. These new headers are included by `jerryscript.h`, so this is not a breaking change but header modularization only. * `jerry-snapshot.h` is still under `jerry-core/api`, keeping it as a non-public header. * This commit also adapts all targets to the include path change. JerryScript-DCO-1.0-Signed-off-by: Akos Kiss akiss@inf.u-szeged.hu
1067 lines
36 KiB
C
1067 lines
36 KiB
C
/* Copyright JS Foundation and other contributors, http://js.foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "ecma-exceptions.h"
|
|
#include "ecma-function-object.h"
|
|
#include "ecma-helpers.h"
|
|
#include "ecma-literal-storage.h"
|
|
#include "jcontext.h"
|
|
#include "jerryscript.h"
|
|
#include "jerry-snapshot.h"
|
|
#include "js-parser.h"
|
|
#include "lit-char-helpers.h"
|
|
#include "re-compiler.h"
|
|
|
|
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
|
|
|
|
/**
|
|
* Variables required to take a snapshot.
|
|
*/
|
|
typedef struct
|
|
{
|
|
bool snapshot_error_occured;
|
|
size_t snapshot_buffer_write_offset;
|
|
} snapshot_globals_t;
|
|
|
|
/** \addtogroup jerrysnapshot Jerry snapshot operations
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Write data into the specified buffer.
|
|
*
|
|
* Note:
|
|
* Offset is in-out and is incremented if the write operation completes successfully.
|
|
*
|
|
* @return true - if write was successful, i.e. offset + data_size doesn't exceed buffer size,
|
|
* false - otherwise
|
|
*/
|
|
static inline bool __attr_always_inline___
|
|
snapshot_write_to_buffer_by_offset (uint8_t *buffer_p, /**< buffer */
|
|
size_t buffer_size, /**< size of buffer */
|
|
size_t *in_out_buffer_offset_p, /**< [in,out] offset to write to
|
|
* incremented with data_size */
|
|
const void *data_p, /**< data */
|
|
size_t data_size) /**< size of the writable data */
|
|
{
|
|
if (*in_out_buffer_offset_p + data_size > buffer_size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memcpy (buffer_p + *in_out_buffer_offset_p, data_p, data_size);
|
|
*in_out_buffer_offset_p += data_size;
|
|
|
|
return true;
|
|
} /* snapshot_write_to_buffer_by_offset */
|
|
|
|
/**
|
|
* Snapshot callback for byte codes.
|
|
*
|
|
* @return start offset
|
|
*/
|
|
static uint16_t
|
|
snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< compiled code */
|
|
uint8_t *snapshot_buffer_p, /**< snapshot buffer */
|
|
size_t snapshot_buffer_size, /**< snapshot buffer size */
|
|
snapshot_globals_t *globals_p) /**< snapshot globals */
|
|
{
|
|
if (globals_p->snapshot_error_occured)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
JERRY_ASSERT ((globals_p->snapshot_buffer_write_offset & (JMEM_ALIGNMENT - 1)) == 0);
|
|
|
|
if ((globals_p->snapshot_buffer_write_offset >> JMEM_ALIGNMENT_LOG) > 0xffffu)
|
|
{
|
|
globals_p->snapshot_error_occured = true;
|
|
return 0;
|
|
}
|
|
|
|
uint16_t start_offset = (uint16_t) (globals_p->snapshot_buffer_write_offset >> JMEM_ALIGNMENT_LOG);
|
|
|
|
uint8_t *copied_code_start_p = snapshot_buffer_p + globals_p->snapshot_buffer_write_offset;
|
|
ecma_compiled_code_t *copied_code_p = (ecma_compiled_code_t *) copied_code_start_p;
|
|
|
|
if (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_FUNCTION))
|
|
{
|
|
#ifndef CONFIG_DISABLE_REGEXP_BUILTIN
|
|
/* Regular expression. */
|
|
if (globals_p->snapshot_buffer_write_offset + sizeof (ecma_compiled_code_t) > snapshot_buffer_size)
|
|
{
|
|
globals_p->snapshot_error_occured = true;
|
|
return 0;
|
|
}
|
|
|
|
globals_p->snapshot_buffer_write_offset += sizeof (ecma_compiled_code_t);
|
|
|
|
jmem_cpointer_t pattern_cp = ((re_compiled_code_t *) compiled_code_p)->pattern_cp;
|
|
ecma_string_t *pattern_string_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t,
|
|
pattern_cp);
|
|
|
|
ecma_length_t pattern_size = 0;
|
|
|
|
ECMA_STRING_TO_UTF8_STRING (pattern_string_p, buffer_p, buffer_size);
|
|
|
|
pattern_size = buffer_size;
|
|
|
|
if (!snapshot_write_to_buffer_by_offset (snapshot_buffer_p,
|
|
snapshot_buffer_size,
|
|
&globals_p->snapshot_buffer_write_offset,
|
|
buffer_p,
|
|
buffer_size))
|
|
{
|
|
globals_p->snapshot_error_occured = true;
|
|
}
|
|
|
|
ECMA_FINALIZE_UTF8_STRING (buffer_p, buffer_size);
|
|
|
|
globals_p->snapshot_buffer_write_offset = JERRY_ALIGNUP (globals_p->snapshot_buffer_write_offset,
|
|
JMEM_ALIGNMENT);
|
|
|
|
/* Regexp character size is stored in refs. */
|
|
copied_code_p->refs = (uint16_t) pattern_size;
|
|
|
|
pattern_size += (ecma_length_t) sizeof (ecma_compiled_code_t);
|
|
copied_code_p->size = (uint16_t) ((pattern_size + JMEM_ALIGNMENT - 1) >> JMEM_ALIGNMENT_LOG);
|
|
|
|
copied_code_p->status_flags = compiled_code_p->status_flags;
|
|
|
|
#else /* CONFIG_DISABLE_REGEXP_BUILTIN */
|
|
JERRY_UNREACHABLE (); /* RegExp is not supported in the selected profile. */
|
|
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
|
|
return start_offset;
|
|
}
|
|
|
|
if (!snapshot_write_to_buffer_by_offset (snapshot_buffer_p,
|
|
snapshot_buffer_size,
|
|
&globals_p->snapshot_buffer_write_offset,
|
|
compiled_code_p,
|
|
((size_t) compiled_code_p->size) << JMEM_ALIGNMENT_LOG))
|
|
{
|
|
globals_p->snapshot_error_occured = true;
|
|
return 0;
|
|
}
|
|
|
|
/* Sub-functions and regular expressions are stored recursively. */
|
|
uint8_t *src_buffer_p = (uint8_t *) compiled_code_p;
|
|
uint8_t *dst_buffer_p = (uint8_t *) copied_code_p;
|
|
jmem_cpointer_t *src_literal_start_p;
|
|
jmem_cpointer_t *dst_literal_start_p;
|
|
uint32_t const_literal_end;
|
|
uint32_t literal_end;
|
|
|
|
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
|
|
{
|
|
src_literal_start_p = (jmem_cpointer_t *) (src_buffer_p + sizeof (cbc_uint16_arguments_t));
|
|
dst_literal_start_p = (jmem_cpointer_t *) (dst_buffer_p + sizeof (cbc_uint16_arguments_t));
|
|
|
|
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) src_buffer_p;
|
|
literal_end = args_p->literal_end;
|
|
const_literal_end = args_p->const_literal_end;
|
|
}
|
|
else
|
|
{
|
|
src_literal_start_p = (jmem_cpointer_t *) (src_buffer_p + sizeof (cbc_uint8_arguments_t));
|
|
dst_literal_start_p = (jmem_cpointer_t *) (dst_buffer_p + sizeof (cbc_uint8_arguments_t));
|
|
|
|
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) src_buffer_p;
|
|
literal_end = args_p->literal_end;
|
|
const_literal_end = args_p->const_literal_end;
|
|
}
|
|
|
|
for (uint32_t i = const_literal_end; i < literal_end; i++)
|
|
{
|
|
ecma_compiled_code_t *bytecode_p = ECMA_GET_NON_NULL_POINTER (ecma_compiled_code_t,
|
|
src_literal_start_p[i]);
|
|
|
|
if (bytecode_p == compiled_code_p)
|
|
{
|
|
dst_literal_start_p[i] = start_offset;
|
|
}
|
|
else
|
|
{
|
|
dst_literal_start_p[i] = snapshot_add_compiled_code (bytecode_p,
|
|
snapshot_buffer_p,
|
|
snapshot_buffer_size,
|
|
globals_p);
|
|
}
|
|
}
|
|
|
|
return start_offset;
|
|
} /* snapshot_add_compiled_code */
|
|
|
|
/**
|
|
* Set the uint16_t offsets in the code area.
|
|
*/
|
|
static void
|
|
jerry_snapshot_set_offsets (uint32_t *buffer_p, /**< buffer */
|
|
uint32_t size, /**< buffer size */
|
|
lit_mem_to_snapshot_id_map_entry_t *lit_map_p) /**< literal map */
|
|
{
|
|
JERRY_ASSERT (size > 0);
|
|
|
|
do
|
|
{
|
|
ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) buffer_p;
|
|
uint32_t code_size = ((uint32_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG;
|
|
|
|
if (bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION)
|
|
{
|
|
jmem_cpointer_t *literal_start_p;
|
|
uint32_t argument_end;
|
|
uint32_t register_end;
|
|
uint32_t const_literal_end;
|
|
|
|
if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
|
|
{
|
|
literal_start_p = (jmem_cpointer_t *) (((uint8_t *) buffer_p) + sizeof (cbc_uint16_arguments_t));
|
|
|
|
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p;
|
|
argument_end = args_p->argument_end;
|
|
register_end = args_p->register_end;
|
|
const_literal_end = args_p->const_literal_end;
|
|
}
|
|
else
|
|
{
|
|
literal_start_p = (jmem_cpointer_t *) (((uint8_t *) buffer_p) + sizeof (cbc_uint8_arguments_t));
|
|
|
|
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p;
|
|
argument_end = args_p->argument_end;
|
|
register_end = args_p->register_end;
|
|
const_literal_end = args_p->const_literal_end;
|
|
}
|
|
|
|
uint32_t register_clear_start = 0;
|
|
|
|
if ((bytecode_p->status_flags & CBC_CODE_FLAGS_ARGUMENTS_NEEDED)
|
|
&& !(bytecode_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE))
|
|
{
|
|
for (uint32_t i = 0; i < argument_end; i++)
|
|
{
|
|
lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p;
|
|
|
|
if (literal_start_p[i] != JMEM_CP_NULL)
|
|
{
|
|
while (current_p->literal_id != literal_start_p[i])
|
|
{
|
|
current_p++;
|
|
}
|
|
|
|
literal_start_p[i] = current_p->literal_offset;
|
|
}
|
|
}
|
|
|
|
register_clear_start = argument_end;
|
|
}
|
|
|
|
for (uint32_t i = register_clear_start; i < register_end; i++)
|
|
{
|
|
literal_start_p[i] = JMEM_CP_NULL;
|
|
}
|
|
|
|
for (uint32_t i = register_end; i < const_literal_end; i++)
|
|
{
|
|
lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p;
|
|
|
|
if (literal_start_p[i] != JMEM_CP_NULL)
|
|
{
|
|
while (current_p->literal_id != literal_start_p[i])
|
|
{
|
|
current_p++;
|
|
}
|
|
|
|
literal_start_p[i] = current_p->literal_offset;
|
|
}
|
|
}
|
|
|
|
/* Set reference counter to 1. */
|
|
bytecode_p->refs = 1;
|
|
}
|
|
|
|
JERRY_ASSERT ((code_size % sizeof (uint32_t)) == 0);
|
|
buffer_p += code_size / sizeof (uint32_t);
|
|
size -= code_size;
|
|
}
|
|
while (size > 0);
|
|
} /* jerry_snapshot_set_offsets */
|
|
|
|
#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */
|
|
|
|
#ifdef JERRY_ENABLE_SNAPSHOT_EXEC
|
|
|
|
/**
|
|
* Byte code blocks shorter than this threshold are always copied into the memory.
|
|
* The memory / performance trade-of of byte code redirection does not worth
|
|
* in such cases.
|
|
*/
|
|
#define BYTECODE_NO_COPY_THRESHOLD 8
|
|
|
|
/**
|
|
* Load byte code from snapshot.
|
|
*
|
|
* @return byte code
|
|
*/
|
|
static ecma_compiled_code_t *
|
|
snapshot_load_compiled_code (const uint8_t *snapshot_data_p, /**< snapshot data */
|
|
size_t offset, /**< byte code offset */
|
|
lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< literal map */
|
|
bool copy_bytecode) /**< byte code should be copied to memory */
|
|
{
|
|
ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) (snapshot_data_p + offset);
|
|
uint32_t code_size = ((uint32_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG;
|
|
|
|
if (!(bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION))
|
|
{
|
|
#ifndef CONFIG_DISABLE_REGEXP_BUILTIN
|
|
const re_compiled_code_t *re_bytecode_p = NULL;
|
|
|
|
const uint8_t *regex_start_p = ((const uint8_t *) bytecode_p) + sizeof (ecma_compiled_code_t);
|
|
|
|
/* Real size is stored in refs. */
|
|
ecma_string_t *pattern_str_p = ecma_new_ecma_string_from_utf8 (regex_start_p,
|
|
bytecode_p->refs);
|
|
|
|
re_compile_bytecode (&re_bytecode_p,
|
|
pattern_str_p,
|
|
bytecode_p->status_flags);
|
|
|
|
ecma_deref_ecma_string (pattern_str_p);
|
|
|
|
return (ecma_compiled_code_t *) re_bytecode_p;
|
|
#else /* CONFIG_DISABLE_REGEXP_BUILTIN */
|
|
JERRY_UNREACHABLE (); /* RegExp is not supported in the selected profile. */
|
|
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
|
|
}
|
|
|
|
size_t header_size;
|
|
uint32_t literal_end;
|
|
uint32_t const_literal_end;
|
|
|
|
if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
|
|
{
|
|
uint8_t *byte_p = (uint8_t *) bytecode_p;
|
|
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) byte_p;
|
|
literal_end = args_p->literal_end;
|
|
const_literal_end = args_p->const_literal_end;
|
|
header_size = sizeof (cbc_uint16_arguments_t);
|
|
}
|
|
else
|
|
{
|
|
uint8_t *byte_p = (uint8_t *) bytecode_p;
|
|
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) byte_p;
|
|
literal_end = args_p->literal_end;
|
|
const_literal_end = args_p->const_literal_end;
|
|
header_size = sizeof (cbc_uint8_arguments_t);
|
|
}
|
|
|
|
if (copy_bytecode
|
|
|| (header_size + (literal_end * sizeof (uint16_t)) + BYTECODE_NO_COPY_THRESHOLD > code_size))
|
|
{
|
|
bytecode_p = (ecma_compiled_code_t *) jmem_heap_alloc_block (code_size);
|
|
|
|
memcpy (bytecode_p, snapshot_data_p + offset, code_size);
|
|
}
|
|
else
|
|
{
|
|
code_size = (uint32_t) (header_size + literal_end * sizeof (jmem_cpointer_t));
|
|
|
|
uint8_t *real_bytecode_p = ((uint8_t *) bytecode_p) + code_size;
|
|
uint32_t total_size = JERRY_ALIGNUP (code_size + 1 + sizeof (uint8_t *), JMEM_ALIGNMENT);
|
|
|
|
bytecode_p = (ecma_compiled_code_t *) jmem_heap_alloc_block (total_size);
|
|
|
|
memcpy (bytecode_p, snapshot_data_p + offset, code_size);
|
|
|
|
bytecode_p->size = (uint16_t) (total_size >> JMEM_ALIGNMENT_LOG);
|
|
|
|
uint8_t *instructions_p = ((uint8_t *) bytecode_p);
|
|
|
|
instructions_p[code_size] = CBC_SET_BYTECODE_PTR;
|
|
memcpy (instructions_p + code_size + 1, &real_bytecode_p, sizeof (uint8_t *));
|
|
}
|
|
|
|
JERRY_ASSERT (bytecode_p->refs == 1);
|
|
|
|
#ifdef JERRY_DEBUGGER
|
|
bytecode_p->status_flags = (uint16_t) (bytecode_p->status_flags | CBC_CODE_FLAGS_DEBUGGER_IGNORE);
|
|
#endif /* JERRY_DEBUGGER */
|
|
|
|
jmem_cpointer_t *literal_start_p = (jmem_cpointer_t *) (((uint8_t *) bytecode_p) + header_size);
|
|
|
|
for (uint32_t i = 0; i < const_literal_end; i++)
|
|
{
|
|
lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p;
|
|
|
|
if (literal_start_p[i] != 0)
|
|
{
|
|
while (current_p->literal_offset != literal_start_p[i])
|
|
{
|
|
current_p++;
|
|
}
|
|
|
|
literal_start_p[i] = current_p->literal_id;
|
|
}
|
|
}
|
|
|
|
for (uint32_t i = const_literal_end; i < literal_end; i++)
|
|
{
|
|
size_t literal_offset = ((size_t) literal_start_p[i]) << JMEM_ALIGNMENT_LOG;
|
|
|
|
if (literal_offset == offset)
|
|
{
|
|
/* Self reference */
|
|
ECMA_SET_NON_NULL_POINTER (literal_start_p[i],
|
|
bytecode_p);
|
|
}
|
|
else
|
|
{
|
|
ecma_compiled_code_t *literal_bytecode_p;
|
|
literal_bytecode_p = snapshot_load_compiled_code (snapshot_data_p,
|
|
literal_offset,
|
|
lit_map_p,
|
|
copy_bytecode);
|
|
|
|
ECMA_SET_NON_NULL_POINTER (literal_start_p[i],
|
|
literal_bytecode_p);
|
|
}
|
|
}
|
|
|
|
return bytecode_p;
|
|
} /* snapshot_load_compiled_code */
|
|
|
|
#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */
|
|
|
|
/**
|
|
* Generate snapshot from specified source
|
|
*
|
|
* @return size of snapshot, if it was generated succesfully
|
|
* (i.e. there are no syntax errors in source code, buffer size is sufficient,
|
|
* and snapshot support is enabled in current configuration through JERRY_ENABLE_SNAPSHOT_SAVE),
|
|
* 0 - otherwise.
|
|
*/
|
|
size_t
|
|
jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source */
|
|
size_t source_size, /**< script source size */
|
|
bool is_for_global, /**< snapshot would be executed as global (true)
|
|
* or eval (false) */
|
|
bool is_strict, /**< strict mode */
|
|
uint32_t *buffer_p, /**< buffer to save snapshot to */
|
|
size_t buffer_size) /**< the buffer's size */
|
|
{
|
|
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
|
|
snapshot_globals_t globals;
|
|
ecma_value_t parse_status;
|
|
ecma_compiled_code_t *bytecode_data_p;
|
|
|
|
globals.snapshot_buffer_write_offset = JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t),
|
|
JMEM_ALIGNMENT);
|
|
globals.snapshot_error_occured = false;
|
|
|
|
parse_status = parser_parse_script (source_p,
|
|
source_size,
|
|
is_strict,
|
|
&bytecode_data_p);
|
|
|
|
if (ECMA_IS_VALUE_ERROR (parse_status))
|
|
{
|
|
ecma_free_value (parse_status);
|
|
return 0;
|
|
}
|
|
|
|
snapshot_add_compiled_code (bytecode_data_p, (uint8_t *) buffer_p, buffer_size, &globals);
|
|
|
|
if (globals.snapshot_error_occured)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
jerry_snapshot_header_t header;
|
|
header.version = JERRY_SNAPSHOT_VERSION;
|
|
header.lit_table_offset = (uint32_t) globals.snapshot_buffer_write_offset;
|
|
header.is_run_global = is_for_global;
|
|
|
|
lit_mem_to_snapshot_id_map_entry_t *lit_map_p = NULL;
|
|
uint32_t literals_num;
|
|
|
|
if (!ecma_save_literals_for_snapshot (buffer_p,
|
|
buffer_size,
|
|
&globals.snapshot_buffer_write_offset,
|
|
&lit_map_p,
|
|
&literals_num,
|
|
&header.lit_table_size))
|
|
{
|
|
JERRY_ASSERT (lit_map_p == NULL);
|
|
return 0;
|
|
}
|
|
|
|
jerry_snapshot_set_offsets (buffer_p + (JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t),
|
|
JMEM_ALIGNMENT) / sizeof (uint32_t)),
|
|
(uint32_t) (header.lit_table_offset - sizeof (jerry_snapshot_header_t)),
|
|
lit_map_p);
|
|
|
|
size_t header_offset = 0;
|
|
|
|
snapshot_write_to_buffer_by_offset ((uint8_t *) buffer_p,
|
|
buffer_size,
|
|
&header_offset,
|
|
&header,
|
|
sizeof (header));
|
|
|
|
if (lit_map_p != NULL)
|
|
{
|
|
jmem_heap_free_block (lit_map_p, literals_num * sizeof (lit_mem_to_snapshot_id_map_entry_t));
|
|
}
|
|
|
|
ecma_bytecode_deref (bytecode_data_p);
|
|
|
|
return globals.snapshot_buffer_write_offset;
|
|
#else /* !JERRY_ENABLE_SNAPSHOT_SAVE */
|
|
JERRY_UNUSED (source_p);
|
|
JERRY_UNUSED (source_size);
|
|
JERRY_UNUSED (is_for_global);
|
|
JERRY_UNUSED (is_strict);
|
|
JERRY_UNUSED (buffer_p);
|
|
JERRY_UNUSED (buffer_size);
|
|
|
|
return 0;
|
|
#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */
|
|
} /* jerry_parse_and_save_snapshot */
|
|
|
|
/**
|
|
* Execute snapshot from specified buffer
|
|
*
|
|
* Note:
|
|
* returned value must be freed with jerry_release_value, when it is no longer needed.
|
|
*
|
|
* @return result of bytecode - if run was successful
|
|
* thrown error - otherwise
|
|
*/
|
|
jerry_value_t
|
|
jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */
|
|
size_t snapshot_size, /**< size of snapshot */
|
|
bool copy_bytecode) /**< flag, indicating whether the passed snapshot
|
|
* buffer should be copied to the engine's memory.
|
|
* If set the engine should not reference the buffer
|
|
* after the function returns (in this case, the passed
|
|
* buffer could be freed after the call).
|
|
* Otherwise (if the flag is not set) - the buffer could only be
|
|
* freed after the engine stops (i.e. after call to jerry_cleanup). */
|
|
{
|
|
#ifdef JERRY_ENABLE_SNAPSHOT_EXEC
|
|
JERRY_ASSERT (snapshot_p != NULL);
|
|
|
|
static const char * const invalid_version_error_p = "Invalid snapshot version";
|
|
static const char * const invalid_format_error_p = "Invalid snapshot format";
|
|
const uint8_t *snapshot_data_p = (uint8_t *) snapshot_p;
|
|
|
|
if (snapshot_size <= sizeof (jerry_snapshot_header_t))
|
|
{
|
|
return ecma_raise_type_error (invalid_format_error_p);
|
|
}
|
|
|
|
const jerry_snapshot_header_t *header_p = (const jerry_snapshot_header_t *) snapshot_data_p;
|
|
|
|
if (header_p->version != JERRY_SNAPSHOT_VERSION)
|
|
{
|
|
return ecma_raise_type_error (invalid_version_error_p);
|
|
}
|
|
|
|
lit_mem_to_snapshot_id_map_entry_t *lit_map_p = NULL;
|
|
uint32_t literals_num;
|
|
|
|
if (header_p->lit_table_offset >= snapshot_size)
|
|
{
|
|
return ecma_raise_type_error (invalid_version_error_p);
|
|
}
|
|
|
|
JERRY_ASSERT ((header_p->lit_table_offset % sizeof (uint32_t)) == 0);
|
|
if (!ecma_load_literals_from_snapshot ((uint32_t *) (snapshot_data_p + header_p->lit_table_offset),
|
|
header_p->lit_table_size,
|
|
&lit_map_p,
|
|
&literals_num))
|
|
{
|
|
JERRY_ASSERT (lit_map_p == NULL);
|
|
return ecma_raise_type_error (invalid_format_error_p);
|
|
}
|
|
|
|
ecma_compiled_code_t *bytecode_p;
|
|
bytecode_p = snapshot_load_compiled_code (snapshot_data_p,
|
|
sizeof (jerry_snapshot_header_t),
|
|
lit_map_p,
|
|
copy_bytecode);
|
|
|
|
if (lit_map_p != NULL)
|
|
{
|
|
jmem_heap_free_block (lit_map_p, literals_num * sizeof (lit_mem_to_snapshot_id_map_entry_t));
|
|
}
|
|
|
|
if (bytecode_p == NULL)
|
|
{
|
|
return ecma_raise_type_error (invalid_format_error_p);
|
|
}
|
|
|
|
ecma_value_t ret_val;
|
|
|
|
if (header_p->is_run_global)
|
|
{
|
|
ret_val = vm_run_global (bytecode_p);
|
|
ecma_bytecode_deref (bytecode_p);
|
|
}
|
|
else
|
|
{
|
|
ret_val = vm_run_eval (bytecode_p, false);
|
|
}
|
|
|
|
return ret_val;
|
|
#else /* !JERRY_ENABLE_SNAPSHOT_EXEC */
|
|
JERRY_UNUSED (snapshot_p);
|
|
JERRY_UNUSED (snapshot_size);
|
|
JERRY_UNUSED (copy_bytecode);
|
|
|
|
return ecma_make_simple_value (ECMA_SIMPLE_VALUE_FALSE);
|
|
#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */
|
|
} /* jerry_exec_snapshot */
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
|
|
|
|
/**
|
|
* ====================== Functions for literal saving ==========================
|
|
*/
|
|
|
|
/**
|
|
* Compare two ecma_strings by size, then lexicographically.
|
|
*
|
|
* @return true - if the first string is less than the second one,
|
|
* false - otherwise
|
|
*/
|
|
static bool
|
|
jerry_save_literals_compare (ecma_string_t *literal1, /**< first literal */
|
|
ecma_string_t *literal2) /**< second literal */
|
|
{
|
|
const lit_utf8_size_t lit1_size = ecma_string_get_size (literal1);
|
|
const lit_utf8_size_t lit2_size = ecma_string_get_size (literal2);
|
|
|
|
if (lit1_size == lit2_size)
|
|
{
|
|
return ecma_compare_ecma_strings_relational (literal1, literal2);
|
|
}
|
|
|
|
return (lit1_size < lit2_size);
|
|
} /* jerry_save_literals_compare */
|
|
|
|
/**
|
|
* Helper function for the heapsort algorithm.
|
|
*
|
|
* @return index of the maximum value
|
|
*/
|
|
static lit_utf8_size_t
|
|
jerry_save_literals_heap_max (ecma_string_t *literals[], /**< array of literals */
|
|
lit_utf8_size_t num_of_nodes, /**< number of nodes */
|
|
lit_utf8_size_t node_idx, /**< index of parent node */
|
|
lit_utf8_size_t child_idx1, /**< index of the first child */
|
|
lit_utf8_size_t child_idx2) /**< index of the second child */
|
|
{
|
|
lit_utf8_size_t max_idx = node_idx;
|
|
|
|
if (child_idx1 < num_of_nodes
|
|
&& jerry_save_literals_compare (literals[max_idx], literals[child_idx1]))
|
|
{
|
|
max_idx = child_idx1;
|
|
}
|
|
|
|
if (child_idx2 < num_of_nodes
|
|
&& jerry_save_literals_compare (literals[max_idx], literals[child_idx2]))
|
|
{
|
|
max_idx = child_idx2;
|
|
}
|
|
|
|
return max_idx;
|
|
} /* jerry_save_literals_heap_max */
|
|
|
|
/**
|
|
* Helper function for the heapsort algorithm.
|
|
*/
|
|
static void
|
|
jerry_save_literals_down_heap (ecma_string_t *literals[], /**< array of literals */
|
|
lit_utf8_size_t num_of_nodes, /**< number of nodes */
|
|
lit_utf8_size_t node_idx) /**< index of parent node */
|
|
{
|
|
while (true)
|
|
{
|
|
lit_utf8_size_t max_idx = jerry_save_literals_heap_max (literals,
|
|
num_of_nodes,
|
|
node_idx,
|
|
2 * node_idx + 1,
|
|
2 * node_idx + 2);
|
|
if (max_idx == node_idx)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ecma_string_t *tmp_str_p = literals[node_idx];
|
|
literals[node_idx] = literals[max_idx];
|
|
literals[max_idx] = tmp_str_p;
|
|
|
|
node_idx = max_idx;
|
|
}
|
|
} /* jerry_save_literals_down_heap */
|
|
|
|
/**
|
|
* Helper function for a heapsort algorithm.
|
|
*/
|
|
static void
|
|
jerry_save_literals_sort (ecma_string_t *literals[], /**< array of literals */
|
|
lit_utf8_size_t num_of_literals) /**< number of literals */
|
|
{
|
|
if (num_of_literals < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lit_utf8_size_t lit_idx = (num_of_literals - 2) / 2;
|
|
|
|
while (lit_idx <= (num_of_literals - 2) / 2)
|
|
{
|
|
jerry_save_literals_down_heap (literals, num_of_literals, lit_idx--);
|
|
}
|
|
|
|
for (lit_idx = 0; lit_idx < num_of_literals; lit_idx++)
|
|
{
|
|
const lit_utf8_size_t last_idx = num_of_literals - lit_idx - 1;
|
|
|
|
ecma_string_t *tmp_str_p = literals[last_idx];
|
|
literals[last_idx] = literals[0];
|
|
literals[0] = tmp_str_p;
|
|
|
|
jerry_save_literals_down_heap (literals, last_idx, 0);
|
|
}
|
|
} /* jerry_save_literals_sort */
|
|
|
|
/**
|
|
* Append characters to the specified buffer.
|
|
*
|
|
* @return the position of the buffer pointer after copy.
|
|
*/
|
|
static uint8_t *
|
|
jerry_append_chars_to_buffer (uint8_t *buffer_p, /**< buffer */
|
|
uint8_t *buffer_end_p, /**< the end of the buffer */
|
|
const char *chars, /**< string */
|
|
lit_utf8_size_t string_size) /**< string size */
|
|
{
|
|
if (buffer_p > buffer_end_p)
|
|
{
|
|
return buffer_p;
|
|
}
|
|
|
|
if (string_size == 0)
|
|
{
|
|
string_size = (lit_utf8_size_t) strlen (chars);
|
|
}
|
|
|
|
if (buffer_p + string_size <= buffer_end_p)
|
|
{
|
|
memcpy ((char *) buffer_p, chars, string_size);
|
|
|
|
return buffer_p + string_size;
|
|
}
|
|
|
|
/* Move the pointer behind the buffer to prevent further writes. */
|
|
return buffer_end_p + 1;
|
|
} /* jerry_append_chars_to_buffer */
|
|
|
|
/**
|
|
* Append an ecma-string to the specified buffer.
|
|
*
|
|
* @return the position of the buffer pointer after copy.
|
|
*/
|
|
static uint8_t *
|
|
jerry_append_ecma_string_to_buffer (uint8_t *buffer_p, /**< buffer */
|
|
uint8_t *buffer_end_p, /**< the end of the buffer */
|
|
ecma_string_t *string_p) /**< ecma-string */
|
|
{
|
|
uint8_t *new_buffer_p = NULL;
|
|
|
|
ECMA_STRING_TO_UTF8_STRING (string_p, str_buffer_p, str_buffer_size);
|
|
|
|
/* Append the string to the buffer. */
|
|
new_buffer_p = jerry_append_chars_to_buffer (buffer_p,
|
|
buffer_end_p,
|
|
(const char *) str_buffer_p,
|
|
str_buffer_size);
|
|
|
|
ECMA_FINALIZE_UTF8_STRING (str_buffer_p, str_buffer_size);
|
|
|
|
return new_buffer_p;
|
|
} /* jerry_append_ecma_string_to_buffer */
|
|
|
|
/**
|
|
* Append an unsigned number to the specified buffer.
|
|
*
|
|
* @return the position of the buffer pointer after copy.
|
|
*/
|
|
static uint8_t *
|
|
jerry_append_number_to_buffer (uint8_t *buffer_p, /**< buffer */
|
|
uint8_t *buffer_end_p, /**< the end of the buffer */
|
|
lit_utf8_size_t number) /**< number */
|
|
{
|
|
lit_utf8_byte_t uint32_to_str_buffer[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32];
|
|
lit_utf8_size_t utf8_str_size = ecma_uint32_to_utf8_string (number,
|
|
uint32_to_str_buffer,
|
|
ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32);
|
|
|
|
JERRY_ASSERT (utf8_str_size <= ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32);
|
|
|
|
return jerry_append_chars_to_buffer (buffer_p,
|
|
buffer_end_p,
|
|
(const char *) uint32_to_str_buffer,
|
|
utf8_str_size);
|
|
} /* jerry_append_number_to_buffer */
|
|
|
|
/**
|
|
* Check whether the passed ecma-string is a valid identifier.
|
|
*
|
|
* @return true - if the ecma-string is a valid identifier,
|
|
* false - otherwise
|
|
*/
|
|
static bool
|
|
ecma_string_is_valid_identifier (const ecma_string_t *string_p)
|
|
{
|
|
bool result = false;
|
|
|
|
ECMA_STRING_TO_UTF8_STRING (string_p, str_buffer_p, str_buffer_size);
|
|
|
|
if (lit_char_is_identifier_start (str_buffer_p))
|
|
{
|
|
const uint8_t *str_start_p = str_buffer_p;
|
|
const uint8_t *str_end_p = str_buffer_p + str_buffer_size;
|
|
|
|
result = true;
|
|
|
|
while (str_start_p < str_end_p)
|
|
{
|
|
if (!lit_char_is_identifier_part (str_start_p))
|
|
{
|
|
result = false;
|
|
break;
|
|
}
|
|
lit_utf8_incr (&str_start_p);
|
|
}
|
|
}
|
|
|
|
ECMA_FINALIZE_UTF8_STRING (str_buffer_p, str_buffer_size);
|
|
|
|
return result;
|
|
} /* ecma_string_is_valid_identifier */
|
|
|
|
#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */
|
|
|
|
/**
|
|
* Copy certain string literals into the given buffer in a specified format,
|
|
* which are valid identifiers and none of them are magic string.
|
|
*
|
|
* @return size of the literal-list in bytes, at most equal to the buffer size,
|
|
* if the source parsed successfully and the list of the literals isn't empty,
|
|
* 0 - otherwise.
|
|
*/
|
|
size_t
|
|
jerry_parse_and_save_literals (const jerry_char_t *source_p, /**< script source */
|
|
size_t source_size, /**< script source size */
|
|
bool is_strict, /**< strict mode */
|
|
uint32_t *buffer_p, /**< [out] buffer to save literals to */
|
|
size_t buffer_size, /**< the buffer's size */
|
|
bool is_c_format) /**< format-flag */
|
|
{
|
|
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
|
|
ecma_value_t parse_status;
|
|
ecma_compiled_code_t *bytecode_data_p;
|
|
parse_status = parser_parse_script (source_p,
|
|
source_size,
|
|
is_strict,
|
|
&bytecode_data_p);
|
|
|
|
const bool error = ECMA_IS_VALUE_ERROR (parse_status);
|
|
ecma_free_value (parse_status);
|
|
|
|
if (error)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ecma_bytecode_deref (bytecode_data_p);
|
|
|
|
ecma_lit_storage_item_t *string_list_p = JERRY_CONTEXT (string_list_first_p);
|
|
lit_utf8_size_t literal_count = 0;
|
|
|
|
/* Count the valid and non-magic identifiers in the list. */
|
|
while (string_list_p != NULL)
|
|
{
|
|
for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
|
|
{
|
|
if (string_list_p->values[i] != JMEM_CP_NULL)
|
|
{
|
|
ecma_string_t *literal_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t,
|
|
string_list_p->values[i]);
|
|
/* We don't save a literal which isn't a valid identifier
|
|
or it's a magic string. */
|
|
if (ecma_get_string_magic (literal_p) == LIT_MAGIC_STRING__COUNT
|
|
&& ecma_string_is_valid_identifier (literal_p))
|
|
{
|
|
literal_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
string_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, string_list_p->next_cp);
|
|
}
|
|
|
|
if (literal_count == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint8_t *destination_p = (uint8_t *) buffer_p;
|
|
|
|
uint8_t *const buffer_start_p = destination_p;
|
|
uint8_t *const buffer_end_p = destination_p + buffer_size;
|
|
|
|
JMEM_DEFINE_LOCAL_ARRAY (literal_array, literal_count, ecma_string_t *);
|
|
lit_utf8_size_t literal_idx = 0;
|
|
|
|
string_list_p = JERRY_CONTEXT (string_list_first_p);
|
|
|
|
while (string_list_p != NULL)
|
|
{
|
|
for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
|
|
{
|
|
if (string_list_p->values[i] != JMEM_CP_NULL)
|
|
{
|
|
ecma_string_t *literal_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t,
|
|
string_list_p->values[i]);
|
|
|
|
if (ecma_get_string_magic (literal_p) == LIT_MAGIC_STRING__COUNT
|
|
&& ecma_string_is_valid_identifier (literal_p))
|
|
{
|
|
literal_array[literal_idx++] = literal_p;
|
|
}
|
|
}
|
|
}
|
|
|
|
string_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, string_list_p->next_cp);
|
|
}
|
|
|
|
/* Sort the strings by size at first, then lexicographically. */
|
|
jerry_save_literals_sort (literal_array, literal_count);
|
|
|
|
if (is_c_format)
|
|
{
|
|
/* Save literal count. */
|
|
destination_p = jerry_append_chars_to_buffer (destination_p,
|
|
buffer_end_p,
|
|
(const char *) "jerry_length_t literal_count = ",
|
|
0);
|
|
|
|
destination_p = jerry_append_number_to_buffer (destination_p, buffer_end_p, literal_count);
|
|
|
|
/* Save the array of literals. */
|
|
destination_p = jerry_append_chars_to_buffer (destination_p,
|
|
buffer_end_p,
|
|
";\n\njerry_char_ptr_t literals[",
|
|
0);
|
|
|
|
destination_p = jerry_append_number_to_buffer (destination_p, buffer_end_p, literal_count);
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, "] =\n{\n", 0);
|
|
|
|
for (lit_utf8_size_t i = 0; i < literal_count; i++)
|
|
{
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, " \"", 0);
|
|
destination_p = jerry_append_ecma_string_to_buffer (destination_p, buffer_end_p, literal_array[i]);
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, "\"", 0);
|
|
|
|
if (i < literal_count - 1)
|
|
{
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, ",", 0);
|
|
}
|
|
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, "\n", 0);
|
|
}
|
|
|
|
destination_p = jerry_append_chars_to_buffer (destination_p,
|
|
buffer_end_p,
|
|
(const char *) "};\n\njerry_length_t literal_sizes[",
|
|
0);
|
|
|
|
destination_p = jerry_append_number_to_buffer (destination_p, buffer_end_p, literal_count);
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, "] =\n{\n", 0);
|
|
}
|
|
|
|
/* Save the literal sizes respectively. */
|
|
for (lit_utf8_size_t i = 0; i < literal_count; i++)
|
|
{
|
|
lit_utf8_size_t str_size = ecma_string_get_size (literal_array[i]);
|
|
|
|
if (is_c_format)
|
|
{
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, " ", 0);
|
|
}
|
|
|
|
destination_p = jerry_append_number_to_buffer (destination_p, buffer_end_p, str_size);
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, " ", 0);
|
|
|
|
if (is_c_format)
|
|
{
|
|
/* Show the given string as a comment. */
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, "/* ", 0);
|
|
destination_p = jerry_append_ecma_string_to_buffer (destination_p, buffer_end_p, literal_array[i]);
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, " */", 0);
|
|
|
|
if (i < literal_count - 1)
|
|
{
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, ",", 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
destination_p = jerry_append_ecma_string_to_buffer (destination_p, buffer_end_p, literal_array[i]);
|
|
}
|
|
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, "\n", 0);
|
|
}
|
|
|
|
if (is_c_format)
|
|
{
|
|
destination_p = jerry_append_chars_to_buffer (destination_p, buffer_end_p, (const char *) "};\n", 0);
|
|
}
|
|
|
|
JMEM_FINALIZE_LOCAL_ARRAY (literal_array);
|
|
|
|
return destination_p <= buffer_end_p ? (size_t) (destination_p - buffer_start_p) : 0;
|
|
#else /* !JERRY_ENABLE_SNAPSHOT_SAVE */
|
|
JERRY_UNUSED (source_p);
|
|
JERRY_UNUSED (source_size);
|
|
JERRY_UNUSED (is_strict);
|
|
JERRY_UNUSED (buffer_p);
|
|
JERRY_UNUSED (buffer_size);
|
|
JERRY_UNUSED (is_c_format);
|
|
|
|
return 0;
|
|
#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */
|
|
} /* jerry_parse_and_save_literals */
|