Dániel Bátyai 68909fc5de
Fix lexical scoping between scripts (#3558)
This change fixes the handling of lexical blocks when executing multiple
scripts, and also fixes a few issues with module environments.

After this change, all script files will run in the same context and
will have access to lexically scoped global variables of previous
scripts, and module environments will no longer have a bound global
'this' value.

The REPL implementation in main-unix is also fixed to correctly handle
lexically scoped variables.

Fixes #3561.

JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai@inf.u-szeged.hu
2020-02-21 13:30:48 +01:00

2734 lines
83 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 "debugger.h"
#include "ecma-exceptions.h"
#include "ecma-helpers.h"
#include "ecma-literal-storage.h"
#include "ecma-module.h"
#include "jcontext.h"
#include "js-parser-internal.h"
#if ENABLED (JERRY_PARSER)
JERRY_STATIC_ASSERT ((int) ECMA_PARSE_STRICT_MODE == (int) PARSER_IS_STRICT,
ecma_parse_strict_mode_must_be_equal_to_parser_is_strict);
#if ENABLED (JERRY_ES2015)
JERRY_STATIC_ASSERT ((ECMA_PARSE_CLASS_CONSTRUCTOR << PARSER_CLASS_PARSE_OPTS_OFFSET) == PARSER_CLASS_CONSTRUCTOR,
ecma_class_parse_options_must_be_able_to_be_shifted_to_ecma_general_flags);
#endif /* ENABLED (JERRY_ES2015) */
/** \addtogroup parser Parser
* @{
*
* \addtogroup jsparser JavaScript
* @{
*
* \addtogroup jsparser_parser Parser
* @{
*/
/**
* Compute real literal indicies.
*
* @return length of the prefix opcodes
*/
static void
parser_compute_indicies (parser_context_t *context_p, /**< context */
uint16_t *ident_end, /**< end of the identifier group */
uint16_t *const_literal_end) /**< end of the const literal group */
{
parser_list_iterator_t literal_iterator;
lexer_literal_t *literal_p;
uint16_t ident_count = 0;
uint16_t const_literal_count = 0;
uint16_t ident_index;
uint16_t const_literal_index;
uint16_t literal_index;
/* First phase: count the number of items in each group. */
parser_list_iterator_init (&context_p->literal_pool, &literal_iterator);
while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)))
{
switch (literal_p->type)
{
case LEXER_IDENT_LITERAL:
{
if (literal_p->status_flags & LEXER_FLAG_USED)
{
ident_count++;
break;
}
#if !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
else if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR))
{
jmem_heap_free_block ((void *) literal_p->u.char_p, literal_p->prop.length);
/* This literal should not be freed even if an error is encountered later. */
literal_p->status_flags |= LEXER_FLAG_SOURCE_PTR;
}
#endif /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
continue;
}
case LEXER_STRING_LITERAL:
{
const_literal_count++;
break;
}
case LEXER_NUMBER_LITERAL:
{
const_literal_count++;
continue;
}
case LEXER_FUNCTION_LITERAL:
case LEXER_REGEXP_LITERAL:
{
continue;
}
default:
{
JERRY_ASSERT (literal_p->type == LEXER_UNUSED_LITERAL);
continue;
}
}
#if !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
const uint8_t *char_p = literal_p->u.char_p;
uint32_t status_flags = context_p->status_flags;
if ((literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)
&& literal_p->prop.length < 0xfff)
{
size_t bytes_to_end = (size_t) (context_p->source_end_p - char_p);
if (bytes_to_end < 0xfffff)
{
literal_p->u.source_data = ((uint32_t) bytes_to_end) | (((uint32_t) literal_p->prop.length) << 20);
literal_p->status_flags |= LEXER_FLAG_LATE_INIT;
status_flags |= PARSER_HAS_LATE_LIT_INIT;
context_p->status_flags = status_flags;
char_p = NULL;
}
}
if (char_p != NULL)
{
literal_p->u.value = ecma_find_or_create_literal_string (char_p,
literal_p->prop.length);
if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR))
{
jmem_heap_free_block ((void *) char_p, literal_p->prop.length);
/* This literal should not be freed even if an error is encountered later. */
literal_p->status_flags |= LEXER_FLAG_SOURCE_PTR;
}
}
#endif /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
}
ident_index = context_p->register_count;
const_literal_index = (uint16_t) (ident_index + ident_count);
literal_index = (uint16_t) (const_literal_index + const_literal_count);
/* Second phase: Assign an index to each literal. */
parser_list_iterator_init (&context_p->literal_pool, &literal_iterator);
while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)))
{
switch (literal_p->type)
{
case LEXER_IDENT_LITERAL:
{
if (literal_p->status_flags & LEXER_FLAG_USED)
{
literal_p->prop.index = ident_index;
ident_index++;
}
break;
}
case LEXER_STRING_LITERAL:
case LEXER_NUMBER_LITERAL:
{
JERRY_ASSERT ((literal_p->status_flags & ~(LEXER_FLAG_SOURCE_PTR | LEXER_FLAG_LATE_INIT)) == 0);
literal_p->prop.index = const_literal_index;
const_literal_index++;
break;
}
case LEXER_FUNCTION_LITERAL:
case LEXER_REGEXP_LITERAL:
{
JERRY_ASSERT (literal_p->status_flags == 0);
literal_p->prop.index = literal_index;
literal_index++;
break;
}
default:
{
JERRY_ASSERT (literal_p->type == LEXER_UNUSED_LITERAL
&& literal_p->status_flags == LEXER_FLAG_FUNCTION_ARGUMENT);
break;
}
}
}
JERRY_ASSERT (ident_index == context_p->register_count + ident_count);
JERRY_ASSERT (const_literal_index == ident_index + const_literal_count);
JERRY_ASSERT (literal_index <= context_p->register_count + context_p->literal_count);
context_p->literal_count = literal_index;
*ident_end = ident_index;
*const_literal_end = const_literal_index;
} /* parser_compute_indicies */
/**
* Initialize literal pool.
*/
static void
parser_init_literal_pool (parser_context_t *context_p, /**< context */
ecma_value_t *literal_pool_p) /**< start of literal pool */
{
parser_list_iterator_t literal_iterator;
lexer_literal_t *literal_p;
parser_list_iterator_init (&context_p->literal_pool, &literal_iterator);
while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)))
{
switch (literal_p->type)
{
case LEXER_IDENT_LITERAL:
{
if (!(literal_p->status_flags & LEXER_FLAG_USED))
{
break;
}
/* FALLTHRU */
}
case LEXER_STRING_LITERAL:
{
ecma_value_t lit_value;
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
lit_value = ecma_find_or_create_literal_string (literal_p->u.char_p,
literal_p->prop.length);
#else /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
lit_value = literal_p->u.value;
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
JERRY_ASSERT (literal_p->prop.index >= context_p->register_count);
literal_pool_p[literal_p->prop.index] = lit_value;
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
if (!context_p->is_show_opcodes
&& !(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR))
{
jmem_heap_free_block ((void *) literal_p->u.char_p, literal_p->prop.length);
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
break;
}
case LEXER_NUMBER_LITERAL:
{
JERRY_ASSERT (literal_p->prop.index >= context_p->register_count);
literal_pool_p[literal_p->prop.index] = literal_p->u.value;
break;
}
case LEXER_FUNCTION_LITERAL:
case LEXER_REGEXP_LITERAL:
{
JERRY_ASSERT (literal_p->prop.index >= context_p->register_count);
ECMA_SET_INTERNAL_VALUE_POINTER (literal_pool_p[literal_p->prop.index],
literal_p->u.bytecode_p);
break;
}
default:
{
JERRY_ASSERT (literal_p->type == LEXER_UNUSED_LITERAL);
break;
}
}
}
} /* parser_init_literal_pool */
/*
* During byte code post processing certain bytes are not
* copied into the final byte code buffer. For example, if
* one byte is enough for encoding a literal index, the
* second byte is not copied. However, when a byte is skipped,
* the offsets of those branches which crosses (jumps over)
* that byte code should also be decreased by one. Instead
* of finding these jumps every time when a byte is skipped,
* all branch offset updates are computed in one step.
*
* Branch offset mapping example:
*
* Let's assume that each parser_mem_page of the byte_code
* buffer is 8 bytes long and only 4 bytes are kept for a
* given page:
*
* +---+---+---+---+---+---+---+---+
* | X | 1 | 2 | 3 | X | 4 | X | X |
* +---+---+---+---+---+---+---+---+
*
* X marks those bytes which are removed. The resulting
* offset mapping is the following:
*
* +---+---+---+---+---+---+---+---+
* | 0 | 1 | 2 | 3 | 3 | 4 | 4 | 4 |
* +---+---+---+---+---+---+---+---+
*
* Each X is simply replaced by the index of the previous
* index starting from zero. This shows the number of
* copied bytes before a given byte including the byte
* itself. The last byte always shows the number of bytes
* copied from this page.
*
* This mapping allows recomputing all branch targets,
* since mapping[to] - mapping[from] is the new argument
* for forward branches. As for backward branches, the
* equation is reversed to mapping[from] - mapping[to].
*
* The mapping is relative to one page, so distance
* computation affecting multiple pages requires a loop.
* We should also note that only argument bytes can
* be skipped, so removed bytes cannot be targeted by
* branches. Valid branches always target instruction
* starts only.
*/
/**
* Recompute the argument of a forward branch.
*
* @return the new distance
*/
static size_t
parser_update_forward_branch (parser_mem_page_t *page_p, /**< current page */
size_t full_distance, /**< full distance */
uint8_t bytes_copied_before_jump) /**< bytes copied before jump */
{
size_t new_distance = 0;
while (full_distance > PARSER_CBC_STREAM_PAGE_SIZE)
{
new_distance += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK;
full_distance -= PARSER_CBC_STREAM_PAGE_SIZE;
page_p = page_p->next_p;
}
new_distance += page_p->bytes[full_distance - 1] & CBC_LOWER_SEVEN_BIT_MASK;
return new_distance - bytes_copied_before_jump;
} /* parser_update_forward_branch */
/**
* Recompute the argument of a backward branch.
*
* @return the new distance
*/
static size_t
parser_update_backward_branch (parser_mem_page_t *page_p, /**< current page */
size_t full_distance, /**< full distance */
uint8_t bytes_copied_before_jump) /**< bytes copied before jump */
{
size_t new_distance = bytes_copied_before_jump;
while (full_distance >= PARSER_CBC_STREAM_PAGE_SIZE)
{
JERRY_ASSERT (page_p != NULL);
new_distance += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK;
full_distance -= PARSER_CBC_STREAM_PAGE_SIZE;
page_p = page_p->next_p;
}
if (full_distance > 0)
{
size_t offset = PARSER_CBC_STREAM_PAGE_SIZE - full_distance;
JERRY_ASSERT (page_p != NULL);
new_distance += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK;
new_distance -= page_p->bytes[offset - 1] & CBC_LOWER_SEVEN_BIT_MASK;
}
return new_distance;
} /* parser_update_backward_branch */
/**
* Update targets of all branches in one step.
*/
static void
parse_update_branches (parser_context_t *context_p, /**< context */
uint8_t *byte_code_p) /**< byte code */
{
parser_mem_page_t *page_p = context_p->byte_code.first_p;
parser_mem_page_t *prev_page_p = NULL;
parser_mem_page_t *last_page_p = context_p->byte_code.last_p;
size_t last_position = context_p->byte_code.last_position;
size_t offset = 0;
size_t bytes_copied = 0;
if (last_position >= PARSER_CBC_STREAM_PAGE_SIZE)
{
last_page_p = NULL;
last_position = 0;
}
while (page_p != last_page_p || offset < last_position)
{
/* Branch instructions are marked to improve search speed. */
if (page_p->bytes[offset] & CBC_HIGHEST_BIT_MASK)
{
uint8_t *bytes_p = byte_code_p + bytes_copied;
uint8_t flags;
uint8_t bytes_copied_before_jump = 0;
size_t branch_argument_length;
size_t target_distance;
size_t length;
if (offset > 0)
{
bytes_copied_before_jump = page_p->bytes[offset - 1] & CBC_LOWER_SEVEN_BIT_MASK;
}
bytes_p += bytes_copied_before_jump;
if (*bytes_p == CBC_EXT_OPCODE)
{
bytes_p++;
flags = cbc_ext_flags[*bytes_p];
}
else
{
flags = cbc_flags[*bytes_p];
}
JERRY_ASSERT (flags & CBC_HAS_BRANCH_ARG);
branch_argument_length = CBC_BRANCH_OFFSET_LENGTH (*bytes_p);
bytes_p++;
/* Decoding target. */
length = branch_argument_length;
target_distance = 0;
do
{
target_distance = (target_distance << 8) | *bytes_p;
bytes_p++;
}
while (--length > 0);
if (CBC_BRANCH_IS_FORWARD (flags))
{
/* Branch target was not set. */
JERRY_ASSERT (target_distance > 0);
target_distance = parser_update_forward_branch (page_p,
offset + target_distance,
bytes_copied_before_jump);
}
else
{
if (target_distance < offset)
{
uint8_t bytes_copied_before_target = page_p->bytes[offset - target_distance - 1];
bytes_copied_before_target = bytes_copied_before_target & CBC_LOWER_SEVEN_BIT_MASK;
target_distance = (size_t) (bytes_copied_before_jump - bytes_copied_before_target);
}
else if (target_distance == offset)
{
target_distance = bytes_copied_before_jump;
}
else
{
target_distance = parser_update_backward_branch (prev_page_p,
target_distance - offset,
bytes_copied_before_jump);
}
}
/* Encoding target again. */
do
{
bytes_p--;
*bytes_p = (uint8_t) (target_distance & 0xff);
target_distance >>= 8;
}
while (--branch_argument_length > 0);
}
offset++;
if (offset >= PARSER_CBC_STREAM_PAGE_SIZE)
{
parser_mem_page_t *next_p = page_p->next_p;
/* We reverse the pages before the current page. */
page_p->next_p = prev_page_p;
prev_page_p = page_p;
bytes_copied += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK;
page_p = next_p;
offset = 0;
}
}
/* After this point the pages of the byte code stream are
* not used anymore. However, they needs to be freed during
* cleanup, so the first and last pointers of the stream
* descriptor are reversed as well. */
if (last_page_p != NULL)
{
JERRY_ASSERT (last_page_p == context_p->byte_code.last_p);
last_page_p->next_p = prev_page_p;
}
else
{
last_page_p = context_p->byte_code.last_p;
}
context_p->byte_code.last_p = context_p->byte_code.first_p;
context_p->byte_code.first_p = last_page_p;
} /* parse_update_branches */
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
/**
* Print literal.
*/
static void
parse_print_literal (ecma_compiled_code_t *compiled_code_p, /**< compiled code */
uint16_t literal_index, /**< literal index */
parser_list_t *literal_pool_p) /**< literal pool */
{
parser_list_iterator_t literal_iterator;
uint16_t argument_end;
uint16_t register_end;
uint16_t ident_end;
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p;
argument_end = args_p->argument_end;
register_end = args_p->register_end;
ident_end = args_p->ident_end;
}
else
{
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p;
argument_end = args_p->argument_end;
register_end = args_p->register_end;
ident_end = args_p->ident_end;
}
#if ENABLED (JERRY_ES2015)
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_REST_PARAMETER)
{
argument_end++;
}
#endif /* ENABLED (JERRY_ES2015) */
if (literal_index < argument_end)
{
JERRY_DEBUG_MSG (" arg:%d", literal_index);
return;
}
if (literal_index < register_end)
{
JERRY_DEBUG_MSG (" reg:%d", literal_index);
return;
}
parser_list_iterator_init (literal_pool_p, &literal_iterator);
while (true)
{
lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator);
JERRY_ASSERT (literal_p != NULL);
if (literal_p->prop.index == literal_index)
{
if (literal_index < ident_end)
{
JERRY_DEBUG_MSG (" ident:%d->", literal_index);
}
else
{
JERRY_DEBUG_MSG (" lit:%d->", literal_index);
}
util_print_literal (literal_p);
return;
}
}
} /* parse_print_literal */
#define PARSER_READ_IDENTIFIER_INDEX(name) \
name = *byte_code_p++; \
if (name >= encoding_limit) \
{ \
name = (uint16_t) (((name << 8) | byte_code_p[0]) - encoding_delta); \
byte_code_p++; \
}
/**
* Print byte code.
*/
static void
parse_print_final_cbc (ecma_compiled_code_t *compiled_code_p, /**< compiled code */
parser_list_t *literal_pool_p, /**< literal pool */
size_t length) /**< length of byte code */
{
uint8_t flags;
uint8_t *byte_code_start_p;
uint8_t *byte_code_end_p;
uint8_t *byte_code_p;
uint16_t encoding_limit;
uint16_t encoding_delta;
uint16_t stack_limit;
uint16_t argument_end;
uint16_t register_end;
uint16_t ident_end;
uint16_t const_literal_end;
uint16_t literal_end;
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
cbc_uint16_arguments_t *args = (cbc_uint16_arguments_t *) compiled_code_p;
stack_limit = args->stack_limit;
argument_end = args->argument_end;
register_end = args->register_end;
ident_end = args->ident_end;
const_literal_end = args->const_literal_end;
literal_end = args->literal_end;
}
else
{
cbc_uint8_arguments_t *args = (cbc_uint8_arguments_t *) compiled_code_p;
stack_limit = args->stack_limit;
argument_end = args->argument_end;
register_end = args->register_end;
ident_end = args->ident_end;
const_literal_end = args->const_literal_end;
literal_end = args->literal_end;
}
JERRY_DEBUG_MSG ("\nFinal byte code dump:\n\n Maximum stack depth: %d\n Flags: [",
(int) (stack_limit + register_end));
if (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING))
{
JERRY_DEBUG_MSG ("small_lit_enc");
encoding_limit = CBC_SMALL_LITERAL_ENCODING_LIMIT;
encoding_delta = CBC_SMALL_LITERAL_ENCODING_DELTA;
}
else
{
JERRY_DEBUG_MSG ("full_lit_enc");
encoding_limit = CBC_FULL_LITERAL_ENCODING_LIMIT;
encoding_delta = CBC_FULL_LITERAL_ENCODING_DELTA;
}
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
JERRY_DEBUG_MSG (",uint16_arguments");
}
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE)
{
JERRY_DEBUG_MSG (",strict_mode");
}
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED)
{
JERRY_DEBUG_MSG (",mapped_arguments_needed");
}
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UNMAPPED_ARGUMENTS_NEEDED)
{
JERRY_DEBUG_MSG (",unmapped_arguments_needed");
}
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED)
{
JERRY_DEBUG_MSG (",no_lexical_env");
}
#if ENABLED (JERRY_ES2015)
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_ARROW_FUNCTION)
{
JERRY_DEBUG_MSG (",arrow");
}
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR)
{
JERRY_DEBUG_MSG (",constructor");
}
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_GENERATOR)
{
JERRY_DEBUG_MSG (",generator");
}
#endif /* ENABLED (JERRY_ES2015) */
JERRY_DEBUG_MSG ("]\n");
JERRY_DEBUG_MSG (" Argument range end: %d\n", (int) argument_end);
JERRY_DEBUG_MSG (" Register range end: %d\n", (int) register_end);
JERRY_DEBUG_MSG (" Identifier range end: %d\n", (int) ident_end);
JERRY_DEBUG_MSG (" Const literal range end: %d\n", (int) const_literal_end);
JERRY_DEBUG_MSG (" Literal range end: %d\n\n", (int) literal_end);
byte_code_start_p = (uint8_t *) compiled_code_p;
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
byte_code_start_p += sizeof (cbc_uint16_arguments_t);
}
else
{
byte_code_start_p += sizeof (cbc_uint8_arguments_t);
}
byte_code_start_p += (unsigned int) (literal_end - register_end) * sizeof (ecma_value_t);
byte_code_end_p = byte_code_start_p + length;
byte_code_p = byte_code_start_p;
while (byte_code_p < byte_code_end_p)
{
cbc_opcode_t opcode = (cbc_opcode_t) *byte_code_p;
cbc_ext_opcode_t ext_opcode = CBC_EXT_NOP;
size_t cbc_offset = (size_t) (byte_code_p - byte_code_start_p);
if (opcode != CBC_EXT_OPCODE)
{
flags = cbc_flags[opcode];
JERRY_DEBUG_MSG (" %3d : %s", (int) cbc_offset, cbc_names[opcode]);
byte_code_p++;
}
else
{
ext_opcode = (cbc_ext_opcode_t) byte_code_p[1];
flags = cbc_ext_flags[ext_opcode];
JERRY_DEBUG_MSG (" %3d : %s", (int) cbc_offset, cbc_ext_names[ext_opcode]);
byte_code_p += 2;
#if ENABLED (JERRY_LINE_INFO)
if (ext_opcode == CBC_EXT_LINE)
{
uint32_t value = 0;
uint8_t byte;
do
{
byte = *byte_code_p++;
value = (value << 7) | (byte & CBC_LOWER_SEVEN_BIT_MASK);
}
while (byte & CBC_HIGHEST_BIT_MASK);
JERRY_DEBUG_MSG (" %d\n", (int) value);
continue;
}
#endif /* ENABLED (JERRY_LINE_INFO) */
}
if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2))
{
uint16_t literal_index;
PARSER_READ_IDENTIFIER_INDEX (literal_index);
parse_print_literal (compiled_code_p, literal_index, literal_pool_p);
}
if (flags & CBC_HAS_LITERAL_ARG2)
{
uint16_t literal_index;
PARSER_READ_IDENTIFIER_INDEX (literal_index);
parse_print_literal (compiled_code_p, literal_index, literal_pool_p);
if (!(flags & CBC_HAS_LITERAL_ARG))
{
PARSER_READ_IDENTIFIER_INDEX (literal_index);
parse_print_literal (compiled_code_p, literal_index, literal_pool_p);
}
}
if (flags & CBC_HAS_BYTE_ARG)
{
if (opcode == CBC_PUSH_NUMBER_POS_BYTE
|| ext_opcode == CBC_EXT_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE)
{
JERRY_DEBUG_MSG (" number:%d", (int) *byte_code_p + 1);
}
else if (opcode == CBC_PUSH_NUMBER_NEG_BYTE
|| ext_opcode == CBC_EXT_PUSH_LITERAL_PUSH_NUMBER_NEG_BYTE)
{
JERRY_DEBUG_MSG (" number:%d", -((int) *byte_code_p + 1));
}
else
{
JERRY_DEBUG_MSG (" byte_arg:%d", *byte_code_p);
}
byte_code_p++;
}
if (flags & CBC_HAS_BRANCH_ARG)
{
size_t branch_offset_length = (opcode != CBC_EXT_OPCODE ? CBC_BRANCH_OFFSET_LENGTH (opcode)
: CBC_BRANCH_OFFSET_LENGTH (ext_opcode));
size_t offset = 0;
do
{
offset = (offset << 8) | *byte_code_p++;
}
while (--branch_offset_length > 0);
JERRY_DEBUG_MSG (" offset:%d(->%d)",
(int) offset,
(int) (cbc_offset + (CBC_BRANCH_IS_FORWARD (flags) ? offset : -offset)));
}
JERRY_DEBUG_MSG ("\n");
}
} /* parse_print_final_cbc */
#undef PARSER_READ_IDENTIFIER_INDEX
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
#if ENABLED (JERRY_DEBUGGER)
/**
* Send current breakpoint list.
*/
static void
parser_send_breakpoints (parser_context_t *context_p, /**< context */
jerry_debugger_header_type_t type) /**< message type */
{
JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED);
JERRY_ASSERT (context_p->breakpoint_info_count > 0);
jerry_debugger_send_data (type,
context_p->breakpoint_info,
context_p->breakpoint_info_count * sizeof (parser_breakpoint_info_t));
context_p->breakpoint_info_count = 0;
} /* parser_send_breakpoints */
/**
* Append a breakpoint info.
*/
void
parser_append_breakpoint_info (parser_context_t *context_p, /**< context */
jerry_debugger_header_type_t type, /**< message type */
uint32_t value) /**< line or offset of the breakpoint */
{
JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED);
context_p->status_flags |= PARSER_DEBUGGER_BREAKPOINT_APPENDED;
if (context_p->breakpoint_info_count >= JERRY_DEBUGGER_SEND_MAX (parser_breakpoint_info_t))
{
parser_send_breakpoints (context_p, type);
}
context_p->breakpoint_info[context_p->breakpoint_info_count].value = value;
context_p->breakpoint_info_count = (uint16_t) (context_p->breakpoint_info_count + 1);
} /* parser_append_breakpoint_info */
#endif /* ENABLED (JERRY_DEBUGGER) */
/**
* Forward iterator: move to the next byte code
*
* @param page_p page
* @param offset offset
*/
#define PARSER_NEXT_BYTE(page_p, offset) \
do { \
if (++(offset) >= PARSER_CBC_STREAM_PAGE_SIZE) \
{ \
offset = 0; \
page_p = page_p->next_p; \
} \
} while (0)
/**
* Forward iterator: move to the next byte code. Also updates the offset of the previous byte code.
*
* @param page_p page
* @param offset offset
* @param real_offset real offset
*/
#define PARSER_NEXT_BYTE_UPDATE(page_p, offset, real_offset) \
do { \
page_p->bytes[offset] = real_offset; \
if (++(offset) >= PARSER_CBC_STREAM_PAGE_SIZE) \
{ \
offset = 0; \
real_offset = 0; \
page_p = page_p->next_p; \
} \
} while (0)
/**
* Post processing main function.
*
* @return compiled code
*/
static ecma_compiled_code_t *
parser_post_processing (parser_context_t *context_p) /**< context */
{
uint16_t literal_one_byte_limit;
uint16_t ident_end;
uint16_t const_literal_end;
parser_mem_page_t *page_p;
parser_mem_page_t *last_page_p;
size_t last_position;
size_t offset;
size_t length;
size_t literal_length;
size_t total_size;
#if ENABLED (JERRY_SNAPSHOT_SAVE)
size_t total_size_used;
#endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */
uint8_t real_offset;
uint8_t *byte_code_p;
bool needs_uint16_arguments;
cbc_opcode_t last_opcode = CBC_EXT_OPCODE;
ecma_compiled_code_t *compiled_code_p;
ecma_value_t *literal_pool_p;
uint8_t *dst_p;
if ((size_t) context_p->stack_limit + (size_t) context_p->register_count > PARSER_MAXIMUM_STACK_LIMIT)
{
parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED);
}
JERRY_ASSERT (context_p->literal_count <= PARSER_MAXIMUM_NUMBER_OF_LITERALS);
#if ENABLED (JERRY_DEBUGGER)
if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
&& !(context_p->status_flags & PARSER_DEBUGGER_BREAKPOINT_APPENDED))
{
/* Always provide at least one breakpoint. */
parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED);
parser_flush_cbc (context_p);
parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, context_p->token.line);
context_p->last_breakpoint_line = context_p->token.line;
}
if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
&& context_p->breakpoint_info_count > 0)
{
parser_send_breakpoints (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST);
JERRY_ASSERT (context_p->breakpoint_info_count == 0);
}
#endif /* ENABLED (JERRY_DEBUGGER) */
parser_compute_indicies (context_p, &ident_end, &const_literal_end);
if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE)
{
literal_one_byte_limit = CBC_MAXIMUM_BYTE_VALUE - 1;
}
else
{
literal_one_byte_limit = CBC_LOWER_SEVEN_BIT_MASK;
}
last_page_p = context_p->byte_code.last_p;
last_position = context_p->byte_code.last_position;
if (last_position >= PARSER_CBC_STREAM_PAGE_SIZE)
{
last_page_p = NULL;
last_position = 0;
}
page_p = context_p->byte_code.first_p;
offset = 0;
length = 0;
while (page_p != last_page_p || offset < last_position)
{
uint8_t *opcode_p;
uint8_t flags;
size_t branch_offset_length;
opcode_p = page_p->bytes + offset;
last_opcode = (cbc_opcode_t) (*opcode_p);
PARSER_NEXT_BYTE (page_p, offset);
branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (last_opcode);
flags = cbc_flags[last_opcode];
length++;
if (last_opcode == CBC_EXT_OPCODE)
{
cbc_ext_opcode_t ext_opcode;
ext_opcode = (cbc_ext_opcode_t) page_p->bytes[offset];
branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (ext_opcode);
flags = cbc_ext_flags[ext_opcode];
PARSER_NEXT_BYTE (page_p, offset);
length++;
#if ENABLED (JERRY_ES2015)
if (ext_opcode == CBC_EXT_CONSTRUCTOR_RETURN
|| ext_opcode == CBC_EXT_RETURN_PROMISE)
{
last_opcode = CBC_RETURN;
}
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_LINE_INFO)
if (ext_opcode == CBC_EXT_LINE)
{
uint8_t last_byte = 0;
do
{
last_byte = page_p->bytes[offset];
PARSER_NEXT_BYTE (page_p, offset);
length++;
}
while (last_byte & CBC_HIGHEST_BIT_MASK);
continue;
}
#endif /* ENABLED (JERRY_LINE_INFO) */
}
while (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2))
{
uint8_t *first_byte = page_p->bytes + offset;
uint32_t literal_index = *first_byte;
PARSER_NEXT_BYTE (page_p, offset);
length++;
literal_index |= ((uint32_t) page_p->bytes[offset]) << 8;
if (literal_index >= PARSER_REGISTER_START)
{
literal_index -= PARSER_REGISTER_START;
}
else
{
literal_index = (PARSER_GET_LITERAL (literal_index))->prop.index;
}
if (literal_index <= literal_one_byte_limit)
{
*first_byte = (uint8_t) literal_index;
}
else
{
if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE)
{
JERRY_ASSERT (literal_index <= CBC_MAXIMUM_SMALL_VALUE);
*first_byte = CBC_MAXIMUM_BYTE_VALUE;
page_p->bytes[offset] = (uint8_t) (literal_index - CBC_MAXIMUM_BYTE_VALUE);
length++;
}
else
{
JERRY_ASSERT (literal_index <= CBC_MAXIMUM_FULL_VALUE);
*first_byte = (uint8_t) ((literal_index >> 8) | CBC_HIGHEST_BIT_MASK);
page_p->bytes[offset] = (uint8_t) (literal_index & 0xff);
length++;
}
}
PARSER_NEXT_BYTE (page_p, offset);
if (flags & CBC_HAS_LITERAL_ARG2)
{
if (flags & CBC_HAS_LITERAL_ARG)
{
flags = CBC_HAS_LITERAL_ARG;
}
else
{
flags = CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2;
}
}
else
{
break;
}
}
if (flags & CBC_HAS_BYTE_ARG)
{
/* This argument will be copied without modification. */
PARSER_NEXT_BYTE (page_p, offset);
length++;
}
if (flags & CBC_HAS_BRANCH_ARG)
{
bool prefix_zero = true;
#if PARSER_MAXIMUM_CODE_SIZE <= 65535
cbc_opcode_t jump_forward = CBC_JUMP_FORWARD_2;
#else /* PARSER_MAXIMUM_CODE_SIZE > 65535 */
cbc_opcode_t jump_forward = CBC_JUMP_FORWARD_3;
#endif /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */
/* The leading zeroes are dropped from the stream.
* Although dropping these zeroes for backward
* branches are unnecessary, we use the same
* code path for simplicity. */
JERRY_ASSERT (branch_offset_length > 0 && branch_offset_length <= 3);
while (--branch_offset_length > 0)
{
uint8_t byte = page_p->bytes[offset];
if (byte > 0 || !prefix_zero)
{
prefix_zero = false;
length++;
}
else
{
JERRY_ASSERT (CBC_BRANCH_IS_FORWARD (flags));
}
PARSER_NEXT_BYTE (page_p, offset);
}
if (last_opcode == jump_forward
&& prefix_zero
&& page_p->bytes[offset] == CBC_BRANCH_OFFSET_LENGTH (jump_forward) + 1)
{
/* Uncoditional jumps which jump right after the instruction
* are effectively NOPs. These jumps are removed from the
* stream. The 1 byte long CBC_JUMP_FORWARD form marks these
* instructions, since this form is constructed during post
* processing and cannot be emitted directly. */
*opcode_p = CBC_JUMP_FORWARD;
length--;
}
else
{
/* Other last bytes are always copied. */
length++;
}
PARSER_NEXT_BYTE (page_p, offset);
}
}
if (!(context_p->status_flags & PARSER_NO_END_LABEL)
|| !(PARSER_OPCODE_IS_RETURN (last_opcode)))
{
context_p->status_flags &= (uint32_t) ~PARSER_NO_END_LABEL;
#if ENABLED (JERRY_ES2015)
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
{
length += 2;
}
#endif /* ENABLED (JERRY_ES2015) */
length++;
}
needs_uint16_arguments = false;
total_size = sizeof (cbc_uint8_arguments_t);
if (context_p->stack_limit > CBC_MAXIMUM_BYTE_VALUE
|| context_p->register_count > CBC_MAXIMUM_BYTE_VALUE
|| context_p->literal_count > CBC_MAXIMUM_BYTE_VALUE)
{
needs_uint16_arguments = true;
total_size = sizeof (cbc_uint16_arguments_t);
}
literal_length = (size_t) (context_p->literal_count - context_p->register_count) * sizeof (ecma_value_t);
total_size += literal_length + length;
if (PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags))
{
total_size += context_p->argument_count * sizeof (ecma_value_t);
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
total_size += sizeof (ecma_value_t);
}
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
{
total_size += sizeof (ecma_value_t);
}
#endif /* ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
#if ENABLED (JERRY_SNAPSHOT_SAVE)
total_size_used = total_size;
#endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */
total_size = JERRY_ALIGNUP (total_size, JMEM_ALIGNMENT);
compiled_code_p = (ecma_compiled_code_t *) parser_malloc (context_p, total_size);
#if ENABLED (JERRY_SNAPSHOT_SAVE)
// Avoid getting junk bytes at the end when bytes at the end remain unused:
if (total_size_used < total_size)
{
memset (((uint8_t *) compiled_code_p) + total_size_used, 0, total_size - total_size_used);
}
#endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */
#if ENABLED (JERRY_MEM_STATS)
jmem_stats_allocate_byte_code_bytes (total_size);
#endif /* ENABLED (JERRY_MEM_STATS) */
byte_code_p = (uint8_t *) compiled_code_p;
compiled_code_p->size = (uint16_t) (total_size >> JMEM_ALIGNMENT_LOG);
compiled_code_p->refs = 1;
compiled_code_p->status_flags = CBC_CODE_FLAGS_FUNCTION;
#if ENABLED (JERRY_ES2015)
if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)
{
JERRY_ASSERT (context_p->argument_count > 0);
context_p->argument_count--;
}
#endif /* ENABLED (JERRY_ES2015) */
if (needs_uint16_arguments)
{
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p;
args_p->stack_limit = context_p->stack_limit;
args_p->argument_end = context_p->argument_count;
args_p->register_end = context_p->register_count;
args_p->ident_end = ident_end;
args_p->const_literal_end = const_literal_end;
args_p->literal_end = context_p->literal_count;
compiled_code_p->status_flags |= CBC_CODE_FLAGS_UINT16_ARGUMENTS;
byte_code_p += sizeof (cbc_uint16_arguments_t);
}
else
{
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p;
args_p->stack_limit = (uint8_t) context_p->stack_limit;
args_p->argument_end = (uint8_t) context_p->argument_count;
args_p->register_end = (uint8_t) context_p->register_count;
args_p->ident_end = (uint8_t) ident_end;
args_p->const_literal_end = (uint8_t) const_literal_end;
args_p->literal_end = (uint8_t) context_p->literal_count;
byte_code_p += sizeof (cbc_uint8_arguments_t);
}
uint16_t encoding_limit;
uint16_t encoding_delta;
if (context_p->literal_count > CBC_MAXIMUM_SMALL_VALUE)
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_FULL_LITERAL_ENCODING;
encoding_limit = CBC_FULL_LITERAL_ENCODING_LIMIT;
encoding_delta = CBC_FULL_LITERAL_ENCODING_DELTA;
}
else
{
encoding_limit = CBC_SMALL_LITERAL_ENCODING_LIMIT;
encoding_delta = CBC_SMALL_LITERAL_ENCODING_DELTA;
}
if (context_p->status_flags & PARSER_IS_STRICT)
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_STRICT_MODE;
}
if (context_p->status_flags & PARSER_ARGUMENTS_NEEDED)
{
if (PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags))
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED;
}
else
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_UNMAPPED_ARGUMENTS_NEEDED;
}
/* Arguments is stored in the lexical environment. */
context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;
}
if (!(context_p->status_flags & PARSER_LEXICAL_ENV_NEEDED))
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED;
}
#if ENABLED (JERRY_ES2015)
if (context_p->status_flags & PARSER_IS_ARROW_FUNCTION)
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_ARROW_FUNCTION;
}
if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR)
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_CONSTRUCTOR;
}
if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_GENERATOR;
}
if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_REST_PARAMETER;
}
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
compiled_code_p->status_flags |= CBC_CODE_FLAG_HAS_TAGGED_LITERALS;
}
if (context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED)
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED;
}
#endif /* ENABLED (JERRY_ES2015) */
literal_pool_p = ((ecma_value_t *) byte_code_p) - context_p->register_count;
byte_code_p += literal_length;
dst_p = byte_code_p;
parser_init_literal_pool (context_p, literal_pool_p);
page_p = context_p->byte_code.first_p;
offset = 0;
real_offset = 0;
uint8_t last_register_index = (uint8_t) JERRY_MIN (context_p->register_count,
(PARSER_MAXIMUM_NUMBER_OF_REGISTERS - 1));
while (page_p != last_page_p || offset < last_position)
{
uint8_t flags;
uint8_t *opcode_p;
uint8_t *branch_mark_p;
cbc_opcode_t opcode;
size_t branch_offset_length;
opcode_p = dst_p;
branch_mark_p = page_p->bytes + offset;
opcode = (cbc_opcode_t) (*branch_mark_p);
branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (opcode);
if (opcode == CBC_JUMP_FORWARD)
{
/* These opcodes are deleted from the stream. */
#if PARSER_MAXIMUM_CODE_SIZE <= 65535
size_t counter = 3;
#else /* PARSER_MAXIMUM_CODE_SIZE > 65535 */
size_t counter = 4;
#endif /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */
do
{
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
}
while (--counter > 0);
continue;
}
/* Storing the opcode */
*dst_p++ = (uint8_t) opcode;
real_offset++;
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
flags = cbc_flags[opcode];
#if ENABLED (JERRY_DEBUGGER)
if (opcode == CBC_BREAKPOINT_DISABLED)
{
uint32_t bp_offset = (uint32_t) (((uint8_t *) dst_p) - ((uint8_t *) compiled_code_p) - 1);
parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST, bp_offset);
}
#endif /* ENABLED (JERRY_DEBUGGER) */
if (opcode == CBC_EXT_OPCODE)
{
cbc_ext_opcode_t ext_opcode;
ext_opcode = (cbc_ext_opcode_t) page_p->bytes[offset];
flags = cbc_ext_flags[ext_opcode];
branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (ext_opcode);
/* Storing the extended opcode */
*dst_p++ = (uint8_t) ext_opcode;
opcode_p++;
real_offset++;
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
#if ENABLED (JERRY_LINE_INFO)
if (ext_opcode == CBC_EXT_LINE)
{
uint8_t last_byte = 0;
do
{
last_byte = page_p->bytes[offset];
*dst_p++ = last_byte;
real_offset++;
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
}
while (last_byte & CBC_HIGHEST_BIT_MASK);
continue;
}
#endif /* ENABLED (JERRY_LINE_INFO) */
}
/* Only literal and call arguments can be combined. */
JERRY_ASSERT (!(flags & CBC_HAS_BRANCH_ARG)
|| !(flags & (CBC_HAS_BYTE_ARG | CBC_HAS_LITERAL_ARG)));
while (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2))
{
uint16_t first_byte = page_p->bytes[offset];
uint8_t *opcode_pos_p = dst_p - 1;
*dst_p++ = (uint8_t) first_byte;
real_offset++;
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
if (first_byte > literal_one_byte_limit)
{
*dst_p++ = page_p->bytes[offset];
if (first_byte > encoding_limit)
{
first_byte = (uint16_t) (((first_byte << 8) | dst_p[-1]) - encoding_delta);
}
real_offset++;
}
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
if (flags & CBC_HAS_LITERAL_ARG2)
{
if (flags & CBC_HAS_LITERAL_ARG)
{
flags = CBC_HAS_LITERAL_ARG;
}
else
{
flags = CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2;
}
}
else
{
if (opcode == CBC_ASSIGN_SET_IDENT && JERRY_LIKELY (first_byte < last_register_index))
{
*opcode_pos_p = CBC_MOV_IDENT;
}
break;
}
}
if (flags & CBC_HAS_BYTE_ARG)
{
/* This argument will be copied without modification. */
*dst_p++ = page_p->bytes[offset];
real_offset++;
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
continue;
}
if (flags & CBC_HAS_BRANCH_ARG)
{
*branch_mark_p |= CBC_HIGHEST_BIT_MASK;
bool prefix_zero = true;
/* The leading zeroes are dropped from the stream. */
JERRY_ASSERT (branch_offset_length > 0 && branch_offset_length <= 3);
while (--branch_offset_length > 0)
{
uint8_t byte = page_p->bytes[offset];
if (byte > 0 || !prefix_zero)
{
prefix_zero = false;
*dst_p++ = page_p->bytes[offset];
real_offset++;
}
else
{
/* When a leading zero is dropped, the branch
* offset length must be decreased as well. */
(*opcode_p)--;
}
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
}
*dst_p++ = page_p->bytes[offset];
real_offset++;
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
continue;
}
}
#if ENABLED (JERRY_DEBUGGER)
if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
&& context_p->breakpoint_info_count > 0)
{
parser_send_breakpoints (context_p, JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST);
JERRY_ASSERT (context_p->breakpoint_info_count == 0);
}
#endif /* ENABLED (JERRY_DEBUGGER) */
if (!(context_p->status_flags & PARSER_NO_END_LABEL))
{
*dst_p++ = CBC_RETURN_WITH_BLOCK;
#if ENABLED (JERRY_ES2015)
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
{
dst_p[-1] = CBC_PUSH_UNDEFINED;
dst_p[0] = CBC_EXT_OPCODE;
dst_p[1] = CBC_EXT_RETURN_PROMISE;
dst_p += 2;
}
#endif /* ENABLED (JERRY_ES2015) */
}
JERRY_ASSERT (dst_p == byte_code_p + length);
parse_update_branches (context_p, byte_code_p);
parser_cbc_stream_free (&context_p->byte_code);
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
if (context_p->is_show_opcodes)
{
parser_list_iterator_t literal_iterator;
lexer_literal_t *literal_p;
parse_print_final_cbc (compiled_code_p, &context_p->literal_pool, length);
JERRY_DEBUG_MSG ("\nByte code size: %d bytes\n", (int) length);
context_p->total_byte_code_size += (uint32_t) length;
parser_list_iterator_init (&context_p->literal_pool, &literal_iterator);
while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)))
{
if ((literal_p->type == LEXER_IDENT_LITERAL || literal_p->type == LEXER_STRING_LITERAL)
&& !(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR))
{
jmem_heap_free_block ((void *) literal_p->u.char_p, literal_p->prop.length);
}
}
}
#else /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
if (context_p->status_flags & PARSER_HAS_LATE_LIT_INIT)
{
parser_list_iterator_t literal_iterator;
lexer_literal_t *literal_p;
uint16_t register_count = context_p->register_count;
parser_list_iterator_init (&context_p->literal_pool, &literal_iterator);
while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)))
{
if ((literal_p->status_flags & LEXER_FLAG_LATE_INIT)
&& literal_p->prop.index >= register_count)
{
uint32_t source_data = literal_p->u.source_data;
const uint8_t *char_p = context_p->source_end_p - (source_data & 0xfffff);
ecma_value_t lit_value = ecma_find_or_create_literal_string (char_p,
source_data >> 20);
literal_pool_p[literal_p->prop.index] = lit_value;
}
}
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
if (PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags))
{
parser_list_iterator_t literal_iterator;
uint16_t argument_count = 0;
uint16_t register_count = context_p->register_count;
ecma_value_t *argument_base_p = (ecma_value_t *) (((uint8_t *) compiled_code_p) + total_size);
argument_base_p -= context_p->argument_count;
parser_list_iterator_init (&context_p->literal_pool, &literal_iterator);
while (argument_count < context_p->argument_count)
{
lexer_literal_t *literal_p;
literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator);
JERRY_ASSERT (literal_p != NULL);
if (!(literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT))
{
continue;
}
/* All arguments must be moved to initialized registers. */
if (literal_p->type == LEXER_UNUSED_LITERAL)
{
argument_base_p[argument_count] = ECMA_VALUE_EMPTY;
argument_count++;
continue;
}
JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL);
JERRY_ASSERT (literal_p->prop.index >= register_count);
argument_base_p[argument_count] = literal_pool_p[literal_p->prop.index];
argument_count++;
}
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
ecma_value_t *tagged_base_p = (ecma_value_t *) (((uint8_t *) compiled_code_p) + total_size);
if (PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags))
{
tagged_base_p -= context_p->argument_count;
}
tagged_base_p[-1] = (ecma_value_t) context_p->tagged_template_literal_cp;
ecma_collection_t *collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
context_p->tagged_template_literal_cp);
for (uint32_t i = 0; i < collection_p->item_count; i++)
{
ecma_free_value (collection_p->buffer_p[i]);
}
}
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
{
ecma_value_t *resource_name_p = (ecma_value_t *) (((uint8_t *) compiled_code_p) + total_size);
if (PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags))
{
resource_name_p -= context_p->argument_count;
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
resource_name_p--;
}
#endif /* ENABLED (JERRY_ES2015) */
resource_name_p[-1] = JERRY_CONTEXT (resource_name);
}
#endif /* ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
#if ENABLED (JERRY_DEBUGGER)
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{
jerry_debugger_send_function_cp (JERRY_DEBUGGER_BYTE_CODE_CP, compiled_code_p);
}
#endif /* ENABLED (JERRY_DEBUGGER) */
return compiled_code_p;
} /* parser_post_processing */
#undef PARSER_NEXT_BYTE
#undef PARSER_NEXT_BYTE_UPDATE
/**
* Free identifiers and literals.
*/
static void
parser_free_literals (parser_list_t *literal_pool_p) /**< literals */
{
parser_list_iterator_t literal_iterator;
lexer_literal_t *literal_p;
parser_list_iterator_init (literal_pool_p, &literal_iterator);
while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
{
util_free_literal (literal_p);
}
parser_list_free (literal_pool_p);
} /* parser_free_literals */
/**
* Parse function arguments
*/
static void
parser_parse_function_arguments (parser_context_t *context_p, /**< context */
lexer_token_type_t end_type) /**< expected end type */
{
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION);
#if ENABLED (JERRY_ES2015)
bool duplicated_argument_names = false;
/* TODO: Currently async iterators are not supported, so generators ignore the async modifier. */
if ((context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_ASYNC)
&& !(context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION))
{
context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION;
}
#endif /* ENABLED (JERRY_ES2015) */
if (context_p->token.type == end_type)
{
#if ENABLED (JERRY_ES2015)
if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)
{
parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_GENERATOR);
parser_emit_cbc (context_p, CBC_POP);
}
context_p->status_flags &= (uint32_t) ~PARSER_DISALLOW_YIELD;
#endif /* ENABLED (JERRY_ES2015) */
scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS);
return;
}
scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_ARGS);
scanner_set_active (context_p);
while (true)
{
#if ENABLED (JERRY_ES2015)
if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)
{
parser_raise_error (context_p, PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER);
}
else if (context_p->token.type == LEXER_THREE_DOTS)
{
if (context_p->status_flags & PARSER_IS_PROPERTY_SETTER)
{
parser_raise_error (context_p, PARSER_ERR_SETTER_REST_PARAMETER);
}
lexer_next_token (context_p);
if (duplicated_argument_names)
{
parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES);
}
context_p->status_flags |= PARSER_FUNCTION_HAS_REST_PARAM | PARSER_FUNCTION_HAS_NON_SIMPLE_PARAM;
}
if (context_p->token.type == LEXER_LEFT_SQUARE || context_p->token.type == LEXER_LEFT_BRACE)
{
if (duplicated_argument_names)
{
parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES);
}
context_p->status_flags |= PARSER_FUNCTION_HAS_NON_SIMPLE_PARAM;
parser_emit_cbc_literal (context_p,
CBC_PUSH_LITERAL,
(uint16_t) (PARSER_REGISTER_START + context_p->argument_count));
uint32_t flags = (PARSER_PATTERN_BINDING
| PARSER_PATTERN_TARGET_ON_STACK
| PARSER_PATTERN_LOCAL
| PARSER_PATTERN_ARGUMENTS);
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
{
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER);
if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)
{
parser_raise_error (context_p, PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER);
}
flags |= PARSER_PATTERN_TARGET_DEFAULT;
}
parser_parse_initializer (context_p, flags);
context_p->argument_count++;
if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
if (context_p->token.type != LEXER_COMMA)
{
break;
}
lexer_next_token (context_p);
continue;
}
#endif /* ENABLED (JERRY_ES2015) */
if (context_p->token.type != LEXER_LITERAL
|| context_p->token.lit_location.type != LEXER_IDENT_LITERAL)
{
parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED);
}
lexer_construct_literal_object (context_p,
&context_p->token.lit_location,
LEXER_IDENT_LITERAL);
if (context_p->token.keyword_type >= LEXER_FIRST_NON_STRICT_ARGUMENTS)
{
context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG;
}
if (JERRY_UNLIKELY (context_p->lit_object.literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT))
{
#if ENABLED (JERRY_ES2015)
if (context_p->status_flags & PARSER_FUNCTION_HAS_NON_SIMPLE_PARAM)
{
parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES);
}
duplicated_argument_names = true;
#endif /* ENABLED (JERRY_ES2015) */
context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG;
}
else
{
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_FUNCTION_ARGUMENT;
}
context_p->argument_count++;
if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
lexer_next_token (context_p);
#if ENABLED (JERRY_ES2015)
if (context_p->token.type == LEXER_ASSIGN)
{
if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)
{
parser_raise_error (context_p, PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER);
}
parser_branch_t skip_init;
if (duplicated_argument_names)
{
parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES);
}
context_p->status_flags |= PARSER_FUNCTION_HAS_NON_SIMPLE_PARAM;
/* LEXER_ASSIGN does not overwrite lit_object. */
parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED);
parser_emit_cbc_literal (context_p, CBC_STRICT_EQUAL_RIGHT_LITERAL, context_p->lit_object.index);
parser_emit_cbc_forward_branch (context_p, CBC_BRANCH_IF_FALSE_FORWARD, &skip_init);
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL);
parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL);
parser_set_branch_to_current_position (context_p, &skip_init);
}
#endif /* ENABLED (JERRY_ES2015) */
if (context_p->token.type != LEXER_COMMA)
{
break;
}
lexer_next_token (context_p);
}
if (context_p->token.type != end_type)
{
parser_error_t error = ((end_type == LEXER_RIGHT_PAREN) ? PARSER_ERR_RIGHT_PAREN_EXPECTED
: PARSER_ERR_IDENTIFIER_EXPECTED);
parser_raise_error (context_p, error);
}
#if ENABLED (JERRY_ES2015)
if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)
{
parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_GENERATOR);
parser_emit_cbc (context_p, CBC_POP);
}
context_p->status_flags &= (uint32_t) ~PARSER_DISALLOW_YIELD;
#endif /* ENABLED (JERRY_ES2015) */
scanner_revert_active (context_p);
scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_BODY);
} /* parser_parse_function_arguments */
#ifndef JERRY_NDEBUG
JERRY_STATIC_ASSERT (PARSER_SCANNING_SUCCESSFUL == PARSER_HAS_LATE_LIT_INIT,
parser_scanning_successful_should_share_the_bit_position_with_parser_has_late_lit_init);
#endif /* !JERRY_NDEBUG */
/**
* Parse and compile EcmaScript source code
*
* Note: source must be a valid UTF-8 string
*
* @return compiled code
*/
static ecma_compiled_code_t *
parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
size_t arg_list_size, /**< size of function argument list */
const uint8_t *source_p, /**< valid UTF-8 source code */
size_t source_size, /**< size of the source code */
uint32_t parse_opts, /**< ecma_parse_opts_t option bits */
parser_error_location_t *error_location_p) /**< error location */
{
parser_context_t context;
ecma_compiled_code_t *compiled_code_p;
context.error = PARSER_ERR_NO_ERROR;
if (error_location_p != NULL)
{
error_location_p->error = PARSER_ERR_NO_ERROR;
}
if (arg_list_p == NULL)
{
context.status_flags = 0;
}
else
{
context.status_flags = PARSER_IS_FUNCTION;
#if ENABLED (JERRY_ES2015)
if (parse_opts & ECMA_PARSE_GENERATOR_FUNCTION)
{
context.status_flags |= PARSER_IS_GENERATOR_FUNCTION;
}
#endif /* ENABLED (JERRY_ES2015) */
}
#if ENABLED (JERRY_ES2015)
context.status_flags |= PARSER_GET_CLASS_PARSER_OPTS (parse_opts);
context.tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* ENABLED (JERRY_ES2015) */
context.stack_depth = 0;
context.stack_limit = 0;
context.last_context_p = NULL;
context.last_statement.current_p = NULL;
context.status_flags |= parse_opts & PARSER_STRICT_MODE_MASK;
context.token.flags = 0;
context.line = 1;
context.column = 1;
scanner_info_t scanner_info_end;
scanner_info_end.next_p = NULL;
scanner_info_end.source_p = NULL;
scanner_info_end.type = SCANNER_TYPE_END;
context.next_scanner_info_p = &scanner_info_end;
context.active_scanner_info_p = NULL;
context.skipped_scanner_info_p = NULL;
context.skipped_scanner_info_end_p = NULL;
context.last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
context.argument_count = 0;
context.register_count = 0;
context.literal_count = 0;
parser_cbc_stream_init (&context.byte_code);
context.byte_code_size = 0;
parser_list_init (&context.literal_pool,
sizeof (lexer_literal_t),
(uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_literal_t)));
context.scope_stack_p = NULL;
context.scope_stack_size = 0;
context.scope_stack_top = 0;
context.scope_stack_reg_top = 0;
#if ENABLED (JERRY_ES2015)
context.scope_stack_global_end = 0;
context.tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
context.context_stack_depth = 0;
#endif /* !JERRY_NDEBUG */
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
context.is_show_opcodes = (JERRY_CONTEXT (jerry_init_flags) & ECMA_INIT_SHOW_OPCODES);
context.total_byte_code_size = 0;
if (context.is_show_opcodes)
{
JERRY_DEBUG_MSG ("\n--- %s parsing start ---\n\n",
(arg_list_p == NULL) ? "Script"
: "Function");
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
#if ENABLED (JERRY_ES2015)
if (parse_opts & ECMA_PARSE_EVAL)
{
context.status_flags |= PARSER_IS_EVAL;
}
if (parse_opts & ECMA_PARSE_DIRECT_EVAL)
{
context.status_flags |= PARSER_IS_DIRECT_EVAL;
}
if (parse_opts & ECMA_PARSE_FUNCTION)
{
context.status_flags |= PARSER_IS_FUNCTION;
}
#endif /* ENABLED (JERRY_ES2015) */
scanner_scan_all (&context,
arg_list_p,
arg_list_p + arg_list_size,
source_p,
source_p + source_size);
if (JERRY_UNLIKELY (context.error != PARSER_ERR_NO_ERROR))
{
JERRY_ASSERT (context.error == PARSER_ERR_OUT_OF_MEMORY);
if (error_location_p != NULL)
{
error_location_p->error = context.error;
error_location_p->line = context.token.line;
error_location_p->column = context.token.column;
}
return NULL;
}
if (arg_list_p == NULL)
{
context.source_p = source_p;
context.source_end_p = source_p + source_size;
}
else
{
context.source_p = arg_list_p;
context.source_end_p = arg_list_p + arg_list_size;
}
context.u.allocated_buffer_p = NULL;
context.line = 1;
context.column = 1;
context.token.flags = 0;
parser_stack_init (&context);
#if ENABLED (JERRY_DEBUGGER)
context.breakpoint_info_count = 0;
#endif /* ENABLED (JERRY_DEBUGGER) */
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (context.status_flags & PARSER_IS_MODULE)
{
context.status_flags |= PARSER_IS_STRICT;
}
context.module_current_node_p = NULL;
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
PARSER_TRY (context.try_buffer)
{
/* Pushing a dummy value ensures the stack is never empty.
* This simplifies the stack management routines. */
parser_stack_push_uint8 (&context, CBC_MAXIMUM_BYTE_VALUE);
/* The next token must always be present to make decisions
* in the parser. Therefore when a token is consumed, the
* lexer_next_token() must be immediately called. */
lexer_next_token (&context);
if (arg_list_p != NULL)
{
parser_parse_function_arguments (&context, LEXER_EOS);
JERRY_ASSERT (context.next_scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS);
scanner_release_next (&context, sizeof (scanner_info_t));
context.source_p = source_p;
context.source_end_p = source_p + source_size;
context.line = 1;
context.column = 1;
lexer_next_token (&context);
}
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
else if (parse_opts & ECMA_PARSE_MODULE)
{
scanner_create_variables (&context, SCANNER_CREATE_VARS_NO_OPTS);
}
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
else
{
JERRY_ASSERT (context.next_scanner_info_p->source_p == source_p
&& context.next_scanner_info_p->type == SCANNER_TYPE_FUNCTION);
#if ENABLED (JERRY_ES2015)
if (scanner_is_global_context_needed (&context))
{
context.status_flags |= PARSER_LEXICAL_BLOCK_NEEDED;
}
if ((parse_opts & ECMA_PARSE_EVAL) == 0)
{
scanner_check_variables (&context);
}
#endif /* ENABLED (JERRY_ES2015) */
scanner_create_variables (&context, SCANNER_CREATE_VARS_IS_SCRIPT);
}
parser_parse_statements (&context);
/* When the parsing is successful, only the
* dummy value can be remained on the stack. */
JERRY_ASSERT (context.stack_top_uint8 == CBC_MAXIMUM_BYTE_VALUE
&& context.stack.last_position == 1
&& context.stack.first_p != NULL
&& context.stack.first_p->next_p == NULL
&& context.stack.last_p == NULL);
JERRY_ASSERT (context.last_statement.current_p == NULL);
JERRY_ASSERT (context.last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
JERRY_ASSERT (context.u.allocated_buffer_p == NULL);
#ifndef JERRY_NDEBUG
JERRY_ASSERT (context.status_flags & PARSER_SCANNING_SUCCESSFUL);
context.status_flags &= (uint32_t) ~PARSER_SCANNING_SUCCESSFUL;
#endif /* !JERRY_NDEBUG */
JERRY_ASSERT (!(context.status_flags & PARSER_HAS_LATE_LIT_INIT));
compiled_code_p = parser_post_processing (&context);
parser_list_free (&context.literal_pool);
JERRY_ASSERT (arg_list_p != NULL || !(context.status_flags & PARSER_ARGUMENTS_NEEDED));
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
if (context.is_show_opcodes)
{
JERRY_DEBUG_MSG ("\n%s parsing successfully completed. Total byte code size: %d bytes\n",
(arg_list_p == NULL) ? "Script"
: "Function",
(int) context.total_byte_code_size);
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
}
PARSER_CATCH
{
if (context.last_statement.current_p != NULL)
{
parser_free_jumps (context.last_statement);
}
parser_free_allocated_buffer (&context);
scanner_cleanup (&context);
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (context.module_current_node_p != NULL)
{
ecma_module_release_module_nodes (context.module_current_node_p);
}
#endif
if (error_location_p != NULL)
{
error_location_p->error = context.error;
error_location_p->line = context.token.line;
error_location_p->column = context.token.column;
}
compiled_code_p = NULL;
parser_free_literals (&context.literal_pool);
parser_cbc_stream_free (&context.byte_code);
}
PARSER_TRY_END
if (context.scope_stack_p != NULL)
{
parser_free (context.scope_stack_p, context.scope_stack_size * sizeof (parser_scope_stack_t));
}
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
if (context.is_show_opcodes)
{
JERRY_DEBUG_MSG ("\n--- %s parsing end ---\n\n",
(arg_list_p == NULL) ? "Script"
: "Function");
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
parser_stack_free (&context);
return compiled_code_p;
} /* parser_parse_source */
/**
* Save parser context before function parsing.
*/
static void
parser_save_context (parser_context_t *context_p, /**< context */
parser_saved_context_t *saved_context_p) /**< target for saving the context */
{
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
#if ENABLED (JERRY_DEBUGGER)
if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
&& context_p->breakpoint_info_count > 0)
{
parser_send_breakpoints (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST);
context_p->breakpoint_info_count = 0;
}
#endif /* ENABLED (JERRY_DEBUGGER) */
/* Save private part of the context. */
saved_context_p->status_flags = context_p->status_flags;
saved_context_p->stack_depth = context_p->stack_depth;
saved_context_p->stack_limit = context_p->stack_limit;
saved_context_p->prev_context_p = context_p->last_context_p;
saved_context_p->last_statement = context_p->last_statement;
saved_context_p->argument_count = context_p->argument_count;
saved_context_p->register_count = context_p->register_count;
saved_context_p->literal_count = context_p->literal_count;
saved_context_p->byte_code = context_p->byte_code;
saved_context_p->byte_code_size = context_p->byte_code_size;
saved_context_p->literal_pool_data = context_p->literal_pool.data;
saved_context_p->scope_stack_p = context_p->scope_stack_p;
saved_context_p->scope_stack_size = context_p->scope_stack_size;
saved_context_p->scope_stack_top = context_p->scope_stack_top;
saved_context_p->scope_stack_reg_top = context_p->scope_stack_reg_top;
#if ENABLED (JERRY_ES2015)
saved_context_p->scope_stack_global_end = context_p->scope_stack_global_end;
saved_context_p->tagged_template_literal_cp = context_p->tagged_template_literal_cp;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
saved_context_p->context_stack_depth = context_p->context_stack_depth;
#endif /* !JERRY_NDEBUG */
/* Reset private part of the context. */
context_p->status_flags &= PARSER_IS_STRICT;
context_p->stack_depth = 0;
context_p->stack_limit = 0;
context_p->last_context_p = saved_context_p;
context_p->last_statement.current_p = NULL;
context_p->argument_count = 0;
context_p->register_count = 0;
context_p->literal_count = 0;
parser_cbc_stream_init (&context_p->byte_code);
context_p->byte_code_size = 0;
parser_list_reset (&context_p->literal_pool);
context_p->scope_stack_p = NULL;
context_p->scope_stack_size = 0;
context_p->scope_stack_top = 0;
context_p->scope_stack_reg_top = 0;
#if ENABLED (JERRY_ES2015)
context_p->scope_stack_global_end = 0;
context_p->tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
context_p->context_stack_depth = 0;
#endif /* !JERRY_NDEBUG */
} /* parser_save_context */
/**
* Restore parser context after function parsing.
*/
static void
parser_restore_context (parser_context_t *context_p, /**< context */
parser_saved_context_t *saved_context_p) /**< target for saving the context */
{
parser_list_free (&context_p->literal_pool);
if (context_p->scope_stack_p != NULL)
{
parser_free (context_p->scope_stack_p, context_p->scope_stack_size * sizeof (parser_scope_stack_t));
}
/* Restore private part of the context. */
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
context_p->status_flags = saved_context_p->status_flags;
context_p->stack_depth = saved_context_p->stack_depth;
context_p->stack_limit = saved_context_p->stack_limit;
context_p->last_context_p = saved_context_p->prev_context_p;
context_p->last_statement = saved_context_p->last_statement;
context_p->argument_count = saved_context_p->argument_count;
context_p->register_count = saved_context_p->register_count;
context_p->literal_count = saved_context_p->literal_count;
context_p->byte_code = saved_context_p->byte_code;
context_p->byte_code_size = saved_context_p->byte_code_size;
context_p->literal_pool.data = saved_context_p->literal_pool_data;
context_p->scope_stack_p = saved_context_p->scope_stack_p;
context_p->scope_stack_size = saved_context_p->scope_stack_size;
context_p->scope_stack_top = saved_context_p->scope_stack_top;
context_p->scope_stack_reg_top = saved_context_p->scope_stack_reg_top;
#if ENABLED (JERRY_ES2015)
context_p->scope_stack_global_end = saved_context_p->scope_stack_global_end;
context_p->tagged_template_literal_cp = saved_context_p->tagged_template_literal_cp;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
context_p->context_stack_depth = saved_context_p->context_stack_depth;
#endif /* !JERRY_NDEBUG */
} /* parser_restore_context */
/**
* Parse function code
*
* @return compiled code
*/
ecma_compiled_code_t *
parser_parse_function (parser_context_t *context_p, /**< context */
uint32_t status_flags) /**< extra status flags */
{
parser_saved_context_t saved_context;
ecma_compiled_code_t *compiled_code_p;
JERRY_ASSERT (status_flags & PARSER_IS_FUNCTION);
parser_save_context (context_p, &saved_context);
context_p->status_flags |= status_flags;
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
if (context_p->is_show_opcodes)
{
#if ENABLED (JERRY_ES2015)
JERRY_DEBUG_MSG ("\n--- %s parsing start ---\n\n",
(context_p->status_flags & PARSER_CLASS_CONSTRUCTOR) ? "Class constructor"
: "Function");
#else /* !ENABLED (JERRY_ES2015) */
JERRY_DEBUG_MSG ("\n--- Function parsing start ---\n\n");
#endif /* ENABLED (JERRY_ES2015) */
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
#if ENABLED (JERRY_DEBUGGER)
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{
jerry_debugger_send_parse_function (context_p->token.line, context_p->token.column);
}
#endif /* ENABLED (JERRY_DEBUGGER) */
lexer_next_token (context_p);
if (context_p->token.type != LEXER_LEFT_PAREN)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED);
}
lexer_next_token (context_p);
parser_parse_function_arguments (context_p, LEXER_RIGHT_PAREN);
lexer_next_token (context_p);
if ((context_p->status_flags & PARSER_IS_PROPERTY_GETTER)
&& context_p->argument_count != 0)
{
parser_raise_error (context_p, PARSER_ERR_NO_ARGUMENTS_EXPECTED);
}
if ((context_p->status_flags & PARSER_IS_PROPERTY_SETTER)
&& context_p->argument_count != 1)
{
parser_raise_error (context_p, PARSER_ERR_ONE_ARGUMENT_EXPECTED);
}
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
if (context_p->is_show_opcodes
&& (context_p->status_flags & PARSER_HAS_NON_STRICT_ARG))
{
JERRY_DEBUG_MSG (" Note: legacy (non-strict) argument definition\n\n");
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
if (context_p->token.type != LEXER_LEFT_BRACE)
{
parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED);
}
lexer_next_token (context_p);
#if ENABLED (JERRY_ES2015)
if ((context_p->status_flags & PARSER_CLASS_CONSTRUCTOR_SUPER) == PARSER_CLASS_CONSTRUCTOR_SUPER)
{
context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;
}
#endif /* ENABLED (JERRY_ES2015) */
parser_parse_statements (context_p);
compiled_code_p = parser_post_processing (context_p);
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
if (context_p->is_show_opcodes)
{
#if ENABLED (JERRY_ES2015)
JERRY_DEBUG_MSG ("\n--- %s parsing end ---\n\n",
(context_p->status_flags & PARSER_CLASS_CONSTRUCTOR) ? "Class constructor"
: "Function");
#else /* !ENABLED (JERRY_ES2015) */
JERRY_DEBUG_MSG ("\n--- Function parsing end ---\n\n");
#endif /* ENABLED (JERRY_ES2015) */
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
parser_restore_context (context_p, &saved_context);
return compiled_code_p;
} /* parser_parse_function */
#if ENABLED (JERRY_ES2015)
/**
* Parse arrow function code
*
* @return compiled code
*/
ecma_compiled_code_t *
parser_parse_arrow_function (parser_context_t *context_p, /**< context */
uint32_t status_flags) /**< extra status flags */
{
parser_saved_context_t saved_context;
ecma_compiled_code_t *compiled_code_p;
JERRY_ASSERT ((status_flags & PARSER_IS_FUNCTION)
&& (status_flags & PARSER_IS_ARROW_FUNCTION));
parser_save_context (context_p, &saved_context);
context_p->status_flags |= status_flags;
#if ENABLED (JERRY_ES2015)
context_p->status_flags |= saved_context.status_flags & PARSER_CLASS_HAS_SUPER;
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
if (context_p->is_show_opcodes)
{
JERRY_DEBUG_MSG ("\n--- Arrow function parsing start ---\n\n");
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
#if ENABLED (JERRY_DEBUGGER)
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{
jerry_debugger_send_parse_function (context_p->token.line, context_p->token.column);
}
#endif /* ENABLED (JERRY_DEBUGGER) */
if (context_p->token.type == LEXER_ARROW_LEFT_PAREN)
{
lexer_next_token (context_p);
parser_parse_function_arguments (context_p, LEXER_RIGHT_PAREN);
lexer_next_token (context_p);
}
else
{
parser_parse_function_arguments (context_p, LEXER_ARROW);
}
JERRY_ASSERT (context_p->token.type == LEXER_ARROW);
lexer_next_token (context_p);
if (context_p->token.type == LEXER_LEFT_BRACE)
{
lexer_next_token (context_p);
context_p->status_flags |= PARSER_IS_CLOSURE;
parser_parse_statements (context_p);
/* Unlike normal function, arrow functions consume their close brace. */
JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE);
lexer_next_token (context_p);
}
else
{
if (context_p->status_flags & PARSER_IS_STRICT
&& context_p->status_flags & PARSER_HAS_NON_STRICT_ARG)
{
parser_raise_error (context_p, PARSER_ERR_NON_STRICT_ARG_DEFINITION);
}
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
#if ENABLED (JERRY_ES2015)
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
{
parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE);
}
else
{
#endif /* ENABLED (JERRY_ES2015) */
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
{
context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL;
}
else
{
parser_emit_cbc (context_p, CBC_RETURN);
}
#if ENABLED (JERRY_ES2015)
}
#endif /* ENABLED (JERRY_ES2015) */
parser_flush_cbc (context_p);
}
compiled_code_p = parser_post_processing (context_p);
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
if (context_p->is_show_opcodes)
{
JERRY_DEBUG_MSG ("\n--- Arrow function parsing end ---\n\n");
}
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
parser_restore_context (context_p, &saved_context);
return compiled_code_p;
} /* parser_parse_arrow_function */
#endif /* ENABLED (JERRY_ES2015) */
/**
* Raise a parse error.
*/
void
parser_raise_error (parser_context_t *context_p, /**< context */
parser_error_t error) /**< error code */
{
/* Must be compatible with the scanner because
* the lexer might throws errors during prescanning. */
parser_saved_context_t *saved_context_p = context_p->last_context_p;
while (saved_context_p != NULL)
{
parser_cbc_stream_free (&saved_context_p->byte_code);
/* First the current literal pool is freed, and then it is replaced
* by the literal pool coming from the saved context. Since literals
* are not used anymore, this is a valid replacement. The last pool
* is freed by parser_parse_source. */
parser_free_literals (&context_p->literal_pool);
context_p->literal_pool.data = saved_context_p->literal_pool_data;
if (context_p->scope_stack_p != NULL)
{
parser_free (context_p->scope_stack_p, context_p->scope_stack_size * sizeof (parser_scope_stack_t));
}
context_p->scope_stack_p = saved_context_p->scope_stack_p;
context_p->scope_stack_size = saved_context_p->scope_stack_size;
if (saved_context_p->last_statement.current_p != NULL)
{
parser_free_jumps (saved_context_p->last_statement);
}
#if ENABLED (JERRY_ES2015)
if (saved_context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
ecma_collection_free (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
saved_context_p->tagged_template_literal_cp));
}
#endif /* ENABLED (JERRY_ES2015) */
saved_context_p = saved_context_p->prev_context_p;
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
ecma_collection_free (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
context_p->tagged_template_literal_cp));
}
#endif /* ENABLED (JERRY_ES2015) */
context_p->error = error;
PARSER_THROW (context_p->try_buffer);
/* Should never been reached. */
JERRY_ASSERT (0);
} /* parser_raise_error */
#endif /* ENABLED (JERRY_PARSER) */
/**
* Parse EcmaScript source code
*
* Note:
* if arg_list_p is not NULL, a function body is parsed
* returned value must be freed with ecma_free_value
*
* @return true - if success
* syntax error - otherwise
*/
ecma_value_t
parser_parse_script (const uint8_t *arg_list_p, /**< function argument list */
size_t arg_list_size, /**< size of function argument list */
const uint8_t *source_p, /**< source code */
size_t source_size, /**< size of the source code */
uint32_t parse_opts, /**< ecma_parse_opts_t option bits */
ecma_compiled_code_t **bytecode_data_p) /**< [out] JS bytecode */
{
#if ENABLED (JERRY_PARSER)
parser_error_location_t parser_error;
#if ENABLED (JERRY_DEBUGGER)
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{
jerry_debugger_send_string (JERRY_DEBUGGER_SOURCE_CODE,
JERRY_DEBUGGER_NO_SUBTYPE,
source_p,
source_size);
}
#endif /* ENABLED (JERRY_DEBUGGER) */
*bytecode_data_p = parser_parse_source (arg_list_p,
arg_list_size,
source_p,
source_size,
parse_opts,
&parser_error);
if (!*bytecode_data_p)
{
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (JERRY_CONTEXT (module_top_context_p) != NULL)
{
ecma_module_cleanup ();
}
#endif
#if ENABLED (JERRY_DEBUGGER)
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{
jerry_debugger_send_type (JERRY_DEBUGGER_PARSE_ERROR);
}
#endif /* ENABLED (JERRY_DEBUGGER) */
if (parser_error.error == PARSER_ERR_OUT_OF_MEMORY)
{
/* It is unlikely that memory can be allocated in an out-of-memory
* situation. However, a simple value can still be thrown. */
jcontext_raise_exception (ECMA_VALUE_NULL);
return ECMA_VALUE_ERROR;
}
#if ENABLED (JERRY_ERROR_MESSAGES)
const lit_utf8_byte_t *err_bytes_p = (const lit_utf8_byte_t *) parser_error_to_string (parser_error.error);
lit_utf8_size_t err_bytes_size = lit_zt_utf8_string_size (err_bytes_p);
ecma_string_t *err_str_p = ecma_new_ecma_string_from_utf8 (err_bytes_p, err_bytes_size);
ecma_value_t err_str_val = ecma_make_string_value (err_str_p);
ecma_value_t line_str_val = ecma_make_uint32_value (parser_error.line);
ecma_value_t col_str_val = ecma_make_uint32_value (parser_error.column);
ecma_value_t error_value = ecma_raise_standard_error_with_format (ECMA_ERROR_SYNTAX,
"% [%:%:%]",
err_str_val,
JERRY_CONTEXT (resource_name),
line_str_val,
col_str_val);
ecma_free_value (col_str_val);
ecma_free_value (line_str_val);
ecma_free_value (err_str_val);
return error_value;
#else /* !ENABLED (JERRY_ERROR_MESSAGES) */
return ecma_raise_syntax_error ("");
#endif /* ENABLED (JERRY_ERROR_MESSAGES) */
}
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (JERRY_CONTEXT (module_top_context_p) != NULL)
{
ecma_value_t ret_value = ecma_module_parse_modules ();
if (ECMA_IS_VALUE_ERROR (ret_value))
{
ecma_bytecode_deref (*bytecode_data_p);
*bytecode_data_p = NULL;
ecma_module_cleanup ();
return ret_value;
}
}
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
#if ENABLED (JERRY_DEBUGGER)
if ((JERRY_CONTEXT (debugger_flags) & (JERRY_DEBUGGER_CONNECTED | JERRY_DEBUGGER_PARSER_WAIT))
== (JERRY_DEBUGGER_CONNECTED | JERRY_DEBUGGER_PARSER_WAIT))
{
JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_PARSER_WAIT_MODE);
jerry_debugger_send_type (JERRY_DEBUGGER_WAITING_AFTER_PARSE);
while (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_PARSER_WAIT_MODE)
{
jerry_debugger_receive (NULL);
if (!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED))
{
break;
}
jerry_debugger_transport_sleep ();
}
}
#endif /* ENABLED (JERRY_DEBUGGER) */
return ECMA_VALUE_TRUE;
#else /* !ENABLED (JERRY_PARSER) */
JERRY_UNUSED (arg_list_p);
JERRY_UNUSED (arg_list_size);
JERRY_UNUSED (source_p);
JERRY_UNUSED (source_size);
JERRY_UNUSED (parse_opts);
JERRY_UNUSED (bytecode_data_p);
return ecma_raise_syntax_error (ECMA_ERR_MSG ("The parser has been disabled."));
#endif /* ENABLED (JERRY_PARSER) */
} /* parser_parse_script */
/**
* @}
* @}
* @}
*/