mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
This patch fixes #4892 JerryScript-DCO-1.0-Signed-off-by: Daniel Batiz daniel.batiz@h-lab.eu
3154 lines
98 KiB
C
3154 lines
98 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-helpers.h"
|
|
#include "ecma-lex-env.h"
|
|
|
|
#include "jcontext.h"
|
|
#include "js-parser-internal.h"
|
|
#include "js-scanner-internal.h"
|
|
#include "lit-char-helpers.h"
|
|
|
|
#if JERRY_PARSER
|
|
|
|
/** \addtogroup parser Parser
|
|
* @{
|
|
*
|
|
* \addtogroup jsparser JavaScript
|
|
* @{
|
|
*
|
|
* \addtogroup jsparser_scanner Scanner
|
|
* @{
|
|
*/
|
|
|
|
JERRY_STATIC_ASSERT (PARSER_MAXIMUM_NUMBER_OF_LITERALS + PARSER_MAXIMUM_NUMBER_OF_REGISTERS < PARSER_REGISTER_START,
|
|
maximum_number_of_literals_plus_registers_must_be_less_than_register_start);
|
|
|
|
#if JERRY_ESNEXT
|
|
|
|
JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG & SCANNER_LITERAL_IS_LOCAL) == 0,
|
|
is_arrow_arg_binding_flag_must_not_use_local_flags);
|
|
|
|
JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_LET & SCANNER_LITERAL_IS_LOCAL) != 0, is_let_flag_must_use_local_flags);
|
|
|
|
JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_CONST & SCANNER_LITERAL_IS_LOCAL) != 0, is_const_flag_must_use_local_flags);
|
|
|
|
JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_FUNC_DECLARATION & SCANNER_LITERAL_IS_LOCAL) != 0,
|
|
is_func_declaration_flag_must_use_local_flags);
|
|
|
|
JERRY_STATIC_ASSERT ((SCANNER_LITERAL_IS_DESTRUCTURED_ARG & SCANNER_LITERAL_IS_LOCAL) != 0,
|
|
is_arg_binding_flag_must_use_local_flags);
|
|
|
|
JERRY_STATIC_ASSERT (SCANNER_LITERAL_IS_FUNC_DECLARATION != SCANNER_LITERAL_IS_DESTRUCTURED_ARG,
|
|
is_func_declaration_must_be_different_from_is_arg_binding);
|
|
|
|
JERRY_STATIC_ASSERT (PARSER_SCOPE_STACK_IS_CONST_REG == PARSER_SCOPE_STACK_IS_LOCAL_CREATED,
|
|
scope_stack_is_const_reg_and_scope_stack_is_local_created_must_be_the_same);
|
|
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
/**
|
|
* Raise a scanner error.
|
|
*/
|
|
void
|
|
scanner_raise_error (parser_context_t *context_p) /**< context */
|
|
{
|
|
PARSER_THROW (context_p->try_buffer);
|
|
/* Should never been reached. */
|
|
JERRY_ASSERT (0);
|
|
} /* scanner_raise_error */
|
|
|
|
#if JERRY_ESNEXT
|
|
|
|
/**
|
|
* Raise a variable redeclaration error.
|
|
*/
|
|
void
|
|
scanner_raise_redeclaration_error (parser_context_t *context_p) /**< context */
|
|
{
|
|
scanner_info_t *info_p = scanner_insert_info (context_p, context_p->source_p, sizeof (scanner_info_t));
|
|
info_p->type = SCANNER_TYPE_ERR_REDECLARED;
|
|
|
|
scanner_raise_error (context_p);
|
|
} /* scanner_raise_redeclaration_error */
|
|
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
/**
|
|
* Allocate memory for scanner.
|
|
*
|
|
* @return allocated memory
|
|
*/
|
|
void *
|
|
scanner_malloc (parser_context_t *context_p, /**< context */
|
|
size_t size) /**< size of the memory block */
|
|
{
|
|
void *result;
|
|
|
|
JERRY_ASSERT (size > 0);
|
|
result = jmem_heap_alloc_block_null_on_error (size);
|
|
|
|
if (result == NULL)
|
|
{
|
|
scanner_cleanup (context_p);
|
|
|
|
/* This is the only error which specify its reason. */
|
|
context_p->error = PARSER_ERR_OUT_OF_MEMORY;
|
|
PARSER_THROW (context_p->try_buffer);
|
|
}
|
|
return result;
|
|
} /* scanner_malloc */
|
|
|
|
/**
|
|
* Free memory allocated by scanner_malloc.
|
|
*/
|
|
extern inline void JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_free (void *ptr, /**< pointer to free */
|
|
size_t size) /**< size of the memory block */
|
|
{
|
|
jmem_heap_free_block (ptr, size);
|
|
} /* scanner_free */
|
|
|
|
/**
|
|
* Count the size of a stream after an info block.
|
|
*
|
|
* @return the size in bytes
|
|
*/
|
|
size_t
|
|
scanner_get_stream_size (scanner_info_t *info_p, /**< scanner info block */
|
|
size_t size) /**< size excluding the stream */
|
|
{
|
|
const uint8_t *data_p = ((const uint8_t *) info_p) + size;
|
|
const uint8_t *data_p_start = data_p;
|
|
|
|
while (data_p[0] != SCANNER_STREAM_TYPE_END)
|
|
{
|
|
switch (data_p[0] & SCANNER_STREAM_TYPE_MASK)
|
|
{
|
|
case SCANNER_STREAM_TYPE_VAR:
|
|
#if JERRY_ESNEXT
|
|
case SCANNER_STREAM_TYPE_LET:
|
|
case SCANNER_STREAM_TYPE_CONST:
|
|
case SCANNER_STREAM_TYPE_LOCAL:
|
|
#endif /* JERRY_ESNEXT */
|
|
#if JERRY_MODULE_SYSTEM
|
|
case SCANNER_STREAM_TYPE_IMPORT:
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
case SCANNER_STREAM_TYPE_ARG:
|
|
#if JERRY_ESNEXT
|
|
case SCANNER_STREAM_TYPE_ARG_VAR:
|
|
case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG:
|
|
case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR:
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
case SCANNER_STREAM_TYPE_ARG_FUNC:
|
|
#if JERRY_ESNEXT
|
|
case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC:
|
|
#endif /* JERRY_ESNEXT */
|
|
case SCANNER_STREAM_TYPE_FUNC:
|
|
{
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
JERRY_ASSERT ((data_p[0] & SCANNER_STREAM_TYPE_MASK) == SCANNER_STREAM_TYPE_HOLE
|
|
|| SCANNER_STREAM_TYPE_IS_ARGUMENTS (data_p[0] & SCANNER_STREAM_TYPE_MASK));
|
|
data_p++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
data_p += 3;
|
|
|
|
if (data_p[-3] & SCANNER_STREAM_UINT16_DIFF)
|
|
{
|
|
data_p++;
|
|
}
|
|
else if (data_p[-1] == 0)
|
|
{
|
|
data_p += sizeof (const uint8_t *);
|
|
}
|
|
}
|
|
|
|
return size + 1 + (size_t) (data_p - data_p_start);
|
|
} /* scanner_get_stream_size */
|
|
|
|
/**
|
|
* Insert a scanner info block into the scanner info chain.
|
|
*
|
|
* @return newly allocated scanner info
|
|
*/
|
|
scanner_info_t *
|
|
scanner_insert_info (parser_context_t *context_p, /**< context */
|
|
const uint8_t *source_p, /**< triggering position */
|
|
size_t size) /**< size of the memory block */
|
|
{
|
|
scanner_info_t *new_scanner_info_p = (scanner_info_t *) scanner_malloc (context_p, size);
|
|
scanner_info_t *scanner_info_p = context_p->next_scanner_info_p;
|
|
scanner_info_t *prev_scanner_info_p = NULL;
|
|
|
|
JERRY_ASSERT (scanner_info_p != NULL);
|
|
JERRY_ASSERT (source_p != NULL);
|
|
|
|
new_scanner_info_p->source_p = source_p;
|
|
|
|
while (source_p < scanner_info_p->source_p)
|
|
{
|
|
prev_scanner_info_p = scanner_info_p;
|
|
scanner_info_p = scanner_info_p->next_p;
|
|
|
|
JERRY_ASSERT (scanner_info_p != NULL);
|
|
}
|
|
|
|
/* Multiple scanner info blocks cannot be assigned to the same position. */
|
|
JERRY_ASSERT (source_p != scanner_info_p->source_p);
|
|
|
|
new_scanner_info_p->next_p = scanner_info_p;
|
|
|
|
if (JERRY_LIKELY (prev_scanner_info_p == NULL))
|
|
{
|
|
context_p->next_scanner_info_p = new_scanner_info_p;
|
|
}
|
|
else
|
|
{
|
|
prev_scanner_info_p->next_p = new_scanner_info_p;
|
|
}
|
|
|
|
return new_scanner_info_p;
|
|
} /* scanner_insert_info */
|
|
|
|
/**
|
|
* Insert a scanner info block into the scanner info chain before a given info block.
|
|
*
|
|
* @return newly allocated scanner info
|
|
*/
|
|
scanner_info_t *
|
|
scanner_insert_info_before (parser_context_t *context_p, /**< context */
|
|
const uint8_t *source_p, /**< triggering position */
|
|
scanner_info_t *start_info_p, /**< first info position */
|
|
size_t size) /**< size of the memory block */
|
|
{
|
|
JERRY_ASSERT (start_info_p != NULL);
|
|
|
|
scanner_info_t *new_scanner_info_p = (scanner_info_t *) scanner_malloc (context_p, size);
|
|
scanner_info_t *scanner_info_p = start_info_p->next_p;
|
|
scanner_info_t *prev_scanner_info_p = start_info_p;
|
|
|
|
new_scanner_info_p->source_p = source_p;
|
|
|
|
while (source_p < scanner_info_p->source_p)
|
|
{
|
|
prev_scanner_info_p = scanner_info_p;
|
|
scanner_info_p = scanner_info_p->next_p;
|
|
|
|
JERRY_ASSERT (scanner_info_p != NULL);
|
|
}
|
|
|
|
/* Multiple scanner info blocks cannot be assigned to the same position. */
|
|
JERRY_ASSERT (source_p != scanner_info_p->source_p);
|
|
|
|
new_scanner_info_p->next_p = scanner_info_p;
|
|
|
|
prev_scanner_info_p->next_p = new_scanner_info_p;
|
|
return new_scanner_info_p;
|
|
} /* scanner_insert_info_before */
|
|
|
|
/**
|
|
* Release the next scanner info.
|
|
*/
|
|
extern inline void JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_release_next (parser_context_t *context_p, /**< context */
|
|
size_t size) /**< size of the memory block */
|
|
{
|
|
scanner_info_t *next_p = context_p->next_scanner_info_p->next_p;
|
|
|
|
jmem_heap_free_block (context_p->next_scanner_info_p, size);
|
|
context_p->next_scanner_info_p = next_p;
|
|
} /* scanner_release_next */
|
|
|
|
/**
|
|
* Set the active scanner info to the next scanner info.
|
|
*/
|
|
extern inline void JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_set_active (parser_context_t *context_p) /**< context */
|
|
{
|
|
scanner_info_t *scanner_info_p = context_p->next_scanner_info_p;
|
|
|
|
context_p->next_scanner_info_p = scanner_info_p->next_p;
|
|
scanner_info_p->next_p = context_p->active_scanner_info_p;
|
|
context_p->active_scanner_info_p = scanner_info_p;
|
|
} /* scanner_set_active */
|
|
|
|
/**
|
|
* Set the next scanner info to the active scanner info.
|
|
*/
|
|
extern inline void JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_revert_active (parser_context_t *context_p) /**< context */
|
|
{
|
|
scanner_info_t *scanner_info_p = context_p->active_scanner_info_p;
|
|
|
|
context_p->active_scanner_info_p = scanner_info_p->next_p;
|
|
scanner_info_p->next_p = context_p->next_scanner_info_p;
|
|
context_p->next_scanner_info_p = scanner_info_p;
|
|
} /* scanner_revert_active */
|
|
|
|
/**
|
|
* Release the active scanner info.
|
|
*/
|
|
extern inline void JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_release_active (parser_context_t *context_p, /**< context */
|
|
size_t size) /**< size of the memory block */
|
|
{
|
|
scanner_info_t *next_p = context_p->active_scanner_info_p->next_p;
|
|
|
|
jmem_heap_free_block (context_p->active_scanner_info_p, size);
|
|
context_p->active_scanner_info_p = next_p;
|
|
} /* scanner_release_active */
|
|
|
|
/**
|
|
* Release switch cases.
|
|
*/
|
|
void
|
|
scanner_release_switch_cases (scanner_case_info_t *case_p) /**< case list */
|
|
{
|
|
while (case_p != NULL)
|
|
{
|
|
scanner_case_info_t *next_p = case_p->next_p;
|
|
|
|
jmem_heap_free_block (case_p, sizeof (scanner_case_info_t));
|
|
case_p = next_p;
|
|
}
|
|
} /* scanner_release_switch_cases */
|
|
|
|
/**
|
|
* Release private fields.
|
|
*/
|
|
void
|
|
scanner_release_private_fields (scanner_class_private_member_t *member_p) /**< private member list */
|
|
{
|
|
while (member_p != NULL)
|
|
{
|
|
scanner_class_private_member_t *prev_p = member_p->prev_p;
|
|
|
|
jmem_heap_free_block (member_p, sizeof (scanner_class_private_member_t));
|
|
member_p = prev_p;
|
|
}
|
|
} /* scanner_release_private_fields */
|
|
|
|
/**
|
|
* Seek to correct position in the scanner info list.
|
|
*/
|
|
void
|
|
scanner_seek (parser_context_t *context_p) /**< context */
|
|
{
|
|
const uint8_t *source_p = context_p->source_p;
|
|
scanner_info_t *prev_p;
|
|
|
|
if (context_p->skipped_scanner_info_p != NULL)
|
|
{
|
|
JERRY_ASSERT (context_p->skipped_scanner_info_p->source_p != NULL);
|
|
|
|
context_p->skipped_scanner_info_end_p->next_p = context_p->next_scanner_info_p;
|
|
|
|
if (context_p->skipped_scanner_info_end_p->source_p <= source_p)
|
|
{
|
|
prev_p = context_p->skipped_scanner_info_end_p;
|
|
}
|
|
else
|
|
{
|
|
prev_p = context_p->skipped_scanner_info_p;
|
|
|
|
if (prev_p->source_p > source_p)
|
|
{
|
|
context_p->next_scanner_info_p = prev_p;
|
|
context_p->skipped_scanner_info_p = NULL;
|
|
return;
|
|
}
|
|
|
|
context_p->skipped_scanner_info_p = prev_p;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prev_p = context_p->next_scanner_info_p;
|
|
|
|
if (prev_p->source_p == NULL || prev_p->source_p > source_p)
|
|
{
|
|
return;
|
|
}
|
|
|
|
context_p->skipped_scanner_info_p = prev_p;
|
|
}
|
|
|
|
while (prev_p->next_p->source_p != NULL && prev_p->next_p->source_p <= source_p)
|
|
{
|
|
prev_p = prev_p->next_p;
|
|
}
|
|
|
|
context_p->skipped_scanner_info_end_p = prev_p;
|
|
context_p->next_scanner_info_p = prev_p->next_p;
|
|
} /* scanner_seek */
|
|
|
|
/**
|
|
* Checks whether a literal is equal to "arguments".
|
|
*/
|
|
static inline bool JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_literal_is_arguments (lexer_lit_location_t *literal_p) /**< literal */
|
|
{
|
|
return lexer_compare_identifier_to_string (literal_p, (const uint8_t *) "arguments", 9);
|
|
} /* scanner_literal_is_arguments */
|
|
|
|
#if JERRY_ESNEXT
|
|
|
|
/**
|
|
* Find if there is a duplicated argument in the given context
|
|
*
|
|
* @return true - if there are duplicates, false - otherwise
|
|
*/
|
|
static bool
|
|
scanner_find_duplicated_arg (parser_context_t *context_p, lexer_lit_location_t *lit_loc_p)
|
|
{
|
|
if (!(context_p->status_flags & PARSER_FUNCTION_IS_PARSING_ARGS))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (scanner_literal_is_arguments (lit_loc_p))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
uint16_t register_end, encoding_limit, encoding_delta;
|
|
ecma_value_t *literal_p;
|
|
ecma_value_t *literal_start_p;
|
|
|
|
const ecma_compiled_code_t *bytecode_header_p = JERRY_CONTEXT (vm_top_context_p)->shared_p->bytecode_header_p;
|
|
|
|
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
|
|
{
|
|
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p;
|
|
|
|
register_end = args_p->register_end;
|
|
|
|
literal_p = (ecma_value_t *) (args_p + 1);
|
|
literal_p -= register_end;
|
|
literal_start_p = literal_p;
|
|
literal_p += args_p->literal_end;
|
|
}
|
|
else
|
|
{
|
|
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p;
|
|
|
|
register_end = args_p->register_end;
|
|
|
|
literal_p = (ecma_value_t *) (args_p + 1);
|
|
literal_p -= register_end;
|
|
literal_start_p = literal_p;
|
|
literal_p += args_p->literal_end;
|
|
}
|
|
|
|
if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING))
|
|
{
|
|
encoding_limit = CBC_SMALL_LITERAL_ENCODING_LIMIT;
|
|
encoding_delta = CBC_SMALL_LITERAL_ENCODING_DELTA;
|
|
}
|
|
else
|
|
{
|
|
encoding_limit = CBC_FULL_LITERAL_ENCODING_LIMIT;
|
|
encoding_delta = CBC_FULL_LITERAL_ENCODING_DELTA;
|
|
}
|
|
|
|
uint8_t *byte_code_p = (uint8_t *) literal_p;
|
|
|
|
bool found_duplicate = false;
|
|
|
|
while (*byte_code_p == CBC_CREATE_LOCAL)
|
|
{
|
|
byte_code_p++;
|
|
uint16_t literal_index = *byte_code_p++;
|
|
|
|
if (literal_index >= encoding_limit)
|
|
{
|
|
literal_index = (uint16_t) (((literal_index << 8) | *byte_code_p++) - encoding_delta);
|
|
}
|
|
|
|
ecma_string_t *arg_string = ecma_get_string_from_value (literal_start_p[literal_index]);
|
|
uint8_t *destination_p = (uint8_t *) parser_malloc (context_p, lit_loc_p->length);
|
|
lexer_convert_ident_to_cesu8 (destination_p, lit_loc_p->char_p, lit_loc_p->length);
|
|
ecma_string_t *search_key_p = ecma_new_ecma_string_from_utf8 (destination_p, lit_loc_p->length);
|
|
scanner_free (destination_p, lit_loc_p->length);
|
|
|
|
found_duplicate = ecma_compare_ecma_strings (arg_string, search_key_p);
|
|
ecma_deref_ecma_string (search_key_p);
|
|
|
|
if (found_duplicate)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return found_duplicate;
|
|
} /* scanner_find_duplicated_arg */
|
|
|
|
/**
|
|
* Find any let/const declaration of a given literal.
|
|
*
|
|
* @return true - if the literal is found, false - otherwise
|
|
*/
|
|
static bool
|
|
scanner_scope_find_lexical_declaration (parser_context_t *context_p, /**< context */
|
|
lexer_lit_location_t *literal_p) /**< literal */
|
|
{
|
|
ecma_string_t *name_p;
|
|
uint32_t flags = context_p->global_status_flags;
|
|
|
|
if (!(flags & ECMA_PARSE_EVAL) || (!(flags & ECMA_PARSE_DIRECT_EVAL) && (context_p->status_flags & PARSER_IS_STRICT)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (JERRY_LIKELY (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)))
|
|
{
|
|
name_p = ecma_new_ecma_string_from_utf8 (literal_p->char_p, literal_p->length);
|
|
}
|
|
else
|
|
{
|
|
uint8_t *destination_p = (uint8_t *) scanner_malloc (context_p, literal_p->length);
|
|
|
|
lexer_convert_ident_to_cesu8 (destination_p, literal_p->char_p, literal_p->length);
|
|
|
|
name_p = ecma_new_ecma_string_from_utf8 (destination_p, literal_p->length);
|
|
|
|
scanner_free (destination_p, literal_p->length);
|
|
}
|
|
|
|
ecma_object_t *lex_env_p;
|
|
|
|
if (flags & ECMA_PARSE_DIRECT_EVAL)
|
|
{
|
|
lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p;
|
|
|
|
while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK)
|
|
{
|
|
if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
|
|
{
|
|
ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p);
|
|
|
|
if (property_p != NULL && ecma_is_property_enumerable (*property_p))
|
|
{
|
|
ecma_deref_ecma_string (name_p);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
|
|
lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lex_env_p = ecma_get_global_scope (ecma_builtin_get_global ());
|
|
}
|
|
|
|
if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
|
|
{
|
|
ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p);
|
|
|
|
if (property_p != NULL
|
|
&& (ecma_is_property_enumerable (*property_p) || scanner_find_duplicated_arg (context_p, literal_p)))
|
|
{
|
|
ecma_deref_ecma_string (name_p);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
ecma_deref_ecma_string (name_p);
|
|
return false;
|
|
} /* scanner_scope_find_lexical_declaration */
|
|
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
/**
|
|
* Push a new literal pool.
|
|
*
|
|
* @return the newly created literal pool
|
|
*/
|
|
scanner_literal_pool_t *
|
|
scanner_push_literal_pool (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p, /**< scanner context */
|
|
uint16_t status_flags) /**< combination of scanner_literal_pool_flags_t flags */
|
|
{
|
|
scanner_literal_pool_t *prev_literal_pool_p = scanner_context_p->active_literal_pool_p;
|
|
scanner_literal_pool_t *literal_pool_p;
|
|
|
|
literal_pool_p = (scanner_literal_pool_t *) scanner_malloc (context_p, sizeof (scanner_literal_pool_t));
|
|
|
|
if (!(status_flags & SCANNER_LITERAL_POOL_FUNCTION))
|
|
{
|
|
JERRY_ASSERT (prev_literal_pool_p != NULL);
|
|
status_flags |= SCANNER_LITERAL_POOL_NO_ARGUMENTS;
|
|
|
|
#if JERRY_ESNEXT
|
|
const uint16_t copied_flags =
|
|
(SCANNER_LITERAL_POOL_IN_WITH | SCANNER_LITERAL_POOL_GENERATOR | SCANNER_LITERAL_POOL_ASYNC);
|
|
#else /* !JERRY_ESNEXT */
|
|
const uint16_t copied_flags = SCANNER_LITERAL_POOL_IN_WITH;
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
status_flags |= (uint16_t) (prev_literal_pool_p->status_flags & copied_flags);
|
|
}
|
|
#if JERRY_ESNEXT
|
|
else
|
|
{
|
|
context_p->status_flags &= (uint32_t) ~(PARSER_IS_GENERATOR_FUNCTION | PARSER_IS_ASYNC_FUNCTION);
|
|
|
|
if (status_flags & SCANNER_LITERAL_POOL_GENERATOR)
|
|
{
|
|
context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION;
|
|
}
|
|
|
|
if (status_flags & SCANNER_LITERAL_POOL_ASYNC)
|
|
{
|
|
context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION;
|
|
}
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (prev_literal_pool_p != NULL)
|
|
{
|
|
const uint16_t copied_flags = SCANNER_LITERAL_POOL_IS_STRICT;
|
|
status_flags |= (uint16_t) (prev_literal_pool_p->status_flags & copied_flags);
|
|
|
|
/* The logical value of these flags must be the same. */
|
|
JERRY_ASSERT (!(status_flags & SCANNER_LITERAL_POOL_IS_STRICT) == !(context_p->status_flags & PARSER_IS_STRICT));
|
|
}
|
|
|
|
parser_list_init (&literal_pool_p->literal_pool,
|
|
sizeof (lexer_lit_location_t),
|
|
(uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_lit_location_t)));
|
|
literal_pool_p->source_p = NULL;
|
|
literal_pool_p->status_flags = status_flags;
|
|
literal_pool_p->no_declarations = 0;
|
|
|
|
literal_pool_p->prev_p = prev_literal_pool_p;
|
|
scanner_context_p->active_literal_pool_p = literal_pool_p;
|
|
|
|
return literal_pool_p;
|
|
} /* scanner_push_literal_pool */
|
|
|
|
JERRY_STATIC_ASSERT (PARSER_MAXIMUM_IDENT_LENGTH <= UINT8_MAX, maximum_ident_length_must_fit_in_a_byte);
|
|
|
|
/**
|
|
* Current status of arguments.
|
|
*/
|
|
typedef enum
|
|
{
|
|
SCANNER_ARGUMENTS_NOT_PRESENT, /**< arguments object must not be created */
|
|
SCANNER_ARGUMENTS_MAY_PRESENT, /**< arguments object can be created */
|
|
SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL, /**< arguments object must be present unless otherwise declared */
|
|
SCANNER_ARGUMENTS_PRESENT, /**< arguments object must be created */
|
|
SCANNER_ARGUMENTS_PRESENT_NO_REG, /**< arguments object must be created and cannot be stored in registers */
|
|
} scanner_arguments_type_t;
|
|
|
|
/**
|
|
* Pop the last literal pool from the end.
|
|
*/
|
|
void
|
|
scanner_pop_literal_pool (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p) /**< scanner context */
|
|
{
|
|
scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p;
|
|
scanner_literal_pool_t *prev_literal_pool_p = literal_pool_p->prev_p;
|
|
|
|
#if JERRY_ESNEXT
|
|
const uint32_t arrow_super_flags = (SCANNER_LITERAL_POOL_ARROW | SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE);
|
|
if ((literal_pool_p->status_flags & arrow_super_flags) == arrow_super_flags)
|
|
{
|
|
prev_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (JERRY_UNLIKELY (literal_pool_p->source_p == NULL))
|
|
{
|
|
JERRY_ASSERT (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION);
|
|
JERRY_ASSERT (literal_pool_p->literal_pool.data.first_p == NULL
|
|
&& literal_pool_p->literal_pool.data.last_p == NULL);
|
|
|
|
scanner_context_p->active_literal_pool_p = literal_pool_p->prev_p;
|
|
scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t));
|
|
return;
|
|
}
|
|
|
|
uint16_t status_flags = literal_pool_p->status_flags;
|
|
scanner_arguments_type_t arguments_type = SCANNER_ARGUMENTS_MAY_PRESENT;
|
|
|
|
if (status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS)
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_NOT_PRESENT;
|
|
}
|
|
else if (status_flags & SCANNER_LITERAL_POOL_CAN_EVAL)
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL;
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
if (status_flags & SCANNER_LITERAL_POOL_ARGUMENTS_IN_ARGS)
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_PRESENT;
|
|
|
|
if (status_flags & (SCANNER_LITERAL_POOL_NO_ARGUMENTS | SCANNER_LITERAL_POOL_CAN_EVAL))
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG;
|
|
status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_NO_ARGUMENTS;
|
|
}
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
uint8_t can_eval_types = 0;
|
|
#if JERRY_ESNEXT
|
|
if (prev_literal_pool_p == NULL && !(context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL))
|
|
{
|
|
can_eval_types |= SCANNER_LITERAL_IS_FUNC;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if ((status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) && prev_literal_pool_p != NULL)
|
|
{
|
|
prev_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_CAN_EVAL;
|
|
}
|
|
|
|
#if JERRY_DEBUGGER
|
|
if (scanner_context_p->status_flags & SCANNER_CONTEXT_DEBUGGER_ENABLED)
|
|
{
|
|
/* When debugger is enabled, identifiers are not stored in registers. However,
|
|
* this does not affect 'eval' detection, so 'arguments' object is not created. */
|
|
status_flags |= SCANNER_LITERAL_POOL_CAN_EVAL;
|
|
}
|
|
#endif /* JERRY_DEBUGGER */
|
|
|
|
parser_list_iterator_t literal_iterator;
|
|
lexer_lit_location_t *literal_p;
|
|
int32_t no_declarations = literal_pool_p->no_declarations;
|
|
|
|
parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator);
|
|
|
|
uint8_t arguments_stream_type = SCANNER_STREAM_TYPE_ARGUMENTS;
|
|
const uint8_t *prev_source_p = literal_pool_p->source_p - 1;
|
|
lexer_lit_location_t *last_argument_p = NULL;
|
|
size_t compressed_size = 1;
|
|
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
uint8_t type = literal_p->type;
|
|
|
|
if (JERRY_UNLIKELY (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!(status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS) && scanner_literal_is_arguments (literal_p))
|
|
{
|
|
#if JERRY_ESNEXT
|
|
JERRY_ASSERT (arguments_type != SCANNER_ARGUMENTS_NOT_PRESENT);
|
|
#else /* !JERRY_ESNEXT */
|
|
JERRY_ASSERT (arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT
|
|
|| arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL);
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
status_flags |= SCANNER_LITERAL_POOL_NO_ARGUMENTS;
|
|
|
|
if (type & SCANNER_LITERAL_IS_ARG)
|
|
{
|
|
JERRY_ASSERT (arguments_type != SCANNER_ARGUMENTS_PRESENT
|
|
&& arguments_type != SCANNER_ARGUMENTS_PRESENT_NO_REG);
|
|
arguments_type = SCANNER_ARGUMENTS_NOT_PRESENT;
|
|
last_argument_p = literal_p;
|
|
}
|
|
#if JERRY_ESNEXT
|
|
else if (type & SCANNER_LITERAL_IS_LOCAL)
|
|
{
|
|
if (arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT || arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL)
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_NOT_PRESENT;
|
|
}
|
|
else
|
|
{
|
|
if (arguments_type == SCANNER_ARGUMENTS_PRESENT_NO_REG)
|
|
{
|
|
type |= SCANNER_LITERAL_NO_REG;
|
|
}
|
|
else if (type & (SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_EARLY_CREATE))
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG;
|
|
}
|
|
|
|
if ((type & SCANNER_LITERAL_IS_LOCAL_FUNC) == SCANNER_LITERAL_IS_LOCAL_FUNC)
|
|
{
|
|
type |= SCANNER_LITERAL_IS_ARG;
|
|
literal_p->type = type;
|
|
no_declarations--;
|
|
arguments_stream_type = SCANNER_STREAM_TYPE_ARGUMENTS_FUNC;
|
|
}
|
|
else
|
|
{
|
|
arguments_stream_type |= SCANNER_STREAM_LOCAL_ARGUMENTS;
|
|
}
|
|
}
|
|
}
|
|
#else /* !JERRY_ESNEXT */
|
|
else if (type & SCANNER_LITERAL_IS_FUNC)
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_NOT_PRESENT;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
else
|
|
{
|
|
#if JERRY_ESNEXT
|
|
if ((type & SCANNER_LITERAL_IS_VAR)
|
|
&& (arguments_type == SCANNER_ARGUMENTS_PRESENT || arguments_type == SCANNER_ARGUMENTS_PRESENT_NO_REG))
|
|
{
|
|
if (arguments_type == SCANNER_ARGUMENTS_PRESENT_NO_REG)
|
|
{
|
|
type |= SCANNER_LITERAL_NO_REG;
|
|
}
|
|
else if (type & (SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_EARLY_CREATE))
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG;
|
|
}
|
|
|
|
type |= SCANNER_LITERAL_IS_ARG;
|
|
literal_p->type = type;
|
|
no_declarations--;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if ((type & SCANNER_LITERAL_NO_REG) || arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL)
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG;
|
|
}
|
|
else if (arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT)
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_PRESENT;
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
/* The SCANNER_LITERAL_IS_ARG may be set above. */
|
|
if (!(type & SCANNER_LITERAL_IS_ARG))
|
|
{
|
|
literal_p->type = 0;
|
|
continue;
|
|
}
|
|
#else /* !JERRY_ESNEXT */
|
|
literal_p->type = 0;
|
|
continue;
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
}
|
|
else if (type & SCANNER_LITERAL_IS_ARG)
|
|
{
|
|
last_argument_p = literal_p;
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
if ((status_flags & SCANNER_LITERAL_POOL_FUNCTION)
|
|
&& (type & SCANNER_LITERAL_IS_LOCAL_FUNC) == SCANNER_LITERAL_IS_FUNC)
|
|
{
|
|
if (prev_literal_pool_p == NULL && scanner_scope_find_lexical_declaration (context_p, literal_p))
|
|
{
|
|
literal_p->type = 0;
|
|
continue;
|
|
}
|
|
|
|
if (!(type & SCANNER_LITERAL_IS_ARG))
|
|
{
|
|
type |= SCANNER_LITERAL_IS_VAR;
|
|
}
|
|
|
|
type &= (uint8_t) ~SCANNER_LITERAL_IS_FUNC;
|
|
literal_p->type = type;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if ((type & SCANNER_LITERAL_IS_LOCAL)
|
|
|| ((type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_ARG))
|
|
&& (status_flags & SCANNER_LITERAL_POOL_FUNCTION)))
|
|
{
|
|
JERRY_ASSERT ((status_flags & SCANNER_LITERAL_POOL_FUNCTION) || !(literal_p->type & SCANNER_LITERAL_IS_ARG));
|
|
|
|
if (literal_p->length == 0)
|
|
{
|
|
compressed_size += 1;
|
|
continue;
|
|
}
|
|
|
|
no_declarations++;
|
|
|
|
if ((status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) || (type & can_eval_types))
|
|
{
|
|
type |= SCANNER_LITERAL_NO_REG;
|
|
literal_p->type = type;
|
|
}
|
|
|
|
if (type & SCANNER_LITERAL_IS_FUNC)
|
|
{
|
|
no_declarations++;
|
|
|
|
#if JERRY_ESNEXT
|
|
if ((type & (SCANNER_LITERAL_IS_CONST | SCANNER_LITERAL_IS_ARG)) == SCANNER_LITERAL_IS_CONST)
|
|
{
|
|
JERRY_ASSERT (type & SCANNER_LITERAL_IS_LET);
|
|
|
|
/* Catch parameters cannot be functions. */
|
|
literal_p->type = (uint8_t) (type & ~SCANNER_LITERAL_IS_FUNC);
|
|
no_declarations--;
|
|
}
|
|
#else /* !JERRY_ESNEXT */
|
|
if (type & SCANNER_LITERAL_IS_LOCAL)
|
|
{
|
|
/* Catch parameters cannot be functions. */
|
|
literal_p->type = (uint8_t) (type & ~SCANNER_LITERAL_IS_FUNC);
|
|
no_declarations--;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
|
|
intptr_t diff = (intptr_t) (literal_p->char_p - prev_source_p);
|
|
|
|
if (diff >= 1 && diff <= (intptr_t) UINT8_MAX)
|
|
{
|
|
compressed_size += 2 + 1;
|
|
}
|
|
else if (diff >= -(intptr_t) UINT8_MAX && diff <= (intptr_t) UINT16_MAX)
|
|
{
|
|
compressed_size += 2 + 2;
|
|
}
|
|
else
|
|
{
|
|
compressed_size += 2 + 1 + sizeof (const uint8_t *);
|
|
}
|
|
|
|
prev_source_p = literal_p->char_p + literal_p->length;
|
|
|
|
if ((status_flags & SCANNER_LITERAL_POOL_FUNCTION)
|
|
#if JERRY_ESNEXT
|
|
|| ((type & SCANNER_LITERAL_IS_FUNC) && (status_flags & SCANNER_LITERAL_POOL_IS_STRICT))
|
|
#endif /* JERRY_ESNEXT */
|
|
|| !(type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC)))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (prev_literal_pool_p != NULL && literal_p->length > 0)
|
|
{
|
|
/* Propagate literal to upper level. */
|
|
lexer_lit_location_t *literal_location_p = scanner_add_custom_literal (context_p, prev_literal_pool_p, literal_p);
|
|
uint8_t extended_type = literal_location_p->type;
|
|
|
|
#if JERRY_ESNEXT
|
|
const uint16_t no_reg_flags = (SCANNER_LITERAL_POOL_FUNCTION | SCANNER_LITERAL_POOL_CLASS_FIELD);
|
|
#else /* !JERRY_ESNEXT */
|
|
const uint16_t no_reg_flags = SCANNER_LITERAL_POOL_FUNCTION;
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if ((status_flags & no_reg_flags) || (type & SCANNER_LITERAL_NO_REG))
|
|
{
|
|
extended_type |= SCANNER_LITERAL_NO_REG;
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
extended_type |= SCANNER_LITERAL_IS_USED;
|
|
|
|
if (status_flags & SCANNER_LITERAL_POOL_FUNCTION_STATEMENT)
|
|
{
|
|
extended_type |= SCANNER_LITERAL_EARLY_CREATE;
|
|
}
|
|
|
|
const uint8_t mask = (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_LOCAL);
|
|
|
|
if ((type & SCANNER_LITERAL_IS_ARG) || (literal_location_p->type & mask) == SCANNER_LITERAL_IS_LET
|
|
|| (literal_location_p->type & mask) == SCANNER_LITERAL_IS_CONST)
|
|
{
|
|
/* Clears the SCANNER_LITERAL_IS_VAR and SCANNER_LITERAL_IS_FUNC flags
|
|
* for speculative arrow parameters and local (non-var) functions. */
|
|
type = 0;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
type = (uint8_t) (type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC));
|
|
JERRY_ASSERT (type == 0 || !(status_flags & SCANNER_LITERAL_POOL_FUNCTION));
|
|
|
|
literal_location_p->type = (uint8_t) (extended_type | type);
|
|
}
|
|
}
|
|
|
|
if ((status_flags & SCANNER_LITERAL_POOL_FUNCTION) || (compressed_size > 1))
|
|
{
|
|
if (arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT)
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_NOT_PRESENT;
|
|
}
|
|
else if (arguments_type == SCANNER_ARGUMENTS_MAY_PRESENT_IN_EVAL)
|
|
{
|
|
arguments_type = SCANNER_ARGUMENTS_PRESENT_NO_REG;
|
|
}
|
|
|
|
if (arguments_type != SCANNER_ARGUMENTS_NOT_PRESENT)
|
|
{
|
|
compressed_size++;
|
|
}
|
|
|
|
compressed_size += sizeof (scanner_info_t);
|
|
|
|
scanner_info_t *info_p;
|
|
|
|
if (prev_literal_pool_p != NULL || scanner_context_p->end_arguments_p == NULL)
|
|
{
|
|
info_p = scanner_insert_info (context_p, literal_pool_p->source_p, compressed_size);
|
|
}
|
|
else
|
|
{
|
|
scanner_info_t *start_info_p = scanner_context_p->end_arguments_p;
|
|
info_p = scanner_insert_info_before (context_p, literal_pool_p->source_p, start_info_p, compressed_size);
|
|
}
|
|
|
|
if (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK)
|
|
{
|
|
no_declarations = PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK;
|
|
}
|
|
|
|
uint8_t *data_p = (uint8_t *) (info_p + 1);
|
|
bool mapped_arguments = false;
|
|
|
|
if (status_flags & SCANNER_LITERAL_POOL_FUNCTION)
|
|
{
|
|
info_p->type = SCANNER_TYPE_FUNCTION;
|
|
|
|
uint8_t u8_arg = 0;
|
|
|
|
if (arguments_type != SCANNER_ARGUMENTS_NOT_PRESENT)
|
|
{
|
|
u8_arg |= SCANNER_FUNCTION_ARGUMENTS_NEEDED;
|
|
|
|
if (no_declarations < PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK)
|
|
{
|
|
no_declarations++;
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
const uint16_t is_unmapped = SCANNER_LITERAL_POOL_IS_STRICT | SCANNER_LITERAL_POOL_HAS_COMPLEX_ARGUMENT;
|
|
#else /* !JERRY_ESNEXT */
|
|
const uint16_t is_unmapped = SCANNER_LITERAL_POOL_IS_STRICT;
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (!(status_flags & is_unmapped))
|
|
{
|
|
mapped_arguments = true;
|
|
}
|
|
|
|
if (arguments_type == SCANNER_ARGUMENTS_PRESENT_NO_REG)
|
|
{
|
|
arguments_stream_type |= SCANNER_STREAM_NO_REG;
|
|
}
|
|
|
|
if (last_argument_p == NULL)
|
|
{
|
|
*data_p++ = arguments_stream_type;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_argument_p = NULL;
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
if (status_flags & (SCANNER_LITERAL_POOL_HAS_COMPLEX_ARGUMENT | SCANNER_LITERAL_POOL_ARROW))
|
|
{
|
|
u8_arg |= SCANNER_FUNCTION_HAS_COMPLEX_ARGUMENT;
|
|
}
|
|
|
|
if (status_flags & SCANNER_LITERAL_POOL_ASYNC)
|
|
{
|
|
u8_arg |= SCANNER_FUNCTION_ASYNC;
|
|
|
|
if (status_flags & SCANNER_LITERAL_POOL_FUNCTION_STATEMENT)
|
|
{
|
|
u8_arg |= SCANNER_FUNCTION_STATEMENT;
|
|
}
|
|
}
|
|
|
|
if (status_flags & SCANNER_LITERAL_POOL_CAN_EVAL)
|
|
{
|
|
u8_arg |= SCANNER_FUNCTION_LEXICAL_ENV_NEEDED;
|
|
}
|
|
|
|
if (status_flags & SCANNER_LITERAL_POOL_IS_STRICT)
|
|
{
|
|
u8_arg |= SCANNER_FUNCTION_IS_STRICT;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
info_p->u8_arg = u8_arg;
|
|
info_p->u16_arg = (uint16_t) no_declarations;
|
|
}
|
|
else
|
|
{
|
|
info_p->type = SCANNER_TYPE_BLOCK;
|
|
|
|
JERRY_ASSERT (prev_literal_pool_p != NULL);
|
|
}
|
|
|
|
parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator);
|
|
prev_source_p = literal_pool_p->source_p - 1;
|
|
no_declarations = literal_pool_p->no_declarations;
|
|
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
if (JERRY_UNLIKELY (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK)
|
|
|| (!(literal_p->type & SCANNER_LITERAL_IS_LOCAL)
|
|
&& (!(literal_p->type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_ARG))
|
|
|| !(status_flags & SCANNER_LITERAL_POOL_FUNCTION))))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (literal_p->length == 0)
|
|
{
|
|
*data_p++ = SCANNER_STREAM_TYPE_HOLE;
|
|
|
|
if (literal_p == last_argument_p)
|
|
{
|
|
*data_p++ = arguments_stream_type;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
no_declarations++;
|
|
|
|
uint8_t type = SCANNER_STREAM_TYPE_VAR;
|
|
|
|
if (literal_p->type & SCANNER_LITERAL_IS_FUNC)
|
|
{
|
|
no_declarations++;
|
|
type = SCANNER_STREAM_TYPE_FUNC;
|
|
|
|
if (literal_p->type & SCANNER_LITERAL_IS_ARG)
|
|
{
|
|
type = SCANNER_STREAM_TYPE_ARG_FUNC;
|
|
|
|
#if JERRY_ESNEXT
|
|
if (literal_p->type & SCANNER_LITERAL_IS_DESTRUCTURED_ARG)
|
|
{
|
|
type = SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
}
|
|
else if (literal_p->type & SCANNER_LITERAL_IS_ARG)
|
|
{
|
|
type = SCANNER_STREAM_TYPE_ARG;
|
|
|
|
#if JERRY_ESNEXT
|
|
if (literal_p->type & SCANNER_LITERAL_IS_DESTRUCTURED_ARG)
|
|
{
|
|
type = SCANNER_STREAM_TYPE_DESTRUCTURED_ARG;
|
|
}
|
|
|
|
if (literal_p->type & SCANNER_LITERAL_IS_VAR)
|
|
{
|
|
type = (uint8_t) (type + 1);
|
|
|
|
JERRY_ASSERT (type == SCANNER_STREAM_TYPE_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR);
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
#if JERRY_ESNEXT
|
|
else if (literal_p->type & SCANNER_LITERAL_IS_LET)
|
|
{
|
|
if (!(literal_p->type & SCANNER_LITERAL_IS_CONST))
|
|
{
|
|
type = SCANNER_STREAM_TYPE_LET;
|
|
|
|
if ((status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) && (literal_p->type & SCANNER_LITERAL_NO_REG))
|
|
{
|
|
literal_p->type |= SCANNER_LITERAL_EARLY_CREATE;
|
|
}
|
|
}
|
|
#if JERRY_MODULE_SYSTEM
|
|
else if (prev_literal_pool_p == NULL)
|
|
{
|
|
type = SCANNER_STREAM_TYPE_IMPORT;
|
|
}
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
else
|
|
{
|
|
type = SCANNER_STREAM_TYPE_LOCAL;
|
|
}
|
|
}
|
|
else if (literal_p->type & SCANNER_LITERAL_IS_CONST)
|
|
{
|
|
type = SCANNER_STREAM_TYPE_CONST;
|
|
|
|
if ((status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) && (literal_p->type & SCANNER_LITERAL_NO_REG))
|
|
{
|
|
literal_p->type |= SCANNER_LITERAL_EARLY_CREATE;
|
|
}
|
|
}
|
|
|
|
if (literal_p->type & SCANNER_LITERAL_EARLY_CREATE)
|
|
{
|
|
type |= SCANNER_STREAM_NO_REG | SCANNER_STREAM_EARLY_CREATE;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)
|
|
{
|
|
type |= SCANNER_STREAM_HAS_ESCAPE;
|
|
}
|
|
|
|
if ((literal_p->type & SCANNER_LITERAL_NO_REG)
|
|
|| (mapped_arguments && (literal_p->type & SCANNER_LITERAL_IS_ARG)))
|
|
{
|
|
type |= SCANNER_STREAM_NO_REG;
|
|
}
|
|
|
|
data_p[0] = type;
|
|
data_p[1] = (uint8_t) literal_p->length;
|
|
data_p += 3;
|
|
|
|
intptr_t diff = (intptr_t) (literal_p->char_p - prev_source_p);
|
|
|
|
if (diff >= 1 && diff <= (intptr_t) UINT8_MAX)
|
|
{
|
|
data_p[-1] = (uint8_t) diff;
|
|
}
|
|
else if (diff >= -(intptr_t) UINT8_MAX && diff <= (intptr_t) UINT16_MAX)
|
|
{
|
|
if (diff < 0)
|
|
{
|
|
diff = -diff;
|
|
}
|
|
|
|
data_p[-3] |= SCANNER_STREAM_UINT16_DIFF;
|
|
data_p[-1] = (uint8_t) diff;
|
|
data_p[0] = (uint8_t) (diff >> 8);
|
|
data_p += 1;
|
|
}
|
|
else
|
|
{
|
|
data_p[-1] = 0;
|
|
memcpy (data_p, &literal_p->char_p, sizeof (uintptr_t));
|
|
data_p += sizeof (uintptr_t);
|
|
}
|
|
|
|
if (literal_p == last_argument_p)
|
|
{
|
|
*data_p++ = arguments_stream_type;
|
|
}
|
|
|
|
prev_source_p = literal_p->char_p + literal_p->length;
|
|
}
|
|
|
|
data_p[0] = SCANNER_STREAM_TYPE_END;
|
|
|
|
JERRY_ASSERT (((uint8_t *) info_p) + compressed_size == data_p + 1);
|
|
}
|
|
|
|
if (!(status_flags & SCANNER_LITERAL_POOL_FUNCTION)
|
|
&& (int32_t) prev_literal_pool_p->no_declarations < no_declarations)
|
|
{
|
|
prev_literal_pool_p->no_declarations = (uint16_t) no_declarations;
|
|
}
|
|
|
|
if ((status_flags & SCANNER_LITERAL_POOL_FUNCTION) && prev_literal_pool_p != NULL)
|
|
{
|
|
if (prev_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IS_STRICT)
|
|
{
|
|
context_p->status_flags |= PARSER_IS_STRICT;
|
|
}
|
|
else
|
|
{
|
|
context_p->status_flags &= (uint32_t) ~PARSER_IS_STRICT;
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
if (prev_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_GENERATOR)
|
|
{
|
|
context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION;
|
|
}
|
|
else
|
|
{
|
|
context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION;
|
|
}
|
|
|
|
if (prev_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_ASYNC)
|
|
{
|
|
context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION;
|
|
}
|
|
else
|
|
{
|
|
context_p->status_flags &= (uint32_t) ~PARSER_IS_ASYNC_FUNCTION;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
|
|
scanner_context_p->active_literal_pool_p = literal_pool_p->prev_p;
|
|
|
|
parser_list_free (&literal_pool_p->literal_pool);
|
|
scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t));
|
|
} /* scanner_pop_literal_pool */
|
|
|
|
#if JERRY_ESNEXT
|
|
|
|
/**
|
|
* Filter out the arguments from a literal pool.
|
|
*/
|
|
void
|
|
scanner_filter_arguments (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p) /**< scanner context */
|
|
{
|
|
/* Fast case: check whether all literals are arguments. */
|
|
scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p;
|
|
scanner_literal_pool_t *prev_literal_pool_p = literal_pool_p->prev_p;
|
|
parser_list_iterator_t literal_iterator;
|
|
lexer_lit_location_t *literal_p;
|
|
bool can_eval = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_CAN_EVAL) != 0;
|
|
bool has_arguments = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS) == 0;
|
|
|
|
JERRY_ASSERT (SCANNER_LITERAL_POOL_MAY_HAVE_ARGUMENTS (literal_pool_p->status_flags));
|
|
|
|
if (JERRY_UNLIKELY (can_eval))
|
|
{
|
|
if (prev_literal_pool_p != NULL)
|
|
{
|
|
prev_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_CAN_EVAL;
|
|
}
|
|
|
|
literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_CAN_EVAL;
|
|
}
|
|
else
|
|
{
|
|
parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator);
|
|
|
|
while (true)
|
|
{
|
|
literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator);
|
|
|
|
if (literal_p == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (can_eval || (literal_p->type & SCANNER_LITERAL_EARLY_CREATE))
|
|
{
|
|
literal_p->type |= SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_EARLY_CREATE;
|
|
}
|
|
|
|
uint8_t type = literal_p->type;
|
|
const uint8_t mask =
|
|
(SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_DESTRUCTURED_ARG | SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG);
|
|
|
|
if ((type & mask) != SCANNER_LITERAL_IS_ARG)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Destructured args are placed after the other arguments because of register assignments. */
|
|
bool has_destructured_arg = false;
|
|
scanner_literal_pool_t *new_literal_pool_p;
|
|
|
|
new_literal_pool_p = (scanner_literal_pool_t *) scanner_malloc (context_p, sizeof (scanner_literal_pool_t));
|
|
|
|
new_literal_pool_p->prev_p = literal_pool_p;
|
|
scanner_context_p->active_literal_pool_p = new_literal_pool_p;
|
|
|
|
*new_literal_pool_p = *literal_pool_p;
|
|
parser_list_init (&new_literal_pool_p->literal_pool,
|
|
sizeof (lexer_lit_location_t),
|
|
(uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_lit_location_t)));
|
|
|
|
parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator);
|
|
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
uint8_t type = literal_p->type;
|
|
|
|
if (type & SCANNER_LITERAL_IS_ARG)
|
|
{
|
|
if (can_eval || (literal_p->type & SCANNER_LITERAL_EARLY_CREATE))
|
|
{
|
|
type |= SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_EARLY_CREATE;
|
|
literal_p->type = type;
|
|
}
|
|
|
|
if (has_arguments && scanner_literal_is_arguments (literal_p))
|
|
{
|
|
has_arguments = false;
|
|
}
|
|
|
|
if (type & (SCANNER_LITERAL_IS_DESTRUCTURED_ARG | SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG))
|
|
{
|
|
has_destructured_arg = true;
|
|
|
|
if (type & SCANNER_LITERAL_IS_DESTRUCTURED_ARG)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
type &= (uint8_t) ~SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG;
|
|
type |= SCANNER_LITERAL_IS_DESTRUCTURED_ARG;
|
|
|
|
literal_p->type = type;
|
|
continue;
|
|
}
|
|
|
|
lexer_lit_location_t *new_literal_p;
|
|
new_literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &new_literal_pool_p->literal_pool);
|
|
*new_literal_p = *literal_p;
|
|
}
|
|
else if (has_arguments && scanner_literal_is_arguments (literal_p))
|
|
{
|
|
/* Arguments object is directly referenced from the function arguments */
|
|
new_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ARGUMENTS_IN_ARGS;
|
|
|
|
if (type & SCANNER_LITERAL_NO_REG)
|
|
{
|
|
new_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_NO_ARGUMENTS;
|
|
}
|
|
}
|
|
else if (prev_literal_pool_p != NULL)
|
|
{
|
|
/* Propagate literal to upper level. */
|
|
lexer_lit_location_t *literal_location_p = scanner_add_custom_literal (context_p, prev_literal_pool_p, literal_p);
|
|
type |= SCANNER_LITERAL_NO_REG | SCANNER_LITERAL_IS_USED;
|
|
literal_location_p->type |= type;
|
|
}
|
|
}
|
|
|
|
if (has_destructured_arg)
|
|
{
|
|
parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator);
|
|
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
const uint8_t expected_flags = SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_DESTRUCTURED_ARG;
|
|
|
|
if ((literal_p->type & expected_flags) == expected_flags)
|
|
{
|
|
lexer_lit_location_t *new_literal_p;
|
|
new_literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &new_literal_pool_p->literal_pool);
|
|
*new_literal_p = *literal_p;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (has_arguments)
|
|
{
|
|
/* Force the lexically stored arguments object creation */
|
|
new_literal_pool_p->status_flags |= (SCANNER_LITERAL_POOL_ARGUMENTS_IN_ARGS | SCANNER_LITERAL_POOL_NO_ARGUMENTS);
|
|
}
|
|
|
|
new_literal_pool_p->prev_p = prev_literal_pool_p;
|
|
|
|
parser_list_free (&literal_pool_p->literal_pool);
|
|
scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t));
|
|
} /* scanner_filter_arguments */
|
|
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
/**
|
|
* Add any literal to the specified literal pool.
|
|
*
|
|
* @return pointer to the literal
|
|
*/
|
|
lexer_lit_location_t *
|
|
scanner_add_custom_literal (parser_context_t *context_p, /**< context */
|
|
scanner_literal_pool_t *literal_pool_p, /**< literal pool */
|
|
const lexer_lit_location_t *literal_location_p) /**< literal */
|
|
{
|
|
while (true)
|
|
{
|
|
parser_list_iterator_t literal_iterator;
|
|
parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator);
|
|
lexer_lit_location_t *literal_p;
|
|
|
|
const uint8_t *char_p = literal_location_p->char_p;
|
|
prop_length_t length = literal_location_p->length;
|
|
|
|
if (JERRY_LIKELY (!(literal_location_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)))
|
|
{
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
if (literal_p->length == length)
|
|
{
|
|
if (JERRY_LIKELY (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)))
|
|
{
|
|
if (memcmp (literal_p->char_p, char_p, length) == 0)
|
|
{
|
|
return literal_p;
|
|
}
|
|
}
|
|
else if (lexer_compare_identifier_to_string (literal_p, char_p, length))
|
|
{
|
|
/* The non-escaped version is preferred. */
|
|
literal_p->char_p = char_p;
|
|
literal_p->status_flags = LEXER_LIT_LOCATION_NO_OPTS;
|
|
return literal_p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
if (lexer_compare_identifiers (context_p, literal_p, literal_location_p))
|
|
{
|
|
return literal_p;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
if (JERRY_UNLIKELY (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_CLASS_NAME))
|
|
{
|
|
literal_pool_p = literal_pool_p->prev_p;
|
|
continue;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &literal_pool_p->literal_pool);
|
|
*literal_p = *literal_location_p;
|
|
|
|
literal_p->type = 0;
|
|
|
|
return literal_p;
|
|
}
|
|
} /* scanner_add_custom_literal */
|
|
|
|
/**
|
|
* Add the current literal token to the current literal pool.
|
|
*
|
|
* @return pointer to the literal
|
|
*/
|
|
extern inline lexer_lit_location_t *JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_add_literal (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p) /**< scanner context */
|
|
{
|
|
return scanner_add_custom_literal (context_p,
|
|
scanner_context_p->active_literal_pool_p,
|
|
&context_p->token.lit_location);
|
|
} /* scanner_add_literal */
|
|
|
|
/**
|
|
* Add the current literal token to the current literal pool and
|
|
* set SCANNER_LITERAL_NO_REG if it is inside a with statement.
|
|
*
|
|
* @return pointer to the literal
|
|
*/
|
|
extern inline void JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_add_reference (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p) /**< scanner context */
|
|
{
|
|
lexer_lit_location_t *lit_location_p =
|
|
scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p, &context_p->token.lit_location);
|
|
#if JERRY_ESNEXT
|
|
lit_location_p->type |= SCANNER_LITERAL_IS_USED;
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH)
|
|
{
|
|
lit_location_p->type |= SCANNER_LITERAL_NO_REG;
|
|
}
|
|
|
|
scanner_detect_eval_call (context_p, scanner_context_p);
|
|
} /* scanner_add_reference */
|
|
|
|
/**
|
|
* Append an argument to the literal pool. If the argument is already present, make it a "hole".
|
|
*
|
|
* @return newly created literal
|
|
*/
|
|
lexer_lit_location_t *
|
|
scanner_append_argument (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p) /**< scanner context */
|
|
{
|
|
scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p;
|
|
parser_list_iterator_t literal_iterator;
|
|
parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator);
|
|
lexer_lit_location_t *literal_location_p = &context_p->token.lit_location;
|
|
lexer_lit_location_t *literal_p;
|
|
|
|
const uint8_t *char_p = literal_location_p->char_p;
|
|
prop_length_t length = literal_location_p->length;
|
|
|
|
JERRY_ASSERT (SCANNER_LITERAL_POOL_MAY_HAVE_ARGUMENTS (literal_pool_p->status_flags));
|
|
|
|
if (JERRY_LIKELY (!(context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)))
|
|
{
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
if (literal_p->length == length)
|
|
{
|
|
if (JERRY_LIKELY (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)))
|
|
{
|
|
if (memcmp (literal_p->char_p, char_p, length) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (lexer_compare_identifier_to_string (literal_p, char_p, length))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
if (lexer_compare_identifiers (context_p, literal_p, literal_location_p))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t literal_type = SCANNER_LITERAL_IS_ARG;
|
|
|
|
if (literal_p != NULL)
|
|
{
|
|
literal_p->length = 0;
|
|
|
|
#if JERRY_ESNEXT
|
|
if (literal_p->type & SCANNER_LITERAL_IS_USED)
|
|
{
|
|
literal_type = SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_EARLY_CREATE;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
|
|
literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &literal_pool_p->literal_pool);
|
|
|
|
*literal_p = context_p->token.lit_location;
|
|
literal_p->type = literal_type;
|
|
|
|
return literal_p;
|
|
} /* scanner_append_argument */
|
|
|
|
/**
|
|
* Add private identifiers to private ident pool
|
|
*/
|
|
void
|
|
scanner_add_private_identifier (parser_context_t *context_p, /**< context */
|
|
scanner_private_field_flags_t opts) /**< options */
|
|
{
|
|
scan_stack_modes_t stack_top = (scan_stack_modes_t) context_p->stack_top_uint8;
|
|
parser_stack_pop_uint8 (context_p);
|
|
scanner_class_info_t *class_info_p;
|
|
parser_stack_pop (context_p, &class_info_p, sizeof (scanner_class_info_t *));
|
|
|
|
scanner_class_private_member_t *iter = class_info_p->members;
|
|
|
|
scanner_private_field_flags_t search_flag =
|
|
((opts & SCANNER_PRIVATE_FIELD_PROPERTY) ? SCANNER_PRIVATE_FIELD_PROPERTY_GETTER_SETTER
|
|
: (opts & SCANNER_PRIVATE_FIELD_GETTER_SETTER));
|
|
|
|
while (iter != NULL)
|
|
{
|
|
if (lexer_compare_identifiers (context_p, &context_p->token.lit_location, &iter->loc)
|
|
&& (iter->u8_arg & search_flag))
|
|
{
|
|
scanner_raise_error (context_p);
|
|
}
|
|
|
|
iter = iter->prev_p;
|
|
}
|
|
|
|
scanner_class_private_member_t *p_member;
|
|
p_member = (scanner_class_private_member_t *) scanner_malloc (context_p, sizeof (scanner_class_private_member_t));
|
|
p_member->loc = context_p->token.lit_location;
|
|
p_member->u8_arg = (uint8_t) opts;
|
|
p_member->prev_p = class_info_p->members;
|
|
class_info_p->members = p_member;
|
|
|
|
parser_stack_push (context_p, &class_info_p, sizeof (scanner_class_info_t *));
|
|
parser_stack_push_uint8 (context_p, (uint8_t) stack_top);
|
|
} /* scanner_add_private_identifier */
|
|
|
|
/**
|
|
* Check whether an eval call is performed and update the status flags accordingly.
|
|
*/
|
|
void
|
|
scanner_detect_eval_call (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p) /**< scanner context */
|
|
{
|
|
if (context_p->token.keyword_type == LEXER_KEYW_EVAL && lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN))
|
|
{
|
|
#if JERRY_ESNEXT
|
|
const uint16_t flags = (uint16_t) (SCANNER_LITERAL_POOL_CAN_EVAL | SCANNER_LITERAL_POOL_HAS_SUPER_REFERENCE);
|
|
#else /* !JERRY_ESNEXT */
|
|
const uint16_t flags = SCANNER_LITERAL_POOL_CAN_EVAL;
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
scanner_context_p->active_literal_pool_p->status_flags |= flags;
|
|
}
|
|
} /* scanner_detect_eval_call */
|
|
|
|
#if JERRY_ESNEXT
|
|
|
|
/**
|
|
* Throws an error for invalid var statements.
|
|
*/
|
|
void
|
|
scanner_detect_invalid_var (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p, /**< scanner context */
|
|
lexer_lit_location_t *var_literal_p) /**< var literal */
|
|
{
|
|
if (var_literal_p->type & SCANNER_LITERAL_IS_LOCAL
|
|
&& !(var_literal_p->type & (SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_ARG))
|
|
&& (var_literal_p->type & SCANNER_LITERAL_IS_LOCAL) != SCANNER_LITERAL_IS_LOCAL)
|
|
{
|
|
scanner_raise_redeclaration_error (context_p);
|
|
}
|
|
|
|
scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p;
|
|
|
|
if (!(literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION)
|
|
&& ((var_literal_p->type & SCANNER_LITERAL_IS_LOCAL_FUNC) == SCANNER_LITERAL_IS_LOCAL_FUNC))
|
|
{
|
|
scanner_raise_redeclaration_error (context_p);
|
|
}
|
|
|
|
const uint8_t *char_p = var_literal_p->char_p;
|
|
prop_length_t length = var_literal_p->length;
|
|
|
|
while (!(literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION))
|
|
{
|
|
literal_pool_p = literal_pool_p->prev_p;
|
|
|
|
parser_list_iterator_t literal_iterator;
|
|
parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator);
|
|
lexer_lit_location_t *literal_p;
|
|
|
|
if (JERRY_LIKELY (!(context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)))
|
|
{
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
if ((literal_p->type & SCANNER_LITERAL_IS_LOCAL) && !(literal_p->type & SCANNER_LITERAL_IS_ARG)
|
|
&& !((literal_p->type & SCANNER_LITERAL_IS_FUNC)
|
|
&& (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION))
|
|
&& (literal_p->type & SCANNER_LITERAL_IS_LOCAL) != SCANNER_LITERAL_IS_LOCAL && literal_p->length == length)
|
|
{
|
|
if (JERRY_LIKELY (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE)))
|
|
{
|
|
if (memcmp (literal_p->char_p, char_p, length) == 0)
|
|
{
|
|
scanner_raise_redeclaration_error (context_p);
|
|
return;
|
|
}
|
|
}
|
|
else if (lexer_compare_identifier_to_string (literal_p, char_p, length))
|
|
{
|
|
scanner_raise_redeclaration_error (context_p);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL)
|
|
{
|
|
if ((literal_p->type & SCANNER_LITERAL_IS_LOCAL) && !(literal_p->type & SCANNER_LITERAL_IS_ARG)
|
|
&& !((literal_p->type & SCANNER_LITERAL_IS_FUNC)
|
|
&& (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION))
|
|
&& (literal_p->type & SCANNER_LITERAL_IS_LOCAL) != SCANNER_LITERAL_IS_LOCAL
|
|
&& lexer_compare_identifiers (context_p, literal_p, var_literal_p))
|
|
{
|
|
scanner_raise_redeclaration_error (context_p);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (scanner_scope_find_lexical_declaration (context_p, var_literal_p))
|
|
{
|
|
scanner_raise_redeclaration_error (context_p);
|
|
}
|
|
} /* scanner_detect_invalid_var */
|
|
|
|
/**
|
|
* Throws an error for invalid let statements.
|
|
*/
|
|
void
|
|
scanner_detect_invalid_let (parser_context_t *context_p, /**< context */
|
|
lexer_lit_location_t *let_literal_p) /**< let literal */
|
|
{
|
|
if (let_literal_p->type & (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_LOCAL))
|
|
{
|
|
scanner_raise_redeclaration_error (context_p);
|
|
}
|
|
|
|
if (let_literal_p->type & SCANNER_LITERAL_IS_FUNC)
|
|
{
|
|
let_literal_p->type &= (uint8_t) ~SCANNER_LITERAL_IS_FUNC;
|
|
}
|
|
} /* scanner_detect_invalid_let */
|
|
|
|
/**
|
|
* Push the values required for class declaration parsing.
|
|
*
|
|
* @return literal reference created for class statements, NULL otherwise
|
|
*/
|
|
lexer_lit_location_t *
|
|
scanner_push_class_declaration (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p, /* scanner context */
|
|
uint8_t stack_mode) /**< stack mode */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_CLASS);
|
|
|
|
const uint8_t *source_p = context_p->source_p;
|
|
lexer_lit_location_t *literal_p = NULL;
|
|
|
|
#if JERRY_MODULE_SYSTEM
|
|
bool is_export_default = context_p->stack_top_uint8 == SCAN_STACK_EXPORT_DEFAULT;
|
|
JERRY_ASSERT (!is_export_default || stack_mode == SCAN_STACK_CLASS_EXPRESSION);
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
|
|
parser_stack_push_uint8 (context_p, stack_mode);
|
|
lexer_next_token (context_p);
|
|
|
|
bool class_has_name =
|
|
(context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
|
|
|
if (class_has_name)
|
|
{
|
|
literal_p = scanner_add_literal (context_p, scanner_context_p);
|
|
scanner_context_p->active_literal_pool_p->no_declarations++;
|
|
|
|
#if JERRY_MODULE_SYSTEM
|
|
if (is_export_default)
|
|
{
|
|
scanner_detect_invalid_let (context_p, literal_p);
|
|
|
|
if (literal_p->type & SCANNER_LITERAL_IS_USED)
|
|
{
|
|
literal_p->type |= SCANNER_LITERAL_EARLY_CREATE;
|
|
}
|
|
|
|
literal_p->type |= SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_NO_REG;
|
|
}
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
}
|
|
|
|
scanner_literal_pool_t *literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0);
|
|
|
|
if (class_has_name)
|
|
{
|
|
scanner_add_literal (context_p, scanner_context_p);
|
|
scanner_context_p->active_literal_pool_p->no_declarations++;
|
|
}
|
|
#if JERRY_MODULE_SYSTEM
|
|
else if (is_export_default)
|
|
{
|
|
lexer_lit_location_t *name_literal_p;
|
|
name_literal_p =
|
|
scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p->prev_p, &lexer_default_literal);
|
|
|
|
name_literal_p->type |= SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_NO_REG;
|
|
scanner_context_p->active_literal_pool_p->no_declarations++;
|
|
}
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
|
|
literal_pool_p->source_p = source_p;
|
|
literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_CLASS_NAME;
|
|
|
|
const uint8_t *class_source_p = scanner_context_p->active_literal_pool_p->source_p;
|
|
scanner_class_info_t *class_info_p =
|
|
(scanner_class_info_t *) scanner_insert_info (context_p, class_source_p, sizeof (scanner_class_info_t));
|
|
|
|
class_info_p->info.type = SCANNER_TYPE_CLASS_CONSTRUCTOR;
|
|
class_info_p->members = NULL;
|
|
class_info_p->info.u8_arg = SCANNER_CONSTRUCTOR_IMPLICIT;
|
|
|
|
parser_stack_push (context_p, &class_info_p, sizeof (scanner_class_info_t *));
|
|
parser_stack_push_uint8 (context_p, SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR);
|
|
scanner_context_p->mode = SCAN_MODE_CLASS_DECLARATION;
|
|
|
|
return literal_p;
|
|
} /* scanner_push_class_declaration */
|
|
|
|
/**
|
|
* Push the start of a class field initializer.
|
|
*/
|
|
void
|
|
scanner_push_class_field_initializer (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p) /* scanner context */
|
|
{
|
|
scanner_source_start_t source_start;
|
|
source_start.source_p = context_p->source_p;
|
|
|
|
parser_stack_push (context_p, &source_start, sizeof (scanner_source_start_t));
|
|
parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_FIELD_INITIALIZER);
|
|
|
|
scanner_literal_pool_t *literal_pool_p;
|
|
literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_CLASS_FIELD);
|
|
literal_pool_p->source_p = context_p->source_p;
|
|
|
|
scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
|
|
} /* scanner_push_class_field_initializer */
|
|
|
|
/**
|
|
* Push the values required for destructuring assignment or binding parsing.
|
|
*/
|
|
void
|
|
scanner_push_destructuring_pattern (parser_context_t *context_p, /**< context */
|
|
scanner_context_t *scanner_context_p, /**< scanner context */
|
|
uint8_t binding_type, /**< type of destructuring binding pattern */
|
|
bool is_nested) /**< nested declaration */
|
|
{
|
|
JERRY_ASSERT (binding_type != SCANNER_BINDING_NONE || !is_nested);
|
|
|
|
scanner_source_start_t source_start;
|
|
source_start.source_p = context_p->source_p;
|
|
|
|
parser_stack_push (context_p, &source_start, sizeof (scanner_source_start_t));
|
|
parser_stack_push_uint8 (context_p, scanner_context_p->binding_type);
|
|
scanner_context_p->binding_type = binding_type;
|
|
|
|
if (SCANNER_NEEDS_BINDING_LIST (binding_type))
|
|
{
|
|
scanner_binding_list_t *binding_list_p;
|
|
binding_list_p = (scanner_binding_list_t *) scanner_malloc (context_p, sizeof (scanner_binding_list_t));
|
|
|
|
binding_list_p->prev_p = scanner_context_p->active_binding_list_p;
|
|
binding_list_p->items_p = NULL;
|
|
binding_list_p->is_nested = is_nested;
|
|
|
|
scanner_context_p->active_binding_list_p = binding_list_p;
|
|
}
|
|
} /* scanner_push_destructuring_pattern */
|
|
|
|
/**
|
|
* Pop binding list.
|
|
*/
|
|
void
|
|
scanner_pop_binding_list (scanner_context_t *scanner_context_p) /**< scanner context */
|
|
{
|
|
scanner_binding_list_t *binding_list_p = scanner_context_p->active_binding_list_p;
|
|
JERRY_ASSERT (binding_list_p != NULL);
|
|
|
|
scanner_binding_item_t *item_p = binding_list_p->items_p;
|
|
scanner_binding_list_t *prev_binding_list_p = binding_list_p->prev_p;
|
|
bool is_nested = binding_list_p->is_nested;
|
|
|
|
scanner_free (binding_list_p, sizeof (scanner_binding_list_t));
|
|
scanner_context_p->active_binding_list_p = prev_binding_list_p;
|
|
|
|
if (!is_nested)
|
|
{
|
|
while (item_p != NULL)
|
|
{
|
|
scanner_binding_item_t *next_p = item_p->next_p;
|
|
|
|
JERRY_ASSERT (item_p->literal_p->type & (SCANNER_LITERAL_IS_LOCAL | SCANNER_LITERAL_IS_ARG));
|
|
|
|
scanner_free (item_p, sizeof (scanner_binding_item_t));
|
|
item_p = next_p;
|
|
}
|
|
return;
|
|
}
|
|
|
|
JERRY_ASSERT (prev_binding_list_p != NULL);
|
|
|
|
while (item_p != NULL)
|
|
{
|
|
scanner_binding_item_t *next_p = item_p->next_p;
|
|
|
|
item_p->next_p = prev_binding_list_p->items_p;
|
|
prev_binding_list_p->items_p = item_p;
|
|
|
|
item_p = next_p;
|
|
}
|
|
} /* scanner_pop_binding_list */
|
|
|
|
/**
|
|
* Append a hole into the literal pool.
|
|
*/
|
|
void
|
|
scanner_append_hole (parser_context_t *context_p, scanner_context_t *scanner_context_p)
|
|
{
|
|
scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p;
|
|
|
|
lexer_lit_location_t *literal_p;
|
|
literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &literal_pool_p->literal_pool);
|
|
|
|
literal_p->char_p = NULL;
|
|
literal_p->length = 0;
|
|
literal_p->type = SCANNER_LITERAL_IS_ARG;
|
|
literal_p->status_flags = LEXER_LIT_LOCATION_NO_OPTS;
|
|
} /* scanner_append_hole */
|
|
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
/**
|
|
* Reverse the scanner info chain after the scanning is completed.
|
|
*/
|
|
void
|
|
scanner_reverse_info_list (parser_context_t *context_p) /**< context */
|
|
{
|
|
scanner_info_t *scanner_info_p = context_p->next_scanner_info_p;
|
|
scanner_info_t *last_scanner_info_p = NULL;
|
|
|
|
if (scanner_info_p->type == SCANNER_TYPE_END)
|
|
{
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
scanner_info_t *next_scanner_info_p = scanner_info_p->next_p;
|
|
scanner_info_p->next_p = last_scanner_info_p;
|
|
|
|
last_scanner_info_p = scanner_info_p;
|
|
scanner_info_p = next_scanner_info_p;
|
|
} while (scanner_info_p->type != SCANNER_TYPE_END);
|
|
|
|
context_p->next_scanner_info_p->next_p = scanner_info_p;
|
|
context_p->next_scanner_info_p = last_scanner_info_p;
|
|
} /* scanner_reverse_info_list */
|
|
|
|
/**
|
|
* Release unused scanner info blocks.
|
|
* This should happen only if an error is occurred.
|
|
*/
|
|
void
|
|
scanner_cleanup (parser_context_t *context_p) /**< context */
|
|
{
|
|
if (context_p->skipped_scanner_info_p != NULL)
|
|
{
|
|
context_p->skipped_scanner_info_end_p->next_p = context_p->next_scanner_info_p;
|
|
context_p->next_scanner_info_p = context_p->skipped_scanner_info_p;
|
|
context_p->skipped_scanner_info_p = NULL;
|
|
}
|
|
|
|
scanner_info_t *scanner_info_p = context_p->next_scanner_info_p;
|
|
|
|
while (scanner_info_p != NULL)
|
|
{
|
|
scanner_info_t *next_scanner_info_p = scanner_info_p->next_p;
|
|
|
|
size_t size = sizeof (scanner_info_t);
|
|
|
|
switch (scanner_info_p->type)
|
|
{
|
|
case SCANNER_TYPE_END:
|
|
{
|
|
scanner_info_p = context_p->active_scanner_info_p;
|
|
continue;
|
|
}
|
|
case SCANNER_TYPE_FUNCTION:
|
|
case SCANNER_TYPE_BLOCK:
|
|
{
|
|
size = scanner_get_stream_size (scanner_info_p, sizeof (scanner_info_t));
|
|
break;
|
|
}
|
|
case SCANNER_TYPE_WHILE:
|
|
case SCANNER_TYPE_FOR_IN:
|
|
#if JERRY_ESNEXT
|
|
case SCANNER_TYPE_FOR_OF:
|
|
#endif /* JERRY_ESNEXT */
|
|
case SCANNER_TYPE_CASE:
|
|
#if JERRY_ESNEXT
|
|
case SCANNER_TYPE_INITIALIZER:
|
|
case SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END:
|
|
case SCANNER_TYPE_CLASS_STATIC_BLOCK_END:
|
|
#endif /* JERRY_ESNEXT */
|
|
{
|
|
size = sizeof (scanner_location_info_t);
|
|
break;
|
|
}
|
|
case SCANNER_TYPE_FOR:
|
|
{
|
|
size = sizeof (scanner_for_info_t);
|
|
break;
|
|
}
|
|
case SCANNER_TYPE_SWITCH:
|
|
{
|
|
scanner_release_switch_cases (((scanner_switch_info_t *) scanner_info_p)->case_p);
|
|
size = sizeof (scanner_switch_info_t);
|
|
break;
|
|
}
|
|
#if JERRY_ESNEXT
|
|
case SCANNER_TYPE_CLASS_CONSTRUCTOR:
|
|
{
|
|
scanner_release_private_fields (((scanner_class_info_t *) scanner_info_p)->members);
|
|
size = sizeof (scanner_class_info_t);
|
|
break;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
default:
|
|
{
|
|
#if JERRY_ESNEXT
|
|
JERRY_ASSERT (
|
|
scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS || scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS
|
|
|| scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION || scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED
|
|
|| scanner_info_p->type == SCANNER_TYPE_ERR_ASYNC_FUNCTION
|
|
|| scanner_info_p->type == SCANNER_TYPE_EXPORT_MODULE_SPECIFIER);
|
|
#else /* !JERRY_ESNEXT */
|
|
JERRY_ASSERT (scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS);
|
|
#endif /* JERRY_ESNEXT */
|
|
break;
|
|
}
|
|
}
|
|
|
|
scanner_free (scanner_info_p, size);
|
|
scanner_info_p = next_scanner_info_p;
|
|
}
|
|
|
|
context_p->next_scanner_info_p = NULL;
|
|
context_p->active_scanner_info_p = NULL;
|
|
} /* scanner_cleanup */
|
|
|
|
/**
|
|
* Checks whether a context needs to be created for a block.
|
|
*
|
|
* @return true - if context is needed,
|
|
* false - otherwise
|
|
*/
|
|
bool
|
|
scanner_is_context_needed (parser_context_t *context_p, /**< context */
|
|
parser_check_context_type_t check_type) /**< context type */
|
|
{
|
|
scanner_info_t *info_p = context_p->next_scanner_info_p;
|
|
const uint8_t *data_p = (const uint8_t *) (info_p + 1);
|
|
|
|
JERRY_UNUSED (check_type);
|
|
|
|
#if JERRY_ESNEXT
|
|
JERRY_ASSERT ((check_type == PARSER_CHECK_BLOCK_CONTEXT ? info_p->type == SCANNER_TYPE_BLOCK
|
|
: info_p->type == SCANNER_TYPE_FUNCTION));
|
|
|
|
uint32_t scope_stack_reg_top =
|
|
(check_type != PARSER_CHECK_GLOBAL_CONTEXT ? context_p->scope_stack_reg_top : 1); /* block result */
|
|
#else /* !JERRY_ESNEXT */
|
|
JERRY_ASSERT (check_type == PARSER_CHECK_BLOCK_CONTEXT);
|
|
JERRY_ASSERT (info_p->type == SCANNER_TYPE_BLOCK);
|
|
|
|
uint32_t scope_stack_reg_top = context_p->scope_stack_reg_top;
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
while (data_p[0] != SCANNER_STREAM_TYPE_END)
|
|
{
|
|
uint8_t data = data_p[0];
|
|
|
|
#if JERRY_ESNEXT
|
|
uint32_t type = data & SCANNER_STREAM_TYPE_MASK;
|
|
|
|
if (JERRY_UNLIKELY (check_type == PARSER_CHECK_FUNCTION_CONTEXT))
|
|
{
|
|
if (JERRY_UNLIKELY (type == SCANNER_STREAM_TYPE_HOLE))
|
|
{
|
|
data_p++;
|
|
continue;
|
|
}
|
|
|
|
if (JERRY_UNLIKELY (SCANNER_STREAM_TYPE_IS_ARGUMENTS (type)))
|
|
{
|
|
if ((data & SCANNER_STREAM_NO_REG) || scope_stack_reg_top >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
scope_stack_reg_top++;
|
|
data_p++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
if (check_type == PARSER_CHECK_BLOCK_CONTEXT)
|
|
{
|
|
JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_LET
|
|
|| type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_LOCAL
|
|
|| type == SCANNER_STREAM_TYPE_FUNC);
|
|
}
|
|
else if (check_type == PARSER_CHECK_GLOBAL_CONTEXT)
|
|
{
|
|
#if JERRY_MODULE_SYSTEM
|
|
const bool is_import = (type == SCANNER_STREAM_TYPE_IMPORT);
|
|
#else /* !JERRY_MODULE_SYSTEM */
|
|
const bool is_import = true;
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
|
|
/* FIXME: a private declarative lexical environment should always be present
|
|
* for modules. Remove SCANNER_STREAM_TYPE_IMPORT after it is implemented. */
|
|
JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_LET
|
|
|| type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_FUNC || is_import);
|
|
|
|
/* Only let/const can be stored in registers */
|
|
JERRY_ASSERT ((data & SCANNER_STREAM_NO_REG)
|
|
|| (type == SCANNER_STREAM_TYPE_FUNC && (context_p->global_status_flags & ECMA_PARSE_DIRECT_EVAL))
|
|
|| type == SCANNER_STREAM_TYPE_LET || type == SCANNER_STREAM_TYPE_CONST);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (check_type == PARSER_CHECK_FUNCTION_CONTEXT);
|
|
|
|
JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_LET
|
|
|| type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_LOCAL
|
|
|| type == SCANNER_STREAM_TYPE_ARG || type == SCANNER_STREAM_TYPE_ARG_VAR
|
|
|| type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR
|
|
|| type == SCANNER_STREAM_TYPE_ARG_FUNC || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC
|
|
|| type == SCANNER_STREAM_TYPE_FUNC);
|
|
}
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
#else /* !JERRY_ESNEXT */
|
|
JERRY_ASSERT ((data & SCANNER_STREAM_TYPE_MASK) == SCANNER_STREAM_TYPE_VAR);
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (!(data & SCANNER_STREAM_UINT16_DIFF))
|
|
{
|
|
if (data_p[2] != 0)
|
|
{
|
|
data_p += 2 + 1;
|
|
}
|
|
else
|
|
{
|
|
data_p += 2 + 1 + sizeof (const uint8_t *);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data_p += 2 + 2;
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
#if JERRY_MODULE_SYSTEM
|
|
const bool is_import = (type == SCANNER_STREAM_TYPE_IMPORT);
|
|
#else /* !JERRY_MODULE_SYSTEM */
|
|
const bool is_import = false;
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
|
|
if (JERRY_UNLIKELY (check_type == PARSER_CHECK_GLOBAL_CONTEXT)
|
|
&& (type == SCANNER_STREAM_TYPE_VAR
|
|
|| (type == SCANNER_STREAM_TYPE_FUNC && !(context_p->global_status_flags & ECMA_PARSE_EVAL)) || is_import))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (JERRY_UNLIKELY (check_type == PARSER_CHECK_FUNCTION_CONTEXT))
|
|
{
|
|
if (SCANNER_STREAM_TYPE_IS_ARG_FUNC (type) || type == SCANNER_STREAM_TYPE_ARG_VAR
|
|
|| type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR)
|
|
{
|
|
/* The return value is true, if the variable is stored in the lexical environment
|
|
* or all registers have already been used for function arguments. This can be
|
|
* inprecise in the latter case, but this is a very rare corner case. A more
|
|
* sophisticated check would require to decode the literal. */
|
|
if ((data & SCANNER_STREAM_NO_REG) || scope_stack_reg_top >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
|
|
{
|
|
return true;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (SCANNER_STREAM_TYPE_IS_ARG (type))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if ((data & SCANNER_STREAM_NO_REG) || scope_stack_reg_top >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
scope_stack_reg_top++;
|
|
}
|
|
|
|
return false;
|
|
} /* scanner_is_context_needed */
|
|
|
|
#if JERRY_ESNEXT
|
|
|
|
/**
|
|
* Try to scan/parse the ".target" part in the "new.target" expression.
|
|
*
|
|
* Upon exiting with "true" the current token will point to the "target"
|
|
* literal.
|
|
*
|
|
* If the "target" literal is not after the "new." then a scanner/parser
|
|
* error will be raised.
|
|
*
|
|
* @returns true if the ".target" part was found
|
|
* false if there is no "." after the new.
|
|
*/
|
|
bool
|
|
scanner_try_scan_new_target (parser_context_t *context_p) /**< parser/scanner context */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_NEW);
|
|
|
|
if (lexer_check_next_character (context_p, LIT_CHAR_DOT))
|
|
{
|
|
lexer_next_token (context_p);
|
|
if (context_p->token.type != LEXER_DOT)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
if (!lexer_token_is_identifier (context_p, "target", 6))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_EXPECTED);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
} /* scanner_try_scan_new_target */
|
|
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
/**
|
|
* Description of "arguments" literal string.
|
|
*/
|
|
const lexer_lit_location_t lexer_arguments_literal = { (const uint8_t *) "arguments",
|
|
9,
|
|
LEXER_IDENT_LITERAL,
|
|
LEXER_LIT_LOCATION_IS_ASCII };
|
|
|
|
/**
|
|
* Create an unused literal.
|
|
*/
|
|
static void
|
|
scanner_create_unused_literal (parser_context_t *context_p, /**< context */
|
|
uint8_t status_flags) /**< initial status flags */
|
|
{
|
|
if (JERRY_UNLIKELY (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED);
|
|
}
|
|
|
|
lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool);
|
|
|
|
literal_p->type = LEXER_UNUSED_LITERAL;
|
|
literal_p->status_flags = status_flags;
|
|
|
|
context_p->literal_count++;
|
|
} /* scanner_create_unused_literal */
|
|
|
|
#if JERRY_ESNEXT
|
|
/**
|
|
* Emit checks for redeclared bindings in the global lexical scope.
|
|
*/
|
|
void
|
|
scanner_check_variables (parser_context_t *context_p) /**< context */
|
|
{
|
|
scanner_info_t *info_p = context_p->next_scanner_info_p;
|
|
const uint8_t *next_data_p = (const uint8_t *) (info_p + 1);
|
|
lexer_lit_location_t literal;
|
|
|
|
JERRY_ASSERT (info_p->type == SCANNER_TYPE_FUNCTION);
|
|
|
|
literal.char_p = info_p->source_p - 1;
|
|
|
|
while (next_data_p[0] != SCANNER_STREAM_TYPE_END)
|
|
{
|
|
uint32_t type = next_data_p[0] & SCANNER_STREAM_TYPE_MASK;
|
|
const uint8_t *data_p = next_data_p;
|
|
|
|
JERRY_ASSERT (type != SCANNER_STREAM_TYPE_HOLE && !SCANNER_STREAM_TYPE_IS_ARG (type)
|
|
&& !SCANNER_STREAM_TYPE_IS_ARG_FUNC (type));
|
|
JERRY_ASSERT (data_p[0] & SCANNER_STREAM_NO_REG);
|
|
|
|
if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF))
|
|
{
|
|
if (data_p[2] != 0)
|
|
{
|
|
literal.char_p += data_p[2];
|
|
next_data_p += 2 + 1;
|
|
}
|
|
else
|
|
{
|
|
memcpy (&literal.char_p, data_p + 2 + 1, sizeof (uintptr_t));
|
|
next_data_p += 2 + 1 + sizeof (uintptr_t);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8;
|
|
|
|
if (diff <= (intptr_t) UINT8_MAX)
|
|
{
|
|
diff = -diff;
|
|
}
|
|
|
|
literal.char_p += diff;
|
|
next_data_p += 2 + 2;
|
|
}
|
|
|
|
literal.length = data_p[1];
|
|
literal.type = LEXER_IDENT_LITERAL;
|
|
literal.status_flags =
|
|
((data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? LEXER_LIT_LOCATION_HAS_ESCAPE : LEXER_LIT_LOCATION_NO_OPTS);
|
|
|
|
lexer_construct_literal_object (context_p, &literal, LEXER_NEW_IDENT_LITERAL);
|
|
literal.char_p += data_p[1];
|
|
|
|
#if JERRY_MODULE_SYSTEM
|
|
if (type == SCANNER_STREAM_TYPE_IMPORT)
|
|
{
|
|
continue;
|
|
}
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
|
|
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED;
|
|
|
|
uint16_t opcode;
|
|
if (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_FUNC)
|
|
{
|
|
opcode = CBC_CHECK_VAR;
|
|
}
|
|
else
|
|
{
|
|
opcode = CBC_CHECK_LET;
|
|
}
|
|
|
|
parser_emit_cbc_literal (context_p, opcode, context_p->lit_object.index);
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
} /* scanner_check_variables */
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
/**
|
|
* Create and/or initialize var/let/const/function/etc. variables.
|
|
*/
|
|
void
|
|
scanner_create_variables (parser_context_t *context_p, /**< context */
|
|
uint32_t option_flags) /**< combination of scanner_create_variables_flags_t bits */
|
|
{
|
|
scanner_info_t *info_p = context_p->next_scanner_info_p;
|
|
const uint8_t *next_data_p = (const uint8_t *) (info_p + 1);
|
|
uint8_t info_type = info_p->type;
|
|
uint8_t info_u8_arg = info_p->u8_arg;
|
|
lexer_lit_location_t literal;
|
|
parser_scope_stack_t *scope_stack_p;
|
|
parser_scope_stack_t *scope_stack_end_p;
|
|
|
|
JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION || info_type == SCANNER_TYPE_BLOCK);
|
|
JERRY_ASSERT (!(option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_ARGS)
|
|
|| !(option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY));
|
|
JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION
|
|
|| !(option_flags & (SCANNER_CREATE_VARS_IS_FUNCTION_ARGS | SCANNER_CREATE_VARS_IS_FUNCTION_BODY)));
|
|
|
|
uint32_t scope_stack_reg_top = context_p->scope_stack_reg_top;
|
|
|
|
if (info_type == SCANNER_TYPE_FUNCTION && !(option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY))
|
|
{
|
|
JERRY_ASSERT (context_p->scope_stack_p == NULL);
|
|
|
|
size_t stack_size = info_p->u16_arg * sizeof (parser_scope_stack_t);
|
|
context_p->scope_stack_size = info_p->u16_arg;
|
|
|
|
scope_stack_p = NULL;
|
|
|
|
if (stack_size > 0)
|
|
{
|
|
scope_stack_p = (parser_scope_stack_t *) parser_malloc (context_p, stack_size);
|
|
}
|
|
|
|
context_p->scope_stack_p = scope_stack_p;
|
|
scope_stack_end_p = scope_stack_p + context_p->scope_stack_size;
|
|
|
|
if (option_flags & (SCANNER_CREATE_VARS_IS_SCRIPT | SCANNER_CREATE_VARS_IS_MODULE))
|
|
{
|
|
scope_stack_reg_top++; /* block result */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->scope_stack_p != NULL || context_p->scope_stack_size == 0);
|
|
|
|
scope_stack_p = context_p->scope_stack_p;
|
|
scope_stack_end_p = scope_stack_p + context_p->scope_stack_size;
|
|
scope_stack_p += context_p->scope_stack_top;
|
|
}
|
|
|
|
literal.char_p = info_p->source_p - 1;
|
|
|
|
while (next_data_p[0] != SCANNER_STREAM_TYPE_END)
|
|
{
|
|
uint32_t type = next_data_p[0] & SCANNER_STREAM_TYPE_MASK;
|
|
const uint8_t *data_p = next_data_p;
|
|
|
|
JERRY_ASSERT ((option_flags & (SCANNER_CREATE_VARS_IS_FUNCTION_BODY | SCANNER_CREATE_VARS_IS_FUNCTION_ARGS))
|
|
|| (type != SCANNER_STREAM_TYPE_HOLE && !SCANNER_STREAM_TYPE_IS_ARG (type)
|
|
&& !SCANNER_STREAM_TYPE_IS_ARG_FUNC (type)));
|
|
|
|
#if JERRY_MODULE_SYSTEM
|
|
JERRY_ASSERT (type != SCANNER_STREAM_TYPE_IMPORT || (data_p[0] & SCANNER_STREAM_NO_REG));
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
|
|
if (JERRY_UNLIKELY (type == SCANNER_STREAM_TYPE_HOLE))
|
|
{
|
|
JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION);
|
|
next_data_p++;
|
|
|
|
if (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
uint8_t mask = SCANNER_FUNCTION_ARGUMENTS_NEEDED | SCANNER_FUNCTION_HAS_COMPLEX_ARGUMENT;
|
|
|
|
if (!(context_p->status_flags & PARSER_IS_STRICT) && (info_u8_arg & mask) == SCANNER_FUNCTION_ARGUMENTS_NEEDED)
|
|
{
|
|
scanner_create_unused_literal (context_p, LEXER_FLAG_FUNCTION_ARGUMENT);
|
|
}
|
|
|
|
if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
|
|
{
|
|
scope_stack_reg_top++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (JERRY_UNLIKELY (SCANNER_STREAM_TYPE_IS_ARGUMENTS (type)))
|
|
{
|
|
JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION);
|
|
next_data_p++;
|
|
|
|
if (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
context_p->status_flags |= PARSER_ARGUMENTS_NEEDED;
|
|
|
|
if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p))
|
|
{
|
|
JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK);
|
|
parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED);
|
|
}
|
|
|
|
lexer_construct_literal_object (context_p, &lexer_arguments_literal, LEXER_NEW_IDENT_LITERAL);
|
|
scope_stack_p->map_from = context_p->lit_object.index;
|
|
|
|
uint16_t map_to;
|
|
|
|
if (!(data_p[0] & SCANNER_STREAM_NO_REG) && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
|
|
{
|
|
map_to = (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top);
|
|
|
|
#if JERRY_ESNEXT
|
|
scope_stack_p->map_to = (uint16_t) (scope_stack_reg_top + 1);
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
scope_stack_reg_top++;
|
|
}
|
|
else
|
|
{
|
|
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED;
|
|
map_to = context_p->lit_object.index;
|
|
|
|
context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;
|
|
|
|
#if JERRY_ESNEXT
|
|
if (data_p[0] & SCANNER_STREAM_LOCAL_ARGUMENTS)
|
|
{
|
|
context_p->status_flags |= PARSER_LEXICAL_BLOCK_NEEDED;
|
|
}
|
|
|
|
scope_stack_p->map_to = 0;
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
|
|
#if !JERRY_ESNEXT
|
|
scope_stack_p->map_to = map_to;
|
|
#endif /* !JERRY_ESNEXT */
|
|
scope_stack_p++;
|
|
|
|
#if JERRY_PARSER_DUMP_BYTE_CODE
|
|
context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p);
|
|
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */
|
|
|
|
parser_emit_cbc_ext_literal (context_p, CBC_EXT_CREATE_ARGUMENTS, map_to);
|
|
|
|
#if JERRY_ESNEXT
|
|
if (type == SCANNER_STREAM_TYPE_ARGUMENTS_FUNC)
|
|
{
|
|
if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p))
|
|
{
|
|
JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK);
|
|
parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED);
|
|
}
|
|
|
|
scope_stack_p->map_from = PARSER_SCOPE_STACK_FUNC;
|
|
scope_stack_p->map_to = context_p->literal_count;
|
|
scope_stack_p++;
|
|
|
|
scanner_create_unused_literal (context_p, 0);
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_ARGS)
|
|
{
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
JERRY_ASSERT (context_p->scope_stack_size != 0);
|
|
|
|
if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF))
|
|
{
|
|
if (data_p[2] != 0)
|
|
{
|
|
literal.char_p += data_p[2];
|
|
next_data_p += 2 + 1;
|
|
}
|
|
else
|
|
{
|
|
memcpy (&literal.char_p, data_p + 2 + 1, sizeof (uintptr_t));
|
|
next_data_p += 2 + 1 + sizeof (uintptr_t);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8;
|
|
|
|
if (diff <= (intptr_t) UINT8_MAX)
|
|
{
|
|
diff = -diff;
|
|
}
|
|
|
|
literal.char_p += diff;
|
|
next_data_p += 2 + 2;
|
|
}
|
|
|
|
if (SCANNER_STREAM_TYPE_IS_ARG (type))
|
|
{
|
|
if (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY)
|
|
{
|
|
#if JERRY_ESNEXT
|
|
if ((context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED)
|
|
&& (type == SCANNER_STREAM_TYPE_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR))
|
|
{
|
|
literal.length = data_p[1];
|
|
literal.type = LEXER_IDENT_LITERAL;
|
|
literal.status_flags =
|
|
((data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? LEXER_LIT_LOCATION_HAS_ESCAPE : LEXER_LIT_LOCATION_NO_OPTS);
|
|
|
|
/* Literal must be exists. */
|
|
lexer_construct_literal_object (context_p, &literal, LEXER_IDENT_LITERAL);
|
|
|
|
if (context_p->lit_object.index < PARSER_REGISTER_START)
|
|
{
|
|
parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_COPY_FROM_ARG);
|
|
}
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
literal.char_p += data_p[1];
|
|
continue;
|
|
}
|
|
}
|
|
else if ((option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_ARGS) && !SCANNER_STREAM_TYPE_IS_ARG_FUNC (type))
|
|
{
|
|
/* Function arguments must come first. */
|
|
break;
|
|
}
|
|
|
|
literal.length = data_p[1];
|
|
literal.type = LEXER_IDENT_LITERAL;
|
|
literal.status_flags =
|
|
((data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? LEXER_LIT_LOCATION_HAS_ESCAPE : LEXER_LIT_LOCATION_NO_OPTS);
|
|
|
|
lexer_construct_literal_object (context_p, &literal, LEXER_NEW_IDENT_LITERAL);
|
|
literal.char_p += data_p[1];
|
|
|
|
if (SCANNER_STREAM_TYPE_IS_ARG_FUNC (type) && (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY))
|
|
{
|
|
JERRY_ASSERT (scope_stack_p >= context_p->scope_stack_p + 2);
|
|
JERRY_ASSERT (context_p->status_flags & PARSER_IS_FUNCTION);
|
|
#if JERRY_ESNEXT
|
|
JERRY_ASSERT (!(context_p->status_flags & PARSER_FUNCTION_IS_PARSING_ARGS));
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
parser_scope_stack_t *function_map_p = scope_stack_p - 2;
|
|
uint16_t literal_index = context_p->lit_object.index;
|
|
|
|
while (literal_index != function_map_p->map_from)
|
|
{
|
|
function_map_p--;
|
|
|
|
JERRY_ASSERT (function_map_p >= context_p->scope_stack_p);
|
|
}
|
|
|
|
JERRY_ASSERT (function_map_p[1].map_from == PARSER_SCOPE_STACK_FUNC);
|
|
|
|
cbc_opcode_t opcode = CBC_SET_VAR_FUNC;
|
|
|
|
#if JERRY_ESNEXT
|
|
if (JERRY_UNLIKELY (context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED)
|
|
&& (function_map_p[0].map_to & PARSER_SCOPE_STACK_REGISTER_MASK) == 0)
|
|
{
|
|
opcode = CBC_INIT_ARG_OR_FUNC;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
parser_emit_cbc_literal_value (context_p,
|
|
(uint16_t) opcode,
|
|
function_map_p[1].map_to,
|
|
scanner_decode_map_to (function_map_p));
|
|
continue;
|
|
}
|
|
|
|
if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p))
|
|
{
|
|
JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK);
|
|
parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED);
|
|
}
|
|
|
|
scope_stack_p->map_from = context_p->lit_object.index;
|
|
|
|
#if JERRY_ESNEXT
|
|
if (info_type == SCANNER_TYPE_FUNCTION)
|
|
{
|
|
if (type != SCANNER_STREAM_TYPE_LET
|
|
#if JERRY_MODULE_SYSTEM
|
|
&& type != SCANNER_STREAM_TYPE_IMPORT
|
|
#endif /* JERRY_MODULE_SYSTEM */
|
|
&& type != SCANNER_STREAM_TYPE_CONST)
|
|
{
|
|
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_GLOBAL;
|
|
}
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
uint16_t map_to;
|
|
uint16_t func_init_opcode = CBC_INIT_ARG_OR_FUNC;
|
|
|
|
if (!(data_p[0] & SCANNER_STREAM_NO_REG) && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
|
|
{
|
|
map_to = (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top);
|
|
|
|
#if JERRY_ESNEXT
|
|
scope_stack_p->map_to = (uint16_t) (scope_stack_reg_top + 1);
|
|
#else /* !JERRY_ESNEXT */
|
|
scope_stack_p->map_to = map_to;
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
scope_stack_reg_top++;
|
|
#if JERRY_ESNEXT
|
|
switch (type)
|
|
{
|
|
case SCANNER_STREAM_TYPE_CONST:
|
|
{
|
|
scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_CONST_REG;
|
|
/* FALLTHRU */
|
|
}
|
|
case SCANNER_STREAM_TYPE_LET:
|
|
case SCANNER_STREAM_TYPE_ARG:
|
|
case SCANNER_STREAM_TYPE_ARG_VAR:
|
|
case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG:
|
|
case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR:
|
|
case SCANNER_STREAM_TYPE_ARG_FUNC:
|
|
case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC:
|
|
{
|
|
scope_stack_p->map_to |= PARSER_SCOPE_STACK_NO_FUNCTION_COPY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
func_init_opcode = CBC_SET_VAR_FUNC;
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
else
|
|
{
|
|
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED;
|
|
map_to = context_p->lit_object.index;
|
|
|
|
#if JERRY_ESNEXT
|
|
uint16_t scope_stack_map_to = 0;
|
|
#else /* !JERRY_ESNEXT */
|
|
scope_stack_p->map_to = map_to;
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (info_type == SCANNER_TYPE_FUNCTION)
|
|
{
|
|
context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
#if JERRY_ESNEXT
|
|
case SCANNER_STREAM_TYPE_LET:
|
|
case SCANNER_STREAM_TYPE_CONST:
|
|
case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG:
|
|
case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR:
|
|
case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC:
|
|
{
|
|
scope_stack_map_to |= PARSER_SCOPE_STACK_NO_FUNCTION_COPY;
|
|
|
|
if (!(data_p[0] & SCANNER_STREAM_EARLY_CREATE))
|
|
{
|
|
break;
|
|
}
|
|
scope_stack_map_to |= PARSER_SCOPE_STACK_IS_LOCAL_CREATED;
|
|
/* FALLTHRU */
|
|
}
|
|
case SCANNER_STREAM_TYPE_LOCAL:
|
|
#endif /* JERRY_ESNEXT */
|
|
case SCANNER_STREAM_TYPE_VAR:
|
|
{
|
|
#if JERRY_PARSER_DUMP_BYTE_CODE
|
|
context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p);
|
|
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */
|
|
|
|
#if JERRY_ESNEXT
|
|
uint16_t opcode;
|
|
|
|
switch (type)
|
|
{
|
|
case SCANNER_STREAM_TYPE_LET:
|
|
{
|
|
opcode = CBC_CREATE_LET;
|
|
break;
|
|
}
|
|
case SCANNER_STREAM_TYPE_CONST:
|
|
{
|
|
opcode = CBC_CREATE_CONST;
|
|
break;
|
|
}
|
|
case SCANNER_STREAM_TYPE_VAR:
|
|
{
|
|
opcode = CBC_CREATE_VAR;
|
|
|
|
if (option_flags & SCANNER_CREATE_VARS_IS_SCRIPT)
|
|
{
|
|
opcode = CBC_CREATE_VAR_EVAL;
|
|
|
|
if ((context_p->global_status_flags & ECMA_PARSE_FUNCTION_CONTEXT)
|
|
&& !(context_p->status_flags & PARSER_IS_STRICT))
|
|
{
|
|
opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_CREATE_VAR_EVAL);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
JERRY_ASSERT (type == SCANNER_STREAM_TYPE_LOCAL || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG
|
|
|| type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR
|
|
|| type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC);
|
|
|
|
opcode = CBC_CREATE_LOCAL;
|
|
break;
|
|
}
|
|
}
|
|
#else /* !JERRY_ESNEXT */
|
|
uint16_t opcode = ((option_flags & SCANNER_CREATE_VARS_IS_SCRIPT) ? CBC_CREATE_VAR_EVAL : CBC_CREATE_VAR);
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
parser_emit_cbc_literal (context_p, opcode, map_to);
|
|
break;
|
|
}
|
|
case SCANNER_STREAM_TYPE_ARG:
|
|
#if JERRY_ESNEXT
|
|
case SCANNER_STREAM_TYPE_ARG_VAR:
|
|
#endif /* JERRY_ESNEXT */
|
|
case SCANNER_STREAM_TYPE_ARG_FUNC:
|
|
{
|
|
#if JERRY_PARSER_DUMP_BYTE_CODE
|
|
context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p);
|
|
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */
|
|
|
|
#if JERRY_ESNEXT
|
|
scope_stack_map_to |= PARSER_SCOPE_STACK_NO_FUNCTION_COPY;
|
|
|
|
/* Argument initializers of functions with simple arguments (e.g. function f(a,b,a) {}) are
|
|
* generated here. The other initializers are handled by parser_parse_function_arguments(). */
|
|
if (!(info_u8_arg & SCANNER_FUNCTION_HAS_COMPLEX_ARGUMENT))
|
|
{
|
|
#endif /* JERRY_ESNEXT */
|
|
parser_emit_cbc_literal_value (context_p,
|
|
CBC_INIT_ARG_OR_FUNC,
|
|
(uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top),
|
|
map_to);
|
|
#if JERRY_ESNEXT
|
|
}
|
|
else if (data_p[0] & SCANNER_STREAM_EARLY_CREATE)
|
|
{
|
|
parser_emit_cbc_literal (context_p, CBC_CREATE_LOCAL, map_to);
|
|
scope_stack_map_to |= PARSER_SCOPE_STACK_IS_LOCAL_CREATED;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
|
|
{
|
|
scope_stack_reg_top++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if JERRY_ESNEXT
|
|
scope_stack_p->map_to = scope_stack_map_to;
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
|
|
scope_stack_p++;
|
|
|
|
if (!SCANNER_STREAM_TYPE_IS_FUNCTION (type))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p))
|
|
{
|
|
JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK);
|
|
parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED);
|
|
}
|
|
|
|
#if JERRY_PARSER_DUMP_BYTE_CODE
|
|
context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p);
|
|
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */
|
|
|
|
if (!SCANNER_STREAM_TYPE_IS_ARG_FUNC (type))
|
|
{
|
|
if (func_init_opcode == CBC_INIT_ARG_OR_FUNC && (option_flags & SCANNER_CREATE_VARS_IS_SCRIPT))
|
|
{
|
|
#if JERRY_ESNEXT
|
|
literal.char_p -= data_p[1];
|
|
|
|
if (!scanner_scope_find_lexical_declaration (context_p, &literal))
|
|
{
|
|
func_init_opcode = CBC_CREATE_VAR_FUNC_EVAL;
|
|
|
|
if (context_p->global_status_flags & ECMA_PARSE_FUNCTION_CONTEXT)
|
|
{
|
|
func_init_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_CREATE_VAR_FUNC_EVAL);
|
|
}
|
|
}
|
|
literal.char_p += data_p[1];
|
|
#else /* !JERRY_ESNEXT */
|
|
func_init_opcode = CBC_CREATE_VAR_FUNC_EVAL;
|
|
#endif /* JERRY_ESNEXT */
|
|
}
|
|
|
|
parser_emit_cbc_literal_value (context_p, func_init_opcode, context_p->literal_count, map_to);
|
|
}
|
|
|
|
scope_stack_p->map_from = PARSER_SCOPE_STACK_FUNC;
|
|
scope_stack_p->map_to = context_p->literal_count;
|
|
scope_stack_p++;
|
|
|
|
scanner_create_unused_literal (context_p, 0);
|
|
}
|
|
|
|
context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p);
|
|
context_p->scope_stack_reg_top = (uint16_t) scope_stack_reg_top;
|
|
|
|
#if JERRY_ESNEXT
|
|
if (info_type == SCANNER_TYPE_FUNCTION)
|
|
{
|
|
context_p->scope_stack_global_end = context_p->scope_stack_top;
|
|
}
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
if (context_p->register_count < scope_stack_reg_top)
|
|
{
|
|
context_p->register_count = (uint16_t) scope_stack_reg_top;
|
|
}
|
|
|
|
if (!(option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_ARGS))
|
|
{
|
|
scanner_release_next (context_p, (size_t) (next_data_p + 1 - ((const uint8_t *) info_p)));
|
|
}
|
|
parser_flush_cbc (context_p);
|
|
} /* scanner_create_variables */
|
|
|
|
/**
|
|
* Get location from context.
|
|
*/
|
|
extern inline void JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_get_location (scanner_location_t *location_p, /**< location */
|
|
parser_context_t *context_p) /**< context */
|
|
{
|
|
location_p->source_p = context_p->source_p;
|
|
location_p->line = context_p->line;
|
|
location_p->column = context_p->column;
|
|
} /* scanner_get_location */
|
|
|
|
/**
|
|
* Set context location.
|
|
*/
|
|
extern inline void JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_set_location (parser_context_t *context_p, /**< context */
|
|
scanner_location_t *location_p) /**< location */
|
|
{
|
|
context_p->source_p = location_p->source_p;
|
|
context_p->line = location_p->line;
|
|
context_p->column = location_p->column;
|
|
} /* scanner_set_location */
|
|
|
|
/**
|
|
* Get the real map_to value.
|
|
*/
|
|
extern inline uint16_t JERRY_ATTR_ALWAYS_INLINE
|
|
scanner_decode_map_to (parser_scope_stack_t *stack_item_p) /**< scope stack item */
|
|
{
|
|
JERRY_ASSERT (stack_item_p->map_from != PARSER_SCOPE_STACK_FUNC);
|
|
|
|
#if JERRY_ESNEXT
|
|
uint16_t value = (stack_item_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK);
|
|
return (value == 0) ? stack_item_p->map_from : (uint16_t) (value + (PARSER_REGISTER_START - 1));
|
|
#else /* !JERRY_ESNEXT */
|
|
return stack_item_p->map_to;
|
|
#endif /* JERRY_ESNEXT */
|
|
} /* scanner_decode_map_to */
|
|
|
|
#if JERRY_ESNEXT
|
|
|
|
/**
|
|
* Find the given literal index in the scope stack
|
|
* and save it the constant literal pool if the literal is register stored
|
|
*
|
|
* @return given literal index - if literal corresponds to this index is not register stored
|
|
* literal index on which literal index has been mapped - otherwise
|
|
*/
|
|
uint16_t
|
|
scanner_save_literal (parser_context_t *context_p, /**< context */
|
|
uint16_t literal_index) /**< literal index */
|
|
{
|
|
if (literal_index >= PARSER_REGISTER_START)
|
|
{
|
|
literal_index = (uint16_t) (literal_index - (PARSER_REGISTER_START - 1));
|
|
|
|
parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top;
|
|
|
|
do
|
|
{
|
|
/* Registers must be found in the scope stack. */
|
|
JERRY_ASSERT (scope_stack_p > context_p->scope_stack_p);
|
|
scope_stack_p--;
|
|
} while (scope_stack_p->map_from == PARSER_SCOPE_STACK_FUNC
|
|
|| literal_index != (scope_stack_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK));
|
|
|
|
literal_index = scope_stack_p->map_from;
|
|
PARSER_GET_LITERAL (literal_index)->status_flags |= LEXER_FLAG_USED;
|
|
}
|
|
|
|
return literal_index;
|
|
} /* scanner_save_literal */
|
|
|
|
/**
|
|
* Checks whether the literal is a const in the current scope.
|
|
*
|
|
* @return true if the literal is a const, false otherwise
|
|
*/
|
|
bool
|
|
scanner_literal_is_const_reg (parser_context_t *context_p, /**< context */
|
|
uint16_t literal_index) /**< literal index */
|
|
{
|
|
if (literal_index < PARSER_REGISTER_START)
|
|
{
|
|
/* Re-assignment of non-register const bindings are detected elsewhere. */
|
|
return false;
|
|
}
|
|
|
|
parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top;
|
|
|
|
literal_index = (uint16_t) (literal_index - (PARSER_REGISTER_START - 1));
|
|
|
|
do
|
|
{
|
|
/* Registers must be found in the scope stack. */
|
|
JERRY_ASSERT (scope_stack_p > context_p->scope_stack_p);
|
|
scope_stack_p--;
|
|
} while (scope_stack_p->map_from == PARSER_SCOPE_STACK_FUNC
|
|
|| literal_index != (scope_stack_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK));
|
|
|
|
return (scope_stack_p->map_to & PARSER_SCOPE_STACK_IS_CONST_REG) != 0;
|
|
} /* scanner_literal_is_const_reg */
|
|
|
|
/**
|
|
* Checks whether the literal is created before.
|
|
*
|
|
* @return true if the literal is created before, false otherwise
|
|
*/
|
|
bool
|
|
scanner_literal_is_created (parser_context_t *context_p, /**< context */
|
|
uint16_t literal_index) /**< literal index */
|
|
{
|
|
JERRY_ASSERT (literal_index < PARSER_REGISTER_START);
|
|
|
|
parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top;
|
|
|
|
do
|
|
{
|
|
/* These literals must be found in the scope stack. */
|
|
JERRY_ASSERT (scope_stack_p > context_p->scope_stack_p);
|
|
scope_stack_p--;
|
|
} while (literal_index != scope_stack_p->map_from);
|
|
|
|
JERRY_ASSERT ((scope_stack_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK) == 0);
|
|
|
|
return (scope_stack_p->map_to & PARSER_SCOPE_STACK_IS_LOCAL_CREATED) != 0;
|
|
} /* scanner_literal_is_created */
|
|
|
|
/**
|
|
* Checks whether the literal exists.
|
|
*
|
|
* @return true if the literal exists, false otherwise
|
|
*/
|
|
bool
|
|
scanner_literal_exists (parser_context_t *context_p, /**< context */
|
|
uint16_t literal_index) /**< literal index */
|
|
{
|
|
JERRY_ASSERT (literal_index < PARSER_REGISTER_START);
|
|
|
|
parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top;
|
|
|
|
while (scope_stack_p-- > context_p->scope_stack_p)
|
|
{
|
|
if (scope_stack_p->map_from != PARSER_SCOPE_STACK_FUNC && scanner_decode_map_to (scope_stack_p) == literal_index)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
} /* scanner_literal_exists */
|
|
|
|
#endif /* JERRY_ESNEXT */
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
* @}
|
|
*/
|
|
|
|
#endif /* JERRY_PARSER */
|